From 420fa8c400b39ff1af90eb598d44785697732e42 Mon Sep 17 00:00:00 2001 From: javanna Date: Fri, 7 Apr 2017 11:11:06 +0200 Subject: [PATCH 001/619] Add ParsedAggregation as base Aggregation impl for high level client ParsedAggregation is the base Aggregation implementation for the high level client, which parses aggs responses into java objects. --- .../aggregations/ParsedAggregation.java | 80 ++++++++++ .../aggregations/ParsedAggregationTests.java | 145 ++++++++++++++++++ 2 files changed, 225 insertions(+) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java new file mode 100644 index 0000000000000..f1f9705214977 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java @@ -0,0 +1,80 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * An implementation of {@link Aggregation} that is parsed from a REST response. + * Serves as a base class for all aggregation implementations that are parsed from REST. + */ +public abstract class ParsedAggregation implements Aggregation, ToXContent { + + //TODO move CommonFields out of InternalAggregation + protected static void declareCommonFields(ObjectParser objectParser) { + objectParser.declareObject((parsedAgg, metadata) -> parsedAgg.metadata.putAll(metadata), + (parser, context) -> parser.map(), InternalAggregation.CommonFields.META); + } + + String name; + final Map metadata = new HashMap<>(); + + @Override + public final String getName() { + return name; + } + + @Override + public final Map getMetaData() { + return Collections.unmodifiableMap(metadata); + } + + /** + * Returns a string representing the type of the aggregation. This type is added to + * the aggregation name in the response, so that it can later be used by REST clients + * to determine the internal type of the aggregation. + */ + //TODO it may make sense to move getType to the Aggregation interface given that we are duplicating it in both implementations + protected abstract String getType(); + + //TODO the only way to avoid duplicating this method is making Aggregation extend ToXContent + //and declare toXContent as a default method in it. Doesn't sound like the right thing to do. + public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { + //TODO move TYPED_KEYS_DELIMITER constant out of InternalAggregation + // Concatenates the type and the name of the aggregation (ex: top_hits#foo) + builder.startObject(String.join(InternalAggregation.TYPED_KEYS_DELIMITER, getType(), name)); + if (metadata.isEmpty() == false) { + builder.field(InternalAggregation.CommonFields.META.getPreferredName()); + builder.map(metadata); + } + doXContentBody(builder, params); + builder.endObject(); + return builder; + } + + protected abstract XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException; +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java new file mode 100644 index 0000000000000..4b75bf1990c58 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java @@ -0,0 +1,145 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.rest.FakeRestRequest; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent; +import static org.hamcrest.CoreMatchers.instanceOf; + +public class ParsedAggregationTests extends ESTestCase { + + //TODO maybe this test will no longer be needed once we have real tests for ParsedAggregation subclasses + public void testParse() throws IOException { + String name = randomAlphaOfLengthBetween(5, 10); + int numMetas = randomIntBetween(0, 5); + Map meta = new HashMap<>(numMetas); + for (int i = 0; i < numMetas; i++) { + meta.put(randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10)); + } + TestInternalAggregation testAgg = new TestInternalAggregation(name, meta); + XContentType xContentType = randomFrom(XContentType.values()); + FakeRestRequest params = new FakeRestRequest.Builder(NamedXContentRegistry.EMPTY) + .withParams(Collections.singletonMap("typed_keys", "true")).build(); + BytesReference bytesAgg = XContentHelper.toXContent(testAgg, xContentType, params, randomBoolean()); + try (XContentParser parser = createParser(xContentType.xContent(), bytesAgg)) { + parser.nextToken(); + assert parser.currentToken() == XContentParser.Token.START_OBJECT; + parser.nextToken(); + assert parser.currentToken() == XContentParser.Token.FIELD_NAME; + String currentName = parser.currentName(); + int i = currentName.indexOf(InternalAggregation.TYPED_KEYS_DELIMITER); + String aggType = currentName.substring(0, i); + String aggName = currentName.substring(i + 1); + Aggregation parsedAgg = parser.namedObject(Aggregation.class, aggType, aggName); + assertThat(parsedAgg, instanceOf(TestParsedAggregation.class)); + assertEquals(testAgg.getName(), parsedAgg.getName()); + assertEquals(testAgg.getMetaData(), parsedAgg.getMetaData()); + BytesReference finalAgg = XContentHelper.toXContent((ToXContent) parsedAgg, xContentType, randomBoolean()); + assertToXContentEquivalent(bytesAgg, finalAgg, xContentType); + } + } + + @Override + protected NamedXContentRegistry xContentRegistry() { + //TODO we may want to un-deprecate this Entry constructor if we are going to use it extensively, which I think we are + NamedXContentRegistry.Entry entry = new NamedXContentRegistry.Entry(Aggregation.class, new ParseField("type"), + (parser, name) -> TestParsedAggregation.fromXContent(parser, (String)name)); + return new NamedXContentRegistry(Collections.singletonList(entry)); + } + + private static class TestParsedAggregation extends ParsedAggregation { + private static ObjectParser PARSER = new ObjectParser<>("testAggParser", TestParsedAggregation::new); + + static { + ParsedAggregation.declareCommonFields(PARSER); + } + + @Override + protected String getType() { + return "type"; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + return builder; + } + + public static TestParsedAggregation fromXContent(XContentParser parser, String name) throws IOException { + TestParsedAggregation parsedAgg = PARSER.parse(parser, null); + parsedAgg.name = name; + return parsedAgg; + } + } + + private static class TestInternalAggregation extends InternalAggregation { + + private TestInternalAggregation(String name, Map metaData) { + super(name, Collections.emptyList(), metaData); + } + + @Override + public String getWriteableName() { + throw new UnsupportedOperationException(); + } + + @Override + protected String getType() { + return "type"; + } + + @Override + protected void doWriteTo(StreamOutput out) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public InternalAggregation doReduce(List aggregations, ReduceContext reduceContext) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getProperty(List path) { + throw new UnsupportedOperationException(); + } + + @Override + public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + return builder; + } + } +} From 67e087088dab9f3bb9ee9f4702d919a19b0ea3eb Mon Sep 17 00:00:00 2001 From: javanna Date: Fri, 7 Apr 2017 12:00:51 +0200 Subject: [PATCH 002/619] add class to suppressions list for line length check --- buildSrc/src/main/resources/checkstyle_suppressions.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 11882b2faa995..ec67f8bb14289 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -1749,6 +1749,7 @@ + @@ -3062,6 +3063,7 @@ + From 306ef086c5218a8ef46debd4bc76c72dd60e85e2 Mon Sep 17 00:00:00 2001 From: javanna Date: Fri, 7 Apr 2017 21:54:46 +0200 Subject: [PATCH 003/619] Align ParsedAggregation meta to InternalAggregation behaviour Empty meta gets printed out, which means that if the request contains an empty meta object, that is returned with the response as well. On the other hand null, meaning when the object is not in the request, is not printed out. ParsedAggregation used to not print out empty metadata, and didn't allow the null value. Aligned behaviour to the existing behaviour from InternalAggregation. --- .../search/aggregations/ParsedAggregation.java | 11 +++++------ .../aggregations/ParsedAggregationTests.java | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java index f1f9705214977..16b58dbf1f7c3 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java @@ -25,7 +25,6 @@ import java.io.IOException; import java.util.Collections; -import java.util.HashMap; import java.util.Map; /** @@ -36,12 +35,12 @@ public abstract class ParsedAggregation implements Aggregation, ToXContent { //TODO move CommonFields out of InternalAggregation protected static void declareCommonFields(ObjectParser objectParser) { - objectParser.declareObject((parsedAgg, metadata) -> parsedAgg.metadata.putAll(metadata), + objectParser.declareObject((parsedAgg, metadata) -> parsedAgg.metadata = Collections.unmodifiableMap(metadata), (parser, context) -> parser.map(), InternalAggregation.CommonFields.META); } String name; - final Map metadata = new HashMap<>(); + Map metadata; @Override public final String getName() { @@ -50,7 +49,7 @@ public final String getName() { @Override public final Map getMetaData() { - return Collections.unmodifiableMap(metadata); + return metadata; } /** @@ -67,9 +66,9 @@ public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params par //TODO move TYPED_KEYS_DELIMITER constant out of InternalAggregation // Concatenates the type and the name of the aggregation (ex: top_hits#foo) builder.startObject(String.join(InternalAggregation.TYPED_KEYS_DELIMITER, getType(), name)); - if (metadata.isEmpty() == false) { + if (this.metadata != null) { builder.field(InternalAggregation.CommonFields.META.getPreferredName()); - builder.map(metadata); + builder.map(this.metadata); } doXContentBody(builder, params); builder.endObject(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java index 4b75bf1990c58..8962ba2195676 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.rest.FakeRestRequest; @@ -46,15 +47,18 @@ public class ParsedAggregationTests extends ESTestCase { //TODO maybe this test will no longer be needed once we have real tests for ParsedAggregation subclasses public void testParse() throws IOException { String name = randomAlphaOfLengthBetween(5, 10); - int numMetas = randomIntBetween(0, 5); - Map meta = new HashMap<>(numMetas); - for (int i = 0; i < numMetas; i++) { - meta.put(randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10)); + Map meta = null; + if (randomBoolean()) { + int numMetas = randomIntBetween(0, 5); + meta = new HashMap<>(numMetas); + for (int i = 0; i < numMetas; i++) { + meta.put(randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10)); + } } TestInternalAggregation testAgg = new TestInternalAggregation(name, meta); XContentType xContentType = randomFrom(XContentType.values()); FakeRestRequest params = new FakeRestRequest.Builder(NamedXContentRegistry.EMPTY) - .withParams(Collections.singletonMap("typed_keys", "true")).build(); + .withParams(Collections.singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")).build(); BytesReference bytesAgg = XContentHelper.toXContent(testAgg, xContentType, params, randomBoolean()); try (XContentParser parser = createParser(xContentType.xContent(), bytesAgg)) { parser.nextToken(); @@ -69,6 +73,9 @@ public void testParse() throws IOException { assertThat(parsedAgg, instanceOf(TestParsedAggregation.class)); assertEquals(testAgg.getName(), parsedAgg.getName()); assertEquals(testAgg.getMetaData(), parsedAgg.getMetaData()); + if (meta != null) { + expectThrows(UnsupportedOperationException.class, () -> parsedAgg.getMetaData().put("test", "test")); + } BytesReference finalAgg = XContentHelper.toXContent((ToXContent) parsedAgg, xContentType, randomBoolean()); assertToXContentEquivalent(bytesAgg, finalAgg, xContentType); } From 8464b4755fc0f2da53909de6775e9ba4f84265d9 Mon Sep 17 00:00:00 2001 From: javanna Date: Sat, 8 Apr 2017 00:18:56 +0200 Subject: [PATCH 004/619] [TEST] replace FareRestRequest usage with ToXContent.MapParams --- .../search/aggregations/ParsedAggregationTests.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java index 8962ba2195676..0a83bf3eeb826 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java @@ -31,7 +31,6 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.rest.FakeRestRequest; import java.io.IOException; import java.util.Collections; @@ -57,8 +56,7 @@ public void testParse() throws IOException { } TestInternalAggregation testAgg = new TestInternalAggregation(name, meta); XContentType xContentType = randomFrom(XContentType.values()); - FakeRestRequest params = new FakeRestRequest.Builder(NamedXContentRegistry.EMPTY) - .withParams(Collections.singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")).build(); + ToXContent.MapParams params = new ToXContent.MapParams(Collections.singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); BytesReference bytesAgg = XContentHelper.toXContent(testAgg, xContentType, params, randomBoolean()); try (XContentParser parser = createParser(xContentType.xContent(), bytesAgg)) { parser.nextToken(); From 9e7b0205781ce8518b84e7e53178091b065f7892 Mon Sep 17 00:00:00 2001 From: javanna Date: Sat, 8 Apr 2017 00:28:49 +0200 Subject: [PATCH 005/619] Remove TODO on un-deprecating NamedContentRegistry.Entry ctor that takes a context --- .../search/aggregations/ParsedAggregationTests.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java index 0a83bf3eeb826..f4015ca8d6b97 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java @@ -81,7 +81,6 @@ public void testParse() throws IOException { @Override protected NamedXContentRegistry xContentRegistry() { - //TODO we may want to un-deprecate this Entry constructor if we are going to use it extensively, which I think we are NamedXContentRegistry.Entry entry = new NamedXContentRegistry.Entry(Aggregation.class, new ParseField("type"), (parser, name) -> TestParsedAggregation.fromXContent(parser, (String)name)); return new NamedXContentRegistry(Collections.singletonList(entry)); From 12e8a45de708ea9ae0acc65fb6e68c42577da792 Mon Sep 17 00:00:00 2001 From: javanna Date: Sat, 8 Apr 2017 00:41:32 +0200 Subject: [PATCH 006/619] remove some more TODOs from ParsedAggregation --- .../elasticsearch/search/aggregations/ParsedAggregation.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java index 16b58dbf1f7c3..92cbe3022964b 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java @@ -33,7 +33,6 @@ */ public abstract class ParsedAggregation implements Aggregation, ToXContent { - //TODO move CommonFields out of InternalAggregation protected static void declareCommonFields(ObjectParser objectParser) { objectParser.declareObject((parsedAgg, metadata) -> parsedAgg.metadata = Collections.unmodifiableMap(metadata), (parser, context) -> parser.map(), InternalAggregation.CommonFields.META); @@ -60,10 +59,7 @@ public final Map getMetaData() { //TODO it may make sense to move getType to the Aggregation interface given that we are duplicating it in both implementations protected abstract String getType(); - //TODO the only way to avoid duplicating this method is making Aggregation extend ToXContent - //and declare toXContent as a default method in it. Doesn't sound like the right thing to do. public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { - //TODO move TYPED_KEYS_DELIMITER constant out of InternalAggregation // Concatenates the type and the name of the aggregation (ex: top_hits#foo) builder.startObject(String.join(InternalAggregation.TYPED_KEYS_DELIMITER, getType(), name)); if (this.metadata != null) { From 356532816ab781f1aa8bfd1d4bcbea10fda65c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 12 Apr 2017 11:58:57 +0200 Subject: [PATCH 007/619] Adding ParsedCardinality (#23973) Adding parsing of InternalCardinality xContent output. Parsing method will return a new implementation of the Cardinality interface, ParsedCardinality. --- .../aggregations/ParsedAggregation.java | 9 ++- .../cardinality/InternalCardinality.java | 1 + .../cardinality/ParsedCardinality.java | 80 +++++++++++++++++++ .../InternalAggregationTestCase.java | 11 ++- .../aggregations/ParsedAggregationTests.java | 2 +- .../cardinality/InternalCardinalityTests.java | 48 +++++++++++ 6 files changed, 144 insertions(+), 7 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java index 92cbe3022964b..ae620ba601ea1 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java @@ -38,7 +38,7 @@ protected static void declareCommonFields(ObjectParser parser.map(), InternalAggregation.CommonFields.META); } - String name; + private String name; Map metadata; @Override @@ -46,6 +46,10 @@ public final String getName() { return name; } + protected void setName(String name) { + this.name = name; + } + @Override public final Map getMetaData() { return metadata; @@ -59,6 +63,7 @@ public final Map getMetaData() { //TODO it may make sense to move getType to the Aggregation interface given that we are duplicating it in both implementations protected abstract String getType(); + @Override public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { // Concatenates the type and the name of the aggregation (ex: top_hits#foo) builder.startObject(String.join(InternalAggregation.TYPED_KEYS_DELIMITER, getType(), name)); @@ -72,4 +77,4 @@ public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params par } protected abstract XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException; -} +} \ No newline at end of file diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinality.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinality.java index 028e97a69ff82..ce1e9fc89396f 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinality.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinality.java @@ -128,3 +128,4 @@ HyperLogLogPlusPlus getState() { return counts; } } + diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java new file mode 100644 index 0000000000000..58f24953aacb5 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java @@ -0,0 +1,80 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.cardinality; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.ParsedAggregation; + +import java.io.IOException; + +public class ParsedCardinality extends ParsedAggregation implements Cardinality { + + private long cardinalityValue; + + @Override + public String getValueAsString() { + // InternalCardinality doesn't print "value_as_string", but you can get a formated value using + // getValueAsString(). That method uses the raw formatter so we also use it here. + return DocValueFormat.RAW.format((double) cardinalityValue); + } + + @Override + public double value() { + return getValue(); + } + + @Override + public long getValue() { + return cardinalityValue; + } + + private void setValue(long cardinalityValue) { + this.cardinalityValue = cardinalityValue; + } + + @Override + protected String getType() { + return CardinalityAggregationBuilder.NAME; + } + + private static final ObjectParser PARSER = new ObjectParser<>( + CardinalityAggregationBuilder.NAME, true, ParsedCardinality::new); + + static { + declareCommonFields(PARSER); + PARSER.declareLong(ParsedCardinality::setValue, CommonFields.VALUE); + } + + public static ParsedCardinality fromXContent(XContentParser parser, final String name) { + ParsedCardinality cardinality = PARSER.apply(parser, null); + cardinality.setName(name); + return cardinality; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) + throws IOException { + builder.field(CommonFields.VALUE.getPreferredName(), cardinalityValue); + return builder; + } +} \ No newline at end of file diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index f899fde087c3b..1f0e9d2d74f30 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -98,10 +98,13 @@ protected final T createTestInstance() { private T createTestInstance(String name) { List pipelineAggregators = new ArrayList<>(); // TODO populate pipelineAggregators - Map metaData = new HashMap<>(); - int metaDataCount = randomBoolean() ? 0 : between(1, 10); - while (metaData.size() < metaDataCount) { - metaData.put(randomAlphaOfLength(5), randomAlphaOfLength(5)); + Map metaData = null; + if (randomBoolean()) { + metaData = new HashMap<>(); + int metaDataCount = between(0, 10); + while (metaData.size() < metaDataCount) { + metaData.put(randomAlphaOfLength(5), randomAlphaOfLength(5)); + } } return createTestInstance(name, pipelineAggregators, metaData); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java index f4015ca8d6b97..a79ccf5c91f07 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java @@ -105,7 +105,7 @@ protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) public static TestParsedAggregation fromXContent(XContentParser parser, String name) throws IOException { TestParsedAggregation parsedAgg = PARSER.parse(parser, null); - parsedAgg.name = name; + parsedAgg.setName(name); return parsedAgg; } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java index 7c5809f323bdf..83b9dfbe46e82 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java @@ -19,20 +19,33 @@ package org.elasticsearch.search.aggregations.metrics.cardinality; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.lease.Releasables; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; +import org.elasticsearch.rest.action.search.RestSearchAction; +import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.junit.After; import org.junit.Before; +import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent; + public class InternalCardinalityTests extends InternalAggregationTestCase { private static List algos; private static int p; @@ -73,6 +86,41 @@ protected void assertReduced(InternalCardinality reduced, List ParsedCardinality.fromXContent(parser, (String) name)); + return new NamedXContentRegistry(Collections.singletonList(entry)); + } + @After public void cleanup() { Releasables.close(algos); From 5ccb4a0bbdb82674773b3b177f69fc96705d7913 Mon Sep 17 00:00:00 2001 From: javanna Date: Fri, 14 Apr 2017 11:22:44 +0200 Subject: [PATCH 008/619] fix typo in ParsedCardinality comment and add //norelease comment on DocValueFormat dep --- .../aggregations/metrics/cardinality/ParsedCardinality.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java index 58f24953aacb5..cac367cc7a213 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java @@ -33,8 +33,9 @@ public class ParsedCardinality extends ParsedAggregation implements Cardinality @Override public String getValueAsString() { - // InternalCardinality doesn't print "value_as_string", but you can get a formated value using + // InternalCardinality doesn't print "value_as_string", but you can get a formatted value using // getValueAsString(). That method uses the raw formatter so we also use it here. + //norelease is it worth doing Double.toString(value) and removing the dependency to DocValueFormat.RAW ? return DocValueFormat.RAW.format((double) cardinalityValue); } From c0036d8516970a52eb33f963deb5d69c0b506fbc Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Tue, 18 Apr 2017 10:19:30 +0200 Subject: [PATCH 009/619] Add parsing for percentiles ranks (#23974) This commit adds the logic for parsing the percentiles ranks aggregations. --- .../elasticsearch/search/DocValueFormat.java | 17 ++ .../AbstractParsedPercentiles.java | 177 ++++++++++++++++++ .../percentiles/ParsedPercentileRanks.java | 33 ++++ .../hdr/ParsedHDRPercentileRanks.java | 47 +++++ .../tdigest/ParsedTDigestPercentileRanks.java | 47 +++++ .../InternalAggregationTestCase.java | 84 +++++++++ .../cardinality/InternalCardinalityTests.java | 52 +---- .../InternalPercentilesRanksTestCase.java | 68 +++++++ .../hdr/InternalHDRPercentilesRanksTests.java | 26 ++- .../InternalTDigestPercentilesRanksTests.java | 19 +- 10 files changed, 502 insertions(+), 68 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentileRanks.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesRanksTestCase.java diff --git a/core/src/main/java/org/elasticsearch/search/DocValueFormat.java b/core/src/main/java/org/elasticsearch/search/DocValueFormat.java index 4c32667aa2acc..3e9bad6fe6464 100644 --- a/core/src/main/java/org/elasticsearch/search/DocValueFormat.java +++ b/core/src/main/java/org/elasticsearch/search/DocValueFormat.java @@ -393,5 +393,22 @@ public double parseDouble(String value, boolean roundUp, LongSupplier now) { public BytesRef parseBytesRef(String value) { throw new UnsupportedOperationException(); } + + @Override + public int hashCode() { + return Objects.hash(pattern); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Decimal that = (Decimal) o; + return Objects.equals(pattern, that.pattern); + } } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java new file mode 100644 index 0000000000000..f48e7257b1c2a --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java @@ -0,0 +1,177 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.percentiles; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.ParsedAggregation; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +public abstract class AbstractParsedPercentiles extends ParsedAggregation implements Iterable { + + private final Map percentiles = new LinkedHashMap<>(); + private final Map percentilesAsString = new HashMap<>(); + + private boolean keyed; + + void addPercentile(Double key, Double value) { + percentiles.put(key, value); + } + + void addPercentileAsString(Double key, String valueAsString) { + percentilesAsString.put(key, valueAsString); + } + + Double getPercentile(double percent) { + if (percentiles.isEmpty()) { + return Double.NaN; + } + return percentiles.get(percent); + } + + String getPercentileAsString(double percent) { + String valueAsString = percentilesAsString.get(percent); + if (valueAsString != null) { + return valueAsString; + } + Double value = getPercentile(percent); + if (value != null) { + return DocValueFormat.RAW.format(value); + } + return null; + } + + void setKeyed(boolean keyed) { + this.keyed = keyed; + } + + @Override + public Iterator iterator() { + return new Iterator() { + final Iterator> iterator = percentiles.entrySet().iterator(); + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public Percentile next() { + Map.Entry next = iterator.next(); + return new InternalPercentile(next.getKey(), next.getValue()); + } + }; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + final boolean valuesAsString = (percentilesAsString.isEmpty() == false); + if (keyed) { + builder.startObject(CommonFields.VALUES.getPreferredName()); + for (Map.Entry percentile : percentiles.entrySet()) { + Double key = percentile.getKey(); + builder.field(String.valueOf(key), percentile.getValue()); + + if (valuesAsString) { + builder.field(key + "_as_string", getPercentileAsString(key)); + } + } + builder.endObject(); + } else { + builder.startArray(CommonFields.VALUES.getPreferredName()); + for (Map.Entry percentile : percentiles.entrySet()) { + Double key = percentile.getKey(); + builder.startObject(); + { + builder.field(CommonFields.KEY.getPreferredName(), key); + builder.field(CommonFields.VALUE.getPreferredName(), percentile.getValue()); + if (valuesAsString) { + builder.field(CommonFields.VALUE_AS_STRING.getPreferredName(), getPercentileAsString(key)); + } + } + builder.endObject(); + } + builder.endArray(); + } + return builder; + } + + protected static void declarePercentilesFields(ObjectParser objectParser) { + ParsedAggregation.declareCommonFields(objectParser); + + objectParser.declareField((parser, aggregation, context) -> { + XContentParser.Token token = parser.currentToken(); + if (token == XContentParser.Token.START_OBJECT) { + aggregation.setKeyed(true); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token.isValue()) { + if (token == XContentParser.Token.VALUE_NUMBER) { + aggregation.addPercentile(Double.valueOf(parser.currentName()), parser.doubleValue()); + + } else if (token == XContentParser.Token.VALUE_STRING) { + int i = parser.currentName().indexOf("_as_string"); + if (i > 0) { + double key = Double.valueOf(parser.currentName().substring(0, i)); + aggregation.addPercentileAsString(key, parser.text()); + } else { + aggregation.addPercentile(Double.valueOf(parser.currentName()), Double.valueOf(parser.text())); + } + } + } + } + } else if (token == XContentParser.Token.START_ARRAY) { + aggregation.setKeyed(false); + + String currentFieldName = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + Double key = null; + Double value = null; + String valueAsString = null; + + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (CommonFields.KEY.getPreferredName().equals(currentFieldName)) { + key = parser.doubleValue(); + } else if (CommonFields.VALUE.getPreferredName().equals(currentFieldName)) { + value = parser.doubleValue(); + } else if (CommonFields.VALUE_AS_STRING.getPreferredName().equals(currentFieldName)) { + valueAsString = parser.text(); + } + } + } + if (key != null) { + aggregation.addPercentile(key, value); + if (valueAsString != null) { + aggregation.addPercentileAsString(key, valueAsString); + } + } + } + } + }, CommonFields.VALUES, ObjectParser.ValueType.OBJECT_ARRAY); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentileRanks.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentileRanks.java new file mode 100644 index 0000000000000..de3e81e966cf2 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentileRanks.java @@ -0,0 +1,33 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.percentiles; + +public abstract class ParsedPercentileRanks extends AbstractParsedPercentiles implements PercentileRanks { + + @Override + public double percent(double value) { + return getPercentile(value); + } + + @Override + public String percentAsString(double value) { + return getPercentileAsString(value); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java new file mode 100644 index 0000000000000..31fc8f88cfca2 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java @@ -0,0 +1,47 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.percentiles.hdr; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.percentiles.AbstractParsedPercentiles; +import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentileRanks; + +import java.io.IOException; + +public class ParsedHDRPercentileRanks extends ParsedPercentileRanks { + + @Override + protected String getType() { + return InternalHDRPercentileRanks.NAME; + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedHDRPercentileRanks.class.getSimpleName(), true, ParsedHDRPercentileRanks::new); + static { + AbstractParsedPercentiles.declarePercentilesFields(PARSER); + } + + public static ParsedHDRPercentileRanks fromXContent(XContentParser parser, String name) throws IOException { + ParsedHDRPercentileRanks aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java new file mode 100644 index 0000000000000..57f3df041154f --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java @@ -0,0 +1,47 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.percentiles.tdigest; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.percentiles.AbstractParsedPercentiles; +import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentileRanks; + +import java.io.IOException; + +public class ParsedTDigestPercentileRanks extends ParsedPercentileRanks { + + @Override + protected String getType() { + return InternalTDigestPercentileRanks.NAME; + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedTDigestPercentileRanks.class.getSimpleName(), true, ParsedTDigestPercentileRanks::new); + static { + AbstractParsedPercentiles.declarePercentilesFields(PARSER); + } + + public static ParsedTDigestPercentileRanks fromXContent(XContentParser parser, String name) throws IOException { + ParsedTDigestPercentileRanks aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index 1f0e9d2d74f30..d6b50f69bc9d2 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -19,27 +19,61 @@ package org.elasticsearch.search.aggregations; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.xcontent.ContextParser; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; +import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.SearchModule; +import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.cardinality.ParsedCardinality; +import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.InternalHDRPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.ParsedHDRPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentileRanks; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.test.AbstractWireSerializingTestCase; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static java.util.Collections.emptyList; +import static java.util.Collections.singletonMap; +import static org.elasticsearch.common.xcontent.XContentHelper.toXContent; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent; +import static org.hamcrest.Matchers.containsString; public abstract class InternalAggregationTestCase extends AbstractWireSerializingTestCase { + private final NamedWriteableRegistry namedWriteableRegistry = new NamedWriteableRegistry( new SearchModule(Settings.EMPTY, false, emptyList()).getNamedWriteables()); + private final NamedXContentRegistry namedXContentRegistry = new NamedXContentRegistry(getNamedXContents()); + + static List getNamedXContents() { + Map> namedXContents = new HashMap<>(); + namedXContents.put(CardinalityAggregationBuilder.NAME, (p, c) -> ParsedCardinality.fromXContent(p, (String) c)); + namedXContents.put(InternalHDRPercentileRanks.NAME, (p, c) -> ParsedHDRPercentileRanks.fromXContent(p, (String) c)); + namedXContents.put(InternalTDigestPercentileRanks.NAME, (p, c) -> ParsedTDigestPercentileRanks.fromXContent(p, (String) c)); + + return namedXContents.entrySet().stream() + .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) + .collect(Collectors.toList()); + } + protected abstract T createTestInstance(String name, List pipelineAggregators, Map metaData); /** Return an instance on an unmapped field. */ @@ -125,4 +159,54 @@ protected final T createUnmappedInstance(String name) { protected NamedWriteableRegistry getNamedWriteableRegistry() { return namedWriteableRegistry; } + + @Override + protected NamedXContentRegistry xContentRegistry() { + return namedXContentRegistry; + } + + public final void testFromXContent() throws IOException { + final NamedXContentRegistry xContentRegistry = xContentRegistry(); + final T aggregation = createTestInstance(); + + final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); + final boolean humanReadable = randomBoolean(); + final XContentType xContentType = randomFrom(XContentType.values()); + final BytesReference originalBytes = toShuffledXContent(aggregation, xContentType, params, humanReadable); + + Aggregation parsedAggregation; + try (XContentParser parser = xContentType.xContent().createParser(xContentRegistry, originalBytes)) { + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + + String currentName = parser.currentName(); + int i = currentName.indexOf(InternalAggregation.TYPED_KEYS_DELIMITER); + String aggType = currentName.substring(0, i); + String aggName = currentName.substring(i + 1); + + parsedAggregation = parser.namedObject(Aggregation.class, aggType, aggName); + + assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken()); + assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); + assertNull(parser.nextToken()); + + assertEquals(aggregation.getName(), parsedAggregation.getName()); + assertEquals(aggregation.getMetaData(), parsedAggregation.getMetaData()); + + assertTrue(parsedAggregation instanceof ParsedAggregation); + assertEquals(aggregation.getType(), ((ParsedAggregation) parsedAggregation).getType()); + + final BytesReference parsedBytes = toXContent((ToXContent) parsedAggregation, xContentType, params, humanReadable); + assertToXContentEquivalent(originalBytes, parsedBytes, xContentType); + assertFromXContent(aggregation, (ParsedAggregation) parsedAggregation); + + } catch (NamedXContentRegistry.UnknownNamedObjectException e) { + //norelease Remove this catch block when all aggregations can be parsed back. + assertThat(e.getMessage(), containsString("Unknown Aggregation")); + } + } + + //norelease TODO make abstract + protected void assertFromXContent(T aggregation, ParsedAggregation parsedAggregation) { + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java index 83b9dfbe46e82..efa5438ae3c57 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java @@ -19,33 +19,21 @@ package org.elasticsearch.search.aggregations.metrics.cardinality; -import org.elasticsearch.common.ParseField; -import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.lease.Releasables; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.MockBigArrays; -import org.elasticsearch.common.xcontent.NamedXContentRegistry; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContentHelper; -import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; -import org.elasticsearch.rest.action.search.RestSearchAction; -import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.junit.After; import org.junit.Before; -import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent; - public class InternalCardinalityTests extends InternalAggregationTestCase { private static List algos; private static int p; @@ -86,39 +74,13 @@ protected void assertReduced(InternalCardinality reduced, List ParsedCardinality.fromXContent(parser, (String) name)); - return new NamedXContentRegistry(Collections.singletonList(entry)); + protected void assertFromXContent(InternalCardinality aggregation, ParsedAggregation parsedAggregation) { + assertTrue(parsedAggregation instanceof ParsedCardinality); + ParsedCardinality parsed = (ParsedCardinality) parsedAggregation; + + assertEquals(aggregation.getValue(), parsed.getValue(), Double.MIN_VALUE); + assertEquals(aggregation.getValueAsString(), parsed.getValueAsString()); } @After diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesRanksTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesRanksTestCase.java new file mode 100644 index 0000000000000..23acfcd779a29 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesRanksTestCase.java @@ -0,0 +1,68 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.percentiles; + +import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; + +import java.util.List; +import java.util.Map; + +public abstract class InternalPercentilesRanksTestCase extends InternalAggregationTestCase { + + @Override + protected final T createTestInstance(String name, List pipelineAggregators, Map metaData) { + final boolean keyed = randomBoolean(); + final DocValueFormat format = randomFrom(DocValueFormat.RAW, new DocValueFormat.Decimal("###.##")); + List randomCdfValues = randomSubsetOf(randomIntBetween(1, 5), 0.01d, 0.05d, 0.25d, 0.50d, 0.75d, 0.95d, 0.99d); + double[] cdfValues = new double[randomCdfValues.size()]; + for (int i = 0; i < randomCdfValues.size(); i++) { + cdfValues[i] = randomCdfValues.get(i); + } + return createTestInstance(name, pipelineAggregators, metaData, cdfValues, keyed, format); + } + + protected abstract T createTestInstance(String name, List aggregators, Map metadata, + double[] cdfValues, boolean keyed, DocValueFormat format); + + @Override + protected final void assertFromXContent(T aggregation, ParsedAggregation parsedAggregation) { + assertTrue(aggregation instanceof PercentileRanks); + PercentileRanks percentileRanks = (PercentileRanks) aggregation; + + assertTrue(parsedAggregation instanceof PercentileRanks); + PercentileRanks parsedPercentileRanks = (PercentileRanks) parsedAggregation; + + for (Percentile percentile : percentileRanks) { + Double value = percentile.getValue(); + assertEquals(percentileRanks.percent(value), parsedPercentileRanks.percent(value), 0); + assertEquals(percentileRanks.percentAsString(value), parsedPercentileRanks.percentAsString(value)); + } + + Class parsedClass = parsedParsedPercentileRanksClass(); + assertNotNull(parsedClass); + assertTrue(parsedClass.isInstance(parsedAggregation)); + } + + protected abstract Class parsedParsedPercentileRanksClass(); +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesRanksTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesRanksTests.java index 15207e1d314ea..a3fe4d62d14e7 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesRanksTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesRanksTests.java @@ -22,28 +22,20 @@ import org.HdrHistogram.DoubleHistogram; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; -import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.InternalHDRPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.InternalPercentilesRanksTestCase; +import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentileRanks; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; import java.util.Map; -public class InternalHDRPercentilesRanksTests extends InternalAggregationTestCase { +public class InternalHDRPercentilesRanksTests extends InternalPercentilesRanksTestCase { @Override - protected InternalHDRPercentileRanks createTestInstance(String name, List pipelineAggregators, - Map metaData) { - double[] cdfValues = new double[] { 0.5 }; - int numberOfSignificantValueDigits = 3; - DoubleHistogram state = new DoubleHistogram(numberOfSignificantValueDigits); - int numValues = randomInt(100); - for (int i = 0; i < numValues; ++i) { - state.recordValue(randomDouble()); - } - boolean keyed = false; - DocValueFormat format = DocValueFormat.RAW; - return new InternalHDRPercentileRanks(name, cdfValues, state, keyed, format, pipelineAggregators, metaData); + protected InternalHDRPercentileRanks createTestInstance(String name, List aggregators, Map metadata, + double[] cdfValues, boolean keyed, DocValueFormat format) { + DoubleHistogram state = new DoubleHistogram(3); + return new InternalHDRPercentileRanks(name, cdfValues, state, keyed, format, aggregators, metadata); } @Override @@ -61,4 +53,8 @@ protected Reader instanceReader() { return InternalHDRPercentileRanks::new; } + @Override + protected Class parsedParsedPercentileRanksClass() { + return ParsedHDRPercentileRanks.class; + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java index bc1df930f6a4f..30d416763c1dc 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java @@ -21,26 +21,25 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.metrics.percentiles.InternalPercentilesRanksTestCase; +import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentileRanks; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; import java.util.Map; -public class InternalTDigestPercentilesRanksTests extends InternalAggregationTestCase { +public class InternalTDigestPercentilesRanksTests extends InternalPercentilesRanksTestCase { @Override - protected InternalTDigestPercentileRanks createTestInstance(String name, List pipelineAggregators, - Map metaData) { - double[] cdfValues = new double[] { 0.5 }; + protected InternalTDigestPercentileRanks createTestInstance(String name, List aggregators, + Map metadata, + double[] cdfValues, boolean keyed, DocValueFormat format) { TDigestState state = new TDigestState(100); int numValues = randomInt(100); for (int i = 0; i < numValues; ++i) { state.add(randomDouble()); } - boolean keyed = false; - DocValueFormat format = DocValueFormat.RAW; - return new InternalTDigestPercentileRanks(name, cdfValues, state, keyed, format, pipelineAggregators, metaData); + return new InternalTDigestPercentileRanks(name, cdfValues, state, keyed, format, aggregators, metadata); } @Override @@ -71,4 +70,8 @@ protected Reader instanceReader() { return InternalTDigestPercentileRanks::new; } + @Override + protected Class parsedParsedPercentileRanksClass() { + return ParsedTDigestPercentileRanks.class; + } } From c1ba6997ff64008e8a843fda77905a54ff33bcec Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Tue, 18 Apr 2017 16:54:17 +0200 Subject: [PATCH 010/619] AbstractParsedPercentiles should use Percentile class (#24160) Now the Percentile interface has been merged with the InternalPercentile class in core (#24154) the AbstractParsedPercentiles should use it. This commit also changes InternalPercentilesRanksTestCase so that it now tests the iterator obtained from parsed percentiles ranks aggregations. Adding this new test raised an issue in the iterators where key and value are "swapped" in internal implementations when building the iterators (see InternalTDigestPercentileRanks.Iter constructor that accepts the `keys` as the first parameter named `values`, each key being mapped to the `value` field of Percentile class). This is because percentiles ranks aggs inverts percentiles/values compared to the percentiles aggs. * Add assume in InternalAggregationTestCase * Update after Luca review --- .../AbstractParsedPercentiles.java | 2 +- .../hdr/ParsedHDRPercentileRanks.java | 19 +++++++++ .../tdigest/ParsedTDigestPercentileRanks.java | 19 +++++++++ .../InternalAggregationTestCase.java | 9 ++-- .../InternalPercentilesRanksTestCase.java | 42 +++++++++++++++++++ 5 files changed, 85 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java index f48e7257b1c2a..a45d089cc79bc 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java @@ -81,7 +81,7 @@ public boolean hasNext() { @Override public Percentile next() { Map.Entry next = iterator.next(); - return new InternalPercentile(next.getKey(), next.getValue()); + return new Percentile(next.getKey(), next.getValue()); } }; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java index 31fc8f88cfca2..79749df11760b 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java @@ -23,8 +23,10 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.metrics.percentiles.AbstractParsedPercentiles; import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; import java.io.IOException; +import java.util.Iterator; public class ParsedHDRPercentileRanks extends ParsedPercentileRanks { @@ -33,6 +35,23 @@ protected String getType() { return InternalHDRPercentileRanks.NAME; } + @Override + public Iterator iterator() { + final Iterator iterator = super.iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public Percentile next() { + Percentile percentile = iterator.next(); + return new Percentile(percentile.getValue(), percentile.getPercent()); + } + }; + } + private static ObjectParser PARSER = new ObjectParser<>(ParsedHDRPercentileRanks.class.getSimpleName(), true, ParsedHDRPercentileRanks::new); static { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java index 57f3df041154f..3d51e98d62201 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java @@ -23,8 +23,10 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.metrics.percentiles.AbstractParsedPercentiles; import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; import java.io.IOException; +import java.util.Iterator; public class ParsedTDigestPercentileRanks extends ParsedPercentileRanks { @@ -33,6 +35,23 @@ protected String getType() { return InternalTDigestPercentileRanks.NAME; } + @Override + public Iterator iterator() { + final Iterator iterator = super.iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public Percentile next() { + Percentile percentile = iterator.next(); + return new Percentile(percentile.getValue(), percentile.getPercent()); + } + }; + } + private static ObjectParser PARSER = new ObjectParser<>(ParsedTDigestPercentileRanks.class.getSimpleName(), true, ParsedTDigestPercentileRanks::new); static { diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index d6b50f69bc9d2..e6c5098761c05 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -54,7 +54,6 @@ import static java.util.Collections.singletonMap; import static org.elasticsearch.common.xcontent.XContentHelper.toXContent; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent; -import static org.hamcrest.Matchers.containsString; public abstract class InternalAggregationTestCase extends AbstractWireSerializingTestCase { @@ -169,6 +168,10 @@ public final void testFromXContent() throws IOException { final NamedXContentRegistry xContentRegistry = xContentRegistry(); final T aggregation = createTestInstance(); + //norelease Remove this assumption when all aggregations can be parsed back. + assumeTrue("This test does not support the aggregation type yet", + getNamedXContents().stream().filter(entry -> entry.name.match(aggregation.getType())).count() > 0); + final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); final boolean humanReadable = randomBoolean(); final XContentType xContentType = randomFrom(XContentType.values()); @@ -199,10 +202,6 @@ public final void testFromXContent() throws IOException { final BytesReference parsedBytes = toXContent((ToXContent) parsedAggregation, xContentType, params, humanReadable); assertToXContentEquivalent(originalBytes, parsedBytes, xContentType); assertFromXContent(aggregation, (ParsedAggregation) parsedAggregation); - - } catch (NamedXContentRegistry.UnknownNamedObjectException e) { - //norelease Remove this catch block when all aggregations can be parsed back. - assertThat(e.getMessage(), containsString("Unknown Aggregation")); } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesRanksTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesRanksTestCase.java index 23acfcd779a29..be7d55e5447e4 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesRanksTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesRanksTestCase.java @@ -19,15 +19,26 @@ package org.elasticsearch.search.aggregations.metrics.percentiles; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import java.io.IOException; +import java.util.Iterator; import java.util.List; import java.util.Map; +import static java.util.Collections.singletonMap; +import static org.elasticsearch.common.xcontent.XContentHelper.toXContent; + public abstract class InternalPercentilesRanksTestCase extends InternalAggregationTestCase { @Override @@ -64,5 +75,36 @@ protected final void assertFromXContent(T aggregation, ParsedAggregation parsedA assertTrue(parsedClass.isInstance(parsedAggregation)); } + public void testPercentilesRanksIterators() throws IOException { + final T aggregation = createTestInstance(); + + final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); + final XContentType xContentType = randomFrom(XContentType.values()); + final BytesReference originalBytes = toXContent(aggregation, xContentType, params, randomBoolean()); + + Aggregation parsedAggregation; + try (XContentParser parser = xContentType.xContent().createParser(xContentRegistry(), originalBytes)) { + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + + String currentName = parser.currentName(); + int i = currentName.indexOf(InternalAggregation.TYPED_KEYS_DELIMITER); + String aggType = currentName.substring(0, i); + String aggName = currentName.substring(i + 1); + + parsedAggregation = parser.namedObject(Aggregation.class, aggType, aggName); + + assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken()); + assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); + assertNull(parser.nextToken()); + } + + final Iterator it = ((PercentileRanks) aggregation).iterator(); + final Iterator parsedIt = ((PercentileRanks) parsedAggregation).iterator(); + while (it.hasNext()) { + assertEquals(it.next(), parsedIt.next()); + } + } + protected abstract Class parsedParsedPercentileRanksClass(); } From 75fdc9449fd1d544cb56ae7e101f6ce323fc331d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Tue, 18 Apr 2017 17:32:09 +0200 Subject: [PATCH 011/619] Adding parsing for InternalMax and InternalMin --- .../common/xcontent/ObjectParser.java | 1 + .../aggregations/ParsedAggregation.java | 2 +- ...dSingleValueNumericMetricsAggregation.java | 72 +++++++++++++++++++ .../cardinality/ParsedCardinality.java | 2 +- .../aggregations/metrics/max/ParsedMax.java | 62 ++++++++++++++++ .../aggregations/metrics/min/ParsedMin.java | 62 ++++++++++++++++ .../AbstractParsedPercentiles.java | 2 +- .../InternalAggregationTestCase.java | 6 ++ .../aggregations/ParsedAggregationTests.java | 2 +- .../metrics/InternalMaxTests.java | 22 +++++- .../metrics/min/InternalMinTests.java | 20 +++++- 11 files changed, 243 insertions(+), 10 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java b/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java index 0a7e71c6f061e..a3a886dadfd8b 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java @@ -448,6 +448,7 @@ public enum ValueType { FLOAT(VALUE_NUMBER, VALUE_STRING), FLOAT_OR_NULL(VALUE_NUMBER, VALUE_STRING, VALUE_NULL), DOUBLE(VALUE_NUMBER, VALUE_STRING), + DOUBLE_OR_NULL(VALUE_NUMBER, VALUE_STRING, VALUE_NULL), LONG(VALUE_NUMBER, VALUE_STRING), LONG_OR_NULL(VALUE_NUMBER, VALUE_STRING, VALUE_NULL), INT(VALUE_NUMBER, VALUE_STRING), diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java index ae620ba601ea1..452fe9dcb0853 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java @@ -33,7 +33,7 @@ */ public abstract class ParsedAggregation implements Aggregation, ToXContent { - protected static void declareCommonFields(ObjectParser objectParser) { + protected static void declareAggregationFields(ObjectParser objectParser) { objectParser.declareObject((parsedAgg, metadata) -> parsedAgg.metadata = Collections.unmodifiableMap(metadata), (parser, context) -> parser.map(), InternalAggregation.CommonFields.META); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java new file mode 100644 index 0000000000000..11da4c09f5754 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java @@ -0,0 +1,72 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations.metrics; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser.ValueType; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParser.Token; +import org.elasticsearch.search.aggregations.ParsedAggregation; + +import java.io.IOException; + +public abstract class ParsedSingleValueNumericMetricsAggregation extends ParsedAggregation implements NumericMetricsAggregation.SingleValue { + + protected double value; + protected String valueAsString; + + @Override + public String getValueAsString() { + if (valueAsString != null) { + return valueAsString; + } else { + return Double.toString(value); + } + } + + @Override + public double value() { + return value; + } + + protected void setValue(double value) { + this.value = value; + } + + protected void setValueAsString(String valueAsString) { + this.valueAsString = valueAsString; + } + + protected static double parseValue(XContentParser parser, double defaultNullValue) throws IOException { + Token currentToken = parser.currentToken(); + if (currentToken == XContentParser.Token.VALUE_NUMBER || currentToken == XContentParser.Token.VALUE_STRING) { + return parser.doubleValue(); + } else { + return defaultNullValue; + } + } + + protected static void declareSingeValueFields(ObjectParser objectParser, + double defaultNullValue) { + declareAggregationFields(objectParser); + objectParser.declareField(ParsedSingleValueNumericMetricsAggregation::setValue, + (parser, context) -> parseValue(parser, defaultNullValue), CommonFields.VALUE, ValueType.DOUBLE_OR_NULL); + objectParser.declareString(ParsedSingleValueNumericMetricsAggregation::setValueAsString, CommonFields.VALUE_AS_STRING); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java index cac367cc7a213..5c7a1bad427c1 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java @@ -62,7 +62,7 @@ protected String getType() { CardinalityAggregationBuilder.NAME, true, ParsedCardinality::new); static { - declareCommonFields(PARSER); + declareAggregationFields(PARSER); PARSER.declareLong(ParsedCardinality::setValue, CommonFields.VALUE); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java new file mode 100644 index 0000000000000..f3a4ff85f80d4 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java @@ -0,0 +1,62 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.max; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.ParsedSingleValueNumericMetricsAggregation; + +import java.io.IOException; + +public class ParsedMax extends ParsedSingleValueNumericMetricsAggregation implements Max { + + @Override + public double getValue() { + return value(); + } + + @Override + protected String getType() { + return MaxAggregationBuilder.NAME; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + boolean hasValue = !Double.isInfinite(value); + builder.field(CommonFields.VALUE.getPreferredName(), hasValue ? value : null); + if (hasValue && valueAsString != null) { + builder.field(CommonFields.VALUE_AS_STRING.getPreferredName(), valueAsString); + } + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedMax.class.getSimpleName(), true, ParsedMax::new); + + static { + declareSingeValueFields(PARSER, Double.NEGATIVE_INFINITY); + } + + public static ParsedMax fromXContent(XContentParser parser, final String name) { + ParsedMax max = PARSER.apply(parser, null); + max.setName(name); + return max; + } +} \ No newline at end of file diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java new file mode 100644 index 0000000000000..5be0444e47870 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java @@ -0,0 +1,62 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.min; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.ParsedSingleValueNumericMetricsAggregation; + +import java.io.IOException; + +public class ParsedMin extends ParsedSingleValueNumericMetricsAggregation implements Min { + + @Override + public double getValue() { + return value(); + } + + @Override + protected String getType() { + return MinAggregationBuilder.NAME; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + boolean hasValue = !Double.isInfinite(value); + builder.field(CommonFields.VALUE.getPreferredName(), hasValue ? value : null); + if (hasValue && valueAsString != null) { + builder.field(CommonFields.VALUE_AS_STRING.getPreferredName(), valueAsString); + } + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedMin.class.getSimpleName(), true, ParsedMin::new); + + static { + declareSingeValueFields(PARSER, Double.POSITIVE_INFINITY); + } + + public static ParsedMin fromXContent(XContentParser parser, final String name) { + ParsedMin min = PARSER.apply(parser, null); + min.setName(name); + return min; + } +} \ No newline at end of file diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java index a45d089cc79bc..e3152bac3a36c 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java @@ -120,7 +120,7 @@ protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) } protected static void declarePercentilesFields(ObjectParser objectParser) { - ParsedAggregation.declareCommonFields(objectParser); + ParsedAggregation.declareAggregationFields(objectParser); objectParser.declareField((parser, aggregation, context) -> { XContentParser.Token token = parser.currentToken(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index e6c5098761c05..0db3277f0b10f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -35,6 +35,10 @@ import org.elasticsearch.search.SearchModule; import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.cardinality.ParsedCardinality; +import org.elasticsearch.search.aggregations.metrics.max.MaxAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.max.ParsedMax; +import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.min.ParsedMin; import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.InternalHDRPercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.ParsedHDRPercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentileRanks; @@ -67,6 +71,8 @@ static List getNamedXContents() { namedXContents.put(CardinalityAggregationBuilder.NAME, (p, c) -> ParsedCardinality.fromXContent(p, (String) c)); namedXContents.put(InternalHDRPercentileRanks.NAME, (p, c) -> ParsedHDRPercentileRanks.fromXContent(p, (String) c)); namedXContents.put(InternalTDigestPercentileRanks.NAME, (p, c) -> ParsedTDigestPercentileRanks.fromXContent(p, (String) c)); + namedXContents.put(MinAggregationBuilder.NAME, (p, c) -> ParsedMin.fromXContent(p, (String) c)); + namedXContents.put(MaxAggregationBuilder.NAME, (p, c) -> ParsedMax.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java index a79ccf5c91f07..f56ae175a86a9 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java @@ -90,7 +90,7 @@ private static class TestParsedAggregation extends ParsedAggregation { private static ObjectParser PARSER = new ObjectParser<>("testAggParser", TestParsedAggregation::new); static { - ParsedAggregation.declareCommonFields(PARSER); + ParsedAggregation.declareAggregationFields(PARSER); } @Override diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java index de045ff533ea3..ec178feab71a4 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java @@ -22,18 +22,21 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.metrics.max.InternalMax; +import org.elasticsearch.search.aggregations.metrics.max.ParsedMax; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; import java.util.Map; public class InternalMaxTests extends InternalAggregationTestCase { + @Override protected InternalMax createTestInstance(String name, List pipelineAggregators, Map metaData) { - return new InternalMax(name, randomDouble(), - randomFrom(DocValueFormat.BOOLEAN, DocValueFormat.GEOHASH, DocValueFormat.IP, DocValueFormat.RAW), pipelineAggregators, - metaData); + double value = frequently() ? randomDouble() : randomFrom(new Double[] { Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY }); + DocValueFormat formatter = randomFrom(new DocValueFormat.Decimal("###.##"), DocValueFormat.BOOLEAN, DocValueFormat.RAW); + return new InternalMax(name, value, formatter, pipelineAggregators, metaData); } @Override @@ -45,4 +48,17 @@ protected Reader instanceReader() { protected void assertReduced(InternalMax reduced, List inputs) { assertEquals(inputs.stream().mapToDouble(InternalMax::value).max().getAsDouble(), reduced.value(), 0); } + + @Override + protected void assertFromXContent(InternalMax max, ParsedAggregation parsedAggregation) { + ParsedMax parsed = ((ParsedMax) parsedAggregation); + if (Double.isInfinite(max.getValue()) == false) { + assertEquals(max.getValue(), parsed.getValue(), Double.MIN_VALUE); + assertEquals(max.getValueAsString(), parsed.getValueAsString()); + } else { + // we write Double.NEGATIVE_INFINITY and Double.POSITIVE_INFINITY to xContent as 'null', so we + // cannot differentiate between them. Also we cannot recreate the exact String representation + assertEquals(parsed.getValue(), Double.NEGATIVE_INFINITY, 0); + } + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java index f93e7c5c81e41..82ba4dec5efcc 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -30,9 +31,9 @@ public class InternalMinTests extends InternalAggregationTestCase { @Override protected InternalMin createTestInstance(String name, List pipelineAggregators, Map metaData) { - return new InternalMin(name, randomDouble(), - randomFrom(DocValueFormat.BOOLEAN, DocValueFormat.GEOHASH, DocValueFormat.IP, DocValueFormat.RAW), pipelineAggregators, - metaData); + double value = frequently() ? randomDouble() : randomFrom(new Double[] { Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY }); + DocValueFormat formatter = randomFrom(new DocValueFormat.Decimal("###.##"), DocValueFormat.BOOLEAN, DocValueFormat.RAW); + return new InternalMin(name, value, formatter, pipelineAggregators, metaData); } @Override @@ -44,4 +45,17 @@ protected Reader instanceReader() { protected void assertReduced(InternalMin reduced, List inputs) { assertEquals(inputs.stream().mapToDouble(InternalMin::value).min().getAsDouble(), reduced.value(), 0); } + + @Override + protected void assertFromXContent(InternalMin min, ParsedAggregation parsedAggregation) { + ParsedMin parsed = ((ParsedMin) parsedAggregation); + if (Double.isInfinite(min.getValue()) == false) { + assertEquals(min.getValue(), parsed.getValue(), Double.MIN_VALUE); + assertEquals(min.getValueAsString(), parsed.getValueAsString()); + } else { + // we write Double.NEGATIVE_INFINITY and Double.POSITIVE_INFINITY to xContent as 'null', so we + // cannot differentiate between them. Also we cannot recreate the exact String representation + assertEquals(parsed.getValue(), Double.POSITIVE_INFINITY, 0); + } + } } From 695b2858f4f22509b264020fc535ca808433f2b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Tue, 18 Apr 2017 17:41:44 +0200 Subject: [PATCH 012/619] Adding parsing for InternalSum --- .../aggregations/metrics/sum/ParsedSum.java | 61 +++++++++++++++++++ .../InternalAggregationTestCase.java | 3 + .../metrics/sum/InternalSumTests.java | 12 +++- 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java new file mode 100644 index 0000000000000..15a85aa3f87ba --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java @@ -0,0 +1,61 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.sum; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.ParsedSingleValueNumericMetricsAggregation; + +import java.io.IOException; + +public class ParsedSum extends ParsedSingleValueNumericMetricsAggregation implements Sum { + + @Override + public double getValue() { + return value(); + } + + @Override + protected String getType() { + return SumAggregationBuilder.NAME; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + builder.field(CommonFields.VALUE.getPreferredName(), value); + if (valueAsString != null) { + builder.field(CommonFields.VALUE_AS_STRING.getPreferredName(), valueAsString); + } + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedSum.class.getSimpleName(), true, ParsedSum::new); + + static { + declareSingeValueFields(PARSER, Double.NEGATIVE_INFINITY); + } + + public static ParsedSum fromXContent(XContentParser parser, final String name) { + ParsedSum sum = PARSER.apply(parser, null); + sum.setName(name); + return sum; + } +} \ No newline at end of file diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index 0db3277f0b10f..e1d3fe5bcdea7 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -43,6 +43,8 @@ import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.ParsedHDRPercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.sum.ParsedSum; +import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.test.AbstractWireSerializingTestCase; @@ -73,6 +75,7 @@ static List getNamedXContents() { namedXContents.put(InternalTDigestPercentileRanks.NAME, (p, c) -> ParsedTDigestPercentileRanks.fromXContent(p, (String) c)); namedXContents.put(MinAggregationBuilder.NAME, (p, c) -> ParsedMin.fromXContent(p, (String) c)); namedXContents.put(MaxAggregationBuilder.NAME, (p, c) -> ParsedMax.fromXContent(p, (String) c)); + namedXContents.put(SumAggregationBuilder.NAME, (p, c) -> ParsedSum.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/sum/InternalSumTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/sum/InternalSumTests.java index 44e5ea940adeb..6fb61257a9e92 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/sum/InternalSumTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/sum/InternalSumTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -30,7 +31,9 @@ public class InternalSumTests extends InternalAggregationTestCase { @Override protected InternalSum createTestInstance(String name, List pipelineAggregators, Map metaData) { - return new InternalSum(name, randomDouble(), DocValueFormat.RAW, pipelineAggregators, metaData); + double value = frequently() ? randomDouble() : randomFrom(new Double[] { Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY }); + DocValueFormat formatter = randomFrom(new DocValueFormat.Decimal("###.##"), DocValueFormat.BOOLEAN, DocValueFormat.RAW); + return new InternalSum(name, value, formatter, pipelineAggregators, metaData); } @Override @@ -43,4 +46,11 @@ protected void assertReduced(InternalSum reduced, List inputs) { double expectedSum = inputs.stream().mapToDouble(InternalSum::getValue).sum(); assertEquals(expectedSum, reduced.getValue(), 0.0001d); } + + @Override + protected void assertFromXContent(InternalSum sum, ParsedAggregation parsedAggregation) { + ParsedSum parsed = ((ParsedSum) parsedAggregation); + assertEquals(sum.getValue(), parsed.getValue(), Double.MIN_VALUE); + assertEquals(sum.getValueAsString(), parsed.getValueAsString()); + } } From 5f96972b042a6a99689601c8e9a873419d341c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Tue, 18 Apr 2017 17:48:11 +0200 Subject: [PATCH 013/619] Adding parsing for InternalAvg --- .../aggregations/metrics/avg/ParsedAvg.java | 64 +++++++++++++++++++ .../InternalAggregationTestCase.java | 3 + .../metrics/avg/InternalAvgTests.java | 17 ++++- 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java new file mode 100644 index 0000000000000..ed4b9416908ee --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java @@ -0,0 +1,64 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.avg; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.ParsedSingleValueNumericMetricsAggregation; + +import java.io.IOException; + +public class ParsedAvg extends ParsedSingleValueNumericMetricsAggregation implements Avg { + + @Override + public double getValue() { + return value(); + } + + @Override + protected String getType() { + return AvgAggregationBuilder.NAME; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + // InternalAvg renders value only if the avg normalizer (count) is not 0. + // We parse back `null` as Double.POSITIVE_INFINITY so we check for that value here to get the same xContent output + boolean hasValue = value != Double.POSITIVE_INFINITY; + builder.field(CommonFields.VALUE.getPreferredName(), hasValue ? value : null); + if (hasValue && valueAsString != null) { + builder.field(CommonFields.VALUE_AS_STRING.getPreferredName(), valueAsString); + } + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedAvg.class.getSimpleName(), true, ParsedAvg::new); + + static { + declareSingeValueFields(PARSER, Double.POSITIVE_INFINITY); + } + + public static ParsedAvg fromXContent(XContentParser parser, final String name) { + ParsedAvg avg = PARSER.apply(parser, null); + avg.setName(name); + return avg; + } +} \ No newline at end of file diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index e1d3fe5bcdea7..bfa17179851ab 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -33,6 +33,8 @@ import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.SearchModule; +import org.elasticsearch.search.aggregations.metrics.avg.AvgAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.avg.ParsedAvg; import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.cardinality.ParsedCardinality; import org.elasticsearch.search.aggregations.metrics.max.MaxAggregationBuilder; @@ -76,6 +78,7 @@ static List getNamedXContents() { namedXContents.put(MinAggregationBuilder.NAME, (p, c) -> ParsedMin.fromXContent(p, (String) c)); namedXContents.put(MaxAggregationBuilder.NAME, (p, c) -> ParsedMax.fromXContent(p, (String) c)); namedXContents.put(SumAggregationBuilder.NAME, (p, c) -> ParsedSum.fromXContent(p, (String) c)); + namedXContents.put(AvgAggregationBuilder.NAME, (p, c) -> ParsedAvg.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java index 0600d7299b46f..bfdf37d5775de 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -31,9 +32,9 @@ public class InternalAvgTests extends InternalAggregationTestCase { @Override protected InternalAvg createTestInstance(String name, List pipelineAggregators, Map metaData) { - return new InternalAvg(name, randomDoubleBetween(0, 100000, true), randomNonNegativeLong() % 100000, - randomFrom(DocValueFormat.BOOLEAN, DocValueFormat.GEOHASH, DocValueFormat.IP, DocValueFormat.RAW), pipelineAggregators, - metaData); + DocValueFormat formatter = randomFrom(new DocValueFormat.Decimal("###.##"), DocValueFormat.BOOLEAN, DocValueFormat.RAW); + long count = frequently() ? randomNonNegativeLong() % 100000 : 0; + return new InternalAvg(name, randomDoubleBetween(0, 100000, true), count, formatter, pipelineAggregators, metaData); } @Override @@ -53,4 +54,14 @@ protected void assertReduced(InternalAvg reduced, List inputs) { assertEquals(sum, reduced.getSum(), 0.0000001); assertEquals(sum / counts, reduced.value(), 0.0000001); } + + @Override + protected void assertFromXContent(InternalAvg avg, ParsedAggregation parsedAggregation) { + ParsedAvg parsed = ((ParsedAvg) parsedAggregation); + assertEquals(avg.getValue(), parsed.getValue(), Double.MIN_VALUE); + // we don't print out VALUE_AS_STRING for avg.getCount() == 0, so we cannot get the exact same value back + if (avg.getCount() != 0) { + assertEquals(avg.getValueAsString(), parsed.getValueAsString()); + } + } } From bc646cf7ad74998be73febf21fd8a258e6ab113a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Tue, 18 Apr 2017 17:56:16 +0200 Subject: [PATCH 014/619] Adding parsing for InternalValueCount --- .../metrics/valuecount/ParsedValueCount.java | 74 +++++++++++++++++++ .../InternalAggregationTestCase.java | 3 + .../valuecount/InternalValueCountTests.java | 12 ++- 3 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ParsedValueCount.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ParsedValueCount.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ParsedValueCount.java new file mode 100644 index 0000000000000..012323f0041e8 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ParsedValueCount.java @@ -0,0 +1,74 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.valuecount; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.ParsedAggregation; + +import java.io.IOException; + +public class ParsedValueCount extends ParsedAggregation implements ValueCount { + + private long valueCount; + + @Override + public double value() { + return getValue(); + } + + @Override + public long getValue() { + return valueCount; + } + + @Override + public String getValueAsString() { + // InternalValueCount doesn't print "value_as_string", but you can get a formatted value using + // getValueAsString() using the raw formatter and converting the value to double + return Double.toString(valueCount); + } + + @Override + protected String getType() { + return ValueCountAggregationBuilder.NAME; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + builder.field(CommonFields.VALUE.getPreferredName(), valueCount); + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedValueCount.class.getSimpleName(), true, + ParsedValueCount::new); + + static { + declareAggregationFields(PARSER); + PARSER.declareLong((agg, value) -> agg.valueCount = value, CommonFields.VALUE); + } + + public static ParsedValueCount fromXContent(XContentParser parser, final String name) { + ParsedValueCount sum = PARSER.apply(parser, null); + sum.setName(name); + return sum; + } +} \ No newline at end of file diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index bfa17179851ab..04c0ed58f3900 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -47,6 +47,8 @@ import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentileRanks; import org.elasticsearch.search.aggregations.metrics.sum.ParsedSum; import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.valuecount.ParsedValueCount; +import org.elasticsearch.search.aggregations.metrics.valuecount.ValueCountAggregationBuilder; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.test.AbstractWireSerializingTestCase; @@ -79,6 +81,7 @@ static List getNamedXContents() { namedXContents.put(MaxAggregationBuilder.NAME, (p, c) -> ParsedMax.fromXContent(p, (String) c)); namedXContents.put(SumAggregationBuilder.NAME, (p, c) -> ParsedSum.fromXContent(p, (String) c)); namedXContents.put(AvgAggregationBuilder.NAME, (p, c) -> ParsedAvg.fromXContent(p, (String) c)); + namedXContents.put(ValueCountAggregationBuilder.NAME, (p, c) -> ParsedValueCount.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/valuecount/InternalValueCountTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/valuecount/InternalValueCountTests.java index 3de87898778d4..17df4b89dc641 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/valuecount/InternalValueCountTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/valuecount/InternalValueCountTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -29,9 +30,8 @@ public class InternalValueCountTests extends InternalAggregationTestCase { @Override - protected InternalValueCount createTestInstance(String name, - List pipelineAggregators, - Map metaData) { + protected InternalValueCount createTestInstance(String name, List pipelineAggregators, + Map metaData) { return new InternalValueCount(name, randomIntBetween(0, 100), pipelineAggregators, metaData); } @@ -44,4 +44,10 @@ protected void assertReduced(InternalValueCount reduced, List instanceReader() { return InternalValueCount::new; } + + @Override + protected void assertFromXContent(InternalValueCount valueCount, ParsedAggregation parsedAggregation) { + assertEquals(valueCount.getValue(), ((ParsedValueCount) parsedAggregation).getValue(), 0); + assertEquals(valueCount.getValueAsString(), ((ParsedValueCount) parsedAggregation).getValueAsString()); + } } From 210e101f6d7677e103b24beb33726a8ed8672c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Tue, 18 Apr 2017 17:56:31 +0200 Subject: [PATCH 015/619] Minor changes in ParsedCardinality --- .../metrics/cardinality/ParsedCardinality.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java index 5c7a1bad427c1..77356db519705 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java @@ -49,21 +49,17 @@ public long getValue() { return cardinalityValue; } - private void setValue(long cardinalityValue) { - this.cardinalityValue = cardinalityValue; - } - @Override protected String getType() { return CardinalityAggregationBuilder.NAME; } private static final ObjectParser PARSER = new ObjectParser<>( - CardinalityAggregationBuilder.NAME, true, ParsedCardinality::new); + ParsedCardinality.class.getSimpleName(), true, ParsedCardinality::new); static { declareAggregationFields(PARSER); - PARSER.declareLong(ParsedCardinality::setValue, CommonFields.VALUE); + PARSER.declareLong((agg, value) -> agg.cardinalityValue = value, CommonFields.VALUE); } public static ParsedCardinality fromXContent(XContentParser parser, final String name) { From bf5cfabe040ae131d94fe3e627b1479ea098f7ac Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Wed, 19 Apr 2017 10:23:25 +0200 Subject: [PATCH 016/619] Fix checkstyle violation --- .../metrics/ParsedSingleValueNumericMetricsAggregation.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java index 11da4c09f5754..9c7972e638f6d 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java @@ -26,7 +26,8 @@ import java.io.IOException; -public abstract class ParsedSingleValueNumericMetricsAggregation extends ParsedAggregation implements NumericMetricsAggregation.SingleValue { +public abstract class ParsedSingleValueNumericMetricsAggregation extends ParsedAggregation + implements NumericMetricsAggregation.SingleValue { protected double value; protected String valueAsString; From 4562c8a345d76024f6ac1812249ab3e187a971ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 19 Apr 2017 12:58:26 +0200 Subject: [PATCH 017/619] Add parsing for InternalSimpleValue and InternalDerivative (#24162) --- ...dSingleValueNumericMetricsAggregation.java | 2 +- .../aggregations/metrics/avg/ParsedAvg.java | 2 +- .../aggregations/metrics/max/ParsedMax.java | 2 +- .../aggregations/metrics/min/ParsedMin.java | 2 +- .../aggregations/metrics/sum/ParsedSum.java | 2 +- .../pipeline/ParsedSimpleValue.java | 58 ++++++++++++++ .../pipeline/derivative/ParsedDerivative.java | 79 +++++++++++++++++++ .../InternalAggregationTestCase.java | 6 ++ .../pipeline/InternalSimpleValueTests.java | 23 ++++-- .../derivative/InternalDerivativeTests.java | 21 ++++- 10 files changed, 183 insertions(+), 14 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/pipeline/ParsedSimpleValue.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java index 9c7972e638f6d..5eb0a7223a11f 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java @@ -63,7 +63,7 @@ protected static double parseValue(XContentParser parser, double defaultNullValu } } - protected static void declareSingeValueFields(ObjectParser objectParser, + protected static void declareSingleValueFields(ObjectParser objectParser, double defaultNullValue) { declareAggregationFields(objectParser); objectParser.declareField(ParsedSingleValueNumericMetricsAggregation::setValue, diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java index ed4b9416908ee..d77fd2bb3ad35 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java @@ -53,7 +53,7 @@ protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) private static final ObjectParser PARSER = new ObjectParser<>(ParsedAvg.class.getSimpleName(), true, ParsedAvg::new); static { - declareSingeValueFields(PARSER, Double.POSITIVE_INFINITY); + declareSingleValueFields(PARSER, Double.POSITIVE_INFINITY); } public static ParsedAvg fromXContent(XContentParser parser, final String name) { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java index f3a4ff85f80d4..86eab0025acd8 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java @@ -51,7 +51,7 @@ protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) private static final ObjectParser PARSER = new ObjectParser<>(ParsedMax.class.getSimpleName(), true, ParsedMax::new); static { - declareSingeValueFields(PARSER, Double.NEGATIVE_INFINITY); + declareSingleValueFields(PARSER, Double.NEGATIVE_INFINITY); } public static ParsedMax fromXContent(XContentParser parser, final String name) { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java index 5be0444e47870..c50cd5c3615be 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java @@ -51,7 +51,7 @@ protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) private static final ObjectParser PARSER = new ObjectParser<>(ParsedMin.class.getSimpleName(), true, ParsedMin::new); static { - declareSingeValueFields(PARSER, Double.POSITIVE_INFINITY); + declareSingleValueFields(PARSER, Double.POSITIVE_INFINITY); } public static ParsedMin fromXContent(XContentParser parser, final String name) { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java index 15a85aa3f87ba..86d3e1c4295e2 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java @@ -50,7 +50,7 @@ protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) private static final ObjectParser PARSER = new ObjectParser<>(ParsedSum.class.getSimpleName(), true, ParsedSum::new); static { - declareSingeValueFields(PARSER, Double.NEGATIVE_INFINITY); + declareSingleValueFields(PARSER, Double.NEGATIVE_INFINITY); } public static ParsedSum fromXContent(XContentParser parser, final String name) { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/ParsedSimpleValue.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/ParsedSimpleValue.java new file mode 100644 index 0000000000000..7d1bcec3bc7ba --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/ParsedSimpleValue.java @@ -0,0 +1,58 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.pipeline; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.ParsedSingleValueNumericMetricsAggregation; + +import java.io.IOException; + +public class ParsedSimpleValue extends ParsedSingleValueNumericMetricsAggregation implements SimpleValue { + + @Override + protected String getType() { + return InternalSimpleValue.NAME; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedSimpleValue.class.getSimpleName(), true, + ParsedSimpleValue::new); + + static { + declareSingleValueFields(PARSER, Double.NaN); + } + + public static ParsedSimpleValue fromXContent(XContentParser parser, final String name) { + ParsedSimpleValue simpleValue = PARSER.apply(parser, null); + simpleValue.setName(name); + return simpleValue; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + boolean hasValue = Double.isNaN(value) == false; + builder.field(CommonFields.VALUE.getPreferredName(), hasValue ? value : null); + if (hasValue && valueAsString != null) { + builder.field(CommonFields.VALUE_AS_STRING.getPreferredName(), valueAsString); + } + return builder; + } +} \ No newline at end of file diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java new file mode 100644 index 0000000000000..ed463239b2440 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java @@ -0,0 +1,79 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.pipeline.derivative; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser.ValueType; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.pipeline.ParsedSimpleValue; + +import java.io.IOException; + +public class ParsedDerivative extends ParsedSimpleValue implements Derivative { + + private double normalizedValue; + private String normalizedAsString; + private boolean hasNormalizationFactor; + private static final ParseField NORMALIZED_AS_STRING = new ParseField("normalized_value_as_string"); + private static final ParseField NORMALIZED = new ParseField("normalized_value"); + + @Override + public double normalizedValue() { + return this.normalizedValue; + } + + @Override + protected String getType() { + return DerivativePipelineAggregationBuilder.NAME; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedDerivative.class.getSimpleName(), true, + ParsedDerivative::new); + + static { + declareSingleValueFields(PARSER, Double.NaN); + PARSER.declareField((agg, normalized) -> { + agg.normalizedValue = normalized; + agg.hasNormalizationFactor = true; + }, (parser, context) -> parseValue(parser, Double.NaN), NORMALIZED, ValueType.DOUBLE_OR_NULL); + PARSER.declareString((agg, normalAsString) -> agg.normalizedAsString = normalAsString, NORMALIZED_AS_STRING); + } + + public static ParsedDerivative fromXContent(XContentParser parser, final String name) { + ParsedDerivative derivative = PARSER.apply(parser, null); + derivative.setName(name); + return derivative; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + super.doXContentBody(builder, params); + if (hasNormalizationFactor) { + boolean hasValue = Double.isNaN(normalizedValue) == false; + builder.field(NORMALIZED.getPreferredName(), hasValue ? normalizedValue : null); + if (hasValue && normalizedAsString != null) { + builder.field(NORMALIZED_AS_STRING.getPreferredName(), normalizedAsString); + } + } + return builder; + } +} \ No newline at end of file diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index 53984332f37ff..e3584f5a85c59 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -51,7 +51,11 @@ import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.valuecount.ParsedValueCount; import org.elasticsearch.search.aggregations.metrics.valuecount.ValueCountAggregationBuilder; +import org.elasticsearch.search.aggregations.pipeline.InternalSimpleValue; +import org.elasticsearch.search.aggregations.pipeline.ParsedSimpleValue; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.pipeline.derivative.DerivativePipelineAggregationBuilder; +import org.elasticsearch.search.aggregations.pipeline.derivative.ParsedDerivative; import org.elasticsearch.test.AbstractWireSerializingTestCase; import java.io.IOException; @@ -85,6 +89,8 @@ static List getNamedXContents() { namedXContents.put(SumAggregationBuilder.NAME, (p, c) -> ParsedSum.fromXContent(p, (String) c)); namedXContents.put(AvgAggregationBuilder.NAME, (p, c) -> ParsedAvg.fromXContent(p, (String) c)); namedXContents.put(ValueCountAggregationBuilder.NAME, (p, c) -> ParsedValueCount.fromXContent(p, (String) c)); + namedXContents.put(InternalSimpleValue.NAME, (p, c) -> ParsedSimpleValue.fromXContent(p, (String) c)); + namedXContents.put(DerivativePipelineAggregationBuilder.NAME, (p, c) -> ParsedDerivative.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/InternalSimpleValueTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/InternalSimpleValueTests.java index 4dae0a8d653c5..ffd5fd1bf8b84 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/InternalSimpleValueTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/InternalSimpleValueTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import java.util.Collections; import java.util.List; @@ -30,18 +31,18 @@ public class InternalSimpleValueTests extends InternalAggregationTestCase{ @Override - protected InternalSimpleValue createTestInstance(String name, - List pipelineAggregators, Map metaData) { + protected InternalSimpleValue createTestInstance(String name, List pipelineAggregators, + Map metaData) { DocValueFormat formatter = randomNumericDocValueFormat(); - double value = randomDoubleBetween(0, 100000, true); + double value = frequently() ? randomDoubleBetween(0, 100000, true) + : randomFrom(new Double[] { Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN }); return new InternalSimpleValue(name, value, formatter, pipelineAggregators, metaData); } @Override public void testReduceRandom() { expectThrows(UnsupportedOperationException.class, - () -> createTestInstance("name", Collections.emptyList(), null).reduce(null, - null)); + () -> createTestInstance("name", Collections.emptyList(), null).reduce(null, null)); } @Override @@ -54,4 +55,16 @@ protected Reader instanceReader() { return InternalSimpleValue::new; } + @Override + protected void assertFromXContent(InternalSimpleValue simpleValue, ParsedAggregation parsedAggregation) { + ParsedSimpleValue parsed = ((ParsedSimpleValue) parsedAggregation); + if (Double.isInfinite(simpleValue.getValue()) == false && Double.isNaN(simpleValue.getValue()) == false) { + assertEquals(simpleValue.getValue(), parsed.value(), 0); + assertEquals(simpleValue.getValueAsString(), parsed.getValueAsString()); + } else { + // we write Double.NEGATIVE_INFINITY, Double.POSITIVE amd Double.NAN to xContent as 'null', so we + // cannot differentiate between them. Also we cannot recreate the exact String representation + assertEquals(parsed.value(), Double.NaN, 0); + } + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/derivative/InternalDerivativeTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/derivative/InternalDerivativeTests.java index d2e4f3cb72017..229c98b4ad4f7 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/derivative/InternalDerivativeTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/derivative/InternalDerivativeTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.Collections; @@ -34,16 +35,16 @@ public class InternalDerivativeTests extends InternalAggregationTestCase pipelineAggregators, Map metaData) { DocValueFormat formatter = randomNumericDocValueFormat(); - double value = randomDoubleBetween(0, 100000, true); - double normalizationFactor = randomDoubleBetween(0, 100000, true); + double value = frequently() ? randomDoubleBetween(-100000, 100000, true) + : randomFrom(new Double[] { Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN }); + double normalizationFactor = frequently() ? randomDoubleBetween(0, 100000, true) : 0; return new InternalDerivative(name, value, normalizationFactor, formatter, pipelineAggregators, metaData); } @Override public void testReduceRandom() { expectThrows(UnsupportedOperationException.class, - () -> createTestInstance("name", Collections.emptyList(), null).reduce(null, - null)); + () -> createTestInstance("name", Collections.emptyList(), null).reduce(null, null)); } @Override @@ -56,4 +57,16 @@ protected Reader instanceReader() { return InternalDerivative::new; } + @Override + protected void assertFromXContent(InternalDerivative derivative, ParsedAggregation parsedAggregation) { + ParsedDerivative parsed = ((ParsedDerivative) parsedAggregation); + if (Double.isInfinite(derivative.getValue()) == false && Double.isNaN(derivative.getValue()) == false) { + assertEquals(derivative.getValue(), parsed.value(), Double.MIN_VALUE); + assertEquals(derivative.getValueAsString(), parsed.getValueAsString()); + } else { + // we write Double.NEGATIVE_INFINITY, Double.POSITIVE amd Double.NAN to xContent as 'null', so we + // cannot differentiate between them. Also we cannot recreate the exact String representation + assertEquals(parsed.value(), Double.NaN, Double.MIN_VALUE); + } + } } From 11da77388a3653896dffd9b3434d2a7f517ae7d3 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Thu, 20 Apr 2017 10:16:51 +0200 Subject: [PATCH 018/619] Add parsing methods for Percentiles aggregations (#24183) --- .../percentiles/ParsedPercentileRanks.java | 2 +- ...ercentiles.java => ParsedPercentiles.java} | 8 +- .../hdr/ParsedHDRPercentileRanks.java | 4 +- .../percentiles/hdr/ParsedHDRPercentiles.java | 57 +++++++++++++ .../tdigest/ParsedTDigestPercentileRanks.java | 4 +- .../tdigest/ParsedTDigestPercentiles.java | 57 +++++++++++++ .../InternalAggregationTestCase.java | 44 +++++++--- .../AbstractPercentilesTestCase.java | 84 +++++++++++++++++++ .../InternalPercentilesRanksTestCase.java | 82 ++---------------- .../InternalPercentilesTestCase.java | 44 +++------- .../hdr/InternalHDRPercentilesRanksTests.java | 14 ++-- .../hdr/InternalHDRPercentilesTests.java | 6 ++ .../InternalTDigestPercentilesRanksTests.java | 18 ++-- .../InternalTDigestPercentilesTests.java | 6 ++ 14 files changed, 289 insertions(+), 141 deletions(-) rename core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/{AbstractParsedPercentiles.java => ParsedPercentiles.java} (96%) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentiles.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentiles.java create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentileRanks.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentileRanks.java index de3e81e966cf2..2c80d0328dd86 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentileRanks.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentileRanks.java @@ -19,7 +19,7 @@ package org.elasticsearch.search.aggregations.metrics.percentiles; -public abstract class ParsedPercentileRanks extends AbstractParsedPercentiles implements PercentileRanks { +public abstract class ParsedPercentileRanks extends ParsedPercentiles implements PercentileRanks { @Override public double percent(double value) { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentiles.java similarity index 96% rename from core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java rename to core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentiles.java index e3152bac3a36c..eee058fc2f861 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentiles.java @@ -31,7 +31,7 @@ import java.util.LinkedHashMap; import java.util.Map; -public abstract class AbstractParsedPercentiles extends ParsedAggregation implements Iterable { +public abstract class ParsedPercentiles extends ParsedAggregation implements Iterable { private final Map percentiles = new LinkedHashMap<>(); private final Map percentilesAsString = new HashMap<>(); @@ -46,14 +46,14 @@ void addPercentileAsString(Double key, String valueAsString) { percentilesAsString.put(key, valueAsString); } - Double getPercentile(double percent) { + protected Double getPercentile(double percent) { if (percentiles.isEmpty()) { return Double.NaN; } return percentiles.get(percent); } - String getPercentileAsString(double percent) { + protected String getPercentileAsString(double percent) { String valueAsString = percentilesAsString.get(percent); if (valueAsString != null) { return valueAsString; @@ -119,7 +119,7 @@ protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) return builder; } - protected static void declarePercentilesFields(ObjectParser objectParser) { + protected static void declarePercentilesFields(ObjectParser objectParser) { ParsedAggregation.declareAggregationFields(objectParser); objectParser.declareField((parser, aggregation, context) -> { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java index 79749df11760b..5d00d8dc8e3f0 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java @@ -21,7 +21,7 @@ import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.search.aggregations.metrics.percentiles.AbstractParsedPercentiles; +import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentiles; import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; @@ -55,7 +55,7 @@ public Percentile next() { private static ObjectParser PARSER = new ObjectParser<>(ParsedHDRPercentileRanks.class.getSimpleName(), true, ParsedHDRPercentileRanks::new); static { - AbstractParsedPercentiles.declarePercentilesFields(PARSER); + ParsedPercentiles.declarePercentilesFields(PARSER); } public static ParsedHDRPercentileRanks fromXContent(XContentParser parser, String name) throws IOException { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentiles.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentiles.java new file mode 100644 index 0000000000000..0b6da9f00e112 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentiles.java @@ -0,0 +1,57 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.percentiles.hdr; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentiles; +import org.elasticsearch.search.aggregations.metrics.percentiles.Percentiles; + +import java.io.IOException; + +public class ParsedHDRPercentiles extends ParsedPercentiles implements Percentiles { + + @Override + protected String getType() { + return InternalHDRPercentiles.NAME; + } + + @Override + public double percentile(double percent) { + return getPercentile(percent); + } + + @Override + public String percentileAsString(double percent) { + return getPercentileAsString(percent); + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedHDRPercentiles.class.getSimpleName(), true, ParsedHDRPercentiles::new); + static { + ParsedPercentiles.declarePercentilesFields(PARSER); + } + + public static ParsedHDRPercentiles fromXContent(XContentParser parser, String name) throws IOException { + ParsedHDRPercentiles aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java index 3d51e98d62201..34fd92ad4fb2d 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java @@ -21,7 +21,7 @@ import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.search.aggregations.metrics.percentiles.AbstractParsedPercentiles; +import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentiles; import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; @@ -55,7 +55,7 @@ public Percentile next() { private static ObjectParser PARSER = new ObjectParser<>(ParsedTDigestPercentileRanks.class.getSimpleName(), true, ParsedTDigestPercentileRanks::new); static { - AbstractParsedPercentiles.declarePercentilesFields(PARSER); + ParsedPercentiles.declarePercentilesFields(PARSER); } public static ParsedTDigestPercentileRanks fromXContent(XContentParser parser, String name) throws IOException { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentiles.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentiles.java new file mode 100644 index 0000000000000..bdd2d6b810a2c --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentiles.java @@ -0,0 +1,57 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.percentiles.tdigest; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentiles; +import org.elasticsearch.search.aggregations.metrics.percentiles.Percentiles; + +import java.io.IOException; + +public class ParsedTDigestPercentiles extends ParsedPercentiles implements Percentiles { + + @Override + protected String getType() { + return InternalTDigestPercentiles.NAME; + } + + @Override + public double percentile(double percent) { + return getPercentile(percent); + } + + @Override + public String percentileAsString(double percent) { + return getPercentileAsString(percent); + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedTDigestPercentiles.class.getSimpleName(), true, ParsedTDigestPercentiles::new); + static { + ParsedPercentiles.declarePercentilesFields(PARSER); + } + + public static ParsedTDigestPercentiles fromXContent(XContentParser parser, String name) throws IOException { + ParsedTDigestPercentiles aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index e3584f5a85c59..c2b547b2d93ec 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -33,7 +33,6 @@ import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.SearchModule; import org.elasticsearch.search.aggregations.metrics.avg.AvgAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.avg.ParsedAvg; @@ -44,9 +43,13 @@ import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.min.ParsedMin; import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.InternalHDRPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.InternalHDRPercentiles; import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.ParsedHDRPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.ParsedHDRPercentiles; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentiles; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentiles; import org.elasticsearch.search.aggregations.metrics.sum.ParsedSum; import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.valuecount.ParsedValueCount; @@ -82,7 +85,9 @@ public abstract class InternalAggregationTestCase static List getNamedXContents() { Map> namedXContents = new HashMap<>(); namedXContents.put(CardinalityAggregationBuilder.NAME, (p, c) -> ParsedCardinality.fromXContent(p, (String) c)); + namedXContents.put(InternalHDRPercentiles.NAME, (p, c) -> ParsedHDRPercentiles.fromXContent(p, (String) c)); namedXContents.put(InternalHDRPercentileRanks.NAME, (p, c) -> ParsedHDRPercentileRanks.fromXContent(p, (String) c)); + namedXContents.put(InternalTDigestPercentiles.NAME, (p, c) -> ParsedTDigestPercentiles.fromXContent(p, (String) c)); namedXContents.put(InternalTDigestPercentileRanks.NAME, (p, c) -> ParsedTDigestPercentileRanks.fromXContent(p, (String) c)); namedXContents.put(MinAggregationBuilder.NAME, (p, c) -> ParsedMin.fromXContent(p, (String) c)); namedXContents.put(MaxAggregationBuilder.NAME, (p, c) -> ParsedMax.fromXContent(p, (String) c)); @@ -189,7 +194,6 @@ protected NamedXContentRegistry xContentRegistry() { } public final void testFromXContent() throws IOException { - final NamedXContentRegistry xContentRegistry = xContentRegistry(); final T aggregation = createTestInstance(); //norelease Remove this assumption when all aggregations can be parsed back. @@ -201,8 +205,33 @@ public final void testFromXContent() throws IOException { final XContentType xContentType = randomFrom(XContentType.values()); final BytesReference originalBytes = toShuffledXContent(aggregation, xContentType, params, humanReadable); + final Aggregation parsedAggregation = parse(aggregation, xContentType, humanReadable, randomBoolean()); + + final BytesReference parsedBytes = toXContent((ToXContent) parsedAggregation, xContentType, params, humanReadable); + assertToXContentEquivalent(originalBytes, parsedBytes, xContentType); + assertFromXContent(aggregation, (ParsedAggregation) parsedAggregation); + } + + //norelease TODO make abstract + protected void assertFromXContent(T aggregation, ParsedAggregation parsedAggregation) { + } + + @SuppressWarnings("unchecked") + protected

P parse(final InternalAggregation aggregation, + final XContentType xContentType, + final boolean humanReadable, + final boolean shuffled) throws IOException { + + final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); + final BytesReference originalBytes; + if (shuffled) { + originalBytes = toShuffledXContent(aggregation, xContentType, params, humanReadable); + } else { + originalBytes = toXContent(aggregation, xContentType, params, humanReadable); + } + Aggregation parsedAggregation; - try (XContentParser parser = xContentType.xContent().createParser(xContentRegistry, originalBytes)) { + try (XContentParser parser = createParser(xContentType.xContent(), originalBytes)) { assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); @@ -222,15 +251,8 @@ public final void testFromXContent() throws IOException { assertTrue(parsedAggregation instanceof ParsedAggregation); assertEquals(aggregation.getType(), ((ParsedAggregation) parsedAggregation).getType()); - - final BytesReference parsedBytes = toXContent((ToXContent) parsedAggregation, xContentType, params, humanReadable); - assertToXContentEquivalent(originalBytes, parsedBytes, xContentType); - assertFromXContent(aggregation, (ParsedAggregation) parsedAggregation); } - } - - //norelease TODO make abstract - protected void assertFromXContent(T aggregation, ParsedAggregation parsedAggregation) { + return (P) parsedAggregation; } /** diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java new file mode 100644 index 0000000000000..0859a5111778d --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java @@ -0,0 +1,84 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.percentiles; + +import com.carrotsearch.randomizedtesting.annotations.Repeat; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.junit.Before; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public abstract class AbstractPercentilesTestCase> + extends InternalAggregationTestCase { + + private double[] percents; + private boolean keyed; + private DocValueFormat docValueFormat; + + @Before + public void init() { + percents = randomPercents(); + keyed = randomBoolean(); + docValueFormat = randomNumericDocValueFormat(); + } + + @Override + protected T createTestInstance(String name, List pipelineAggregators, Map metaData) { + int numValues = randomInt(100); + double[] values = new double[numValues]; + for (int i = 0; i < numValues; ++i) { + values[i] = randomDouble(); + } + return createTestInstance(name, pipelineAggregators, metaData, keyed, docValueFormat, percents, values); + } + + protected abstract T createTestInstance(String name, List pipelineAggregators, Map metaData, + boolean keyed, DocValueFormat format, double[] percents, double[] values); + + protected abstract Class implementationClass(); + + @Repeat(iterations = 1000) + public void testPercentilesIterators() throws IOException { + final T aggregation = createTestInstance(); + final Iterable parsedAggregation = parse(aggregation, randomFrom(XContentType.values()), randomBoolean(), false); + + Iterator it = aggregation.iterator(); + Iterator parsedIt = parsedAggregation.iterator(); + while (it.hasNext()) { + assertEquals(it.next(), parsedIt.next()); + } + } + + private static double[] randomPercents() { + List randomCdfValues = randomSubsetOf(randomIntBetween(1, 7), 0.01d, 0.05d, 0.25d, 0.50d, 0.75d, 0.95d, 0.99d); + double[] percents = new double[randomCdfValues.size()]; + for (int i = 0; i < randomCdfValues.size(); i++) { + percents[i] = randomCdfValues.get(i); + } + return percents; + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesRanksTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesRanksTestCase.java index be7d55e5447e4..f45b7cce51e37 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesRanksTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesRanksTestCase.java @@ -19,92 +19,24 @@ package org.elasticsearch.search.aggregations.metrics.percentiles; -import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentType; -import org.elasticsearch.rest.action.search.RestSearchAction; -import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.InternalAggregation; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.ParsedAggregation; -import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import java.io.IOException; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import static java.util.Collections.singletonMap; -import static org.elasticsearch.common.xcontent.XContentHelper.toXContent; - -public abstract class InternalPercentilesRanksTestCase extends InternalAggregationTestCase { - - @Override - protected final T createTestInstance(String name, List pipelineAggregators, Map metaData) { - final boolean keyed = randomBoolean(); - final DocValueFormat format = randomFrom(DocValueFormat.RAW, new DocValueFormat.Decimal("###.##")); - List randomCdfValues = randomSubsetOf(randomIntBetween(1, 5), 0.01d, 0.05d, 0.25d, 0.50d, 0.75d, 0.95d, 0.99d); - double[] cdfValues = new double[randomCdfValues.size()]; - for (int i = 0; i < randomCdfValues.size(); i++) { - cdfValues[i] = randomCdfValues.get(i); - } - return createTestInstance(name, pipelineAggregators, metaData, cdfValues, keyed, format); - } - - protected abstract T createTestInstance(String name, List aggregators, Map metadata, - double[] cdfValues, boolean keyed, DocValueFormat format); +public abstract class InternalPercentilesRanksTestCase + extends AbstractPercentilesTestCase { @Override protected final void assertFromXContent(T aggregation, ParsedAggregation parsedAggregation) { - assertTrue(aggregation instanceof PercentileRanks); - PercentileRanks percentileRanks = (PercentileRanks) aggregation; - assertTrue(parsedAggregation instanceof PercentileRanks); PercentileRanks parsedPercentileRanks = (PercentileRanks) parsedAggregation; - for (Percentile percentile : percentileRanks) { + for (Percentile percentile : aggregation) { Double value = percentile.getValue(); - assertEquals(percentileRanks.percent(value), parsedPercentileRanks.percent(value), 0); - assertEquals(percentileRanks.percentAsString(value), parsedPercentileRanks.percentAsString(value)); + assertEquals(aggregation.percent(value), parsedPercentileRanks.percent(value), 0); + assertEquals(aggregation.percentAsString(value), parsedPercentileRanks.percentAsString(value)); } - Class parsedClass = parsedParsedPercentileRanksClass(); - assertNotNull(parsedClass); - assertTrue(parsedClass.isInstance(parsedAggregation)); + Class parsedClass = implementationClass(); + assertTrue(parsedClass != null && parsedClass.isInstance(parsedAggregation)); } - - public void testPercentilesRanksIterators() throws IOException { - final T aggregation = createTestInstance(); - - final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); - final XContentType xContentType = randomFrom(XContentType.values()); - final BytesReference originalBytes = toXContent(aggregation, xContentType, params, randomBoolean()); - - Aggregation parsedAggregation; - try (XContentParser parser = xContentType.xContent().createParser(xContentRegistry(), originalBytes)) { - assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); - assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); - - String currentName = parser.currentName(); - int i = currentName.indexOf(InternalAggregation.TYPED_KEYS_DELIMITER); - String aggType = currentName.substring(0, i); - String aggName = currentName.substring(i + 1); - - parsedAggregation = parser.namedObject(Aggregation.class, aggType, aggName); - - assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken()); - assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); - assertNull(parser.nextToken()); - } - - final Iterator it = ((PercentileRanks) aggregation).iterator(); - final Iterator parsedIt = ((PercentileRanks) parsedAggregation).iterator(); - while (it.hasNext()) { - assertEquals(it.next(), parsedIt.next()); - } - } - - protected abstract Class parsedParsedPercentileRanksClass(); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesTestCase.java index 0cfa07538e424..404c033f11af8 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesTestCase.java @@ -19,43 +19,23 @@ package org.elasticsearch.search.aggregations.metrics.percentiles; -import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregation; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; -import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import org.junit.Before; +import org.elasticsearch.search.aggregations.ParsedAggregation; -import java.util.List; -import java.util.Map; - -public abstract class InternalPercentilesTestCase extends InternalAggregationTestCase { - - private double[] percents; - - @Before - public void init() { - percents = randomPercents(); - } +public abstract class InternalPercentilesTestCase extends AbstractPercentilesTestCase { @Override - protected T createTestInstance(String name, List pipelineAggregators, Map metaData) { - int numValues = randomInt(100); - double[] values = new double[numValues]; - for (int i = 0; i < numValues; ++i) { - values[i] = randomDouble(); + protected final void assertFromXContent(T aggregation, ParsedAggregation parsedAggregation) { + assertTrue(parsedAggregation instanceof Percentiles); + Percentiles parsedPercentiles = (Percentiles) parsedAggregation; + + for (Percentile percentile : aggregation) { + Double percent = percentile.getPercent(); + assertEquals(aggregation.percentile(percent), parsedPercentiles.percentile(percent), 0); + assertEquals(aggregation.percentileAsString(percent), parsedPercentiles.percentileAsString(percent)); } - return createTestInstance(name, pipelineAggregators, metaData, randomBoolean(), DocValueFormat.RAW, percents, values); - } - - protected abstract T createTestInstance(String name, List pipelineAggregators, Map metaData, - boolean keyed, DocValueFormat format, double[] percents, double[] values); - private static double[] randomPercents() { - List randomCdfValues = randomSubsetOf(randomIntBetween(1, 7), 0.01d, 0.05d, 0.25d, 0.50d, 0.75d, 0.95d, 0.99d); - double[] percents = new double[randomCdfValues.size()]; - for (int i = 0; i < randomCdfValues.size(); i++) { - percents[i] = randomCdfValues.get(i); - } - return percents; + Class parsedClass = implementationClass(); + assertTrue(parsedClass != null && parsedClass.isInstance(parsedAggregation)); } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesRanksTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesRanksTests.java index a3fe4d62d14e7..d9379edefefa7 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesRanksTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesRanksTests.java @@ -23,9 +23,10 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.metrics.percentiles.InternalPercentilesRanksTestCase; -import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentiles; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -33,9 +34,12 @@ public class InternalHDRPercentilesRanksTests extends InternalPercentilesRanksTe @Override protected InternalHDRPercentileRanks createTestInstance(String name, List aggregators, Map metadata, - double[] cdfValues, boolean keyed, DocValueFormat format) { - DoubleHistogram state = new DoubleHistogram(3); - return new InternalHDRPercentileRanks(name, cdfValues, state, keyed, format, aggregators, metadata); + boolean keyed, DocValueFormat format, double[] percents, double[] values) { + + final DoubleHistogram state = new DoubleHistogram(3); + Arrays.stream(values).forEach(state::recordValue); + + return new InternalHDRPercentileRanks(name, percents, state, keyed, format, aggregators, metadata); } @Override @@ -54,7 +58,7 @@ protected Reader instanceReader() { } @Override - protected Class parsedParsedPercentileRanksClass() { + protected Class implementationClass() { return ParsedHDRPercentileRanks.class; } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesTests.java index bff026d5cf4b0..5a24e0dfbf233 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.metrics.percentiles.InternalPercentilesTestCase; +import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentiles; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.Arrays; @@ -57,4 +58,9 @@ protected void assertReduced(InternalHDRPercentiles reduced, List instanceReader() { return InternalHDRPercentiles::new; } + + @Override + protected Class implementationClass() { + return ParsedHDRPercentiles.class; + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java index 30d416763c1dc..f8698fda2cb7a 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java @@ -22,9 +22,10 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.metrics.percentiles.InternalPercentilesRanksTestCase; -import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentiles; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -33,13 +34,12 @@ public class InternalTDigestPercentilesRanksTests extends InternalPercentilesRan @Override protected InternalTDigestPercentileRanks createTestInstance(String name, List aggregators, Map metadata, - double[] cdfValues, boolean keyed, DocValueFormat format) { - TDigestState state = new TDigestState(100); - int numValues = randomInt(100); - for (int i = 0; i < numValues; ++i) { - state.add(randomDouble()); - } - return new InternalTDigestPercentileRanks(name, cdfValues, state, keyed, format, aggregators, metadata); + boolean keyed, DocValueFormat format, double[] percents, double[] values) { + final TDigestState state = new TDigestState(100); + Arrays.stream(values).forEach(state::add); + + assertEquals(state.centroidCount(), values.length); + return new InternalTDigestPercentileRanks(name, percents, state, keyed, format, aggregators, metadata); } @Override @@ -71,7 +71,7 @@ protected Reader instanceReader() { } @Override - protected Class parsedParsedPercentileRanksClass() { + protected Class implementationClass() { return ParsedTDigestPercentileRanks.class; } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesTests.java index f2db4a48530ed..867469592e969 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.metrics.percentiles.InternalPercentilesTestCase; +import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentiles; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.Arrays; @@ -64,4 +65,9 @@ protected void assertReduced(InternalTDigestPercentiles reduced, List instanceReader() { return InternalTDigestPercentiles::new; } + + @Override + protected Class implementationClass() { + return ParsedTDigestPercentiles.class; + } } From 2b14db9b70a16349d8541e11bbc14497a087dea1 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Thu, 20 Apr 2017 10:44:12 +0200 Subject: [PATCH 019/619] Remove @Repeat(iterations = 1000) in tests --- .../metrics/percentiles/AbstractPercentilesTestCase.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java index 0859a5111778d..b8648f5199ceb 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java @@ -19,7 +19,6 @@ package org.elasticsearch.search.aggregations.metrics.percentiles; -import com.carrotsearch.randomizedtesting.annotations.Repeat; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregation; @@ -61,7 +60,6 @@ protected abstract T createTestInstance(String name, List pi protected abstract Class implementationClass(); - @Repeat(iterations = 1000) public void testPercentilesIterators() throws IOException { final T aggregation = createTestInstance(); final Iterable parsedAggregation = parse(aggregation, randomFrom(XContentType.values()), randomBoolean(), false); From 6e22a1e9bad79a47d4877382afe05249a46d4b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Thu, 20 Apr 2017 11:49:09 +0200 Subject: [PATCH 020/619] Add parsing for InternalBucketMetricValue (#24182) --- .../InternalBucketMetricValue.java | 19 ++++- .../ParsedBucketMetricValue.java | 73 ++++++++++++++++++ .../InternalAggregationTestCase.java | 3 + .../InternalBucketMetricValueTests.java | 75 +++++++++++++++++++ 4 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/ParsedBucketMetricValue.java create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/InternalBucketMetricValueTests.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/InternalBucketMetricValue.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/InternalBucketMetricValue.java index 9c9da2f26bd53..95b3782bd54f4 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/InternalBucketMetricValue.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/InternalBucketMetricValue.java @@ -19,6 +19,7 @@ package org.elasticsearch.search.aggregations.pipeline.bucketmetrics; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -28,11 +29,14 @@ import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.io.IOException; +import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Objects; public class InternalBucketMetricValue extends InternalNumericMetricsAggregation.SingleValue implements BucketMetricValue { public static final String NAME = "bucket_metric_value"; + static final ParseField KEYS_FIELD = new ParseField("keys"); private double value; private String[] keys; @@ -88,7 +92,7 @@ public Object getProperty(List path) { return this; } else if (path.size() == 1 && "value".equals(path.get(0))) { return value(); - } else if (path.size() == 1 && "keys".equals(path.get(0))) { + } else if (path.size() == 1 && KEYS_FIELD.getPreferredName().equals(path.get(0))) { return keys(); } else { throw new IllegalArgumentException("path not supported for [" + getName() + "]: " + path); @@ -102,7 +106,7 @@ public XContentBuilder doXContentBody(XContentBuilder builder, Params params) th if (hasValue && format != DocValueFormat.RAW) { builder.field(CommonFields.VALUE_AS_STRING.getPreferredName(), format.format(value)); } - builder.startArray("keys"); + builder.startArray(KEYS_FIELD.getPreferredName()); for (String key : keys) { builder.value(key); } @@ -110,4 +114,15 @@ public XContentBuilder doXContentBody(XContentBuilder builder, Params params) th return builder; } + @Override + protected int doHashCode() { + return Objects.hash(value, Arrays.hashCode(keys)); + } + + @Override + protected boolean doEquals(Object obj) { + InternalBucketMetricValue other = (InternalBucketMetricValue) obj; + return Objects.equals(value, other.value) + && Arrays.equals(keys, other.keys); + } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/ParsedBucketMetricValue.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/ParsedBucketMetricValue.java new file mode 100644 index 0000000000000..9008c56c5caaa --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/ParsedBucketMetricValue.java @@ -0,0 +1,73 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.pipeline.bucketmetrics; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.ParsedSingleValueNumericMetricsAggregation; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +public class ParsedBucketMetricValue extends ParsedSingleValueNumericMetricsAggregation implements BucketMetricValue { + + private List keys = Collections.emptyList(); + + @Override + public String[] keys() { + return this.keys.toArray(new String[keys.size()]); + } + + @Override + protected String getType() { + return InternalBucketMetricValue.NAME; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + boolean hasValue = !Double.isInfinite(value); + builder.field(CommonFields.VALUE.getPreferredName(), hasValue ? value : null); + if (hasValue && valueAsString != null) { + builder.field(CommonFields.VALUE_AS_STRING.getPreferredName(), valueAsString); + } + builder.startArray(InternalBucketMetricValue.KEYS_FIELD.getPreferredName()); + for (String key : keys) { + builder.value(key); + } + builder.endArray(); + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>( + ParsedBucketMetricValue.class.getSimpleName(), true, ParsedBucketMetricValue::new); + + static { + declareSingleValueFields(PARSER, Double.NEGATIVE_INFINITY); + PARSER.declareStringArray((agg, value) -> agg.keys = value, InternalBucketMetricValue.KEYS_FIELD); + } + + public static ParsedBucketMetricValue fromXContent(XContentParser parser, final String name) { + ParsedBucketMetricValue bucketMetricValue = PARSER.apply(parser, null); + bucketMetricValue.setName(name); + return bucketMetricValue; + } +} \ No newline at end of file diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index c2b547b2d93ec..09b05dab84bd8 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -57,6 +57,8 @@ import org.elasticsearch.search.aggregations.pipeline.InternalSimpleValue; import org.elasticsearch.search.aggregations.pipeline.ParsedSimpleValue; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.InternalBucketMetricValue; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.ParsedBucketMetricValue; import org.elasticsearch.search.aggregations.pipeline.derivative.DerivativePipelineAggregationBuilder; import org.elasticsearch.search.aggregations.pipeline.derivative.ParsedDerivative; import org.elasticsearch.test.AbstractWireSerializingTestCase; @@ -96,6 +98,7 @@ static List getNamedXContents() { namedXContents.put(ValueCountAggregationBuilder.NAME, (p, c) -> ParsedValueCount.fromXContent(p, (String) c)); namedXContents.put(InternalSimpleValue.NAME, (p, c) -> ParsedSimpleValue.fromXContent(p, (String) c)); namedXContents.put(DerivativePipelineAggregationBuilder.NAME, (p, c) -> ParsedDerivative.fromXContent(p, (String) c)); + namedXContents.put(InternalBucketMetricValue.NAME, (p, c) -> ParsedBucketMetricValue.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/InternalBucketMetricValueTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/InternalBucketMetricValueTests.java new file mode 100644 index 0000000000000..8de1700141ba3 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/InternalBucketMetricValueTests.java @@ -0,0 +1,75 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.pipeline.bucketmetrics; + +import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class InternalBucketMetricValueTests extends InternalAggregationTestCase { + + @Override + protected InternalBucketMetricValue createTestInstance(String name, List pipelineAggregators, + Map metaData) { + double value = frequently() ? randomDoubleBetween(-10000, 100000, true) + : randomFrom(new Double[] { Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN }); + String[] keys = new String[randomIntBetween(0, 5)]; + for (int i = 0; i < keys.length; i++) { + keys[i] = randomAlphaOfLength(10); + } + return new InternalBucketMetricValue(name, keys, value, randomNumericDocValueFormat(), pipelineAggregators, metaData); + } + + @Override + public void testReduceRandom() { + expectThrows(UnsupportedOperationException.class, + () -> createTestInstance("name", Collections.emptyList(), null).reduce(null, + null)); + } + + @Override + protected void assertReduced(InternalBucketMetricValue reduced, List inputs) { + // no test since reduce operation is unsupported + } + + @Override + protected Reader instanceReader() { + return InternalBucketMetricValue::new; + } + + @Override + protected void assertFromXContent(InternalBucketMetricValue bucketMetricValue, ParsedAggregation parsedAggregation) { + BucketMetricValue parsed = ((BucketMetricValue) parsedAggregation); + assertArrayEquals(bucketMetricValue.keys(), parsed.keys()); + if (Double.isInfinite(bucketMetricValue.value()) == false) { + assertEquals(bucketMetricValue.value(), parsed.value(), 0); + assertEquals(bucketMetricValue.getValueAsString(), parsed.getValueAsString()); + } else { + // we write Double.NEGATIVE_INFINITY and Double.POSITIVE_INFINITY to xContent as 'null', so we + // cannot differentiate between them. Also we cannot recreate the exact String representation + assertEquals(parsed.value(), Double.NEGATIVE_INFINITY, 0); + } + } +} From d0df1ed193a86f1ddb6662b976e8cb89ae6fe9c8 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Thu, 20 Apr 2017 13:22:50 +0200 Subject: [PATCH 021/619] [Test] Always check the XContent equivalent when parsing aggregations (#24208) In InternalAggregationTestCase, we can check that the internal aggregation and the parsed aggregation always produce the same XContent even if the original internal aggregation has been shuffled or not. --- .../InternalAggregationTestCase.java | 23 ++++++++----------- .../AbstractPercentilesTestCase.java | 2 +- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index 09b05dab84bd8..fd6b9ecdc44c0 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -203,15 +203,7 @@ public final void testFromXContent() throws IOException { assumeTrue("This test does not support the aggregation type yet", getNamedXContents().stream().filter(entry -> entry.name.match(aggregation.getType())).count() > 0); - final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); - final boolean humanReadable = randomBoolean(); - final XContentType xContentType = randomFrom(XContentType.values()); - final BytesReference originalBytes = toShuffledXContent(aggregation, xContentType, params, humanReadable); - - final Aggregation parsedAggregation = parse(aggregation, xContentType, humanReadable, randomBoolean()); - - final BytesReference parsedBytes = toXContent((ToXContent) parsedAggregation, xContentType, params, humanReadable); - assertToXContentEquivalent(originalBytes, parsedBytes, xContentType); + final Aggregation parsedAggregation = parseAndAssert(aggregation, randomBoolean()); assertFromXContent(aggregation, (ParsedAggregation) parsedAggregation); } @@ -220,12 +212,13 @@ protected void assertFromXContent(T aggregation, ParsedAggregation parsedAggrega } @SuppressWarnings("unchecked") - protected

P parse(final InternalAggregation aggregation, - final XContentType xContentType, - final boolean humanReadable, - final boolean shuffled) throws IOException { + protected

P parseAndAssert(final InternalAggregation aggregation, + final boolean shuffled) throws IOException { final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); + final XContentType xContentType = randomFrom(XContentType.values()); + final boolean humanReadable = randomBoolean(); + final BytesReference originalBytes; if (shuffled) { originalBytes = toShuffledXContent(aggregation, xContentType, params, humanReadable); @@ -255,6 +248,10 @@ protected

P parse(final InternalAggregation aggreg assertTrue(parsedAggregation instanceof ParsedAggregation); assertEquals(aggregation.getType(), ((ParsedAggregation) parsedAggregation).getType()); } + + BytesReference parsedBytes = toXContent((ToXContent) parsedAggregation, xContentType, params, humanReadable); + assertToXContentEquivalent(originalBytes, parsedBytes, xContentType); + return (P) parsedAggregation; } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java index b8648f5199ceb..510620d12aa59 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java @@ -62,7 +62,7 @@ protected abstract T createTestInstance(String name, List pi public void testPercentilesIterators() throws IOException { final T aggregation = createTestInstance(); - final Iterable parsedAggregation = parse(aggregation, randomFrom(XContentType.values()), randomBoolean(), false); + final Iterable parsedAggregation = parseAndAssert(aggregation, false); Iterator it = aggregation.iterator(); Iterator parsedIt = parsedAggregation.iterator(); From c8fc30a999b748417988fecf806bf024baf7a5fc Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Thu, 20 Apr 2017 13:36:16 +0200 Subject: [PATCH 022/619] [Test] Expose AbstractPercentilesTestCase.randomPercents() --- .../metrics/percentiles/AbstractPercentilesTestCase.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java index 510620d12aa59..0223db0359994 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java @@ -19,7 +19,6 @@ package org.elasticsearch.search.aggregations.metrics.percentiles; -import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; @@ -71,7 +70,7 @@ public void testPercentilesIterators() throws IOException { } } - private static double[] randomPercents() { + protected static double[] randomPercents() { List randomCdfValues = randomSubsetOf(randomIntBetween(1, 7), 0.01d, 0.05d, 0.25d, 0.50d, 0.75d, 0.95d, 0.99d); double[] percents = new double[randomCdfValues.size()]; for (int i = 0; i < randomCdfValues.size(); i++) { From f322f537e41273fd33eb06752c5adcfa5850323f Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Fri, 21 Apr 2017 10:32:33 +0200 Subject: [PATCH 023/619] Speed up parsing of large `terms` queries. (#24210) The addition of the normalization feature on keywords slowed down the parsing of large `terms` queries since all terms now have to go through normalization. However this can be avoided in the default case that the analyzer is a `keyword` analyzer since all that normalization will do is a UTF8 conversion. Using `Analyzer.normalize` for that is a bit overkill and could be skipped. --- .../elasticsearch/index/mapper/KeywordFieldMapper.java | 9 +++++++++ .../index/mapper/KeywordFieldTypeTests.java | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/core/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java b/core/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java index f988a68d5efcc..3e09199abca0c 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java @@ -236,6 +236,15 @@ public Object valueForDisplay(Object value) { @Override protected BytesRef indexedValueForSearch(Object value) { + if (searchAnalyzer() == Lucene.KEYWORD_ANALYZER) { + // keyword analyzer with the default attribute source which encodes terms using UTF8 + // in that case we skip normalization, which may be slow if there many terms need to + // parse (eg. large terms query) since Analyzer.normalize involves things like creating + // attributes through reflection + // This if statement will be used whenever a normalizer is NOT configured + return super.indexedValueForSearch(value); + } + if (value == null) { return null; } diff --git a/core/src/test/java/org/elasticsearch/index/mapper/KeywordFieldTypeTests.java b/core/src/test/java/org/elasticsearch/index/mapper/KeywordFieldTypeTests.java index 5c418b7ce265e..809ceb5831004 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/KeywordFieldTypeTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/KeywordFieldTypeTests.java @@ -150,4 +150,13 @@ public void testFuzzyQuery() { () -> ft.fuzzyQuery("foo", Fuzziness.fromEdits(2), 1, 50, true)); assertEquals("Cannot search on field [field] since it is not indexed.", e.getMessage()); } + + public void testNormalizeQueries() { + MappedFieldType ft = createDefaultFieldType(); + ft.setName("field"); + ft.setSearchAnalyzer(Lucene.KEYWORD_ANALYZER); + assertEquals(new TermQuery(new Term("field", new BytesRef("FOO"))), ft.termQuery("FOO", null)); + ft.setSearchAnalyzer(Lucene.STANDARD_ANALYZER); + assertEquals(new TermQuery(new Term("field", new BytesRef("foo"))), ft.termQuery("FOO", null)); + } } From 81b64ed587817f9f9398dc38d0db2c3fac7b5af7 Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Fri, 21 Apr 2017 10:33:02 +0200 Subject: [PATCH 024/619] IndicesQueryCache should delegate the scorerSupplier method. (#24209) Otherwise the range improvements that we did on range queries would not work. This is similar to https://issues.apache.org/jira/browse/LUCENE-7749. --- .../indices/IndicesQueryCache.java | 7 ++ .../indices/IndicesQueryCacheTests.java | 79 ++++++++++++++++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/indices/IndicesQueryCache.java b/core/src/main/java/org/elasticsearch/indices/IndicesQueryCache.java index 3fda3d3f806d6..479208a5038cc 100644 --- a/core/src/main/java/org/elasticsearch/indices/IndicesQueryCache.java +++ b/core/src/main/java/org/elasticsearch/indices/IndicesQueryCache.java @@ -28,6 +28,7 @@ import org.apache.lucene.search.QueryCache; import org.apache.lucene.search.QueryCachingPolicy; import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.ScorerSupplier; import org.apache.lucene.search.Weight; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.lucene.ShardCoreKeyMap; @@ -145,6 +146,12 @@ public Scorer scorer(LeafReaderContext context) throws IOException { return in.scorer(context); } + @Override + public ScorerSupplier scorerSupplier(LeafReaderContext context) throws IOException { + shardKeyMap.add(context.reader()); + return in.scorerSupplier(context); + } + @Override public BulkScorer bulkScorer(LeafReaderContext context) throws IOException { shardKeyMap.add(context.reader()); diff --git a/core/src/test/java/org/elasticsearch/indices/IndicesQueryCacheTests.java b/core/src/test/java/org/elasticsearch/indices/IndicesQueryCacheTests.java index 10f098787c027..24cbed2d4bcaa 100644 --- a/core/src/test/java/org/elasticsearch/indices/IndicesQueryCacheTests.java +++ b/core/src/test/java/org/elasticsearch/indices/IndicesQueryCacheTests.java @@ -23,25 +23,28 @@ import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; import org.apache.lucene.search.ConstantScoreScorer; import org.apache.lucene.search.ConstantScoreWeight; import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.QueryCachingPolicy; import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.ScorerSupplier; import org.apache.lucene.search.Weight; import org.apache.lucene.store.Directory; import org.apache.lucene.util.IOUtils; import org.elasticsearch.common.lucene.index.ElasticsearchDirectoryReader; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.cache.query.QueryCacheStats; import org.elasticsearch.index.shard.ShardId; -import org.elasticsearch.indices.IndicesQueryCache; import org.elasticsearch.test.ESTestCase; import java.io.IOException; +import java.util.Set; public class IndicesQueryCacheTests extends ESTestCase { @@ -328,4 +331,76 @@ public void testStatsOnEviction() throws IOException { cache.close(); // this triggers some assertions } + private static class DummyWeight extends Weight { + + private final Weight weight; + private boolean scorerCalled; + private boolean scorerSupplierCalled; + + DummyWeight(Weight weight) { + super(weight.getQuery()); + this.weight = weight; + } + + @Override + public void extractTerms(Set terms) { + weight.extractTerms(terms); + } + + @Override + public Explanation explain(LeafReaderContext context, int doc) throws IOException { + return weight.explain(context, doc); + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException { + scorerCalled = true; + return weight.scorer(context); + } + + @Override + public ScorerSupplier scorerSupplier(LeafReaderContext context) throws IOException { + scorerSupplierCalled = true; + return weight.scorerSupplier(context); + } + + } + + public void testDelegatesScorerSupplier() throws Exception { + Directory dir = newDirectory(); + IndexWriter w = new IndexWriter(dir, newIndexWriterConfig()); + w.addDocument(new Document()); + DirectoryReader r = DirectoryReader.open(w); + w.close(); + ShardId shard = new ShardId("index", "_na_", 0); + r = ElasticsearchDirectoryReader.wrap(r, shard); + IndexSearcher s = new IndexSearcher(r); + s.setQueryCachingPolicy(new QueryCachingPolicy() { + @Override + public boolean shouldCache(Query query) throws IOException { + return false; // never cache + } + @Override + public void onUse(Query query) {} + }); + + Settings settings = Settings.builder() + .put(IndicesQueryCache.INDICES_CACHE_QUERY_COUNT_SETTING.getKey(), 10) + .put(IndicesQueryCache.INDICES_QUERIES_CACHE_ALL_SEGMENTS_SETTING.getKey(), true) + .build(); + IndicesQueryCache cache = new IndicesQueryCache(settings); + s.setQueryCache(cache); + Query query = new MatchAllDocsQuery(); + final DummyWeight weight = new DummyWeight(s.createNormalizedWeight(query, false)); + final Weight cached = cache.doCache(weight, s.getQueryCachingPolicy()); + assertNotSame(weight, cached); + assertFalse(weight.scorerCalled); + assertFalse(weight.scorerSupplierCalled); + cached.scorerSupplier(s.getIndexReader().leaves().get(0)); + assertFalse(weight.scorerCalled); + assertTrue(weight.scorerSupplierCalled); + IOUtils.close(r, dir); + cache.onClose(shard); + cache.close(); + } } From c8ad26edc957ef30a8166df8aef9b241fdfcfb7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Fri, 21 Apr 2017 10:38:09 +0200 Subject: [PATCH 025/619] Tests: Extend InternalStatsTests (#24212) Currently we don't test for count = 0 which will make a difference when adding tests for parsing for the high level rest client. Also min/max/sum should also be tested with negative values and on a larger range. --- .../aggregations/metrics/InternalStatsTests.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java index db64bb8c65c76..c99d208581c13 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java @@ -32,13 +32,12 @@ public class InternalStatsTests extends InternalAggregationTestCase pipelineAggregators, Map metaData) { - long count = randomIntBetween(1, 50); - double[] minMax = new double[2]; - minMax[0] = randomDouble(); - minMax[0] = randomDouble(); - double sum = randomDoubleBetween(0, 100, true); - return new InternalStats(name, count, sum, minMax[0], minMax[1], DocValueFormat.RAW, - pipelineAggregators, Collections.emptyMap()); + long count = frequently() ? randomIntBetween(1, Integer.MAX_VALUE) : 0; + double min = randomDoubleBetween(-1000000, 1000000, true); + double max = randomDoubleBetween(-1000000, 1000000, true); + double sum = randomDoubleBetween(-1000000, 1000000, true); + DocValueFormat format = randomNumericDocValueFormat(); + return new InternalStats(name, count, sum, min, max, format, pipelineAggregators, Collections.emptyMap()); } @Override @@ -58,7 +57,7 @@ protected void assertReduced(InternalStats reduced, List inputs) expectedSum += stats.getSum(); } assertEquals(expectedCount, reduced.getCount()); - assertEquals(expectedSum, reduced.getSum(), 1e-10); + assertEquals(expectedSum, reduced.getSum(), 1e-7); assertEquals(expectedMin, reduced.getMin(), 0d); assertEquals(expectedMax, reduced.getMax(), 0d); } From 2b8fa64cf74c06733a891de3cf9d0f0904d8996a Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Fri, 21 Apr 2017 10:38:36 +0200 Subject: [PATCH 026/619] ESIntegTestCase.indexRandom should not introduce types. (#24202) Since we plan on removing types, `indexRandom` should not introduce new types. This commit refactors `indexRandom` to reuse existing types. --- .../elasticsearch/test/ESIntegTestCase.java | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java index 6d15a5e164ef5..7c8d8cd1a551f 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java @@ -1338,9 +1338,6 @@ public void indexRandom(boolean forceRefresh, boolean dummyDocuments, IndexReque indexRandom(forceRefresh, dummyDocuments, Arrays.asList(builders)); } - - private static final String RANDOM_BOGUS_TYPE = "RANDOM_BOGUS_TYPE______"; - /** * Indexes the given {@link IndexRequestBuilder} instances randomly. It shuffles the given builders and either * indexes them in a blocking or async fashion. This is very useful to catch problems that relate to internal document @@ -1388,31 +1385,33 @@ public void indexRandom(boolean forceRefresh, boolean dummyDocuments, List builders) throws InterruptedException, ExecutionException { - Random random = random(); - Set indicesSet = new HashSet<>(); + Map> indicesAndTypes = new HashMap<>(); for (IndexRequestBuilder builder : builders) { - indicesSet.add(builder.request().index()); + final Set types = indicesAndTypes.computeIfAbsent(builder.request().index(), index -> new HashSet<>()); + types.add(builder.request().type()); } - Set> bogusIds = new HashSet<>(); + Set> bogusIds = new HashSet<>(); // (index, type, id) if (random.nextBoolean() && !builders.isEmpty() && dummyDocuments) { builders = new ArrayList<>(builders); - final String[] indices = indicesSet.toArray(new String[indicesSet.size()]); // inject some bogus docs final int numBogusDocs = scaledRandomIntBetween(1, builders.size() * 2); final int unicodeLen = between(1, 10); for (int i = 0; i < numBogusDocs; i++) { - String id = randomRealisticUnicodeOfLength(unicodeLen) + Integer.toString(dummmyDocIdGenerator.incrementAndGet()); - String index = RandomPicks.randomFrom(random, indices); - bogusIds.add(new Tuple<>(index, id)); - builders.add(client().prepareIndex(index, RANDOM_BOGUS_TYPE, id).setSource("{}", XContentType.JSON)); + String id = "bogus_doc_" + randomRealisticUnicodeOfLength(unicodeLen) + Integer.toString(dummmyDocIdGenerator.incrementAndGet()); + Map.Entry> indexAndTypes = RandomPicks.randomFrom(random, indicesAndTypes.entrySet()); + String index = indexAndTypes.getKey(); + String type = RandomPicks.randomFrom(random, indexAndTypes.getValue()); + bogusIds.add(Arrays.asList(index, type, id)); + // We configure a routing key in case the mapping requires it + builders.add(client().prepareIndex(index, type, id).setSource("{}", XContentType.JSON).setRouting(id)); } } - final String[] indices = indicesSet.toArray(new String[indicesSet.size()]); Collections.shuffle(builders, random()); final CopyOnWriteArrayList> errors = new CopyOnWriteArrayList<>(); List inFlightAsyncOperations = new ArrayList<>(); // If you are indexing just a few documents then frequently do it one at a time. If many then frequently in bulk. + final String[] indices = indicesAndTypes.keySet().toArray(new String[0]); if (builders.size() < FREQUENT_BULK_THRESHOLD ? frequently() : builders.size() < ALWAYS_BULK_THRESHOLD ? rarely() : false) { if (frequently()) { logger.info("Index [{}] docs async: [{}] bulk: [{}]", builders.size(), true, false); @@ -1454,10 +1453,10 @@ public void indexRandom(boolean forceRefresh, boolean dummyDocuments, boolean ma assertThat(actualErrors, emptyIterable()); if (!bogusIds.isEmpty()) { // delete the bogus types again - it might trigger merges or at least holes in the segments and enforces deleted docs! - for (Tuple doc : bogusIds) { - assertEquals("failed to delete a dummy doc [" + doc.v1() + "][" + doc.v2() + "]", + for (List doc : bogusIds) { + assertEquals("failed to delete a dummy doc [" + doc.get(0) + "][" + doc.get(2) + "]", DocWriteResponse.Result.DELETED, - client().prepareDelete(doc.v1(), RANDOM_BOGUS_TYPE, doc.v2()).get().getResult()); + client().prepareDelete(doc.get(0), doc.get(1), doc.get(2)).setRouting(doc.get(2)).get().getResult()); } } if (forceRefresh) { From 3c7c4bc824520a42c9afcf29169e23540e02bf24 Mon Sep 17 00:00:00 2001 From: Colin Goodheart-Smithe Date: Fri, 21 Apr 2017 09:50:30 +0100 Subject: [PATCH 027/619] Adds declareNamedObjects methods to ConstructingObjectParser (#24219) * Adds declareNamedObjects methods to ConstructingObjectParser * Addresses review comments --- .../common/xcontent/AbstractObjectParser.java | 90 +++++++++ .../xcontent/ConstructingObjectParser.java | 100 +++++++++- .../common/xcontent/ObjectParser.java | 57 +----- .../ConstructingObjectParserTests.java | 186 ++++++++++++++++++ 4 files changed, 375 insertions(+), 58 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/AbstractObjectParser.java b/core/src/main/java/org/elasticsearch/common/xcontent/AbstractObjectParser.java index 91acb267056b0..6c0ce35b8045a 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/AbstractObjectParser.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/AbstractObjectParser.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.CheckedFunction; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.xcontent.ObjectParser.NamedObjectParser; import org.elasticsearch.common.xcontent.ObjectParser.ValueType; import org.elasticsearch.common.xcontent.json.JsonXContent; @@ -30,6 +31,7 @@ import java.util.List; import java.util.function.BiConsumer; import java.util.function.BiFunction; +import java.util.function.Consumer; /** * Superclass for {@link ObjectParser} and {@link ConstructingObjectParser}. Defines most of the "declare" methods so they can be shared. @@ -44,6 +46,94 @@ public abstract class AbstractObjectParser public abstract void declareField(BiConsumer consumer, ContextParser parser, ParseField parseField, ValueType type); + /** + * Declares named objects in the style of aggregations. These are named + * inside and object like this: + * + *

+     * 
+     * {
+     *   "aggregations": {
+     *     "name_1": { "aggregation_type": {} },
+     *     "name_2": { "aggregation_type": {} },
+     *     "name_3": { "aggregation_type": {} }
+     *     }
+     *   }
+     * }
+     * 
+     * 
+ * + * Unlike the other version of this method, "ordered" mode (arrays of + * objects) is not supported. + * + * See NamedObjectHolder in ObjectParserTests for examples of how to invoke + * this. + * + * @param consumer + * sets the values once they have been parsed + * @param namedObjectParser + * parses each named object + * @param parseField + * the field to parse + */ + public abstract void declareNamedObjects(BiConsumer> consumer, NamedObjectParser namedObjectParser, + ParseField parseField); + + /** + * Declares named objects in the style of highlighting's field element. + * These are usually named inside and object like this: + * + *
+     * 
+     * {
+     *   "highlight": {
+     *     "fields": {        <------ this one
+     *       "title": {},
+     *       "body": {},
+     *       "category": {}
+     *     }
+     *   }
+     * }
+     * 
+     * 
+ * + * but, when order is important, some may be written this way: + * + *
+     * 
+     * {
+     *   "highlight": {
+     *     "fields": [        <------ this one
+     *       {"title": {}},
+     *       {"body": {}},
+     *       {"category": {}}
+     *     ]
+     *   }
+     * }
+     * 
+     * 
+ * + * This is because json doesn't enforce ordering. Elasticsearch reads it in + * the order sent but tools that generate json are free to put object + * members in an unordered Map, jumbling them. Thus, if you care about order + * you can send the object in the second way. + * + * See NamedObjectHolder in ObjectParserTests for examples of how to invoke + * this. + * + * @param consumer + * sets the values once they have been parsed + * @param namedObjectParser + * parses each named object + * @param orderedModeCallback + * called when the named object is parsed using the "ordered" + * mode (the array of objects) + * @param parseField + * the field to parse + */ + public abstract void declareNamedObjects(BiConsumer> consumer, NamedObjectParser namedObjectParser, + Consumer orderedModeCallback, ParseField parseField); + public void declareField(BiConsumer consumer, CheckedFunction parser, ParseField parseField, ValueType type) { if (parser == null) { diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/ConstructingObjectParser.java b/core/src/main/java/org/elasticsearch/common/xcontent/ConstructingObjectParser.java index 82ee94550c161..95e6d2b97a69d 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/ConstructingObjectParser.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/ConstructingObjectParser.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; +import org.elasticsearch.common.xcontent.ObjectParser.NamedObjectParser; import org.elasticsearch.common.xcontent.ObjectParser.ValueType; import java.io.IOException; @@ -77,14 +78,14 @@ public final class ConstructingObjectParser extends AbstractObje /** * Consumer that marks a field as a required constructor argument instead of a real object field. */ - private static final BiConsumer REQUIRED_CONSTRUCTOR_ARG_MARKER = (a, b) -> { + private static final BiConsumer REQUIRED_CONSTRUCTOR_ARG_MARKER = (a, b) -> { throw new UnsupportedOperationException("I am just a marker I should never be called."); }; /** * Consumer that marks a field as an optional constructor argument instead of a real object field. */ - private static final BiConsumer OPTIONAL_CONSTRUCTOR_ARG_MARKER = (a, b) -> { + private static final BiConsumer OPTIONAL_CONSTRUCTOR_ARG_MARKER = (a, b) -> { throw new UnsupportedOperationException("I am just a marker I should never be called."); }; @@ -189,7 +190,7 @@ public void declareField(BiConsumer consumer, ContextParser void declareField(BiConsumer consumer, ContextParser void declareNamedObjects(BiConsumer> consumer, NamedObjectParser namedObjectParser, + ParseField parseField) { + if (consumer == null) { + throw new IllegalArgumentException("[consumer] is required"); + } + if (namedObjectParser == null) { + throw new IllegalArgumentException("[parser] is required"); + } + if (parseField == null) { + throw new IllegalArgumentException("[parseField] is required"); + } + + if (consumer == REQUIRED_CONSTRUCTOR_ARG_MARKER || consumer == OPTIONAL_CONSTRUCTOR_ARG_MARKER) { + /* + * Constructor arguments are detected by this "marker" consumer. It + * keeps the API looking clean even if it is a bit sleezy. We then + * build a new consumer directly against the object parser that + * triggers the "constructor arg just arrived behavior" of the + * parser. Conveniently, we can close over the position of the + * constructor in the argument list so we don't need to do any fancy + * or expensive lookups whenever the constructor args come in. + */ + int position = constructorArgInfos.size(); + boolean required = consumer == REQUIRED_CONSTRUCTOR_ARG_MARKER; + constructorArgInfos.add(new ConstructorArgInfo(parseField, required)); + objectParser.declareNamedObjects((target, v) -> target.constructorArg(position, parseField, v), namedObjectParser, parseField); + } else { + numberOfFields += 1; + objectParser.declareNamedObjects(queueingConsumer(consumer, parseField), namedObjectParser, parseField); + } + } + + @Override + public void declareNamedObjects(BiConsumer> consumer, NamedObjectParser namedObjectParser, + Consumer orderedModeCallback, ParseField parseField) { + if (consumer == null) { + throw new IllegalArgumentException("[consumer] is required"); + } + if (namedObjectParser == null) { + throw new IllegalArgumentException("[parser] is required"); + } + if (orderedModeCallback == null) { + throw new IllegalArgumentException("[orderedModeCallback] is required"); + } + if (parseField == null) { + throw new IllegalArgumentException("[parseField] is required"); + } + + if (consumer == REQUIRED_CONSTRUCTOR_ARG_MARKER || consumer == OPTIONAL_CONSTRUCTOR_ARG_MARKER) { + /* + * Constructor arguments are detected by this "marker" consumer. It + * keeps the API looking clean even if it is a bit sleezy. We then + * build a new consumer directly against the object parser that + * triggers the "constructor arg just arrived behavior" of the + * parser. Conveniently, we can close over the position of the + * constructor in the argument list so we don't need to do any fancy + * or expensive lookups whenever the constructor args come in. + */ + int position = constructorArgInfos.size(); + boolean required = consumer == REQUIRED_CONSTRUCTOR_ARG_MARKER; + constructorArgInfos.add(new ConstructorArgInfo(parseField, required)); + objectParser.declareNamedObjects((target, v) -> target.constructorArg(position, parseField, v), namedObjectParser, + wrapOrderedModeCallBack(orderedModeCallback), parseField); + } else { + numberOfFields += 1; + objectParser.declareNamedObjects(queueingConsumer(consumer, parseField), namedObjectParser, + wrapOrderedModeCallBack(orderedModeCallback), parseField); + } + } + + private Consumer wrapOrderedModeCallBack(Consumer callback) { + return (target) -> { + if (target.targetObject != null) { + // The target has already been built. Call the callback now. + callback.accept(target.targetObject); + return; + } + /* + * The target hasn't been built. Queue the callback. + */ + target.queuedOrderedModeCallback = callback; + }; + } + /** * Creates the consumer that does the "field just arrived" behavior. If the targetObject hasn't been built then it queues the value. * Otherwise it just applies the value just like {@linkplain ObjectParser} does. @@ -258,6 +344,11 @@ private class Target { * Fields to be saved to the target object when we can build it. This is only allocated if a field has to be queued. */ private Consumer[] queuedFields; + /** + * OrderedModeCallback to be called with the target object when we can + * build it. This is only allocated if the callback has to be queued. + */ + private Consumer queuedOrderedModeCallback; /** * The count of fields already queued. */ @@ -343,6 +434,9 @@ private Value finish() { private void buildTarget() { try { targetObject = builder.apply(constructorArgs); + if (queuedOrderedModeCallback != null) { + queuedOrderedModeCallback.accept(targetObject); + } while (queuedFieldsCount > 0) { queuedFieldsCount -= 1; queuedFields[queuedFieldsCount].accept(targetObject); diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java b/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java index 0a7e71c6f061e..7e3001b75a2d6 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java @@ -227,41 +227,7 @@ public void declareObjectOrDefault(BiConsumer consumer, BiFunction }, field, ValueType.OBJECT_OR_BOOLEAN); } - /** - * Declares named objects in the style of highlighting's field element. These are usually named inside and object like this: - *

-     * {
-     *   "highlight": {
-     *     "fields": {        <------ this one
-     *       "title": {},
-     *       "body": {},
-     *       "category": {}
-     *     }
-     *   }
-     * }
-     * 
- * but, when order is important, some may be written this way: - *

-     * {
-     *   "highlight": {
-     *     "fields": [        <------ this one
-     *       {"title": {}},
-     *       {"body": {}},
-     *       {"category": {}}
-     *     ]
-     *   }
-     * }
-     * 
- * This is because json doesn't enforce ordering. Elasticsearch reads it in the order sent but tools that generate json are free to put - * object members in an unordered Map, jumbling them. Thus, if you care about order you can send the object in the second way. - * - * See NamedObjectHolder in ObjectParserTests for examples of how to invoke this. - * - * @param consumer sets the values once they have been parsed - * @param namedObjectParser parses each named object - * @param orderedModeCallback called when the named object is parsed using the "ordered" mode (the array of objects) - * @param field the field to parse - */ + @Override public void declareNamedObjects(BiConsumer> consumer, NamedObjectParser namedObjectParser, Consumer orderedModeCallback, ParseField field) { // This creates and parses the named object @@ -311,26 +277,7 @@ public void declareNamedObjects(BiConsumer> consumer, NamedOb }, field, ValueType.OBJECT_ARRAY); } - /** - * Declares named objects in the style of aggregations. These are named inside and object like this: - *

-     * {
-     *   "aggregations": {
-     *     "name_1": { "aggregation_type": {} },
-     *     "name_2": { "aggregation_type": {} },
-     *     "name_3": { "aggregation_type": {} }
-     *     }
-     *   }
-     * }
-     * 
- * Unlike the other version of this method, "ordered" mode (arrays of objects) is not supported. - * - * See NamedObjectHolder in ObjectParserTests for examples of how to invoke this. - * - * @param consumer sets the values once they have been parsed - * @param namedObjectParser parses each named object - * @param field the field to parse - */ + @Override public void declareNamedObjects(BiConsumer> consumer, NamedObjectParser namedObjectParser, ParseField field) { Consumer orderedModeCallback = (v) -> { diff --git a/core/src/test/java/org/elasticsearch/common/xcontent/ConstructingObjectParserTests.java b/core/src/test/java/org/elasticsearch/common/xcontent/ConstructingObjectParserTests.java index 32ffb33b694cf..7f6187800c2e4 100644 --- a/core/src/test/java/org/elasticsearch/common/xcontent/ConstructingObjectParserTests.java +++ b/core/src/test/java/org/elasticsearch/common/xcontent/ConstructingObjectParserTests.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.xcontent.ObjectParserTests.NamedObject; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.test.ESTestCase; import org.hamcrest.Matcher; @@ -37,6 +38,7 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.nullValue; @@ -397,4 +399,188 @@ private static ConstructingObjectParser buildParser(bool return parser; } } + + public void testParseNamedObject() throws IOException { + XContentParser parser = createParser(JsonXContent.jsonXContent, + "{\"named\": {\n" + + " \"a\": {}" + + "},\"named_in_constructor\": {\n" + + " \"b\": {}" + + "}}"); + NamedObjectHolder h = NamedObjectHolder.PARSER.apply(parser, null); + assertThat(h.named, hasSize(1)); + assertEquals("a", h.named.get(0).name); + assertThat(h.namedInConstructor, hasSize(1)); + assertEquals("b", h.namedInConstructor.get(0).name); + assertFalse(h.namedSuppliedInOrder); + } + + public void testParseNamedObjectInOrder() throws IOException { + XContentParser parser = createParser(JsonXContent.jsonXContent, + "{\"named\": [\n" + + " {\"a\": {}}" + + "],\"named_in_constructor\": [\n" + + " {\"b\": {}}" + + "]}"); + NamedObjectHolder h = NamedObjectHolder.PARSER.apply(parser, null); + assertThat(h.named, hasSize(1)); + assertEquals("a", h.named.get(0).name); + assertThat(h.namedInConstructor, hasSize(1)); + assertEquals("b", h.namedInConstructor.get(0).name); + assertTrue(h.namedSuppliedInOrder); + } + + public void testParseNamedObjectTwoFieldsInArray() throws IOException { + XContentParser parser = createParser(JsonXContent.jsonXContent, + "{\"named\": [\n" + + " {\"a\": {}, \"b\": {}}" + + "],\"named_in_constructor\": [\n" + + " {\"c\": {}}" + + "]}"); + ParsingException e = expectThrows(ParsingException.class, () -> NamedObjectHolder.PARSER.apply(parser, null)); + assertEquals("[named_object_holder] failed to parse field [named]", e.getMessage()); + assertEquals( + "[named] can be a single object with any number of fields or an array where each entry is an object with a single field", + e.getCause().getMessage()); + } + + public void testParseNamedObjectTwoFieldsInArrayConstructorArg() throws IOException { + XContentParser parser = createParser(JsonXContent.jsonXContent, + "{\"named\": [\n" + + " {\"a\": {}}" + + "],\"named_in_constructor\": [\n" + + " {\"c\": {}, \"d\": {}}" + + "]}"); + ParsingException e = expectThrows(ParsingException.class, () -> NamedObjectHolder.PARSER.apply(parser, null)); + assertEquals("[named_object_holder] failed to parse field [named_in_constructor]", e.getMessage()); + assertEquals( + "[named_in_constructor] can be a single object with any number of fields or an array where each entry is an object with a " + + "single field", e.getCause().getMessage()); + } + + public void testParseNamedObjectNoFieldsInArray() throws IOException { + XContentParser parser = createParser(JsonXContent.jsonXContent, + "{\"named\": [\n" + + " {}" + + "],\"named_in_constructor\": [\n" + + " {\"a\": {}}" + + "]}"); + ParsingException e = expectThrows(ParsingException.class, () -> NamedObjectHolder.PARSER.apply(parser, null)); + assertEquals("[named_object_holder] failed to parse field [named]", e.getMessage()); + assertEquals( + "[named] can be a single object with any number of fields or an array where each entry is an object with a single field", + e.getCause().getMessage()); + } + + public void testParseNamedObjectNoFieldsInArrayConstructorArg() throws IOException { + XContentParser parser = createParser(JsonXContent.jsonXContent, + "{\"named\": [\n" + + " {\"a\": {}}" + + "],\"named_in_constructor\": [\n" + + " {}" + + "]}"); + ParsingException e = expectThrows(ParsingException.class, () -> NamedObjectHolder.PARSER.apply(parser, null)); + assertEquals("[named_object_holder] failed to parse field [named_in_constructor]", e.getMessage()); + assertEquals( + "[named_in_constructor] can be a single object with any number of fields or an array where each entry is an object with a " + + "single field", e.getCause().getMessage()); + } + + public void testParseNamedObjectJunkInArray() throws IOException { + XContentParser parser = createParser(JsonXContent.jsonXContent, + "{\"named\": [\n" + + " \"junk\"" + + "],\"named_in_constructor\": [\n" + + " {\"a\": {}}" + + "]}"); + ParsingException e = expectThrows(ParsingException.class, () -> NamedObjectHolder.PARSER.apply(parser, null)); + assertEquals("[named_object_holder] failed to parse field [named]", e.getMessage()); + assertEquals( + "[named] can be a single object with any number of fields or an array where each entry is an object with a single field", + e.getCause().getMessage()); + } + + public void testParseNamedObjectJunkInArrayConstructorArg() throws IOException { + XContentParser parser = createParser(JsonXContent.jsonXContent, + "{\"named\": [\n" + + " {\"a\": {}}" + + "],\"named_in_constructor\": [\n" + + " \"junk\"" + + "]}"); + ParsingException e = expectThrows(ParsingException.class, () -> NamedObjectHolder.PARSER.apply(parser, null)); + assertEquals("[named_object_holder] failed to parse field [named_in_constructor]", e.getMessage()); + assertEquals( + "[named_in_constructor] can be a single object with any number of fields or an array where each entry is an object with a " + + "single field", e.getCause().getMessage()); + } + + public void testParseNamedObjectInOrderNotSupported() throws IOException { + XContentParser parser = createParser(JsonXContent.jsonXContent, + "{\"named\": [\n" + + " {\"a\": {}}" + + "],\"named_in_constructor\": {\"b\": {}}" + + "}"); + + // Create our own parser for this test so we can disable support for the "ordered" mode specified by the array above + @SuppressWarnings("unchecked") + ConstructingObjectParser objectParser = new ConstructingObjectParser<>("named_object_holder", + a -> new NamedObjectHolder(((List) a[0]))); + objectParser.declareNamedObjects(ConstructingObjectParser.constructorArg(), NamedObject.PARSER, + new ParseField("named_in_constructor")); + objectParser.declareNamedObjects(NamedObjectHolder::setNamed, NamedObject.PARSER, new ParseField("named")); + + // Now firing the xml through it fails + ParsingException e = expectThrows(ParsingException.class, () -> objectParser.apply(parser, null)); + assertEquals("[named_object_holder] failed to parse field [named]", e.getMessage()); + assertEquals("[named] doesn't support arrays. Use a single object with multiple fields.", e.getCause().getMessage()); + } + + public void testParseNamedObjectInOrderNotSupportedConstructorArg() throws IOException { + XContentParser parser = createParser(JsonXContent.jsonXContent, + "{\"named\": {\"a\": {}}" + + ",\"named_in_constructor\": [\n" + + " {\"b\": {}}" + + "]}"); + + // Create our own parser for this test so we can disable support for the "ordered" mode specified by the array above + @SuppressWarnings("unchecked") + ConstructingObjectParser objectParser = new ConstructingObjectParser<>("named_object_holder", + a -> new NamedObjectHolder(((List) a[0]))); + objectParser.declareNamedObjects(ConstructingObjectParser.constructorArg(), NamedObject.PARSER, + new ParseField("named_in_constructor")); + objectParser.declareNamedObjects(NamedObjectHolder::setNamed, NamedObject.PARSER, new ParseField("named")); + + // Now firing the xml through it fails + ParsingException e = expectThrows(ParsingException.class, () -> objectParser.apply(parser, null)); + assertEquals("[named_object_holder] failed to parse field [named_in_constructor]", e.getMessage()); + assertEquals("[named_in_constructor] doesn't support arrays. Use a single object with multiple fields.", e.getCause().getMessage()); + } + + static class NamedObjectHolder { + @SuppressWarnings("unchecked") + public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("named_object_holder", + a -> new NamedObjectHolder(((List) a[0]))); + static { + PARSER.declareNamedObjects(ConstructingObjectParser.constructorArg(), NamedObject.PARSER, NamedObjectHolder::keepNamedInOrder, + new ParseField("named_in_constructor")); + PARSER.declareNamedObjects(NamedObjectHolder::setNamed, NamedObject.PARSER, NamedObjectHolder::keepNamedInOrder, + new ParseField("named")); + } + + private List named; + private List namedInConstructor; + private boolean namedSuppliedInOrder = false; + + NamedObjectHolder(List namedInConstructor) { + this.namedInConstructor = namedInConstructor; + } + + public void setNamed(List named) { + this.named = named; + } + + public void keepNamedInOrder() { + namedSuppliedInOrder = true; + } + } } From c2deb1c81d086dc95088d2d614def362ddd32d3f Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Fri, 21 Apr 2017 15:25:19 +0200 Subject: [PATCH 028/619] Don't expose cleaned-up tasks as pending in PrioritizedEsThreadPoolExecutor (#24237) Changes in #24102 exposed the following oddity: PrioritizedEsThreadPoolExecutor.getPending() can return Pending entries where pending.task == null. This can happen for example when tasks are added to the pending list while they are in the clean up phase, i.e. TieBreakingPrioritizedRunnable#runAndClean has run already, but afterExecute has not removed the task yet. Instead of safeguarding consumers of the API (as was done before #24102) this changes the executor to not count these tasks as pending at all. --- .../util/concurrent/PrioritizedEsThreadPoolExecutor.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/common/util/concurrent/PrioritizedEsThreadPoolExecutor.java b/core/src/main/java/org/elasticsearch/common/util/concurrent/PrioritizedEsThreadPoolExecutor.java index 5b3dae7ffae71..ee38637b04c0c 100644 --- a/core/src/main/java/org/elasticsearch/common/util/concurrent/PrioritizedEsThreadPoolExecutor.java +++ b/core/src/main/java/org/elasticsearch/common/util/concurrent/PrioritizedEsThreadPoolExecutor.java @@ -91,7 +91,13 @@ private void addPending(List runnables, List pending, boolean for (Runnable runnable : runnables) { if (runnable instanceof TieBreakingPrioritizedRunnable) { TieBreakingPrioritizedRunnable t = (TieBreakingPrioritizedRunnable) runnable; - pending.add(new Pending(unwrap(t.runnable), t.priority(), t.insertionOrder, executing)); + Runnable innerRunnable = t.runnable; + if (innerRunnable != null) { + /** innerRunnable can be null if task is finished but not removed from executor yet, + * see {@link TieBreakingPrioritizedRunnable#run} and {@link TieBreakingPrioritizedRunnable#runAndClean} + */ + pending.add(new Pending(unwrap(innerRunnable), t.priority(), t.insertionOrder, executing)); + } } else if (runnable instanceof PrioritizedFutureTask) { PrioritizedFutureTask t = (PrioritizedFutureTask) runnable; Object task = t.task; From 251b6d452b1fbf5a3422eb296a602b7f15b39cea Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Fri, 21 Apr 2017 15:29:53 +0200 Subject: [PATCH 029/619] MultiBucketsAggregation.Bucket should not extend Writeable (#24216) The MultiBucketsAggregation.Bucket interface extends Writeable, forcing all implementation classes to implement writeTo(). This commit removes the Writeable from the interface and move it down to the InternalBucket implementation. --- .../search/aggregations/InternalMultiBucketAggregation.java | 3 ++- .../search/aggregations/bucket/MultiBucketsAggregation.java | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregation.java index 5317f51981914..8e8f4edcf3193 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregation.java @@ -20,6 +20,7 @@ package org.elasticsearch.search.aggregations; import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; @@ -81,7 +82,7 @@ public Object getProperty(List path) { } } - public abstract static class InternalBucket implements Bucket { + public abstract static class InternalBucket implements Bucket, Writeable { public Object getProperty(String containingAggName, List path) { if (path.isEmpty()) { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/MultiBucketsAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/MultiBucketsAggregation.java index 49c5800222781..fc223916f72c1 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/MultiBucketsAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/MultiBucketsAggregation.java @@ -19,7 +19,6 @@ package org.elasticsearch.search.aggregations.bucket; -import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.util.Comparators; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.search.aggregations.Aggregation; @@ -37,7 +36,7 @@ public interface MultiBucketsAggregation extends Aggregation { * A bucket represents a criteria to which all documents that fall in it adhere to. It is also uniquely identified * by a key, and can potentially hold sub-aggregations computed over all documents in it. */ - interface Bucket extends HasAggregations, ToXContent, Writeable { + interface Bucket extends HasAggregations, ToXContent { /** * @return The key associated with the bucket */ From 480bf0996d1c2255051f9f9c5ddad5ee08246c30 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Fri, 21 Apr 2017 15:41:27 +0200 Subject: [PATCH 030/619] Add utility method to parse named XContent objects with typed prefix (#24240) This commit adds a XContentParserUtils.parseTypedKeysObject() method that can be used to parse named XContent objects identified by a field name containing a type identifier, a delimiter and the name of the object to parse. --- .../common/xcontent/XContentParserUtils.java | 35 +++++++++ .../elasticsearch/search/suggest/Suggest.java | 19 +---- .../xcontent/XContentParserUtilsTests.java | 71 +++++++++++++++++++ .../search/suggest/SuggestionTests.java | 2 +- 4 files changed, 109 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/XContentParserUtils.java b/core/src/main/java/org/elasticsearch/common/xcontent/XContentParserUtils.java index fec83eefbdfa0..169202e40d7b2 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/XContentParserUtils.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/XContentParserUtils.java @@ -20,8 +20,10 @@ package org.elasticsearch.common.xcontent; import org.elasticsearch.common.ParsingException; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.xcontent.XContentParser.Token; +import org.elasticsearch.rest.action.search.RestSearchAction; import java.io.IOException; import java.util.Locale; @@ -107,4 +109,37 @@ public static Object parseStoredFieldsValue(XContentParser parser) throws IOExce } return value; } + + /** + * This method expects that the current token is a {@code XContentParser.Token.FIELD_NAME} and + * that the current field name is the concatenation of a type, delimiter and name (ex: terms#foo + * where "terms" refers to the type of a registered {@link NamedXContentRegistry.Entry}, "#" is + * the delimiter and "foo" the name of the object to parse). + * + * The method splits the field's name to extract the type and name and then parses the object + * using the {@link XContentParser#namedObject(Class, String, Object)} method. + * + * @param parser the current {@link XContentParser} + * @param delimiter the delimiter to use to splits the field's name + * @param objectClass the object class of the object to parse + * @param the type of the object to parse + * @return the parsed object + * @throws IOException if anything went wrong during parsing or if the type or name cannot be derived + * from the field's name + */ + public static T parseTypedKeysObject(XContentParser parser, String delimiter, Class objectClass) throws IOException { + ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.currentToken(), parser::getTokenLocation); + String currentFieldName = parser.currentName(); + if (Strings.hasLength(currentFieldName)) { + int position = currentFieldName.indexOf(delimiter); + if (position > 0) { + String type = currentFieldName.substring(0, position); + String name = currentFieldName.substring(position + 1); + return parser.namedObject(objectClass, type, name); + } + } + throw new ParsingException(parser.getTokenLocation(), "Cannot parse object of class [" + objectClass.getSimpleName() + + "] without type information. Set [" + RestSearchAction.TYPED_KEYS_PARAM + "] parameter on the request to ensure the" + + " type information is added to the response output"); + } } diff --git a/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java b/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java index f483b5de89f7e..73cad2310f848 100644 --- a/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java +++ b/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java @@ -21,7 +21,6 @@ import org.apache.lucene.util.CollectionUtil; import org.elasticsearch.common.CheckedFunction; import org.elasticsearch.common.ParseField; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Streamable; @@ -33,6 +32,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParserUtils; import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.suggest.Suggest.Suggestion.Entry; @@ -386,22 +386,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @SuppressWarnings("unchecked") public static Suggestion> fromXContent(XContentParser parser) throws IOException { - ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.currentToken(), parser::getTokenLocation); - String typeAndName = parser.currentName(); - // we need to extract the type prefix from the name and throw error if it is not present - int delimiterPos = typeAndName.indexOf(Aggregation.TYPED_KEYS_DELIMITER); - String type; - String name; - if (delimiterPos > 0) { - type = typeAndName.substring(0, delimiterPos); - name = typeAndName.substring(delimiterPos + 1); - } else { - throw new ParsingException(parser.getTokenLocation(), - "Cannot parse suggestion response without type information. Set [" + RestSearchAction.TYPED_KEYS_PARAM - + "] parameter on the request to ensure the type information is added to the response output"); - } - - return parser.namedObject(Suggestion.class, type, name); + return XContentParserUtils.parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Suggestion.class); } protected static > void parseEntries(XContentParser parser, Suggestion suggestion, diff --git a/core/src/test/java/org/elasticsearch/common/xcontent/XContentParserUtilsTests.java b/core/src/test/java/org/elasticsearch/common/xcontent/XContentParserUtilsTests.java index 00edb25d5c30f..d0426e1e1040e 100644 --- a/core/src/test/java/org/elasticsearch/common/xcontent/XContentParserUtilsTests.java +++ b/core/src/test/java/org/elasticsearch/common/xcontent/XContentParserUtilsTests.java @@ -19,15 +19,22 @@ package org.elasticsearch.common.xcontent; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.test.ESTestCase; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import static org.elasticsearch.common.xcontent.XContentHelper.toXContent; import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.elasticsearch.common.xcontent.XContentParserUtils.parseTypedKeysObject; public class XContentParserUtilsTests extends ESTestCase { + public void testEnsureExpectedToken() throws IOException { final XContentParser.Token randomToken = randomFrom(XContentParser.Token.values()); try (XContentParser parser = createParser(JsonXContent.jsonXContent, "{}")) { @@ -40,4 +47,68 @@ public void testEnsureExpectedToken() throws IOException { ensureExpectedToken(XContentParser.Token.END_OBJECT, parser.nextToken(), parser::getTokenLocation); } } + + public void testParseTypedKeysObject() throws IOException { + final String delimiter = randomFrom("#", ":", "/", "-", "_", "|", "_delim_"); + final XContentType xContentType = randomFrom(XContentType.values()); + + List namedXContents = new ArrayList<>(); + namedXContents.add(new NamedXContentRegistry.Entry(Boolean.class, new ParseField("bool"), parser -> { + ensureExpectedToken(XContentParser.Token.VALUE_BOOLEAN, parser.nextToken(), parser::getTokenLocation); + return parser.booleanValue(); + })); + namedXContents.add(new NamedXContentRegistry.Entry(Long.class, new ParseField("long"), parser -> { + ensureExpectedToken(XContentParser.Token.VALUE_NUMBER, parser.nextToken(), parser::getTokenLocation); + return parser.longValue(); + })); + final NamedXContentRegistry namedXContentRegistry = new NamedXContentRegistry(namedXContents); + + BytesReference bytes = toXContent((builder, params) -> builder.field("test", 0), xContentType, randomBoolean()); + try (XContentParser parser = xContentType.xContent().createParser(namedXContentRegistry, bytes)) { + parser.nextToken(); + ParsingException e = expectThrows(ParsingException.class, () -> parseTypedKeysObject(parser, delimiter, Boolean.class)); + assertEquals("Failed to parse object: expecting token of type [FIELD_NAME] but found [START_OBJECT]", e.getMessage()); + + parser.nextToken(); + e = expectThrows(ParsingException.class, () -> parseTypedKeysObject(parser, delimiter, Boolean.class)); + assertEquals("Cannot parse object of class [Boolean] without type information. Set [typed_keys] parameter " + + "on the request to ensure the type information is added to the response output", e.getMessage()); + } + + bytes = toXContent((builder, params) -> builder.field("type" + delimiter + "name", 0), xContentType, randomBoolean()); + try (XContentParser parser = xContentType.xContent().createParser(namedXContentRegistry, bytes)) { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.nextToken(), parser::getTokenLocation); + + NamedXContentRegistry.UnknownNamedObjectException e = expectThrows(NamedXContentRegistry.UnknownNamedObjectException.class, + () -> parseTypedKeysObject(parser, delimiter, Boolean.class)); + assertEquals("Unknown Boolean [type]", e.getMessage()); + assertEquals("type", e.getName()); + assertEquals("java.lang.Boolean", e.getCategoryClass()); + } + + final long longValue = randomLong(); + final boolean boolValue = randomBoolean(); + bytes = toXContent((builder, params) -> { + builder.field("long" + delimiter + "l", longValue); + builder.field("bool" + delimiter + "b", boolValue); + return builder; + }, xContentType, randomBoolean()); + + try (XContentParser parser = xContentType.xContent().createParser(namedXContentRegistry, bytes)) { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + + ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.nextToken(), parser::getTokenLocation); + Long parsedLong = parseTypedKeysObject(parser, delimiter, Long.class); + assertNotNull(parsedLong); + assertEquals(longValue, parsedLong.longValue()); + + ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.nextToken(), parser::getTokenLocation); + Boolean parsedBoolean = parseTypedKeysObject(parser, delimiter, Boolean.class); + assertNotNull(parsedBoolean); + assertEquals(boolValue, parsedBoolean); + + ensureExpectedToken(XContentParser.Token.END_OBJECT, parser.nextToken(), parser::getTokenLocation); + } + } } diff --git a/core/src/test/java/org/elasticsearch/search/suggest/SuggestionTests.java b/core/src/test/java/org/elasticsearch/search/suggest/SuggestionTests.java index 9f1607d9d6500..e4deb634c7776 100644 --- a/core/src/test/java/org/elasticsearch/search/suggest/SuggestionTests.java +++ b/core/src/test/java/org/elasticsearch/search/suggest/SuggestionTests.java @@ -133,7 +133,7 @@ public void testFromXContentFailsWithoutTypeParam() throws IOException { ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.nextToken(), parser::getTokenLocation); ParsingException e = expectThrows(ParsingException.class, () -> Suggestion.fromXContent(parser)); assertEquals( - "Cannot parse suggestion response without type information. " + "Cannot parse object of class [Suggestion] without type information. " + "Set [typed_keys] parameter on the request to ensure the type information " + "is added to the response output", e.getMessage()); } From 63e5aff5d6c07b6c84eb87685859567f0597d91f Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Fri, 21 Apr 2017 10:48:41 -0400 Subject: [PATCH 031/619] Adds version 5.3.2 and backwards compatibility indices for 5.3.1 --- .../main/java/org/elasticsearch/Version.java | 4 ++++ .../test/resources/indices/bwc/index-5.3.1.zip | Bin 0 -> 506719 bytes .../test/resources/indices/bwc/repo-5.3.1.zip | Bin 0 -> 249843 bytes 3 files changed, 4 insertions(+) create mode 100644 core/src/test/resources/indices/bwc/index-5.3.1.zip create mode 100644 core/src/test/resources/indices/bwc/repo-5.3.1.zip diff --git a/core/src/main/java/org/elasticsearch/Version.java b/core/src/main/java/org/elasticsearch/Version.java index b3f38e57b522e..199f67bcb0bed 100644 --- a/core/src/main/java/org/elasticsearch/Version.java +++ b/core/src/main/java/org/elasticsearch/Version.java @@ -76,6 +76,8 @@ public class Version implements Comparable { public static final Version V_5_3_0_UNRELEASED = new Version(V_5_3_0_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_1); public static final int V_5_3_1_ID_UNRELEASED = 5030199; public static final Version V_5_3_1_UNRELEASED = new Version(V_5_3_1_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_2); + public static final int V_5_3_2_ID_UNRELEASED = 5030299; + public static final Version V_5_3_2_UNRELEASED = new Version(V_5_3_2_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_2); public static final int V_5_4_0_ID_UNRELEASED = 5040099; public static final Version V_5_4_0_UNRELEASED = new Version(V_5_4_0_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_5_0); public static final int V_5_5_0_ID_UNRELEASED = 5050099; @@ -104,6 +106,8 @@ public static Version fromId(int id) { return V_5_5_0_UNRELEASED; case V_5_4_0_ID_UNRELEASED: return V_5_4_0_UNRELEASED; + case V_5_3_2_ID_UNRELEASED: + return V_5_3_2_UNRELEASED; case V_5_3_1_ID_UNRELEASED: return V_5_3_1_UNRELEASED; case V_5_3_0_ID_UNRELEASED: diff --git a/core/src/test/resources/indices/bwc/index-5.3.1.zip b/core/src/test/resources/indices/bwc/index-5.3.1.zip new file mode 100644 index 0000000000000000000000000000000000000000..251dc0e41a149e602b49906cb967eba897254a17 GIT binary patch literal 506719 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

VkiC+%xy+YFIdiF&+c4KA}mp6@tHlu%}4)pqHxZ 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

izc%3&n0`yj?6{5Zoy*q=yIA*LD5g# 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�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

#j!ngj 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

3tE zqv$nhPSOHGB%?|dUJA?JX%I(%8$*V0EJ3LH8LsK^F96TEl+G@@r*|GW*5Sx*Lj$`G z|8kxQ8K9VEye=%pvjag8eNd z#>f@A^yEiqY}p_$NT{#@8A}gvPX8!%HHcAH9V>=u($ArrbUJxkn_y?Xx_=GS>?U;$ z5tgsBIB5vFBK_d}1ihlv#e3}Da&uo&shb7K-Pvei$p=y~PHZ|Ri3?Et5v(54DLcJ+ zdO}7SzUL)E-a5Rdy(8o^ynq1^Vx?IUo}#k9EhvTKS74*26tKoI`vRzQgg!fd=Uui@ z5crsfn|+#n;3MjiX^dJ`{+#n;V3&7je!OO#h;7 zyp8245Cv7P_>59WE(AvrE8L1v$~v9SUa>qt!sDQ7qU>y8Z8^3(BuE~v4qaM-y#Up4 zG_@k$3TPLS+BJ;Rgnj`BAwC3X)ZQWAz;(WW&}SP`s>|2zIz#6QDUF zYCK}{c4x#HG#zVT_r#49zXfca*D|6hDd1AZX=iY-<|B!10ZLQf42quzctqTXukw$zCayp{HzX-t&vF;dLj-F#^_IL_TU3XB|0;JR95cMQ+g;+Jv zq8FQ|2`#ej)Ff~rOZT=4e$8qL%WVD%vhFrI1i}F%9IZk@)NzT6-d6nsR03<@&W1h_ z`+M-D!`@wzeaRFWyh65zE>oJBrL%;cC%a@7VojE5|3m__U=w9eOm4{gysFKOKTm^N zS3V{IpYBa3ETK&jO(AJR-Dx;ip*f~%rC6655H(io6Ik9KG{p7^h)9|*_y(D_p<7k_ zZ7gqD@4&6wTIn>*6R+QMM&1#@ctg-w?@;F^GX(P}^k0*WN4roKuA$=CMiB5N6u~3f zTc3gJ09>VXW(F#9dq>_hjpl$E#uAl7#WWYnw9pA??sSTE1(#Q??rjm(whmd^g$-h3 zO>-F{P4=MJna=MpgJhhD4@62gU=GqZOm!4Kfy?#^{L7HxqQUM%U~So9 zP#xX&XVBw8{FZv!mSC6(-q1CI#{3g3u$~b>Xq%zZC}clCyeCx8KagRn`Z7KCbTKD~ z`9m-*K{b0nftX%_e@GOqHLi)Fghz53Y8kJj9}(&a`;tRK{r6Upbms`ST%?dHXG=<#=rLcC}>nte9Iqs&2G@XV07-*P*3qiz6f z8I;v8AifX0YAu8A%}8B!>=fa>fpn~1iu;;GB_NM;L-G1WK4>|NI$jZs3*eE55GoK>^7MB| z!Hn`Y&Xd=Z$e{}QZ;vF~OI}gu+!>EmsDynLwzuS>MXBm^U3_EJ^w zs@5u!)eK1`?Rr!R<_8gIsF*d=L9Rz@_Qp`k_XGPA63;l;X4WwVZAQo^smN_EgEka1 zal3$2tb4r($9Umk1~dBgqfca??O0|v0c9{6ep>eeZrNd4pJrS1UC^@VB2B3uS|Yl~ zZZGVjkmHaA+SPDguYTaG(nVOoLQxIh5f~0jsf2T^<2jgT;Ny8m()4~qCWzFh+u9UE zmZ%}e2e9Dn0aaE#6JW@Jkma#WqLXMU`@~`P9+LZn?t?F>yPBl#amGyf9L{OlG6W+? zQ4RSO*n|u4EfI^YpCWV^q+pPX#->2`M7`eX8@%1|V2=02JpoR_WcAudprIf1P|57b zqCbjJTf6MHanCWE4AnabE|ev%BQipSF>o4^k)U-eh`{Z_0WFO{XV)GCZosH)A*F26 z0kVSXZVfJhj|FyWmPp+?(g6(wS#Fes?3x-bkhF zlGJ6za|k*`wJ!3F+2U(RhB#XjpZ`psfj~#uwktXa?mN0em1(eiX1%-sje9lq+C6O7 z3_^4m#ik?(Xse_Zcn%!ObA#Q0kTY0)(_{i11-^tiZVZF$a1B7gW>;j9>$0aKjgCS$ z@9YXLf>Cx^nMOsVk@jHus`|-#sZ)vulj|T`mS%(niJPY9L~d5jYU&cwHCmR>c#m{t z0p3t`6gJuS=<=4(=ieYsJ|XPy5PQ1R(5Q6$7nE3$7U>pLSt_TT8Qw?P=q4g88c-B$ zZf+Rd<4|_kqH2d3QLRqIb5E4>rHUv{>{NUKT*OG)d+pi~6t0M|#UhEYK zX?jKmDC;iE@o|u|n#f;5qboU!BS{J2$S&~x5Vm+x@TM%*u6oCz&(jYf>lg=X&o;5L zDmdbf*HAnF(4xYDRG4GL<}L>u8P;yJ24TnU8eX)d4X43C_xSn#O>uxBPY# z$lEYNb1iD$4GO-zbp8m<-%EH4h4dwvU{-5}L6CzKJ#O#}y@eB*%PFt}$Zii*WaCD6 zlO{^GfU55kiA!XnW<2!@q*6R=D3qr{(ZacL*_Y6P;-oMPJ?=;mC7~d8hWSP4iEdyN zxu!#yU>+cg4#ho(aM*(?ypX{B4jzy@plmzUn`Bh)tz(b~`!OMBAlSJM5hdnY zf^IZPjkLqvR#_q3B%KBK2o&!iya$I+9%gn?U}*iANuSAEXlxsSbF{6At}SpagKeLf zIm)#(ya2P#m41QqIfwW*837j#kb1Px5~}=o)+HLAh}dD5TciV|oq*1|S;q!x=0#sq zx?AK_z4-`Y$Uw2IYDVL(52$sOQ94ObysVQcW)yD8GQ5QN3wlfrXkrsOBFBt4O_B+F zrk_ZDn&S)>NzxDHCA(;(Jc=ClQd?08w>m;@sc8$+E|ooo$~ri9z~**sfk&g< zE+}DdS=Xa3y-mUuv)NT?5%`1|7Lo&CI))(WsrRuHP`pQB*Z7$n(mgT*b<5N?fa38b z_*dx!L8J2)K0`jT$GC^!h{iHaD8Jk4{l*pu^)Cq5YvwghmaGyljYC6p5CRGt{tigW z;5_~$~`kp9T&LD4clau z=Z1;$nR?jLeExp6>DT**d?2;w~jd9>gsx9f- z05dNqe7Y91i=I(v^eF5iI3!1>&DSPz5vSC^hjM9Y3jK&%?O>hO3`~H{ThDhqBAxMI zKJ4p^4M6)T)O&eRzl3tk-L971StObnEY+7pABMOW<;XSu7heZ!gya?wYeq32yw_;{ zincBCoSLCTK}+2ME9ph*bJWMPSzmq1%Qu%OmEtFmc1XqiW8&`%d$u6M3R$jA;9Q!U zx0$Gh{Zr^U&v9V9y+H^zxB0~O2ddd$KtBlJDy@eze3F zBv}8B93v)I{Eic{hHLZ=^lU&@)k0@-4k1ny;$3Zg%3;c`qT4Rz-M)Ii&f=s-AWi*i z&^-i+oGMr)gf$*lSYr8QFlkFf%eDJEBg3GFgYsMOWczW-vyf7}#vq|1Aciis7>}o^nXD}Bl=P!whtaTrw`yDhM;vB62s8909kg6 zsM-f2&%ywmho%qY6(r5R56m#pJ~4aHVBIqVRNn@@O~YLwg*eC=P;=whMX!Q=ne!=F3r!)X^bT=eK)Mf(f+$DB188QNczz%`EX9lEWsOH!p@NUfYBXR-e1q>k` za{FWsBIs_cnEB{NI1Qt>%P>ov`otvtuZmBy|eXe?>YwB&jXwBAeO z=vJ1YS!(b-BSr5fNWIcNR5F%0052%-Hi>xS3&7P7mmp|7%0PAyR8yg4wIXQt7Igs$FSZSlmf!&4 z5p-Nmnv=Rpymdovuw?R6%oR)#`8h0c#ci~Mp&e$7_@-;FA-n*pQVyyqxp|NHk|Bk6 zaqjpiWL_Z?@&)N5xE}ybf_)ImT)@vJ5-nPM3M!H?fw=Ks0cqTYW}C-7h}`&BiPf?X z?RDWd&^!;9oOhD(G+e_2uFN1buRIz%p7CaLn%t=cdYsQe@YVP9!#1=C zwsV5k)NV?yO4^_YXw&!rYf@9>ba&RSApd0SN)zAl6O=PsWSZ{beS96=hujH0NAd3p z<}*Eu6j4n=+q_agy=R^kP=-DV$jK<_Z3zl=HZ;qcEPVj z{c6(g@N3?Pr8-B!e+nLN!lXsXE)~Tt?u-cCeQnYj1cWRWv|HIk1Ed!wh+GMj8>v~~ zgupiOUP8+mrqwxoB=zq}u&y5pRjP_vd#HfwLen&}LS(_C&%y?-p4}5RkyfZDpuGms zXTyYwM(n(;Pf;slptssA4OOM+H8n{@4kEAR|=FOp#nYsz4-y9oR!v)U%0f-oC~*zlUQ?Kk&0gE`a%j%oR3= zfZYR!raA>HkR1dYhs&rXCP7LU=>tf0Hvb)z>pJ4uQLvZYO^ppUBPDPpeF8eYG-UYv zUYoH5@;atLvB@8>yHSh11im3T0u!XvFhuX@N3<^H2gK(qa!BMu+P4pldgy@_;Pdxi z|JR@Y|MOph|EGWcfBSm$nX_yE=+O^+eeyp7^8e-UuV(kF+5KvEzna~zX7{Vv{c3i< zn%%Ev_p90cYIgr`|5E#vn%`3e5PCtGIDj5$pCYvr+Q$_f0{6Zy3w z>-Y>=`Kehi2cB3@)F<*|xz?=x8t*~}mCVzB`DcG`BJ=af?;2|hzxB)GAOCmtzw@Vm z_Ol7|Z~oWgPyZyJ-uTM<(?9b5$;8ioar7I1GS1Yh@;CTuIj-O(OxdiJEm@7pwNK<) z*);Js%2Vsf6H~5@netx`RBv2ci}U-sHdd>cf%RHO{^i$7%rq@i{u@@U{N%A*eYR-@ zsy`@ZZB7p6cIK<&99&K*Tx<{u8#Lx^T{`OIrAIF`XLU?v@ix1URZgGx09=5 zm8o=_ zFaE&-OR*mDM^gQf!H0ll*ncJc8~USv4c0xx{!fVgf>{3sV#6f%8DgIx_6H>P@4%WR zF~*d?$>nh={`QkXJ+Ab(wT{}~lVgv+HvM=n`DwiVF~&I$s?UQeQUi(AE zG#g|E)4+*Yq$-%-W;Fp6tdnXIVsTV|bjsrx*4jAU=857^D#m-GJgw@W|$_GjPae*UN5x}QuZo{krO?eC<2 zGW<`belq+AIp%l&Myy^oe`r?oUU~B5TLmwJBh_U2aYqhk^Vq_S!P!5lwM@nT*!-D8 z{xTp_c2@J$WS*Mxw@;iFriuLc@#AlB#Q6JK`G-0AiKF&O% z>_DcYBe(%=2b_`hZPJg_HwznZz}*wuIhlp*XL1DUDAUmqm}a->EO`#kX?%<<60u>R z+Q?v#%#dT~C%cfCq%T1b)?gH>ZIYhBTYPC@%3eX8y`NEM!O^nE1Ww?HuF&pf zD19P%VT{oC)Di3_i_rKHrXfE@2jL@=n1f?@1#jV~x_@Gw<)+~R4q+1dp`~S(tkMxE z9E0;7LUpdmFZ&)WCHto4kc5Va!zxOzaG`L8mTJz3rJUe+Y91h^w_HKccLU{7=1c3G zX<>yDYlX>stOs~XxTHyqLLn}WOm{vHF=Q<);Ru5o6SHE&77J$*-9pU67QkjCoSxLq*(EfL)p zj@h|2xWn~pX?;(dCdd#ufqCfF-jQlgj!_)cICXt#u(3^^!7vPxo0=tZiD@_o`W0kD zJJ7uc2V@V|y3jKKi!egZ@R|FMR){e}sx29s!&^)swY1^Q)%myg& zeEkPlChQJ8FpeX(SiB-Ta1O#Gl-uSYQ?^H}6;MMjt7r6k&Z#^MAMpt*unO*tN7fP< zA!20^qT&c<^&JE|``X*Z-#xKb@n522=nv}R1>=5kq!|Q zsynM+6Y-2J(sRx?1q}Oio$_xP=M+_?(0eF0?}2EobA`nBN3w^ZXN&14 z{dC|f_bDdz9ITQ!t;F6z{WJ(iq}(Wfppn!aq(tv9nSw2F95MsoOdBikoT)UdGR0@` z0gU%BOg3SQIU-w(V~{FR&kU(H?;2uyo$eLPTa$OJ5yu2HoI`sX|B=M~ao;WPY%Au& z<{MVMBxd&{V>C}8!eDYSL!^2Ncz_uPT)}hFIZ0hJ@S^4vEVe@DNg^69ynsO61eG6Z z#QJw+tR}VyH12y~{J9U1@PFjpTX2eWu?tl~kX;*ejCs$5KENJalJaY+eE`2to+NkV z4cw7w#vZNYlJQ<|A)dQ|9rn);xXzP%`U>3sZMdbE)cJj1rx9H3Fpl9Kx#NoPS46DO zE;8F>lzmUU`V5_{nPZf)Yo4Y@V3*9p4aCDE#NTGxF<1E!EYWyNX@}~$0aD#DpeU8M zWR_ghYr0rHscCT5!w&HgV}t?-a+yZz8o{$hG&d_6e^~zQAoWZ`&a_n9Ic)SoT6>P zc`^y_=>>U)`G3PKK;I`QjzVW~m+m6DRNCrF2UuX@RZZ%Ki~HZQ)2vT=K;BFp!Yj&6 zkP!$N%aDDE#oFQ<#(a8>Q8NZu6hR8K!znr-_Co8}m5?Z{)2_3*^7^=A? z)uS=thBXXVb4S8GjlVs{xseO%I3v{BrPU)br_~cNVa}YVrQ{sc?SMGVoItSo3`*X( zqc`x1)_3rY+hA?d>UkA)j~fNwI0=rD57gOvORm80TxD*l;b@wMt|@3Ur%2YhkAaEI zkfd*m_?!1hc9k@pfo+>z2KOGSqIi99jCkH4PfS7}eZfX95PO45^pIS$NPZ;$90^!W zB0de^rIj6GO+&NjyC8?Gc}464WCF$BOwBT|#lBVYz$9Oj*b5W{{a6!yb?3|k^f`5X z3)iW6f(|mN8EDqM7bKg%g3+2myjRiNy1*Jeo6r{9VD4yoi0rZhC_6Do)dLCndD7Ue zCfX2@-G0=z>s9@8O7=}>6|-ao#c}-cw1&qj zp&Mj@*j^!FXSnDZN=4%w)ETfpXPrH&FY^*rs@c|CZP576J8-oq!h zQC%e`WDNC5)QgueZ7_d>QP^#5;+Ui8p--EnjwX-;>s`)Kq$b6JM{XKSKeNxq|9;?L)Fe zLg$d51-B=QuWlNLKi_0MfCaHbW~i00I}?Fnlv$40XJQ1Vq2BS1tDew#0kg~mdrllj zNR#pwu`9*{?dCsJTL-R#J=(s(-GAB0{1+RP9c;!vNZv8$Al-t0mUOJp1M1r&eV1V7 z-b1z9S=KHML;irBg-QCEER!9qyn80w;`BCtqM_<}rfM@Q{VnxpJ`g<--z2f}5`v!ofY$A(|AZ`|qKrZ#N>}OCX31_{+#h8HPIy-{TSYq#Kb^GlN^8!K> z2(#QW>6m6wYiyyi?0H7tAg4PQNmOCi$sTmRgbBu`y@3MPF^;f#Pgl^aC=-nR9qBlO z*_u;k5#fx+LQ9y;?xs~_!%@Pzk7#(4aOwb@(CWU^H3VMsk{H=~(YPdQxZVTsiK(1n z5_mLz8TF}@i`neYp)q_0<~u}H-yKTU=ionsXDHd7bMS_0C(y`w-hkcL(5PkkW3(3h zBrR`%=Y-jY>P}BwfW{zF=mel(r8i9P7KukH@;m}1RJUoalB{*`uCz`IhwKvF$B~bqyIg~}bO2wnjFXq+Q^*=kw@609K4enmLGWMH zwE0%pD`pjIBe{#2wSdcbL++XGV`#HVm*fFBi&biGUaRg?J|NwR z;yk9oJl5V3`ptdVdvr&(faj99{^XW?*=+UuRO8(#zK0IGX-E7gr(=xNv`2%a#{A$)`|cTW3sSNkZ+&@<%I>W;1O zGSLpmm#tc;kQcx7Nwixnhlpm)F*dt2S2B>q1_LJxI z`yEPi7`=z@cP94Vn8JMN$304yh_HdeYy_O+^gV`R5w6HV6>wh? z<2kC#c{)l_sLazd<`$8tpWTKD%G%}$o5|>vw4d8U=^ikuU9E);#um^ z`d85OO0=6=#E9zE85Yyxto?z!K1-0e5868-g2J= z^#;CKirZ#rvcYl2G|ReoxF(qXgd?7#ovry9tg;ov;x~wuilF6gm`Pkywb0ai$TZ(T zdxm>YBaKH0cL{wQy}%N*-5~VW(fU`&5a{FhSthegdb-1FAZLz|rt5`MRF_AOhHCc7 z2WA!mQfQk^>U!rVQooDp9PJalLe)2bLroq&`iS{2iS~YCxy<|-RD1qj)4gi90nJ|B zah0%JC-90SpVvG`+dBx?Ku_5rSf#SEf{x-XNIqc*?5qeoatu22?LhJ#lwi|t5V#f$V{jkAIGQm%HMN6z=yi@(iZ)3dPLGBGvLaFBL98-E- zGr}ArNvZFkPZNa93>dpe{rL^@eTUA(XXspjpyfKCa%B+qNTF>A*#Z^tDD-aA@Ew}X zWq8Gm0AIc%)z+E#5a;m*s~b#L!O@%O&UNgPz!9R|5!ZT88l#9(g?)HQSox?bKoFMe zQ4?scVq%#Nk=G2y3Z;kdZQ z4N|DTV5|ig!Z3JyOPxDNEyg)M{{iC>vHUwkT7#Yis0=c;cQmJ{4LlmY6_{qDPRmqK=HX zgwfDF-hFxjx#tL}<7jLT$u@b5(0L3|bB%51JbUPMd(d>&tPu7Js?O#DS5e(1`{;>` z0j#}cP#jUyu$_b;!4e?2yF0-pxVt+GEbfbIf?IHRcXx;2EQ{-+K^7;$T|Vx+-aqeK zUp>E`A3L>kx@)R>&Y9`yneMB*NW2x1zTIb3zA+KEOfUW>W_0)&gwmJgzWd#xuy#AG zW^bSBB`Zj&lIk6rMX*}{YmElKWc$M`0)OkxDAiTW+=-w~)Taa;vHO#rpaXdw z6KOUoMdKA#!+zRuuV`}joP&E350O16H?+9Mu#7vgJFtbl&4MAzUG8R-tlU>Dis3rc zxtMZF)(Nq)+VKZI?NsF{N`rUv`HIOzs=w{NMu&J4aIs=!mY?N-U@v6E=(NJUFIakv z%S)bnlpTMB|Jt23j*tV%3r2#neKdn`Hx+NbZu<1~0BM!xin35XF)q8p9n?8+k0bc` z1T~r+Br|rba*EXX(ZYh@r)OwaUok%*>mLc)R{D~CKQ%nMIZV2H#cOmRQY z{m8ymw_(4s1eI`@<~fs=dBP{T)f-74RO)f9OEyF z5gQdGL$OnXzB|g!ul@b+xGW>!kw!0DwCqOgFUjz$eg9~oVTV#xR)f631>EclHMmoI zIP?+C~-*NpIyDGsNud2x_*QvX?;m+CazL8kN$>lEiij zmzFF8_@wTaC$ja>;M3^4?kSn^uQYqK7bK*A)Nx(IL;ZXQ$V44QLdBm@xdFjjNA1Mk zXx9H2H4F~SHU<#EQ(`R8_K&qQU%f#)y9q3X)^xX%n6K1~p6#}#L~NGfFsYG`hnHFT z&m=UP9{-@0S}I>-6Kb{@d64Ox?g)jB34DLb|9nt) zG!kxx+~XM9_T--0ymJ?67+b;vsztIB&}edxk|P;SHMJHYqheWnh*~ag2`|FWJtwmfJqX zcW<3DL}O1(vPrhLyWZ)$BXT*)Nuf|UunUywZHP|`zbDLEWppu3>DYrepBqi+jVv#5UN8?8e`_RZ zoG@&ua$bO+CXVQgR>OEz;HZL+){l2I$E<-fXP%OhVXQGFc_IoqJ1gNg0!c)gnV(iF zmR!p>M(zKIs1IB;Asp)kB^SK@6}91qU;1F7xevdRIm;HI#L3uXj~5KMz;Gzn6|&<& zBwpb81YVHByp8sc3eobjoZ$`Oq_Y>bK`gR;$f%1-$~BL%Niv&{-;r@7m6hIzhI|;d zL!Z43zl=Kb^9WPq2TR0Ex8X*+>77FCIuRYB@u@2hax%6lA=wA`c>{wvBxWsE&Z#Li z?-euEHa(;q()X>deJ5>QXHw>;ABpVV03n?Q7vV_aO-ECfWU=tkwxF;d_2@m4QsW9U zHdF7f<;Q;_THsfCv1ze~#$(fQ6?6Bsf5iL{{_??cscVGdJYe%X8C&RgS7djA?duQB z6JpQQN6bF*bt~L|iFrcbbs^oh&8M-`@)p4 z%45|KvX%K~)s4BI$1}wyJ-u#>b@Ufc0w5G7YqMs$jaFv`CPbG}9nT?`(yv#&NOxZo z?}nb#t`)$tK6oX#e%6|5zhhwO+Qt7W1ZQ5l(%7O?oF$Hw_p219s+VJa*Q3mvDf-Z| zhYy0Mr7L&;6CHe7`K^7A?;<5pzlD5soqVNtr(gA&uVf6(b2xs;;f<|Pj|a2&Rvow% zk77{BC-8z>5Qkdkq0dyZ=EeY};?FQwgu1C%$s6C7LcaQiGPQiNiXK)CbB&?6SfL;; z{qy?c0p;HMJ)>`qT;{i0*=q%m$R)x!myyOB=Xcg7y`4O>C=#ms%2Z7vv_W=7eEM{j z%pMlff!>11ABwkl=>JQrze<3tR>n8C1D!X(rPQ0vF{;HOZciW6C-$pvV94&&zj&Mx z+p|b122czg(#j|lUii|tswCHD$pUn7q(w7~GzlI>7pxpy4nr)dSndp0jK zk4<0Mxr4$GHlxy&NMEiMs>9-`HgRkQhTUw0GQP^Ie> zE2X(+-sh<)`thZYT_-K}QI@3gW>(IoZv-C@^gmIdRrd?pGI2hyAs);ljA9bDt|T}J z-;s#^K}U$dO)p=^d3}MH?Qn^?MEQrs^@g^X1uA`yX$=T9y=L3S&UEVeh-+9j#_dnV z`R(})6CZT@r%_X%Pq~j56v0phql5x2^*k`tNx z;S&l;#CU)Wu2NZ>Vu|YjrDL`FguuRm^Xz_bE9B)Q>X}9KZXefl9N!XHc)Ei7XwQ24 zB2IqHfkd+$RWxjFjJ9&{O-!Y_XP#hRp*~h84P#LGVGop@lY>Im{ec{;SQoLXqs&EZ zxdVvdWv~3x@hjo~4F=rG zXSEQ2<5nuZ{r;E0fKC9f{|OBEU-awY|G`*(i8D4xNKZ8)GD^}-(EA#vGxq<3-hAWF zJN_U1W*qLJ8N&YquuPOS%Lzn(_s-_$|DFksp#RB))9mgVB38D-4GV+HD1i!?Dc)R9~cKd z-QOKPrYb)Q{qqvC=rNwUdV#^NuhzX|N+k%(;Z);><$8EjTJ&iF1qI~U7S3BCmz&Yk|DLiHHp4~$y=ywRa@PGliDOcQ|zp%LL=CVbtmD7-$&wq>39MxKU&D4H?+lA zPg+ez{4vI|C;DXN`onVj*Cp#lz;L>Dz^e=G_Z*|+6_XawkgT&&uc(S#75l0}=e;bs zxSznJpt|Wadco8cWj;-4LekRGPtR)BJ@Mh_0C%<7aMVjNDE}b6WT5$0BQ;CN9?)Vl zyfTKLoSTxrUv+?hv-UVQ&+1p4m{}ySq?(gTPI9Ktj1RHl%VQE!&RfHvl89Z%X|$@- z2nT1ZOsz9zlzamN#g|{>*CH-ClDebX<`9x8h>s1jBn|`*SzW`lL7Ag%$jdoKVk4T< zc-Yxws8f>Y%K#85*fl<3CN3$(Up!Cl2ZQEkL~rZ~#y)qYwJ76Ja1JIJ&5)$c)Tm-S zS)Br5+P^FwzqWTp#bJMhPu#aLP?@IYoh?9ydrG?M&J?|KYeqfs`kB94?VmJ1c=k7NI_vaoT9(*%LPyd?lvh!P?;*_| zaaOM4ed}?3sS1;ekmp&)7e}M$^01%iblA>zX%fTh{>qaGqmD2Bg5y8i_1EIF%Ue&J zrERtS*+&iidtkPKp+`(s5;;8exV+)X3p$|GCg~!L!KXB?J{(7VwQXa;P zO(7Uq#BYCK>6C!t#*(BKTcnwy1U)Ip=)%*legi5FW4~hv7laqiTFd_;7n61rLRsBH zfy3$4r0TMk;F8e=y6&XE?h2gA(jINRWfeJbr}=zZ=?ii*P5HhjGE;xex}2x(+`q_?S(W1 z$7}D{)rcFLBcx6wzH+~7K4GvW`6i^&$-Y>3Mf^7gJp5ooulp-}J9Veg0;}D zctfA&XHRq_?Ay$P)8Mgk&}wl<+=G~ZHft74gCEj+YMuCi->{qCBAuRfSMu*44N;h=g|`Sx^-g?%yxdT6gLeT1#{V z_Y8NOQX(gg?%xq?JzBi;MX5AmKcBc^w-!s}`McU9(|9$a+^WkHd6UW0Y z5~GiwJCz#Yv_}3hy@M8G6EXo?*Q8TQ)MohUV90JOKM-J%Fab-kW|cF0aeAD}165K5 z>3QxURc62g!uHgFJ6-l-%%9jZg$Lj5(b&!jHfkVOHKn3(L-T6|PBevJNAEzg1+u#j zCEnV>l52ryMS|`4pd;odD!QA22Jx<$wN(nECV1kFF3D+r!Msrhz=a}m{%t;W541&q zjQqHGMD+pBA7XYS*M#m@qtKhdZDC*(HqJFgZoaY1E&g!~xYye6b>6j~1plG?MlGpZ6<226oz#I}C`<0%^>4!xU0apQlI;?)V>o8KR3S`D4wH6%0c;j*+jb;wA(`48>atuze zReBJ|q-zWLK@9 zsZO*U>nb;ze5=$8UKDP_M^(OuUGFtqKRj6|4`^_#}H0KSW}DkC~gM#hIC^2o-*Z+ zvc4q*#b5lUE~OG#{W4u=$<(KI#Jlr>1&O)>N7c#bRi&yAOKr_Hb*txNOwBenkIu{x zRr(`>`fhTd`mp!v#0MVjzIUIX6+gj?B~({NKNJ&PZp<~~J4~$t%RuWn>Lsuq!nkrH zqk6DgKG%Xhjpjj$_yeFKbjJc$j2aCd8A47k!a%kP|8CrVx3vTX0n7p~KxRd%sM1V5 z=)KmKIW2_w6A@0X!t^yUz}ReX-1NaMbijn8QkVG4dRwX#p)s%Ko>C=l2;l5ocdEst z%?!Vdj(Q}F)4iFLV|D3E^h*!Pmw?C z$iWCf31TYcR&s^%K-_ln*k3N4Y+dU|$r866alxKJY%^;3S)YiKjoaX>yohjv`4*lQ z9jq7W6hpKgh@e>m?A2-`zFk=m1xCf2>F5(k+c@v%M)?6tQ+^-!f2xt{Jv?|4v{LOg zc;AusqeCg1=_j3S>2I&L69z$q*0}~vV5xq&4DmRzrf9OZ9_9+pB0nOWqb6y;11zcj zn0EN5g!a8ih0M+R%wIIwQ2=ACAq&+u-ti!bjt~Q=+u$V;IY{w9~5ZiVlPnrM_xHo!;)#9+4XKik9Z` z0xlegxkj^{R)ocrWmyWw0*x^<9qb4?_^-rxMCpHX6DnzyMs+T`@xGq0A7_jrR*fR8 z^hJ$j3?At6NJGS;vEz*VMS(TV&p;=ZG+wjn?jQFq_C7;Ho*FP*_t!3W@~Cz^4dg&9 zAbQe(+|pt2=$KZlR3Xvsu+j9Q{YGIgtcBgm5E7zzhWuf!o@1Jw*Ek)2Zw0E;SiaO; zzsc9TE491#O-hRAfm*r*8lo1`>pNmejQ{H-^(~pK{c}lx)qdPi@>dEnhY7R*EJAc6ImIff1y-z_TF2o_0ra%(jcV}PuV&4qPF}nFCk*MmBZEJ9`{|8iJTsDzyPhV}Q{UhY5d4F4WY=cYgn8(Kl-S4+T5lf=A(Lhw%3p$DMm^+8^U0MfnH8T&`GW^79Yqvi4|kquDCzk^H@Q?r(~%Bi3{m~FDg1l<+^9z$ zvdJQ>Qm)+sb~lNe}_=s4l# z5BkOK4?(b=_14F?Sa8*^)A9E7|9|JPclqUIjP#|k=XKxs<$VA3!~Y$`+v6?B6;Qo< zM`7~676H3j|Bt;W<}- z2^uP8c~own&BlHHb>D9MpN-#j%*nS(Qj3YYJ!VGEo#9{MAoq>Sf0xgXSz(XcG2Q{V z&s9dxdI7$LqS^e8^VVmjtN})?i`w5s*&1&gBmL|2{a#_L(n2>7rqze*rRF1F)zYT7 zC^KsIxc;p%Z0CcqzvgY!RF7|`=z8rDR~a(-`h9?_V_8y_dQyL2KQ3}|L0|zc@>{K;!I&ZTyyb!UC1S+0@ejs^YOf-3&aPt4HeAm*Yq*#3-)AYm=m3qJ-GM+_ zuYUss-!{_n&J6f+9o6qdF-WVcIv!k8`)@CNdyKcjL|{?2$Rb|pvAY&CjK1z-O04I~ zlwWfmgs)@hy3W*XCbjSU`I@yDzxF_KYZ$s3^a zU0PYcn%X27C#CcZDzGgnnOWu{8{=_OZU3Xk_+Pzbz4~a4F`6{F86?#rbAj0&6V0cVW(zoY1zqXR_&08<(H=Fxr27GP=j9z2qg0 zSJU@O3ixtrq~4=w-^08j+^;YA`w@O~oB5jFpZ4^aPFY3SOM)vvtfqHBz2+8Irn(edeg+y@V5eg%kJZ{E$T|M5`xWQ;HJ;6-pDts;TB0i)0wbu9)Z-nQW{hv@#@_nHS_LS5mEub?0YYs=1={^zxG}ROK1p6yzPj5I=E_OGXmx1=9WM zKw3}fw})zu%LQpK?S?D;oRX3Y)Lxo$FnDBkKPYgdGN+eePGLE!ZNPW-VThSk;*($;atro5;CQ&=HsD>Y?ED4KxTWdfYHpY(Od=6DGA8A z#fe<$-E9OVjB0y1{_IbjiU;oCR|TjYl>&C`>hUX{8+MY84~{y;G8H zfay6vpq{Q~uS+%4Tw|5kf0Qfd!3|8wxaJz@&R~;9`=Xt$Yu-te{ zzOhMlP2DFAy!+||B>C909L!HaB1x#iL#YX4w?*r$$Sz&X&Kn7+x9T@9&sDvPcUEEj z9yz5OGYo#Ob<$UU5v(;DV^8UVj{v@A9u328a++(1@|{(;LAM_KCa1_=Hq_-B+3{E9 ztnkjsJ%MkV#8&6jd1|NT@GatdScGLcV?N_%(cT5a8UaJ0M$Uxx^FS$eG~4xV?7aqu z?h_`dqqe8^tcGgbCsoxlP4hsluh!VSgo|^=pQmm= z5A9E#>l^=FfcX4r4`-X9r8T-uVvUkNiV#>@Ts4FBVa)hi%J zXOpo@xTK>0=}=G8V#8P~Icka{!)W(1QB(b;yIC`Q0YmnZ_42V?^HT*i(XMfEjYcl1 zrkbUt=Czl6Ak26h&bhrbXJ-OKvy_!Z>-Syz*Zlney^u5&9Un{0iw%7>SRuY??5Rpq z2I*2|>C#if%MzN+!oAc)?aRYHzurXc@gld)3n&}Gc(M9*h)1OSacBhXrNU3G*I%g9 z{kc}8Tw7X?=kACwcd=T%wng*$p}%+j`3Us7x!I%A`J!!{aeWkUuB$oc=zp&Xr^_PP z!FU_+$gF#jS_Afnz5b(PL$O(^E&dDkE7X*4e`=4Gr`JG%)ygn4X`x)&03!5sDsWQD zLtr+55IvppuVWqJI0OyDaj=joCUaHt<4?MBqYPp=U*ziNt>~xJ(u?Wjg;La*G^b$_P z(qMdkPB)7~$f9mRqlRVrq*pcyKkl^$VZ>(CQq)A$Kva8FZB%hoW>kDsSdL zny(yWxF0%6ojuMEGni>FZ&bBRoV3Vh=GS*?Ika1X=Bxi!$~NThbh|k?S%8*;H1{(g zSV81c-#!3zV&p;zP&x4&*!oS1e$xf%^S8SFJy=_~|GYNWUVHhyG+&f5Xc$2eHG_bQ z)5_sYT@SB`3(uZy*re(=Z_qgc7Ag&a0!IZJ(rY|E!W>!&fdXfm9otY|sVHmE27(`| z&Gb@Uu4q8edjvQ%1pForEBoo;yz1Wp1=*rLLFfp?X#DuD+y|!93wfEM9y|FTwLyG&f1?LwBXpwP5E8K!DUfjSnma9PC1()xxjh)-gLHTqLOC)N!o) zZBqD~CTIddA2o&m!ey?_F>ccGn<%Iq;SzO^kcmyls&3T8+3f?sQz^wo9yNu4iBrd+ zuHU2>;+Tj-!>p*6{v=ebxRTcN&DwHyqbNvLI#G!xTX8s}YY;E-7=Z`X0a*;8740wH z+P5}Kr{0GjXPhK>TwK=Xv&%&hvg(NzG?a=`>CxO?>1GHcXi9ilT)aT1l|RwHb%Q_% z`)K}?@Ig@ssi-W32JCG%P7@|&V{D0Ad*Yw^8Kc?oO*p2_tYjG-o`zvU2EWG)~DWAK^or9r->TXJmfmW{C|!qcEA;-oWi16!L0&CAC)5@9rq z3R)Re9J=-m!{*iW0u^pF$BNu(c3e>N2B>+|m~3JYO&G)mPAq1CMp~gI!;TZ`&@fwUtNb^|%`re4U-PC1aHzUs8Wni~N78IpV)2axKTw`yyb(t#4m!ynRrzusOOw0Pl zck2{*hP1E9l4iiwPBm#V11YGN#EQbkVPuAKFxTtY)K8nn51B@_<6JWB=`+=(*^K@5 z${2Eu!p9kBO3`nsv$CE67c@%}MvYk8&P*0`OCm$}z;*vw2+NRdTD#{BC$ zB|k^y;B+&2>tEF$*v?E9^bWm?qQ$dfA2qET`Vy6oGr$z8A6!GGmLt0Vx zIIGO=ItERiVe&YLOz3(<4ZL>Fg9Yu1D48kT)pq`(wwc`A_Vz2#rq)A~xtWqgMa#^5 z?q2h>38zd9?n3+FWz;%+E6!1ix`{8H1V7mrMrsdC=N3vb6+JRZxP|PcR-zjYfpe>S znh(oY&4-qAn-*0Q*in@@OH4KTYjt;1YhUM@+U%W2wrhrjq8@QRGZCzCHB_P@)y^!J zL@26fT5wC-xi6nIL4l2{7O@j^kd|~a&LxYS3Exb3ZWMbQC{v5B=_RVXeG2rX8EV z)x81(=@=v^jT=^jzE0$@x>nzA3!2*NnSjd-MHeZG7dH!|L02Ts;N$YJ+M1rt?-c(W zwtsjyJ)YkxP8!yO{!A1DC2HjZfEwPeTuLQqRtujQXmKxV!YdPn8`qu%YS41eu5DMd zV$j&`02n&JcfvLZC>vngf16ucR1dr@wpl>c z0B_hidLj{xfP!<{k;*7n*daP6(IPLzH4VnL3Wymn2untnta8!Wt6Kwv4CsXoq01Ac z@K?E~?W;h1!{E_Th;jJ1T&;I!my09h)l(T4J_V!k3GY=5d<#29=OJ?7KXG5)sapc% z4)}*D^Zs#~-ijLlgmI!<5>N7Fd9?j;n#spWC1Md+1GW7->jPvDc!!~(6B2XtEg%H1 zyv^r!-2xzUz&(r@U4Yn~kI0$#NF~e+eS}zvH_Me5=Crbvk`^>T9M*}RI}0gpmq$rW zVW~FI>uhqiKAxQ{?tWAWJi{k8;O}sCIXs!2D()SBm->?>$N-_!cSs^tmL<_Jv<=k_ ze~)7g7(6bM%EDqiw@{oZ?~zKvB4p^@e&v3+GrI)L7|+Gu=hy%Sj(<%pXDKw0>Wp^A zKW^BlmLju*oa1u0*}3ZOHLL+c#^q8qSt<>=JFA?n`}9)XSo94_Iw4N!haBU;mT{ug zc9vI&VCp?frU6bHldJA-!!j^pTrG9n;q9`OuW2)IYu|I3gmsO>r$(`)8nkrSfyzhR zQ;Asw4BgvL+@U)SOTgT5|5P}ZfbG0-^3(wqX@i&!A`stSmws5$czGCBFe~q=%P_2J z+%VOL<+Fi9`~$nFpq&l$C?NJKKq7Q;D|)5EJ>meg17G^S?q5Talue;)Jwd- zGt-6f(q^$VByQG6Gmye|X_VL$l4?OcpQdxGX9oxDb7m}czjT`)fON=1jH;;Udf5Zu~Y@0&L zO&4zTwRl|k7(68zamrUh?|OOocW$7xRGBq*L)g#v;o<7?@E@lkAZE%S*GqWEkLFI{ zDt(s|Y8k1Fl8cYb?5B6%bYs0Uw^*93?30TwOzg*hzYrLL`k0QK?bmVVdfmS5ys(oo z@>XI+!XxDH=^t{d&|lBB>$ZL8&7Ge*S;~~T!@`OZG$AFz6TTUb(O38|&K1k(DV$vKiFz!k2zHcT88hyN%115pPehILPC^EsrN>(8I=Y%ibxyTsPrk zKkm^Y)#Zc6HOr7Gxm-=*O25TBy9?+^qhv`^zgDijFrx3x!^tJ|v{70E%?fjxt4vDf zWOPP4U-$dv51jWxAG{IPZrXQ1OZz!fegN^UpUMM2q~7oQvD_J4weNy-_mZbH$)&e4 zrrdG~g?aoOJ|Vo%hU@s)gknXiK}hw$=!$fJH_ef!`Th6@_j`M|4g^<}0}{6HTX|FD zxdXz|zA+C(mwbOg{rg2z^ydO0bHbLslMh)~11NYnY=R~deWO2Ae~U*9l7-B?kNSXv z2>Gn@rH;)+st@u*_kANA0^=3(0r50#p5yo4A1?)(eSJSfFSty?`{RDFzc+&$hL8T7 z_JxhzM5%A>hu3>?Y#Mk4^fXMiZzg(uS3d~f^T2T<*i|UWoIuzW0HD5K7&{d)8VAt`RDE6)VaC?6Y$Ne!2jhEKF*h%=0SYOSL_4hs>ZV*mTmPwt+ z{+N8{3{fJ_7VijlMLWcq6)KV*osI*L`}K!mM(iQ`i9duip;}|l@)s$}tj4*MpNjJb zH>n)paqybUj560leh9qxfcp!-_SusRr*N9BNL6Mhj)q)OJR^iFHHI8c96?qmB!|3P z%scGrGyWG2L34@G(KuD|!U3D$!3v^k?BU>OG<;kRVRNz3!8lp+MDdbfyVTFMb(H3! zqy2G`BnOJ3%nSkNi;_J(viU@2d;% zuFU96oEiBDsS?#NT^14_z6-;y>nQ$~V1cy6RJajFH{~$HRm1_|3~PaxL{xYRMm2?= zbW?;CA-ht(p~PeOPYhxTeks?;1ELw~0$YjJ@N?v6N(6~4G(KDx;a#!Op*VT+RPh=O zTq!^iVP{P#CR!% z@H<=@DTT;1B6ez1o57}VFN{k{90{iA3U~}?%R_G+ovHKSVz@oVDy6%`Nwf}jJ-?~q zps_AtJ&UQ|U|#rd3~`E3Dby%D5_XF1UJedZjlqL(b`E888VQBiGz@l5Q?0AIEtTf# z7e`1;8WuZ`srcY#uuK*C;4< zy@09m;6}I~#v>)V1W`2a*K?6K=Sa0}HCP|MgF#E7B$XA(OYBTjAw^d;cHYa&Xfom& zeuy_m|JF(_HBF1X+H7TzOT&tAj%Cki@Hre21I>(Jh%3e#6Qo2IrztfU5*~$-O7Z4i zBH9R?nYX5mSU{gjARLIqL4OJL&y==7fjV|)UIbOft@D#RM5idMu+ zWfKO7^$!M5$bJ(uHH`5>*2ioC_W`Xgwy^f7A<8;&b!>oNUD`UCbu0kC{>fm&Pn}pd zRCe;qId$TC3`}x5S#?wZ>;8sd*e6CzEt)Dho$R;!HV%WG%yL>CeZIC_W>O3NsoF}Z ztIT11gFbfvy`g$Xv8ysPvyK%I(El?y=u?>aGIAJJ3>A?gZ;F%PmPG$pu==M`tVzKv z89PueWkny`LDdX!JMSR_Ha(sQa=ltAPTvm5koH0b@*ABwM(@#8qyfGXfWN#yk6>Wg%!^7uI%6hL!u zSyBXjq8d%9f^2$*79rIlawmP1dP-5W5`H2FH_!llm-LBVUwx6@P9B=mKnb)2v;3q2 zDuXYQu<04q7a6NCCCwr9w#D6Rve38&cAz?V@9J%e!_rK_@ks;QALt*|Zc2&d`7&I{ zV0kj+&8jM6GZfN^M2a9HWxjY94wwem8bfYN0j&Fz3g!zANs1EFj`5+tRVR_r2?^yR zfLUejXo2m?nPCp#;-qnU?O8S5Ih?QPEUa`#DjF)jw_))2>Qi5n>;ek%TqjzQ!4{J6 zuz~ErNR~|v#%6H8qZW>ZbUe|J%Px#=roj;t*A{vk2J+DMlqF} zaFx}0OP%CH3!N0VIuv$^Qoph=8pQkk>W6n`4tYjK{E99oj4id#TjeCNRo2fQveOW( z##`&Kr^CPuMtAdAg<|Md8K+-=^~w_x0TbsOmvG)GD@UwT{Npx93iKk zY{4+0saF;~KBHEgEN7d{&0w#&Qr@I}STieLT>7A5Dj+P)n(WW;tcg&Ds&SY%t6H2W zXPL~xfaT(=dVhIg()v@)|TS z9fpbBUtsfOdIojPy-Fq(-7*gSYAKnkDR#aQqU3glOO29p1?}{F4s~D(m?C-T;(jOq z*-EX?FBy>m4aG{M&mmce!AJ9E?7-zjn=j8r1r`SeBvUd-YsQolY4hd3t*;UoZ@G&c zFd1x<{Efj@bFsWi8=BXkijZrT2)0OOX3*DcDYw&x<~OK6FWF2|@`4#7c6!rqJ4O(E z47m>yX^`0|C#=4H7elIJ)!$$s_PzQ0J5{KvThr&lTG{8l0z$k5LSp`3*+?`9TuV?X2D+TM(cy=b!#K=#N0S!|#n`UTbK9NARh z6Xvd0T&Ac)cXpD_KWBl_Y%-6aH53<-(I7I`H6@H)&A27e(6+g8w&}XrI&j@e-EiHa z*FfD|-2&UZStg(}A6mCj_q%SUZmbSkH&i#x1~H2V)B_>_<$!cRJ0Jwm0*C?BSW73p zxq&saocx(-^E5{tlVt#&9o}{vKT{}%!!I9#BQ#31LexSuGgLD)v{2{Czw8@qzu9Kk z#@L{2Lu?Cd6KB;WJ{m46E?O#TDq1IMCz=nc z589(a(K_P~K=0O%t-Smpr%|V6r$MKACkX6kWfJNpp%NYmVNTnbeu-{RvqJ0BcEjL; zYbb7LYN&STl0(3^F7~VDtH!I=({N1Aw@8p+k)w}enq!n>nPZS+o&&3ya+8U{g<{hT!ZWfkM2*3hRanWz$&gu(><)>lD-I6fkK})U(#j zD%ce7w9;50U!;((`(*%0YiqMLL2QGE(z}*(o%5l z3JUehVHf+VS!pGF@NqxdR^BuP5hgl{bZ}um`}-J_O#x)2&mq$NW?Lmr@@>p(7#pw} zCpBK`ESyOs5hTkHP`}r{qBU;t=yUDHPYYZ^Kxv))GV=+>4f12~zu;#vX>_FVWNa{N zKWHoDOwE(J3&$UOW9Qej_F?Ijc8@9o9bB>~*aR5fV{do%svVG<4`u_lafp>`OEDBC z&4BTMRTs1bNzPzTU?7KTdXa+GB12;mRPF~K*Z`O_TUMpDNXMA02{r{rY;QCs1LYDq zjCJ#vG;rwElghzd_OuG4_NgF2gX~%bZ3{ZiWO*p(QGeiQ&+y%ZLe8ViDBGi zq)kc$)5Ds!6O8Xg`6{#2CmC^*qQHKzpBE~tXc$SEU}jiLgFdwIMDvDzlm0OoPA)u1 zF3LrQuZl=DhG8U08f*rOxj^?w_5ou7vm1<{d3GAy^qI-bjAjAfuw9O zi}djqadA|XbG`xbXe3oqQ}b|Da;dO1+yZt9_3(T07jWPgha4do56@I3{5LsGn4B`AfbadaNW%KF<-+4jChva$Ye9+Y`hR7nwqfd?`1u9@*i_qI{*0JiJY+MLlkI zKDmIN0t)J!otc&4HqcFCDxVN4Bo;x2wo9_sL@W`YF2wiVqx zng8M+l_iyM1zFptRz?bMMZ*8VsX7f8-xv zvuCvFvrgP<1c%wO`vPb29Hk!DYWIE|l3q}&e+rTX+{wru$9hENa#WX?*$GDfT)E@* zlYa&CpM3Upnpr-gEh%Xiw|@@np4u~hF4&6V%=zSctB>yCE7Eo~1(z9U*|mt%WiR9c z_tV78u!hosY-zQny0==lP1`r~u2gqag!SR-6Z}MQ%3{PC;Cxb>*r8dbZZG}Njca~I ziwEyapD17fu)6U2?dR+LW9lj=+LHAn>gz6ePYD7 z#MYWxc#d5~M*Q?^#=o>TqbUg)Z)Yk7WKCMIpvcmNoe%i2fOz+Z9auPd_xoH~)OjuM zEm^R5OJ4<3>!~P{w0kn#aKGj+VimAy;CLpUs(rS#%wI+?&kl~ zIygKzTfkiR%v@siD-n6m^KFVM#Gqs?IA0wkWAwED8Qg-e~VoJ zuerT~fK)G9D#xsUXtng$gdkk?J9c$jiMxzK_TBqW8k%#w9aH`$0fU1(t*KYDk72D> z-bd%Nm9IYk&V6u6J$o)UJTiXGJm&aZ3z2&6KT}@&XigBu7<)0>wu^k69JmdjNP5T` zdTPz)Il1mB-TQYZ@FKFk`WpMWM{}xB9OCZ0`qw;U_yW?t2$aOTl)2pQ5;GljlK*F> zffP1^kctdg0`M~ z&J&G7Eh)X<>H5HOYbx8I+?SS9#+Mm6*g-qjQ1J7RCZ{DZT0ShI%Aup&P9R!{h{sCYvwo8*}I zbbWZ+BWO(^ylFz%lPAjOAn~`hjnJ)+AHGWLW#HGdhvbFHE#&S&Vu?#33I{U{Z5f{3 zf%r8&#qBRDz3YUu0CxdpN3#*L5<~OQ=x>t}Wb))z14Y+Xm9o$@2By|;5&>BuWgUt1 zUp}LGGYU+?(UP#?x-QQh9G>+L+e`BQ0&y0|Ut$%OZy+=kx5!)KRTegm3sOsk^8SCd zeFan%+Vb~7xBm=8|en6ySux)I|Zbq8v*G?K#=Z`?ym26@4NS@@4o-@dEe)9 zvyLv-%x}-k-ZN+Rp4sZ3)#=wfRBa9o@0F1)nz+T%XB9u13SI>8FidR+MRO4#GNM(q z5dgoUdFLZZH0Q~3Z9{33F!QxK7D9`3e_0!9g~N`M6Fs8T^NTa3Dl&fJR;tVDjxonD z#~t!~q$93n-=e8lTvi%JMm?rMp7V4{m9|D746ciRoCRyBK^RkS{TRM=zpA0IBA7Na zM`>Tqsl{+Wds#apTuJ@FIGj=TP&qg8xe5_ySedAa-zPJ4f$|rGGU~{Li8js~jx{BF zyV+xDFH7O0{H*B}*q%m0A7Dx}TgsmT(4S_^9wg8Z1|g)6ADrGkK+z_Qlm#3(dXX{3 z2$VjzF`O068hx=)B3u<5E-dAT5T@w>BZM%bdeE4F5!e8JJRAD@ZHG}g=(FWO7H^76 zkJgBpoa{~*(GnN)Xve~cXM?8+d)}xE|#PK7} z(I-7(;%ylm{9bRb!Zlw|C#Z83b-T0+pHJW$E0`CBFNu;GBE+pbhIU_TL1n?jKesLt zbnV5NSug$I(?Pb_F3&pGp>W1Bma7+&WKiF%*c|y;x=W7@YSx-}<;zV>z_mGZ(nK@% zi~LK^wKwxajhyre)~@wJ&&tfPDMcnqdo~M{NR&A35s)7h3uQL(_AkJM1>C$%Wl1b3 z3H0JEh4C&d<5~paqCORZ?(i1C!tcebPHDPEkGlkZoWYr?#FMIuQj_Zx(}*i<{gmu{ z(ccO)qkDt-$#%~UtKA8e(N-j3VWnjsFKFxK&7vgSObzo=(eswKN9WIT1dGmK!)1sX zxtPx`C8?$X#fwn0EIv~P&!3he+uBR8404I`B*~BlI^e@s=eYAvmj_`Ffn#D9@sBbz zN5Zigin!Fn!Xq7+RO8u zFpOt#pp)(&k?OJye&RVO!k~`{H8}$H%2nA7tYkfEEEp*m)0zs`_{==m@ox|I;?ao z%@QdDo7E^*6;98Gw|q=jLikZEsb%!87+9MiZp7ZJe8Rt+>VDQzJI~yml<25c+SQm0 zkFUwfgsH*%irr8cu{u8N>2d*^OyUXstx`-0ZcQg=B6&-;yrPW=1^UQ)O28>(%f@xg zXMAtT(*WzzcO(l~^)E$so)n`sFRvOKbZug1JlRq#m?`%JDx6(2_ zOSK(!K38&ir3}>+^2sc%3{Io-)eUN{ljMX9_RU?%``aDl-fA$7s*NNXwEbzTLYftw zwBji@hB=|h@X;}r3c41j@>&6l&;DBL-V$4s-S4SQ9Mj;3TQaNQ5Frxz%o2+5D51|h zNY=ocT)3qmI)zqTanImx6ep5Cv&QleS(gaN;yE5j?Fn8@sn46$92x+w}RnrtAQ^G*^$CCWSLisF# zIl-1|ee&arhdfmAFrHLrdxaOzX)i*UXJ)(Fap&dja&p(D|b6Jwsg+qJWU6C2&Kkn_>N=0ZaXyZLfRZz)owFp8Vs4XB#XpW6|Xq zLG5QWFrVpKi^c_PX;zX}esJGYvqMW7Em#+rL8<8VH?rax5FiS!vUK%av~XJ`lck@z z?u!;1b%n<0YWu5eqs2_B;90nioFr$|fc3vM5m%kXa6=}#uv;CYo5t+k%65)Bin((l zrZ}HAiLQ4!!r#fMPm&pd1q2EXUw^O$_L~@wvAz)VJqPa2H=f$=Z~7b}16#(>tzN}e ztG_Q|h{1DmD_3FOcJQtqdO|1p*_O_(MrSA%P9GUcmJX-n8h?xhUjT<+-;Sg4j6Ewb zEU{7looS^?ZRILUaX+v5VduGLktcOcrxA;>cz`xf_T?tvIvH&Wl}=O|p~P2}Wilx- z4>GG|EdF4*JcH=1mA$7U5`;)#!6dh+i%cIlYkfpC{DB;?Q(xCn-;*~fdI4(A>7Gp5 z=;KzF7GEq}$&~7p=e@g2)D|tVi|I)`>P|z=E>0OHs}gs4fc=g_!^;N6axI`^%|O{Q z(?n}8xMgU8Rkse*i5>N8h7{J;x&bg1Y82by~ew>_c!pa&3(eSVO-VYZFDP#&6a7NnyT5ZaQSgrGT*_KO2fBYG!w18CJMT#A7JRny1sjs~^XJ7+&DhZx;l z5Wf&Ir}1`4Vt(r33cISrg4Z3a!u>p03iMQ=iEVi&dag^^M~PP8Bgo?9fT)Y7^r8`S z4latB{Hx03ogG>dm9x|cyRet!ynv4OKyxkf$}bHp7#gzSC4>RJ@ze!)`>6{C#o>L- z46LLXO#%*#wX})n2-)T@4OTe$;$jR6^zsj;trsVCR%#xOs9KD(aOSR~ymwFs$99eb zuTw&?WK-te+;x@YlE0*B@6DKtg{#36D8|fOsv8LgJ#dLh-G>n^O1*ByQ{6dMD)_|R zFoLknse9x;J>Fcc9%ZkABV)o4{X9vI)qIZ`GGTJ8hVApkmH@Ts`PMz8#u&>?^Q^?$ ziq&n&s$S$g6AH7sX;7;4fNOW`)Aund2wos5h=cI=rpcF10ixDkMC4F!5d7X1#1kMj z+4LvQ)E+hCOYOisD<>f_IV)h6weCXDJg{vu(M&>_`D99s0erT^a1NbKLZpP=LV;n0 zKMDDDG}Ed6WS{2Ffpwjz!Z}IUaP_TuvrwqzgfLpF-D$Q47%haZ+pydo;dqF6&d0nr zPWHtK=dn|q;<3^o&fL=+Go(6 zG*?T}8Y8CQ*>!W?k~h8$T^tiux(9yb)4n_tr;_oZu7GrRp4Zgi8!+I$FfT~f8ks&eXCt~7BgK^c-# z;?~T{wfwS{R8SUjtyZ-VhhYRR#!{++W@N3i_OmklLJT^h+K3M|^bFLXq#1P0N|cz~ z`{Q7SYqvc1+_ZJ8UiL zbmD5)+`3vYhBb-XN&E4qBCD8i?SYpF?Y4ZRyM39F)2^-vm#1!w=`WuxWL$xJ5c5H+ zwggM22xH2@nl#ph6e}|0BF7rpitVzcBoK=Av!#s!BJnN>vQx9eZlhm6>&0SNIQle* zGHhWKf|&&hdY3KAraMo(8(BYk3k&{+-SJ&S(=tB<3cPsr=6QCX3Mp!7du@hnp$(O;-3(ysAD9xHcx!% zh^94i2Ft4uoJAOc@XRJvO)NP*QfFw76tRx4v47G%&&>Uf1gB`x9{(VIQ(R?}wppsx zYytw0V4=WOl_d+@I)pZLoT*@lSa&5^N@}I#<*aH|pS_C7@sTea?)ync(AsPLt=O`H#)o3aF^}Y3G%%+^cBK-P3)y&hrO3nA(@HTVI`QmMk4T;1K zDX6t9K6xYRr_`blpxj>kEN%KAv(8@7)o*JBVqNb1i@sK=4PvCGBat1pF3LP~nO&VE zJ0sC#Fc@0stZ7T-I4U-ZmL_|TVWw!FF~`DwbqZF$(3&iNK9fQj9hJ?;7-v|ynTl!g zz-QJ=3m#$BkDH)yDR8XKy&E2o^X8yyg^1fzTLDReAR7mEJ0#(NM)w!4UI-cUkgQ`a z`ffqeB+YJy$zModDZ_5U=*i+UW&$E*duWWCtOU+KJ^f-Wa${(JKt~_Stfi?}MWE}t zt)`}U(qfvJLAq6!*W=t(p1GY?fKIN1n~;brf9*e@VwQv2Z@(`2A;y#qxH`CTWrv z?@#fk{jgP~Ne~CMAvftgt?&!nAcHO=p`cXE$SPHA_lIUf8zJnn(ix|Ogi|n2U1+;_ zpI0i3jxfc!bIJ1>u1w5*_S5aq8{JwUQP*`ytxf7LVD@?YY+YYBtu(w7@wxMt=pt^T zu55?;T#l=wekZx@P5piEESS!~6>Yr;riG@Km24~bS4KfI7F6t?- zE)uQDEDU(VC8xTr>snHtg$SPZ#PrhejQ%<^)taBb5m+AAB;@)cPGf>B)$F-blXTRg zY1n8MV!pZ43q!}45!Ns7J{uw0rEpq$-)|3>Z4g{fMum7Cn@quObS~}AMOhwR7wSB0 z>jdRy-7I@u>pb$@Zt-2&rHmA|t@X}6?o%%zJ|fj#)vgnG5ZvYu(ci5?^4^>A-7FN0 z-WBSMyG$P5MDT??*j({>;XK^b^AKDQkD7X&!#nFHs?ZC*P zuYZ_qdz{YlIH_LayNXoE5|UPXE=k~)YR$MC9*7JAsRdicP$c+Iv#V72s6 z_2{?ce0jU{{=s?Gh3Jvx(v9y9g_UIIews_?DuZCrSLea4kniCh(ewQNk^OyK+jUY_ z()pCm3g2~G+r?tJ=jzf!JD-B*ZQE16(>SlCOD@{a1VRUNGdBu8<)2!Q%4X<24lo`T z9(hpidgpr|x0djcJP)n~z3#n!$PW*!2YdZe5?ZjGnq>7OoM>?ZQNKhZE-2lCtCOMv(P{k5$Bk_vH1zl@H!A z=bqnls}trFz2shG5^YRaSRzp*G#8*C@Gpkx4!;oS+{qt&Tz``vP0DC$li25O1YMK`m~$5z8E@i!2#D~W%<92yP2O4Got*ZT?&Ef4 zLs*7lY~u|JYY4nkS?VSpBHA50bqy?Cq+6;(qD`SkF0RhJ=-EpvIbX) zv5Vhr@JUPVp`Gu+Tc6+H#a|}0C0J+3T&A>@UFXnVCfz*3wmL#TU83)I&?ycXUnSpa zdc5@Ix%q6ZG*W+O8lgFhr;_#kfl)UY+VC8(CtfYk7`Onq0R7kYujwtV^bPFjpVOOI z>YL~RKUop5xVk7ZsTs&|ILa9P{tE`q^g2c?bb5vc|JUijZ>K~D|7eD{ zoSvl-t&^IxnyMlnpQ1kWudVPp+k11966HGpAg)NEGXZ?~rh(!uKH3s$Fp4cu#dPp= zR)BbVu~PqmCGUY-U$`>w3N^(XUqTvJm*;V+*_@cAFMJzcexQB%fh7Ba`|Yz5OhsGx zFl`Ak#pqKc#SN?IS7iRfwHQ=ie0G}dbc3I>Q7b->q#kAm+GhEE45xm1R=7?s*yS>q zee;;pm(-5i4GAy2*=c+Nj$}h<_x52Fl9>$HI2J42$nE`;9~M>`Zz+ia6ac`9@^|I= ze|F{@R`D=b_>9(e*}AoL`Gjx;gAf)3MLm^{r1JjmZV@&tI6_aC#U`DTkXYCO@0QeN zXS&Oi3>uWBre+QaM{EI75T!ETK%rJh>C3>Dt^ zw1-Ye9D9xzs>P*8`t8X3OQ(az%XGJqIWJl{g~^Nv3Gt9>JTYlpX@fWhE1^h$CJk&Q zzT29E{W||>-O9nvmXsDD(v9h%HTs3ZJE=>^9_b61EVe0K?syTqUmOlx6nUZ;MS)Qb&H8=>wH zt$RMoa-Z69&GDJ5F1V-Z3#%=SpDcK%f@US>2U}IBaI$htEpSlc;zteAX{gxOfO<+) zdJalGL|@g<0UxaF_Hy6foa19CT%opWA2`9ZL15(!7wC<6y)G^389=N!&W*?7f zI+*%>Xz?l%_q`1`+S{!J9Lih~FUYv>*G?MRicDJUB%nkBiRjqe`J3+02|2BvK6qZt zmybAl72O93*3VaD4uc$#$yJHfaN^-r6sLP&$Z@m?$k`AvOdlTEb37GzJp0t{T~=?` zB>Q3IK5-~(3$Z?H7K?AJ`8}2W@|o3qMYU!KFMA;P^=S69-PUB zmfhk-%jn0)l?-}gytER%z?9@LacPyMu}y2y9&y#sp)8lm|+;Y)M5%E6P%@cB~-}bkn-aF&qOBAQ-wuN{KKe zW=%16;vcY#zjC#|7*MxnuX_9MP{WY-jq47Q%^N6Ff!d93$H zNmT;SMx0j4R8Ogxq3P0!i}^TW2T%i+%zH$YS@9C$r-EET+EEjYA|;Ssj4#KWzo0Dh z#kyVTw2SJaC8o-W0AGAzsjF7@uFjgPZv(e(2JR=#C96zwGEj}khO1P9GR?mmHpffVscSVeEdx`x>5?-2! zSVP}Ej@}I@2Uy+M?2+Y@f)2rKk#xog<@&qBt+ECw<#+V=`Nz+kv1^tbl{OvCZF^B0 zO|q_)!W68Es}WQkxD1$s0*cm#0rVW%33}KRfx#+0+$DOS+tnK&dEw`ldD+3T{E**@ z5pjqS>`TzXlQbVoTY7yil}_ujSp21UE%7Cu4-d;k)*kRm|pkxe6N39Skr@C|R#G zz|}nhScC5pHsq2QsRQpj)RQ*hWhzyC@3bV#vN+{l=>WeEsq%08oZL(bUdC+9>*jq? zjn+vPOOMMnARk+$?Zmvuo>0#?U|*;JcgGRH`?Eb~FHihv@yfcd33i`MIzGivg4Kd8t4&Tkd;itWUI5AFFrlp?m$1Sv9KO)s5^J8;6-02ae&eT9jH@p`wWv$1wh_01G)Sz>UmBe5 zIKJHuj~j%;?xW}_fJ`ocVS5}c+5&$}7rVs}hY|hk*;t?u*V7EK^%x4rWfs*fft|eM zNs-U}TXS;>ai}|p9Z|NF+!BwhD?wA&hJLapVD)3|fM@3;VuxZos9*dj-!FU#+j;kb zMGAYnEb(J8<(j->A*2e+X9_7(DN`gGwaCK~!F$`td?VX@sn?VVFl|tQ?QI;A8yTyU_w`25&LOn?Im_$&VQ*H3kC3;>!bk)H#j#2e zue%-1=JrZeo7K*pmZP4*ABBJ)6Iwn&I;>RVfXIG|JD^dy{kp`jR98!etB%Z^Yg&wk zI^u~hu+eql0L5DYWNg%q=srzdVYXm$M<#z?($F-CvF>r2y#3hL@aZ+Wq_yXzG|4B= zAQqHbRZ3HhMZP$qZBh-4!!;>JU|>-Wgv!KSRW?HJs zYD!Wf@cvIb#SfQ&gfB?0Yln^LYH#rk+aYH&yQu=xOXa@7{>wMw%pW4AX!%Ndd4ABn3!H2`sNT<#HZ@(fmU(>>wfdJ;Z6-|xCW2T>Pft;PosY|xT*7#(1-Xt z{b2UbC~vQ+$A;*=q3kXNHuylbQstk#K4s>Ie-HMw)Z6;XH=9cE<$by;bb<~9frPDV(6)evoCtg%tuM1OSkV_;;f9ud@6P(Yl$j zq9|%6lhZ{)kXpp?7^bwYi9)eW*33NrNAuO#vb5xV5n9Fhcg<{u=Z02QmzMZtiSgM#0G6GUz} z+WGCVi*{DWVVD+Z&Y^bP;(LnIDz$Z{$hw?*ui%orG76En7$zp-a05Hs&%lF)nEJtb zq7`uX@SkdJlTRHW4R(2GnsX*}@Xj<*XB^XWe;Dwq zWhd0XI87(&Y++`EnxThk0T+zCex$z}a9!WN$$9fhBMXUStO>QyWHC#sdz`pVtzSHs zA(Ng+^4U9*VoWsY?i=<>XG;0OV5KNxBRX{d3*RIVHNUro$OT2>g|vCtOl-_ApopF^ z>ECh9QiQa>orIZvQa>dE1(GxbN^Ncof+>nHunSg(VVsfefPnF24Z**M{7Z0q_E?~_ z!Y-fU(bOpHvEYJ9q)&w{K=?_|~anl@MavgBZSs z0n?ZhkIKsX>6@F4Ty1igS6dZIuok2f5z)Hx0u{z&&6{Mia2+P>>Js7k_E|Pv)5-ow zkiL8%IA#S45v76_>85M)IBX#5=7szS)TUAW5FlTA;Gz}UpUs)>nC=*5g*8nSHy543 zo%KUZKnZwTm?d<+GPtZpSjzifCo2rD#M zgiUl;1#oPyQ9 z^}S2ActBBidtpW%f2KU(2ob~4T)!)CYv!H98-&%MhuJ4RtWf2V8Jpu`^O6bsKSCy6P zLIte_@$geIrN*41)~p^VZ`&^iH=tkPcKdvX>)w0eKt2|i5=k)eH}LkPbP%j+8(}`BH3Kx z&+|cv_P%7<4Su$ZIlE_uDd<4M73g@;<;)N!Ry#TwGNg9aw`zLHxljh;`NpZj>FE63 zg*C;zn>9t)1KCLS{bqBD2FtwuMh3xv*SurprZc({hsLYwdWNt%XKpn`5bUI*9E2Q? z3Zp)*RY!P|rjCbtKRm8ZMhumY2UcYokH$BPn?YOUkNUu;=A2$PhV5(=hNr95d>}vN zK-gqBn;tsc*~6DlJkh`117vWhS(TPH$BcW?VRRcuv$h}ke4F^aH? z*x^MrrmFNs=-fKf>y0YN9T`VL`E!xX;YWAw+K-<#&Glp8SQ&4JRCo>UuF`$x1V(W| zKRw74ikjR9PWi*&iY(Ie47HM-YV+=P)-9j*H#qFbELYgvDP8ew{}5q0WXBzn{_O~h zmF|BRVYU1jVId9ob;s)rt)wNFE)$d@eaJ{qO?<18n3@!;L$a^U3kob7gs^3YKc_i^TZNB+yC&>te2zs&tiY39VHumlpo1{2^&Cr?i3f#;_~K6Y zeHZY;Q5_8M9X2Kuj@s`*j%?1hWhV5s1)1jx5VX+Eo6SfjzOby<%ImOfP@ZPN3 zDb~I<=t(4-=ttP8s@N$S$ynH!IM}I7)xCvj+oXm00^7Q*NLuOr|7C#Xa~Wq9^;=K? z0}QDE0666YiQhi_BsJS1qQ>=TbRI@TV%L=L@6yZEXl%jg9!f2;&BWxO+&UA zo7>g0ntpe7H9kIsTKQH8qfN{S@v^;2lXy^K$=BC>jYdoKbwjRwcBOC#Y09ubX;-7rKssJdOKmV}( z*L>}5Ut8LlTN%;+Df;@qX860#zgo9l;8pzrG5}yH1^|ftI>SFW(=xI#(%Azc{AqQ6 zq5S-Gw^H+SrCxf~?pb9xi&9E>ZTfoJVL+FFj=&^5qyXRU%WayEHIJ1iM=FwS>hO>%xVx}#COsOxrPx1d1! zZY*6UsIjR5U#Z}@X;_i2d2iYZ51J1ld9-OKuDmPYan=hL`ti8I{`T0y^593fNKr;3^%6RBAKfmpYF$i>Wo*D>mCJ-x8~RbT8af#& z8ao+WL%X~!3S?XnOHyPxJZC^%iG^Kp)*%8_!{Hb2+eWiB9rxj#QA6Zje~Fds<0Gn# zG1QRYY5|kHjih`8jj&OYp1DtXr-FTP-gm?|%VJ_9g0@&|Z$1@GQG&AWR}Lh0>r~z_RB?i@FE-bs z%Y>f*v^>_Wm-{ah#VUK>zdf>tiQkSUZJ#NtuHK9&R@IlLZTv#JQ+)x!m!SrD*o@*k z7K4hsDu5!Q69Z_z84ALUL6qD#<>@{rt7X=qnY)&@q3vB`cZT&w%o_7T^(F41^hs+V z;g3;}5GFj4Xu>M31i*q972+j*gvtZW_O1h}f6y4yg)wUI$55yBGDRHCq=>PT6J zX6rQd6vsIy^K^P@E*WA=AL{1>!hS2cp_r@;{HqF1A_P(7VQjT3Bv9IATBKUFIz1@g zY(%D)`3a3#lUfDzq`dWB$-wFbuDr!bb|BJnexf|p`bs*>;8^HKB%*5w1-nv3PlZFv zJy{7739rL9QUJ9-=e^^P$A}0zi?2Byw7CVY)0@xT5Cn z9q5!LB=!;7GD<{b_EuUOpUh`9u66Rkfks}DNv(c7!AK)q|x?ldsLVy3q7dTxRFzSN^n$pEsw&FwMmk-X$t z52asce@7`%aJlUI2SYu#@QKspm3Ev#JkhKgyN14U$mM&dDwUuh1z`konuX;*}lb!7=;PqZ}g(veg%cGH?k-u0phQM7 zt?UMjBv0tREIQXO&dR)J)Y@_u3>$@I9q~X}_I%i0(}4kvL`{EcUc#SW5x)$hMyawY zBQ^r4NDsa-C_v!k#-D(VF~CL+u=u;xJ{3eIZpZjY))I%J9CMa!U4kiZ9#6y)x?$ zLk*pBk}B+rF?Xy+ZeW%F+O__R%%HR|gTmEu3#>Pv^JJJNrY%;WCP)!(W7$y+F?3au zQZ%SzX2vch-?FYpSbe>~X7ur-hj;qp_*VUEUAbr%cIZf4*_~^e7*(2X)v2MfjsnTj zw&Y1)B@2gOmqo+|oMy8&ED_x8DXM3LFwa*R&#ywQWk==xOvKBW(J9}vpo;R?t@IqN zT?3vy&L4FN-v)6e>y{{LbvF|Dn&jUUsR&rU z(SWUpX@!xUF+dMqT;UvkvwrvC!_bvPB?~UXUi|i&_{QsYkF$kCSITvfQWP2a?5?8n zAR+86&?Pmdo!ohXNt`fEOCpC}GtHH#CkKY>E9HA=bVO5>z87G&DzA&o*mBk1S<6u~ z4chHdG{ub%Hl*}fAuwbEpGt=xiaFe$Xb6trcp;DcN-ryKn95&~rZy$RK*X|&6ds++ zfHNafor4>T7omO0wSF&XS*$q`6!@#~By!r-B)RH|Gn#snR^@j)&SkbO4MUuAXrHf= zK%5_;9yQo0k%0JikIIPosd+iAWF!ka&Zu>tGX5p4Ddp1s6CazM`QtR zt*~@z$@0dKPie;d*QOTK>re+}w|s<`>bHQ+!WOj2>6676>_X zGnuIca4q^S&BpjD+k4NGS{El_%$(?U3le36&*5Yq;Q1dgPY zRH=u>p0_ntN^^4wa+$zwtx;6a+Po1xG2=P$xQOO6SJYZXqWUjv&>q~^gYskB2NH{07DryXDFSt%iEvsL%@joQW-ff}1 z?yG!FzUM)7dlL_9upS|dA2wrdgTjU_V!u|hO2kpr=uG!W2=Ka_J?s^*eq%!YftFDJ zsnE<4d3i9ao}CHpmy&>@nDa2{`UEye_A6MxUA0w{Q^V)baAu2@1H$p#mv6~${FO-@ zOt(Yixnd0@%iz;!ocvf?4~sIu``Rvcy)Z8N4Z3(&;9yfG4-I-GRrWIx2P#8p=rw`SLr6g zKL)xLz6tlM9OCi>6vn9dn^$Nc>d|6z!$j(+jsnrUy2GzyZ zT1p3jh{DKXTI(B~oWGmvQ|2!##w5~$84)(} zyXg?J`DI+GGxW^ZOh$mK7mv>z**XNOM_-Wfd(wC=YQ645K3rdZ3eH%V5B$b+@_LG* z_PTBhe&Jyrhs-RtwK|FY3g}ltxcKZ1pd$C7KLn}tp@SQ+yl~`osP0DG+_ra{mOQJ5 z7H7(<1b>&11-VS`UUDz-77d)i<5fliOHYz!c6PX4}*AqOr%hMs->F0+m=lrO} zHlTY0Y2X@#ZmD8FhH@}E>8jv|3{`1TkcDLk_k3AF_M!%~9rW*PU8u`dC=q@PSzBfa zyD^n)b)sJ17s{V_zCT;ljh1Ap$qI|>$sP6NgwN|ZUp8QU+D`^ML4p2RvA=3=Y2xnn z_DOC${_d$75`}P2M}HK^iQD`oZ#A1NID;(Ox~)E5WMRuYB+}|J$vTN1SIf=w#_`Is z{H`~U?3Xs}_gv&ty@D~so6up4Rh|P|f?% z;d*0`nPlfeysSGo>rdoQ(iXlKqFqefn4WJ9y)YH0|6MVJw{a$%FZJ} z?tC(tY;;bwi@zZ=W=@Wcq@OXx06i+{3Hr{s(dsOO#2z| zZPC5CqTJ6mc^X2K3!&z+`c8QF$H_tP8h=y-&U7fT#k^=Y~kA)Yt zsi|}`t>bXr`i0D1FuuOkRM&T*-8LVtZG4wFk>K84bKLc0>5b=&XjFh~ieeXcOb3c- zz)dIx%2qWaWyAJ41yVgX8$lxVu~vciBQ_*_tHk~4VfCfI?hAB_m09wtIjQ@)IQC4L zJk=$dS$GqynnY6uKFm-V?=W!QefP6@f~6KG4>Y%&Cv1by*z)`Oi&_&gPVagW3px!4 z7rG}>F$}nz$%01DbCa|`LV-pmlczI2>ZomarAZ?#PmbHp4OQt$FM|%2)(~^BeOX;T z8k8lu=$e+b2AkC-HDFe>A|U+;egq$xB(V#H`^`RL&U}yX~Kq8FrKUn zwwDxL9^5Han;iqDmgs&WtOiO8r3Y&4Z0=>30f_TYd1d8dwkWObo>meJ6!}X$^h7nV zMMCe!Ck-0OZxnjCa%^|e1MtZvz;oab{QLPPIz6+0wj=qA zRMT&H#`JV8V1}{14*-Gh!cPR=VTj6}&2SA4ikfcA>4Eu8y7$M-AJcycP{+>xwY|ab z*|)E$zW+1(_LbR(J&I6%U~d+0;NbZ+X1}qdWu&wFJ-hsGvW&mUL0vOeHFf|f38)U` zRL#=1CdN~Fd1`y<^YC*xuiAusbTp&Qt&C*VcpY`%tYnYixnfYi{*+f5c z^Yt;c6f<_QGqQZF8lv}r5d#0jhjf`=(2xSm@nfp1KUj`R(@+3hmV0#ycrn-r0#W%y z-*rN8w>6KFx6UA$TXAoH(IR7MZJl^s_%O3i2OXT6WotW&9%Qt~Jx_)>hhBLwqT?d0 zSAC+nElBVwMM54vLmqL14fpuE49^17F#E;@ruVk--NxQP0O3Rwn>0t;>3!~bPc?CQ zkZ>5me)-*p+=>HtThZf|upBK|M83#bwD%_$6DqbB?G)FKSt%>@Ypl_JwysYa5vZXW zT9)j+7JVi-%?Brx14UBl<=jt3Y8d$S4jzJxj7HC!u-~=vU7eJHn{wq270MJNW!&2- ze?V#i#! zc?Sj0SR!>6d3FQ`4PQ^g3_m&yRWiUz32ZBLdH9cZGWQlda^I{GkRP(t=s%k6*W2R% z%zEblm%-QZFIjJpC-|VhpLhI75gdT|j~qA>VE@I}kH6hQS5gEAfc}@HV80{9_Xnxp zFSP#*!5?=7`0oh%e^2l^aODFr{$-N?pVR#@&5!yW$-y5<{<)_BDgVU=_fqmJ<$s>x zR4&Ff+X7EnJ^}~9pOgWBH$GUv|M~7Z|34@jnOo_;Hm7C&Z3p?!OulNX4%bCc00>kR zIPkt>vJTwH{))+0iPJv+gT;T);*UZ{`;*02gYW?TfyEzX2|pwJA4$`EM|$Yrk^VOo z55K~Z?mLc*z=E6a02w>J21t^0Qy{K|TerCh)ZXc~dQ z!S^TYuQmt`{@+gbAFO}(a(^1ozXkEvC#cMSg7|7cAix`5|DDW%N&(ij{HE={bm{+G zf-u18dxF6pi2$em^)Qd?PtISbZ}p3ubqpQo^i6Z1hTFgZ z3*z;U0H`2;ZR)=R02Xxp902PLg;t2gCxAt9&>tia%>L^Demg+_IRKZ%{Bzzw@dy8l z3P3SHe@XdYC19uzJWT&N09JS71NguVw*;v1?``1f?*Op;X%%!UxlObEHv1m|gkk-< z1T4Qcefv2HEa$4$ivt1Z{)-COu>TH#pO(NnA()>HPz8cOm&EzqGCcnq0KQ(Y`8feR z&I3G?Wr3^E;%iOiA0f@xZD1Dnmj$3B<)CL^X~6RQYX?3ny`ROhVXXUB5U2=g;P@WP z39ziDI}bktk*OdtmMb(HYc zk65bQ|0h^}dr$c1u-N84`{D((*WY_}erqqCIsYRpz^}?bhsB^>LM9Q|FLD*@N4a$5 z{cS9Nc8~lyEDW`izCl2_C<4Ql?`?Ow@qdKnr}mVuw`H_YyZ~){NdVv0)POjHG?;B z8~az+E;9)6Yg-vm1%J5qp8&8-DY5g{u3d2GZvgm*YySxVp-J#Kf9=|*hJPKvAFll; z0Fd*K()_h+_Z|5=0DjsCarTpa{@S(kkN!G)J_`|3_GUckMrc<@;99-(sOv{o7c+b?rZa z<@>fH-(r!r{2yWY={@E9&KKWeQMUe7EdS_(0gQ8gLKxq-iue{lfz96m@Ov=)69B%i z@%$EmrR%Q)`1xJ+`%1=d`9rz?D*u1fHU69izAyj#7J#D1-vRK`8u-2j|+2r0VQdtM^{JtGmzHD)KO}$WZ?{P_&Zd{^RDqPH<2}P|g;P4lJ4)NKi11gikt6 zf18^p3e*SK+YeAsusDBTRQYTEk1OJTnA6QxQF{D+YWv$z|8H|^dys`E6YKwj@(-kc zLMhXtpO9yyofu-F)2fZA)Ebc=Q`D?gqX#lEgLyQjRq2_=6+T>{%^>$k2xeW0BL}*u zGa6j=_PU{ok0#leyCt$;tFb4je{U@l8=FNoR2MQAl3>Lvs3Q{);>`b5$jJJev5dEu zX5%*_Kx78firwOFetsVMpU7KpP*}kIM^~VrF#j8QW;;_?3m4aa)dO>rZ~y6x|Jj#l z{~tVSa|@UM)u;b64B($(9BnN%tsEuYRaLodT&xrv*tj(uti}F+qEr7LqO<<5`2VjW zHuH3vfPbB8_P5dfALAPv|4X^lb!AgBAE~Ejrl;80oTL~h>YrrjK~f<)kRd2oXaO}} zG%kC#eqOw)M6IRaU%z^w;JAn!KjB4bUXmg{pj#>Uf54_2k8?!$CjmITOplQK6?Pf? zzsHsBUkT$sxbFNX*DssNY3T{uX(`FZWB(J;UJ~O>W4**oWXLNzoPzU*5-F2t=Eq@E zFkfLwzx|_B3z*8=+9U$Tthoe9$*sT8+Byw43Ldrmk}3(VBuXKQk+oMBWSS0r6)Det zJzbOWRkWPY5g)+?+LWiZFXc-SD;pa-su7KIUrOO8T+6+EHV#(SX|nXi2>FO_AI(io zNcz72N=wK|`KVASMOBW9T0V;oC;jm(@nyOoQ=^*LuD&KHgo;i?BrFRY?{)#t89*Sb zS0z{nkYJ(eDo8a~p+RXZ7;Ep#lT%v8F=4y}^YIxl2Ju08Zg*ee=A@)%=#2>#kJi8t zBp9=bem{F69sKa$hYx>Plnd#)_(;6BQ7URTkRy-I59(K`>$sH zN4@m5u1BBti4FWGQLz{4$cleb%C41JvBY1JivK22j{lYD;eV+bBhJ_Wl9pmbWR$22 z(Zh+;8U7yx1E)^x;3#T-g#u6eTM@eg{-0Q~HOvKu{nfJu?7!zu&VR+yVn&%Z$du^U z<^Ixo8fZyOfr!X`vD?%pLT~BW(J|FIj!3KglqoEvHYunc0EEN=tbtc7Kt!PYbd0m9 zQj7q9>*LI`-@?H8mDl?I<8AxowNLiXYbwu@`sBb zknT0xo96DnM7k;xae7xtB_#>%q`1?4Rf}Nhu}|rVipdF`lhi&fFLvhzQ5M&!p+E)T&~Ruy&MNFS_Ab-JeGYD;WD#-*Qx7K2Z;y{5vL zWQjGPR@lPHPXM--s)RpR`cshi>|H&Itc|;sl!de>r4@f`T{w_ae>puOi*w?pCigw;bj--Y{># zMylpLLpRMtz6KpwKt>@;nF|e{N5LHk4gZ-e_ge%lH7$J}g_%jNNgjoXiHZ5e__OeS zdqDe!zyMA7{)&0Dli7)x$Z^_n%<)r$zojo2vi9>MDW4N4!MlSxI*ZrB`6qB+;WKw( zZ#7--1!>59^nq&EQ(O1uUBWrDp@qwicT1EbtjlPZ*~V z2u5+_*(MqI3Rr+z?!`^jO+`RL#`;Lv{_Vo7r$JH*EIg>x&D<><{EG2RI1B_V#Ep@+ zWbH6a^A~8x*;A~Ed54#CE@9P%4XyWPiB?jyB(SZq_9C@nmy_$rr($Z6^JiP(Orf68 zp71TA)aX}}V%}4e$ejd8>P{~gSVEYo4W*tCmxe)QLyeornWFC-QxVkHXN0HDmAMwt3rmqUzfW}m>sW(tdthF4DbB<_Elunj^ zCc0nc&U6$g$4tf$#|mNl5l67Ifu87F62Z!txRf?BJH~B=i$3Acm?9D{)WcK&?Kodi zWNW_ZqJqhUPO0Z`&9N+HrPkRBJ5T1 z8~8?oMrwD0JN)wZoagXsOl=DP)CaEZ)4pffc}TmoH`gIa?P>G*aKo<$k=b}7_yM%-0(PM6%zB`LumidvjWon!-bja^YNbFqkXLY7pa6;o zVT*S)j5^*7={38UaUcs44F(rkrSfW+C;g=zmdtcS0mHB?L}Vx(5>0b`-oU*>h8G_> zj7zPZVc=o)fpW`ljB~6FRv9}PP%gup<;bvQyND`)A%OTS0F;8r0UBhN<1!W4<93`O zWwPhww=BEcCd;F^Z&Y-WMKfr1tx?}muT@adn=*bBuFuOMY zY3>oP1EBzD$_~V}f6oZkmp#Lwg&ynyIuf6BKdk%;nDjubBx_FFWaz0w+{b1H&`ZsR z9nj~M)i_{E7-TmiTvh-Ifz$~GqT8{NGr$eNYx0e9?bi#MBETMMpgIyY))~M?+!djb zsOH^|ARd*hC2mJ*I{3z;)_jOR-i<~8T^mU|auw;V9X@~{Gn6jOI`l9m?OO}}HRqmf z;0$6Bh8<;aHLIy|4+9bpc9tAxiWS_{`6846N?Twvr<2<@3fO_#Kd3L~3(sa_{4tEg zs|ysjDb|=}>N(;v<`|WZ!eWvm>=sIJ753Mj#@;iU#=bS#1;`m-1)LG3AMJzvsTWxR zO@VX_ddv`33h@e&0OR4lDu88@%0PlM@`3>`Ct ztR;B|X_^f*xUFVDxk~FSW%{8#G%YYq#tP9TI3fI{$wuymu@Uyp;N-PW5v)w^+u9UDW)c zR@`^y+su71;412tkR42j2aq9Ni{`d!VFu(jTuG@P$`Dm1Q;A~$U1utA5(ty}2z!c@ zLnbIX3ofRaqR$%)U++}{oXPu&4Ce#h068-A;pH$(Dz%f2>W&MLYZ`IfF@v#tSUs-_O@!DE!ddmE%ZToI?34*Q z&=y~?2gXwhBgF`27O+0^K>6fwk+23m;lbE}x1@{BpQ=o3F9%NEDfSO{!nKSyUMdo4 zcS1FMeHYW|cOfnE3(XYj+n5zj-I1SXL$W2mbc}I2LjpsS8R|-JYW0pIAnU2gc35 zk~ElxVL~uI!guf|@{5yTnC+gvAlXt#yTlW#E-Cwa@*^A%ng{vB`=0W4HqE|m9o=S5 z>Q_W&nv1y!7}z)94UH*X4|{MHq6}82I1W}4Isf+quDtW=H;jkGW+*3=?NP7;;0^qs z4RHn}0ey8Bqg<(UHEZ>nUz(G5`SJ|)71~ucF5S~2I;P1!w)kZ@>09V zK`-ELy`9!T(rD6zHe0|jG%j*7twxFCu=&U*I>UOqn%e%+6k5YtP>GzErrdrKopz&I zi`lNNMzLOvU7-qpgb8OiYM@Z?BVrlmy5z$LRbXDOq$S2$h!DPy(4FSVeP0mRZ|RBl zG6A6cr%UkGW3sT|ZR!D67o|2~h&%3Ue+TqiW(O{Jk9MkiYJBQ5(u<;TLN)Q0nz(U| zB2DM_dV&%bY9q>L%mpY9dVA(G{8j%8A!R~deBu6fMV+W>;VtPr2Cxyuatu?{ZPSqR zNkZQ^ur#5bhA$R`Zw)s+P%tCQLel}caj(T5V(wM!ujuzvs6?m_nK30WK#*+CKnH|$&qbN{~fR&wfJ88*02Fw4zFh@2{3p}pp7l(T2coY$N2?a zbH%X`z3|-ZZ#sIST(fmki@zt@eDjO)M7*Q{ynXk;8y*F$0A=EF$;w3C`Im@l+Z_9y zw6`m}4P}@`_ySV72WNX9Hb6LA>^--Vz2V#@C?Ut1AK1?1D-SQqyd%kL@XN-ve%?kkN_AV!+eHWD!5jh) zn32tqhR;3m!-Gi?024@HNk&`fag7tUa|r7%Vch7cVH09&aQuA8{gH>($KHyfF0m zv4z+GX|e|;m1vJ(%)f^R=^fCTbKq3Y;(m{H59o||;5U0O?u_+BxC{f}CCt+hi@PH& zi5XZpcV5z?{MllQ>YB^Ia;039;SKqnqS6uW-}lt?DAh+AS5CI1%>V61m3<@Xk@;R} zF`6#QI^wd>*Cj9n)&u*FS}^Ovs7E=x(bP}ES?E$5pa@i=xxBqUE-;2PP^|=A`^ow_ zzm>2eQ}?`Hz!YH&EPzC?qbPZEet9ItV*ASS#$AZ@#DXh-C3iDZSIW01?!ZnX6>!J~ zkd-OV$DUBGLOG+EG7AP@-bQ!z`d~gF{IU#w)4Cz<30jn|3Bd56cuc$jvIqQ81hl1& zy?Y}#4*q!|enaC6xB^^|4+NC0!9F61+^X zi?_!-U|6G1#~0v1CIA^qjUTmf?}g9woxcu0W;fD5P#y?+5;U4En^BvS{30T2hWuKH z>=e9xedLf#rX^1g{|fdaXbWqmx&&>{^<{(yP~(f$M%I%Zh82j!8;ZHpEFJHRK*~O% zZBmWp!;*`k^aKV+f^D!26?b~b#NnirY07#@H~iDL1#c5C@i%z2zM#3` zMJX+e@n@7I1*Oc8$n|V(K}pS=8PqT0>m%b?Is`n~Ba)SAAD0 zR_k~-Iy)67cq*GrCwkkJRlfatPmHT3jA!{_33;$ZU%CX*Kn#`cK2*Y1a-CT^R}8s@ zI{(I6@7^(eGV`ZIL|i8qO#Ypt_tx=_Gxaeqwx+P=E%{jag$yVUVFDN@%v0DZ>`lJp zHj{R5z+-RfD>x|-kI{X8mnKwT4cml=em@bA)d{R-*`n_m1jkU?C~{&QlLnk)O;X(j zY~^=#EnWI?BtVym_fiQM&^5+ zac~>aJyA>NroMgTyfq!gBP&h#9ay8vF4j30^%O%?Kuzm3gm&t)USazWV-&f$)E+uV@fmI z8K?7`sVmCMce+rzNc?c!n58H$%sQC5uuaK8HrP7EN}3CYM>a_oY~o*91C;|$9`lxc zg|fbDi*Yy5)00DA;vPBrHajWyHx5I(MU)+WDfB;_)zEYg!{T8GihByj2RuLYmSQ+r zZifJntevLW3*O*f6UQKK0K$-rkT%>V=sk1sD3;I5SWg=};&vpB(2d-U%#HYs=y%j7 z=zS?zRpd*uSAyUGSAQiNSwCCV(6JQ$!A<_{FPpxx_a8Gu4g+;VE~y4hdThhtv9siF zI2vgi2^)Fbq1~D9+_&}m9I($R(v)f;JN8%`Yp>$l`oFOX&}0Ls1wLw%c;NB9CvQND zn5&Itz;;Tq$ao5>e#lFf46FWH1=X_=K7t)h){3j2-8}W~ijy>=$*fu^K6f=IN337uz3yaTn^=~nU1o4P z?G`aVUZ&mXKMapcpk=So;~4KD#V1|P@{n~x|=O(ge#o6Eu;hM`E|O-g89 zMaHaT5a0aa65KZvPD=h3-?y>DcBl*59oG8e8?cV56w<_|$U4Jz%r=QJOOYzNNt#}T z?W=r{V}@s{ZRY2+dwgdOrom*P;Y;pYz|w{oDAtDW&|$q(`cqc`mm^#mbS2%H(?qa8 zZNHHJi4Cb5ezbX1(}5%Xg$Fx|NJ6=b8pk|!tG5Z;Pq82B~fHJO*HGs=_Xl6~6} zj2`!ZkRZ~z{EvK=d?L~{UL|+#JS;{u-%=RkOZ_{~#}Zb2pi*_ntC6h2KJB?1d>Z^+ z($^dQ$}xLCXB>9};7hvUTwQC%b0US5!CbQvvkI|BvU!;M(+nZ{)(9nC_)r9Z48K)` z+=dPNZqN7eU_K9QQ9^_Pe!wD#fs8ZZu-NpsdagtF+H_D;{A0|CQSG_o;C3gO2N9pK z=BHnYzcl)y1{C@P<*Q>%IJ!UeBnSUN^h0aR8tVG9hq)G{SpCy%!OV~9h0;mVm#9YM z)H1#g@D&K3@Gw&jci2*Z7w@ll2Q{%h(Kilbyr4+o8Cy-88_?)4kx1%8)Cf1f@ar6s zNOP%Nh#>o7%uBfv8|nCF$`3X3c?Nz4$%Wzz@g90$6QV!)LlA3hF=G7rHzjiZ$OEE0 zd2q6s19=J1nV^I+OX-PUH>R9j*N5G>a6_Rnb&T{xz){Mn(8+C>80L+(IsR7@E(_)v zb*b15wUht0d*2%?zMQi#m}RAtMkuYEDD8Y=WK%0%fFvz21#06w6Ooctf~~VaFysPD z+_=esl0W$X-A?Mn`-Gv-Eqs#vj%636Pb5yECkGQ8K=?%X$9Fmdm=aeA64~Vc!%Ufi z9{P*58Gg^?thZgvm2lpeFnk63*#^Ws%ⓈiC=5eEVv46Mr&4f3W#HX=_DwL;X}Ko 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 z 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

1%D^Pxd`nXs(6m3lRtg=-HBxi7&%A=#rDTZ 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

epg4RnH z36V~#4~pFBg3OuMu-O$DDCo>T8E7i5!6+i&-*C(Iddqb>auj{X_ zq~*8A;`rt!LYF9pr~Zz@4N)+791vE%NboRO;i|`6!N8l+lWVJA#k&00;ljH&1oB6q2OSYKbP2d08*Uv5L+jcvJK%3YBL5m=e z+|zELQkvQ+!vwJ+8}OuK8o$79PT1{oIooXeZW6dKvEBJyd!4^}duHg}yLo$Pwhshv zFF$A)XpHq%8gfP2#vZ4h-@2fP_yuZ2ENt?L?;lV*#K>ConV3UkfMVX?hT z_L}a?1U?v=de!zG;}7yvTWCi*pZ~b;k2T#b*p0*e1*LSe>okmUJPtWEk_d*BeZ4}S z!;=r_B4H0->#{5JQCWrDzykc|$yZJ&8QDj>1NS)f-V@YAxK1J1=oz$WT%H-FoxvjY z+W~$z#P_7*vXspfu_`jUywfN(C}x7p0RRC${>i{Uwl~dC%MjEq%C*wy1Y(oE4ZH7zdQM!N(HLbZYgH8!nmj_@T+9af*vIp6J;&m4k@8FPqU9Ln$#&YhcMk;c}`Z=T|}w6zmY@>aY3FrCO#s`3SD}(wSZJvb_UPr|1IO#k+uZ z^X2d;WB{$tlQRXHV4~^@bD{-%(7Aqe-_eey>ss!->Vc2; ztU_Sw2$CG0CFH+Ir;Y%_(?q)93g|1zDY~r=uGG#k_|m?Bk`oT=ih$A=6q)Z!xD+PKn5?ijHM9br`jM`6y_BYX6fMq9GTmZu?Her zc|dZ_haHU)b87J;kNo+@{YREwuN|33WLZ0k%$@+;59Tl0#mZ)y9VH;UTkG(8rpPEt zi-|4dHuWRmcHZfj2Ocb9h=E-c^-g~SshTz98w#nUj8CBX|S(e z5qoMek3OTQdG%ojH&gL4GToHRb$H*NPq?d=emhd2yVdaR8-0(S!#>t6IM9yeE=Srq z_l?8?eiV6jEau~f2|Q(nt6*?FLYXDYGH0ZrI9tgSU>^$f4ZrpZNB;QP50j_AcM+~# zqj+s9u2T#s>j$8E{SDVdYJR`tB0*xV$s}dI-kh*-BOZCpA9OROHb})T;C>@fhi49B zxeV^ei>~2so8g=5t%WTRkj6L-li>b_7J#oJFPj2NEq6x5;N;`;yI>eycA(f_rtZJ0)S34Mz9Lu8d z$;0NlA{`L0Zrk+XxKgq-4|%F}LjUo(QGBQPDFW^LHi%+EX;D0$&fWzzGJ1>75jUlr z5rDxm5VZhTIFgRSRG&lB^f1PQvXXE(sui7XVVb z1wb3a#*Alr!$D6{;7j##7(8~cw?aN&pK~iQPYq5z=D*>=hi1Kc%9U@u^#65chc*rL zZygvK7#bYt+Y07gH-TR5)-7AN4D=7x`HBYy`UW>`85|g@_h<*V)PFHJI5aT0rN915 zpb|SY&_4hOj%#smF>n+E@PXO_+RUt_O=|En|07X1J3%nDflS7%mVv$44< zjyvWwX`BvL&MbA<#s59_$_I0LOy15fQ}?a1-R>~Vm^*spAN9_xxL%y-VYBXlDE-hh z4!a+^vEG~=f^ajqV$hdmIN9--)5_Oq3ihfs+8g%CjKv7(%L2&Z@b!UJ_THX5M(!P# zzqNnu3!pFC$sdAd39vx0O)P`|M^07+i1`0!_D`wgrJa#lksHXmeTq!MB1n58S6GQh z-7wYJ^y9I&T~X~!*<+tAJp=b}MGlCff}01SHqIafh3lPJI){aTDMzBf&MIl$MqMha zE1UAZoKhVMmMhXm#&zzR*;f^`58ku=>K7g#RoV2~8F&_t-3K*qhNcNc-@x>Vr(sA? zZ$$Tqg>4O0rkcyCbBPLts>C1GTa+%Tl%>^z=no(eL)vQ(&3p?zJ_~l@JIPbwMbmJQ z4I)e6PQaukfid@iS@<0=S|?i!TMS0I-y6u~JsCA4WGO~+9MG}_2L5{!Pw;vE@z44f zZI*sE<0yRFZ3vzBYrRu0;Pqk8$Hl@s>~6gZhX=|epc!P*vE5-)%&)SE+%;>gH?2vT zih#@&v-1T{xo-LPp*H5&BjcWZc-6WW!3gp!5nlo~Z-J)quyaeFI2r-UN*%(9K*qV( z%PlK)fl#*WYT~Z@<;5>=&u+NqEId=p zQ()JKa4R&8%U6O53l^xYLM{+eeM5=(C4w>sIY}aC_`&EPA29{J$-I|9LX*DkzczU8L*h0p#(={y}6oM`26 zQOX0vZqWn8(R9um4KnDo%A(a7l=4h9WzR{Y`Cgf~O@+oQ>j@nQY%2WKjx^xAw zv+$A2AMRUrcPHf-Bs?CK&t+rv1|soD z$!wDfoeaSB2YOd;D#x{Lz30QJJ?p4#-XOlCVbM)UnhOU-=4+&ppu*;f*6XuXpz-wC`oxUtNXJ#Sg$(4A{ka57)+f znE)ihCsMFnWEC=u`s1p!Cu~djoUE!;TS(hXo@{azaOJsbbfRP~wZ z8|IRbWA*t;?lq(~UJj21rFbC2ej0OJC8;nOt~wKCMopu(1_H`*Mq<69Mz*W#^rF$h z*M2zAId=S*i;x1#?>9q0)s@NTd;^c6v+o4ry_X2`DqHW++1TkgU*xvsBaui{A!%$f zty5AI~u#|MP0(y_KIGA`)6oV4J)MX%j9& zx+f9|Xwz$yi>M|W6A9A(P^cjE${cYgBc@i?$2u@?GV%W1-<7|rz4JL){qe;KdvQcU z4_{CMvRhpO0jW)_N3pyJPv>eID1(expOi^U>6*!<^{@@{OgZG1`SHUc2;>CA@^Z4m-dA0-TiYB%TYZHI)aH{@o{K&cL8NE zk^x^pDmA9qjsmB^waBgfhy_b$1fYF&>D+a(*Cm3K{kep20k3z-FX#j|>opX>q(y^J zH*X|?-XZ*ldI9Ac^a`n|B2Cl`7IwkS^f;m_JsAEIfC9~zZ@z{90bb8-{bcXIKDBao z9^Y~b26+fz>(ArK$cj-XB-{-pRE0InvD4o88BTE4zKC#J-ed;VP9&(Fv47*C0lrwzcxLsQoY-vW$5MU&u4ztaX)kk?w zc`3zG<@y?tl4bwAjiYHAt#0LV*~=;Pi9V=P@DH_>_X3GB1=l8MsY7Kzq{PJn?&DY{ zA{FQg!E!kx@H%V)M$D~Dmr{*A1~r+1C(D<Ssr8Y3GT|cX7XjMy--J? zHs|nZ%A2B{%iUxGJRY=kX^UI1{2WUrIw)ud4@^KAl3rn4oEPXF0kuVuEk#UKQTU4f zILCSOokuUuQV|zu(~Ez&Wzhg;fjjDbM)uN{4*nh*?FmA?9tZrpLfUH-8+uhP00jtO z$+07uj$P>$wT@r=`{5PWE{EIR?7UkOxqT`Q89^lh^TWapAd@#0M}U6>au36Vc-$f2 zh|(;#BPuVaMf`%o#hBBKZ4anguH#?*#lhdNSIb^qB;KKTb;DE~U1&i7oiM-_?88w4 ztuUKJ`Xwyb@hV%K<>|Es70>Dyax6ch7wfgl<=O44=a&B5Or8A3F2$>V zO6E0#d2>D%^9gzDh`<6S;UI+4NQx6uXH*hMh0B?EDakRYY#vd*wyEKz?;7oc5B9x! z`%m`;P|cl;BX8l5HazB|!F1wf>gW#c^AsRIAkt=htePgLv4)ZizoNp)Nv~Kn-bCK= z6nEkPy>{b6Bby&5oPd#~qrg537bBDmxqBjsKtM^Br%RY%Ze-G z(WFXja>_Vfb$u1gnzt^V_}RDh}$y z)9yf^R^hk?Rxu=%mqS<%FwPuyy;E|Pr`BK`S354YhZNt;Io|(LUUveMv($Swb zy*&QbK?wd7@CwoHYl0}65%4}7?MD>XA=-zMUA*|Zqu-x+uK&rKIcMRPKd4=tbtB0?p|p)nL^@B>RIkD(@|k4V&NRsiNn_FA zQ!(TrS?bD+!#yuwb?f|hCMLD*!-#es7TR~wh)oYRT_m{0 zZhx+L| zul|a(@0V?DC*V#2=9^#cfXUBLXa{hlrgL3b#KW+1!x6KaV=MEFser2z>J8cC4H7!W zHM6H6ew845lk(WD9q&AQivmXc1Q5Q9r{KCd3hWAe2t`5YZm!r|j<7OmN$8zU zk1eV{zyC=DWR<1|g)*Y1PHMli0?-lZYK|EQ0uu4$+7*AXLPomkbMMEmf+_8k zy10h`ND?}&rDRU47Vs;ksD$0?VMYTPwhF#nA4za@Y<=|Zi)V3LaM#`c&)8|7V#!~S z2r@1)FVadIfW{zENr@@bSDWg7tX@9v&AIHj)F9IE(rpxcHlJmE&dqG>T(%$yhx2-4O)VnT%9Tz z`&cRTM?97*j{V48+y7=Bzs&w|25v#({&5R(qg&24w`?R%!|az|C$*$2shw3pz{ z`g#>=yHU(C*mSnCF0K< zZv=rh9W%Y1SWFN$OOsM&F`0-d?X|eymFC9RQJS{2&oCzgvGaRt_aIIJz}S&mSb2sWtWoWJKRvH*l_FRYkJ&&*tt)9 zQ~r;ym{^H4Vc&UP8$_lPXu~L}Y2=t;lxcNpwPC%C%Tk)+GMB?q6)Eix6B~*3-=j5G z{rcu{iE+(U_u9G%2FxdL!=PQ=%6^4JxxIzr1br|Pa(6Sj8o`oAmd>uPCNqL8zwFT& z8F`N){_{gu$1_h6I@w>6y2O3hmdvF6&_$wd zr(<@jQs`J=t4F4cSVcKO!favcQ}eF?=j}eq`@g$MZu+lJ4_!k$^w0p*vX9oqKZv6o z>VZzev;k~WpTvwmAq%idE& zNR$qGJ>3+zjjM*CJrF=sAiNf0KXehVEc^KxzFA+@hr$Y(No;mk0965)aJ+Es7iZR) zm?OTrWx=}N|2RKg3OA?lEz@w+7iWXgKeBy1co;&Jr_R-?0hUB*cQG>_fh%6*r&}B9 zfBTnw_wHC8WdG!!6<2%b^l}d4npYyN;vSfC3eZlEMfNqoPa2OtS7+A^{8`P ztxgd;3}!X4b2(w@=^gj|IOBW#@+IGWdC#&{3K(|`!TdSRM}^4Wz_jN89J#Ndsa5o- zr1GebTS-+-al1&y45yi*=?#e4&LvA)#=Q7u(ztd$^7rt+=dpWXE>aKHpCh!3mbcUV zV~NPAI*$#(EQ>ReN|D|fQ@C`Z09z)IaacXr^I%7G?zDgIy^-piFov<8PwjtU37vp^ z2Rv~ruR^4pgWLIEkYEN3K0KYxZ%Cq*lzD|nlC4$k!I;GnOl!*7BI_@-l@p$J3G%*m zo>JH=^h;Eci5F8JZh<<5pOa|s^hV6+vraY|vgr;ylLX|0(BQ0TGw+M@8DZkZG4|L9T zFe<|`S$3_+aw&4DsE`pUN6YGrU(RO$)jmF8jQMm<(XHLK^0od~JN9qcF?{K5T+6hn zU4mR|n_yK7)JmXzLBdmS1&p-8NXExbs3azV*&0)7t)a9{HUM45VTZNtvuOzr3jK7a za)L&2?t@kBGex2^aJQ(N0?}@784Z7k)VpAciZ3p;dgFnrIj5;+?6!0`Dkfbf@Iljj zBsxeK6DW-QZc_V5ZRLcRL>H=wfTcp&47CG2R|O1iyu+XY1(z$=ec+Lq|C%2)y!7Gm`wrt;?jv^bS_$MsI0!pqXJH(o9$Bwj zR|PspE*P?K`AMtN>JtaVvZ`~-6)CIz1NT1m>0QSku#6t_pTc|3xK<&^hh$LF#S-f_ zP749B!8W}?xquq89=AABvnISuHLDykRIPrk1&Lbci!s=Nl#b`ktJ856<5$qp1)LWvEX8oN0ia(*I`7HdRRmL@6|8B2Ldf_Jhp zR%V_TT)Ig8TPJDd@#dFzOg_~@X!(0mHy4ZV$qN7ih}5)a>R&{*l#$8sB3^wu?#cuU z`e2zS<64*i%Li~;A9X)EczfII>L**ipqBqZj$s$){oN3>2c8zpc;~cnpCN#m%kkU| z#EXPNJYx`O|BxmPsh`@7!QvEx5);BJ3-(o5T#5aI~ZG#u^k zIS}Q`QLPiF<7g>79vO#6592Nn6P7bwP{$!^@^dDPS^VOu5ADX*_=V0<32yMD-J173xt(&=i`YNPT^hi^e;L)Zwz=pjFA$16E zC0{^Ofl9o{;Q9OltJ*DP6?vIJE`c3ck^u0_KX7=`i$AqzhE&&e&N{hr#@+Cu>*ut5 zNdl%v#*XI0WFW-b)Jh*Fq?ocyO%ip+Vvdm8CaGwW8l4f;bn4vl=N~;xa;(0p>-rwn zL@UL$0H$-kC6bmRG(HLQ6(eY^bW&aG5`dsfF`?ffx0V^wV9Z}rDGS`9>if2Oi1G65 zFU-8mg|Yq&d--!WD<|zB!1uzSM9#lyQnx_XJPMJGtV8tkN}n(qF8YGOTC7r(<|-^* z`Pt6%^^We7&R3RS@|M0l`R04^J<{#&6Qdg9un!LmK0Gi0da8h5c%W}!U|?uq>mV48 z9U2)SfGrN0l54G#_W_YVyYZrS=!A85ORjw|+)p-p{YmUamH-GPBETetMryRicU zeSO&9*wVjcun+v%-^c$y;;{Vxw-3wzZ~Ec$V|f2pAGY2T{olP<1JolF8UMdt?1kwj zPF@}k8*-eOIczE`vROmGp_TksFIE70(sSZ_ZvAb>(MiXz{$={m{!x?Rgw4pewSjgAmlg z#_~>LHpdSnDl%ozk*%s_1xKWuP^cn<4PBFMPhYnlT3*uT#xq7O9mcnxfLl395R7#S z`k^*cJ2wdLEj97=ER5U#NR}I$RWy9^1e9VYPCpQ{DO#Sj92D0 zYVW5{&mE*3z4Knn0>Vqry?f+N>>b&xlW@JV2iOw9yypM}Q*T3tiNRD^tBQ$~C4Ire z@JSOKS1lK_Tv0eFCl0nV3A0 zEVYA41n4M4!$L(cXL}Be6vwM7Mb77v80`U}!V}5$O5FxkBSp~8RgT!U5&G!MACx^$ z^tjkqM#>-HyHjb9WqaAFJnKJxVnI!4sX#(M-y_`*EVlt=x4y17<4nHqN zoVzCMKW)4i8tB=;Jl8;wVvay~7{?TG&cUPT;)fctDJ+9pqRI!{Og&#+OGm_|oXTW- zhR~padhdTX;fUb+^6r-018Ta2a2s5DNlA>m916w*XupiyMocKF9}P4mVruW%HR z?1JFk1K6H#gu#d{Tet%0;4~9xSCa^UR`w!l&2T;Ls$P=Ihim?dIqu6TZN}|{%fQZ+ zYd^bj#Osg0_*({dD!#=k9DthlF}hd2`$T!F78>lU3?!AY9i2vAbbZ892rl7 zT?;#N48Ovtafyn>yudEb9|Amx*jD=ugDDg> zo_P;mv7OL~Jr2!+Vjfi1OrJ<6K%dtU5Oh9f?m!I39&%Wje5qJnl8MVgt%sT9Wh|-& zd7t4l{olK8?fUvZuRZJj=Z0z0odmc6sNj7Mw+YrEpozYdfa~A@%dA;E0k$?@kXBt$ ziGgoT>1$cXU}NC&2s-|w2Y(=r-U7X2xqtUL>!VGbLV!7Z4@dqTg+3rO%Gxuq(;P^# zg=MbIV-2v&;zE$C$hvfP>^F#@Yn@Ns{95sT#gWXP`zs?htTw?*zJWUh*fqvofwXad zM`=3uHLSKoc*weQcPbpER-X))%|Ux%stapdj)r@aP96Tlc~+G!AoggUQQTF`RLVRffGyU~&ifv%mJNAe`GkA=Cx1flN3JY8PQ4IE6r>3$BCf5TvjqoYrRe za#KOY4@66;YB3)Q4>jgcZXuqXvQBpO8!zQMw(UFm)vZGi#KPBGp}Q#UoZDOAJvc~M zzZ#lepRD>+}3pRSqmA+y}h3HGvUhTqYK>|%!U;K~PnyZXgDo3Gh)=(@wgA*hr0 zGyzaXQ3so_6-c0Gi>k4$;-zy;wL5Fm8ifv%OkYVVeJ;1UQLjH)d#$r(o&42DqKW74 zb#A==4g|&ma1Gogpb{t=7<#K-%xIcPyGSr`W5TLmBGc);a)w?L%KOBof2e=s#h7u= zslKvz2Quvi*@b7jp4x8whYAoheNZ>gIszhhQ0jzwym{EOm}JI8%x>fPf`KY8lV=EA zERTO)V=7EEDY%NaEA}b-uO{PExZ`Q!YB+RHud!k?p&oJ2i{+LDT5Sr7yKE+RH-Hcj)--*1o#_+<)1*7N8yXCMonxfPsWo4^)|GTnJnwS^xS&Eg2}!) z<|=jZrkTH?$e$39;KTNnbh;TjfWvkN*TG+p9gU1$Ca|VTW~ou=3V-H{V=_vT^{W!V^%e4D}bQ-RW`yUE+wc(N3NS#-Np9;qnyr?KC zEwD@li^!`>XDXm%2oT0+BBw^)e#^ZbysO`u8+`2B*T2S6i%07Ad~qz)#s>=2qmZuf zg|SVbufeq%!pfk@;^N0_+N6l%%D9T2dx)3o(9>fhyGMU`*R}WGGv!8scllF+$vC*q zd)FmCh-(wQNTDsok|qK8Y-Te@q)&Py2DV<4iI+IuEQfz|RD*DK?%%s-J8$Az4-CwC zA-w6ORhOwP#t~qGlrn_|Y9gRtggFjFDIPBtWP-HIsO@!nH6@mi*8*nzv6J=up}}=u zAm5eNPQA)A@y9tQ&eX{=ftcI8wl)El`H!QZV2XVuGK}i{T&v9*5v0QgUP>J`76N{G zW(#z=4hoDqVnSFb<+)V^ueR&Ji&UuLf$zf;%fwmff=)4B^4a&*s z#X)A7&j>Md;&`@dN+lSDPnE4|WtEIv;q>_H^NE_)?{7ViBs5>gbCYLIYf`w=SOSSf zre`r7aXe(1gs)#03HT2=2;2zH*eeKi=P&JKKE+7^T0}ErWi~Ua1Z0!dDz$bV+0X-YHWRD@}9CK z63c4r8nIO!3R%;hbcLg8vc79>U7L54};fR;<$?La8cpT zTf{=UoUsOrYeD7ObLXzNf84mU@*}O8t1acd4b98F0Y^<xy1fN#Ue#3i#M*^kO zx_o1KbQ|v)m^m>sZVaJAw5jO=DwCK@(nKg;QJRJRs$Z>O=L)J`GI$uw97(u!sA}+a z+ojBy{Ug0kb__ts?L8nv6^o9;)DK2sNgGOM*XtYDiS5nWxq^ztR&>jJIbKC>E^*@# z{N;N6nlZz6{K;1B$8W};W;I=Uaa{xte^}R3JJ|+6s(({y|BfXg#~TixZFd(LqM9^O zVnsD_N6qExEm;Q}z{HRFKVCZg#yvky=;3oc$)}YOe9Jyum$!`Yc;jdHp|+{v@!@Z!*B{6HkZl}r_w5v@n}0hp z3USdJH%Cz9$?I>4+&CW)%Zbto*cygTm|Xl2Z3=~W8YWh}Tm|26tz2``=T=~J6m zQ7@#ooPp`Wuj)cS+@Yp+?*Ff>?|^Tr?EgLIWHxDYn#!;(9h6lJHG8QD*|XWRAey9U zW|L+Qak01JK;;FYE#N{CK@r6PilCqp}({BHap!NV}C9fA;7rV@wrHnZF>^2~rNgXAOQhbcTF%j%opFwwN+_GzvDPg2BW=)#E2AbamV^bg=~Oyb zRAQlWOvF0&Z#C!_I5I*VNj#4wK;7Lkj-DqBWXx7yQPLGqgv3S-h$Gl1TKUks%w;FG z<5%q`ov2|NTHwdNA=CCwXcLS7AXb=&*k>Y8!PpyBK4Nz{GTNj)p0}s9*`&Vri(_zyxSI*|JxUO(U z>YarQ5d~uGdp2ITPB?Mw@B^Rz&@G&C?-S8ZJlsJiAIBo;ahT%6LqEglqC0SvZ%}TM z%(7rAn)WgBd|8prkNM-lsGCum0STCYQ(L1eez7C{I)vdEkVlMFh0 z-ZPgf?h;kpd}b`|JdyTbHFehnv=dKk7V~bKl(5+@8=Jva33T#+#Adbd{N`>cT=shX z!G7q6-<0Rqo`8AFPo5c{=)@z>!{oV0lW->vs^!#B0M4-(0>sgJ;cJ8eLM|%thnapI z*TXBMOvbdz!U-(_u^6DFyG)Hw-dJ?(P0m|?yiwcGcXAyB{QlQLG$l+;RshCAGe`U( zgr)BT1b39(T{NYH%pPTcX$&P;Old?7u5=)a_dd|If9T%7mp^pt{A-7 zK%ZF$SFp(NOd9$%4p(^*`xL4;+bq_|C2UEG*^@TJ3%m}^lJY2wx}~N+AOGY#>XZ$K zg8SyFle!ujd1AG2DS>)lEd|SU55QnVG>~-yEmF>4mhxWzQa?*06XEC2MLE03J|Eo}t7)#W5~RV73{fNLrYjv>PT5K3Y?TiEFm z`P;?5h|wHZnYm(?O5(?oZ=w*eqzwG$ubrNpls&b!Q+Px2c%Dw64N)7pL)1F)h!OB> z6+nK5;HYJPI+ikL^oNTwt0ZK}yR9h=C(1qx1Mgu?^)^Y5`1~Ig%`K`CHE%6l|NVM; z)fNJ}jRMsPS69Nnz|dA)DWdaCP^on1TX{*P*_LH_OfF$Kk@cG6oh-OU(| zzw&MW50~%R)J{BMX{1Ib;S5$Tf zqOqWP4y`PMFkE@6r(;b~_V#IZ*DUu***@T!2Mnx_k1NghhTksfUn>_i4HDBnbHJP6 z1hYC-&Lm_;)n=PQxEA^c1e|w1YmU9qyQBB5DIcG0z<+jSB8~-;RuOZa%Tv4 zC*)dggv$teUl{iw%!m}q7go7hyzcTIY+`nIJC_&k69R7P0YAdjz zBOq&tC38HPm>HHQ{ROeON9BkcLj^@jz%A+6unU8F{4{Ob+c%CX9kcuP>h3uvg=v>- z(2WF$(u@K{Fr8hp{u>|^T!EywtEf?TbJ;?9Iul4ZOtI0}R}MH*U*s9xxAr!6U%zy3 zY}R_?1HTjL!cXwnQAMX#VJDS24E_R#j>qBXoZI@xyk4X>D!p!Xu#*?$+wCG&#_I!h z1yH?y_AyE1IMO&WKmWO5f9U?zqktQPZxI?f$HJ>S3>6u~o;ODP=Qu*7JCl;(Iku*>zb|i!fz}X`qpR|>ZgfJ$v zr?_3LPJ6pq9tjxZU77ZU6k3WN_#CKEp{)bLRz*YHw(Fi!*+1Z zkGt0BrYT#du36kN!Ni({&?bmb7<-oxzA5e)NvM)nVQ*s0h0R=p(;%@p4LPT!Xip3I zl5*D|dTER-<>28RLc;Q|bPu0hMnW3IWC>RCkA^AlQLyY}Sf#vikjPc)Ig0k4m{MwH zC5=uo!>4p(p{mq-{I}&l)n5jN4dZP*`{BS&*Q~Xbu(=YXHZmBdJV}B+$DxPFc;r!F zhcQ4jvD~hFUeO^7*~AP{*wbZ|3X_1L2avKU%8`5MwUn`c4edSpNb|!Ru`~Edb;%R_ zAh}Mct3emih?VcQ3=lgwE+#*ovgX1(UBYUyW^6)-tu$%@umt@7oLgM8>we0sV=de7 zKLBJvo4?$?oJ4!65`DJ?0`jIsG>BB{V>gR99dK#lIycPIikuN+zC)a2t2N$uu*h?) zEjMU?zQ5vHh_-u8&v(iCzEL;9#PwZb>_&zjg(!0oN_==ziv%VjZM0Hl81}#SZu7FP|bI6e?J!bB)+erqP>PxMWhj z5SZmS1S(5AzYtRgOzo;dn!~Yi`~j?bzAR!0=}N@pbiqUNsmv?37Ukuv_{;Ov`LxS#0@I+Kg$7t zNJGCKyhucbj{+O;&yi4_crOmv-&Mv&W2Zah2P8%X*Wq$&v>vTbA=kLUxdiz0=M$E$ zx)XoQex!ND!k(#ny=UO6B_wouD;BE8;LxMB6-Xzg)H1N@Jjn#JpeV$O-5LekoOMSm z`VbzPfxU`)!|Si#n)Bt%bIt9h4;RuWwc=>l{~xQ-sq+!EgG#99Gl6@MP^gz{c}Wr5 zrU*HmDNoSQp}^wV(Q!lY&`0|!Ut;`W`)r|qWB3mD9az4fss{|5r${h4fQL2`(03`g zs%ykSqB^XRi$XkeR>alYxyER!s8Z|upsV0)H*#>l-(CIjSNp7~^;_8UnR|wpr^NR6 z_O0#hUALxx^%|g=?OnI7x37QQ`n4sS;`OCTuWR~x`_}>#w{Pw0-riMxy=$?b_OAhe zZr?iW7nmuF{q??HAgt|QkNp|!=j*|I*>xpL{x|E-oa#JOokRI7*xS#hn?U@vBzq5nrMD+FzO!P4IbE%IkRY#ryb zeSNm?Iy|Qr!YaD54KJVD2etA;buNCNbZN)guH&}PGKL^)FOw8_3U0?Jghcb zOpFLW9n^NTcLvmEaVpyr05KgqmG`CTQ!a?#t#PxDkYqnGH^8&y@FW2XSa*s5=bGJD z0>5N1o-S$!c^8&mVJU8u?^U%s85#vsmW@UvP5^~q2lMdAe?PzCb9Q{e9alH4m^$J* z5Rd{g;(Vl5d={p+%!fcXt(ndL4Jw7(!Zsb#H&nDJOIgs&G#;hUte zK3_~rJKGa-C74fONtvSti)0Q`Hj-YlBeiv;{uOr5!l#yRu4tFTb5Fs|B4K&Z##(41 zm)8rydvQP%j3wjEJaKJV;wtYkg-sq)(qc);iuQol$BD3YQ9SlgEM1^Ib9!?BMMXXG zmAXC^;!lJhUx&TnPCS-Gx3LB1;97Aju8AGQly>0j~7_G9R~E>au$;m9;a#pbw_GNC_2ZrkX?xQS_%Q*FbbSFII*N*+FNwMTRA;Tz=RpL}rc z^oGtE8=QOJ!xLaGLY@pavhjpk!6OKYCy?N7T*=MGm?}EdonaMM=}0+4lAeUv=rT0| z=VDFuJ^K$g9Qpj;_Gm}*#Mci!KN+3}AA?JS1%bruBbYLYT*t=~&`)s{B?T|&kVP08 zugzwS8hut@G?d{N+ap|E$)p090G+CD=dBliwrT}q`~EM4trv-O@tt_`dZ>|qkyy)J zO2W!A5}osDIa8AfJr|ptOdl-T!!|fMI^nj5Br1sQzKi*BMm^m>t^y;IYD^Obn zfzH0K1R>8usH&2s^&T7^_(u&8Lqs#$Pl z?MtWZy#Kr(%3)ugt;BB^kO@s(1D2IMMH(bo+ zh516Xn;*Yz@chEc-#2U?+R-@qoy-S&CU%`$2Q`RUI6z67$P@fQL@&VP%6!Eju~Y05 z$OYa4H=kfQvs@8iC_?*dG#qcn17P-{8Ame%Du z5eMH-Drqj=Zb_koC16Bia+_Y;m9V<#l7BZ92*gme%dl_pkoD(-m?h7?$1WiH@G_fG$Qm1jHdATdhtl?%;tDqrUWO! ztpw{_LFGqp(Y*^SA~)) ziPR-nE~1i(%?gQFmy0P2I!!JpPbIm&>BKU>{mv#+HLBf}NVYB+P5bgjVhMueDA=Mj zikY}Nj=BnMp^)mipcCZ?o0BnCD!>$L1d5zJ#|w+Yk^XYd=-@Hd=$ijIc+VXK)~PAJ z<=^7y+!8aRk-G$`V~-%9ccUcuGKqLMb_|S87QdJaOI0B=-(%DYWF4X?zYi)~=!(0= z2gp0?#)^ZdiWO_do$Z658zs;|N`@$R5YTUMWaLU&q|f6NJZY&@YY(#JPPX07?6Cyo zDSX*Q_jGjS*5L~+4YFzWVShjS5c)2mfjbG;DEfz3Cz8YPei&OGs4`3)Ao|(5b}=uL zO|l$b2RF^p=M8Qz7_oDKeXiqs`SkVZ3HEGdBx13nH^c?caKdP44?lj&kh?Sd|5h?DDKL=$Bj1Yj>b ze)P2K*9nF-f49B$@`dn5Y-zfwjod}ZL^kUrxv8a=jup~J5aL(3L4r>#(swz+5^1#C z$K!?#k{+}2efljtuzE-xr#|BPgy>#%I{y{&Ea826GoNoofF~FK96XUHo`rBveTjTgDDs+v6Tr!6wr4L(ytNS=pfMef^N>ouy-1Lo=Vb`jPiJ8BFIsjw`7W zH&-?Z?8M>la=6ryYG-g0X@$beZRh*gIXTzP?TA}IISp*vxxf9oRZ{=&uc!Y2-LPhZ ziXYJ`E0HEfDV3F|nMkDbJIcdHHG%GI!rs{xQJcasn}?C*8?9cftRsp*_q(NH)2pfn zpGuuxE%@l;XP%BrU^I${Xs6(bV*V-MsDVA`7F_B`2|S9?+d*Rk38Tu^up_R9sl%A-?IS5#UkHnxRHArrqt2VBV;1N1hn>>#4dT1!|fC$ zH4cu7sj>KdtU|8)6bzn(?VOq7?6KpF(GSeKHD^Xj|JI8?l4(DnjlxX?%5x-B3vl+D zhC?HGh`JXyNbHW7n5j;lPpe9M*}|-mY1L;mw`E2vW&dpcdDPg*v>!hB`mdJDqeoHT zACc0)*IPAI8Q|TJp!Z6>KZ3#=b#fUU=9D+83L85lTB%%}uLduKtu%eR7~ zZg(-m^=32bkUMvqbw!x|;FM8^>$1zj|SRPwcn z$wDEoDCl#_v;jqz+V78acFES2fuU8ceeu21T`dpMul-z9JxKWqS4AVx7GP2M>@c{* zcf%pG$R%vZDNAP(j-G@`5(^bLzIK5u%1zvbD@8sRBlc=X_`Zr$`vR5cC+1#VgwT2a z0YhU^FBPg~|4u{~A*6aN9uHuf(Gu|R3GJTi$c$l*$U$0DVr(5)*(Jy;H`$~m02E?u|gEy4f+ zU_-X#3mex5?=jXB9PPhvXodqL8^lq3Bl`?ok*R<-l$_t|Ig>%lL#gp_yVO#CQ75%n zT>)c?&ylaju6TfV-e;*hc5UY`oF^3HPTcc#w(@ZV{vE2gA4X4AL6lfE{B5aPs7zw{ zMa~o`isZ&@B9`bDDXm@cc1n z82;{d)xkhY`L0^2WoKDxXHcE-%Z0vVL1RcrMq?Q!fI3H6G9OF6pY!lb=r+dQ*?uOPAV)0q6}3)mQ3iKe66N9xr}z+`2vNsz7X<8WW_EuN6vr< zWt~obl|=LKqkrz`i%s1!^O4zu$6@5-QRFEl`5Yg6n;Cc#ox8f+h7KBeRt?wh5xe8M zDBo}QhvLr8<1n;|G6c`LTr(!O^7(M*r-eK7c(x{|kDw zFzR9T-3RW$E_rr!iJvZXqhUn_p`Hi$VaB-FVBrMRMSd(~=PNZm9R*F~wtvPw zPqwRqqysOWvMyJzU$x-rXhj2WJh_oAtD#IFqxwcNva+$H7j}i>(QsUBV)Mnyq}U`7 z<{h5=xpFIU#$Dre9?%{6Nv+UI0+E`sH9YoHox zhBiPnI2}xv-IfzGi;N)C&rdQnVh(7Rf}vr2|6>oxuim)z_xu~%H~l{O$XFb5g-*U7 ztF@>jD#ny--zz6IWBb>}^atchc`j(Tu!TK^fHNKE_LUcvt_%6(|>qB`9vi6 z?>?-s{fmIrGU#js&uZ|obO4vn>iXD{r zydBbzk)_eNg{iQPIz#~c72Nc_^{%OjcO?scBadEo=}Ar%a&Jjme$O~8(P<|_Pmv%5 zMfc&bd!vE45BU39Sgwv@fE$XZ1$;X*84TF0s@u*l*BeF#M8ZXji)W1&=iv|5uY=&- z6`l{|8he;-yVJT?L1=N91GobVj?1+N`fA0Yp6P zyV^SV;1{-6uYH9b(?7=F;NsRnRR?jXeI$flf=43N)g@}7B-Cw)2gCMM#*vMwGzLB2 zl#Q$_r|ihYrG0mP_x9QNDsL6@nJdq5aEPjjti}%HN)klL;?eJLLNMM@4T- zH3YIaEL5qtcK@t9rfYtB_P*cX=gyj^!VQ8(N~7SD5>B;^K&n)ayGaZPe9=5pkcrw- zF`2ZJ)5CCSx&RMa1Tg8@FOS>v_nXh%JAphh`|{bnk7Cc@H&L*gyooy%u7S7ViBqvN z91uD3QKMYu%DK#%f>&S_N|c}BO6k_@-HB&U?0S&!&6x3ny0-|=JY0cv*OQ+>&<`nC zGR($o_ zP8>3?7O*ab4_5#a2y{A?Ol>C)5R_rQo*@XR{TaO}733yUMFnSD{VjqBuop($uN!)W zR6KRy?S_k6*n(~KS~j<3(hVj`nFvvzK;W%NNzVF79q|hAWEE^Gf(Q;Sdkvrax?7r$c1=ETZ)tp^0rC3qR9gCtnad<>qvRNT2 zeSDVF(~(NbS`$0SfulCZfIuu3Ucb7pDsULSn|v) z8l8I-0Z1Trjn{F%BcrEbLgnt}0fI9XRJa5@jz#Qsq@6usF~g;}Jo09R5HM{!_R1f8 zQ=t0(@H=7OjkBK}g=fR(YG{XQ+qj%eMJ)%55C2d{4iMBDsXvQVGLbI7sYfLgs1?HO zb>=Oi2mrOC9-r~C>)`SmPp*p%jxt<%_Z(bxfQ%lhg=zs-0jzW9(y&Juu$XVK*z{a} z-siPN3i${#G#Xo6U{g2h`_|U)tjtFfiLGb$)lP}-X$IQ{xTYy!Dt{8Z4uT*&`W2Y< zG?Icv?})dURGSkW7PUC+3_AotQ9w{lD0^D8DF4`)zROSlYiaQCuT9Mt^hDYcQll6p z)N5Lz*}F!r!?_`(CtqO`CYD9M`zI{ zaq_y~pMWh90Bc*8eBk+Y=s`AZ0tjf9HV3Fu`a8F`_G z)CUdXBRW<%!8M!s8oep!G0MzpIcwNIc!1ZoNVhI~PG@RluLJhE>xN!|TM? zMq;S|5qYXS-isOQwiGlhl|N@z%LIBxu;>a#&z99Ae;1bTT}pfyPonnyJ)eK_xqGT; zu2D@~ISipcSB-@G%5H+8sE1_`xkFfrE(}Z5DOoWeh&@Xx6HymAwhBLQ`O^Er68IAJ zzK2#t@l|)>8u?K?_4{#H?O8$RJV+p|hXx6PVAN{$>Ac;_a8fBU1-;5l`+;)LZt*LA z%>+u!R)4(H)jslvvz!QV+taLX}_==MRK@_f4ycHqPw$Hv-E_! zLKK`}jUOL=ee)~0SS^-HtS5p}Rs1PRIR`^n zQFp2lSI=dDzLY(xO7d(;j;O<0u=_$5w^yTO+{S6e8s(-r7k~QP@`b8JF>mkK16Xc< z7JJMA9ChRb(9K*y!dHTrtG6)tol2iWuVG{uEUVsNHFxJfqm&Yxwx;cA=kQ5SzBwdc z%pW^`kB>@I+=Y%F0Z~R%(C4uSY$Xg3-I4@TA`)3U4BBvatX-U9@=b;{5R@JN5Bw12 zW88t7&V#;{smWXZHVSVLX$^u2xF*gP!Z75BkT?$+Bxn?j9xo>?avK|Zr)-pS}TJaIGYLR_6gV(DTiTNsfSR{ zo{5!jF&{J4&G$<@iU5{dr3zlP()quQ(N-V0^snb152`1tbew3yab5<4!3)4v`2|uF z+d~~*_F)_#IwYo8tS8=?3U^s~96L8#lqjTCgtDuV4fo4SD;wSx9z5+Ae$2uXah0_7 zl}+p_0u8MjK|+?pB@(BLms8l8ex{P;b~wYSP_{$v=K_5cc5EJ8g16rIc%*W8$Gt5d zKK1kQD4s4{2?HiLk4C8DJYIpF9dhNL0#F<0#6DLf&oC-{HkUrY(M7HCbe($jUS7cDjCd2C z*AG>5%6O=V-(MoP+#VP!gi7ov;j?gw9i?OE91(Vg70VQ}QeTg&E7C4<1DFTUd8ZG5 zX4=-5E?psf>3mdnpBJ9H9%>c>_;x*1%{~kR3kdQ05<0pLBGd~s*p*=ksP!>sF;`3( zRTh<66piTHt?SB2s}kO~=GN7(KbsuZ5pj2Q{!krZ3dM6GehcFx-Ryz_40$ z1uE5uO218{DT<;z3xgfhXAL^PiXU+#-O+5SOM{7$HC9xv0 zY7JB;Sd3J&=a-a4%>sTcz*=vT9J#EyN6O}e)Hq`r*-t9#E=kVZ2hW~^z@Om(!D2Q7SDb-iEYQ~w zaP@59;U}@U6P*^Z+a6^Kl3IW!fkxc zS-6$Wo(ea$jKdM=f^QKppMjijS8E)(0yo>GPSTUAbZ=7K_R?O14)W zGFd=~0V8ML*i%io#eKhUw(^NgH223Z6>sevHgoBzQnR?IzL>r*+Y(a zD$lly+~Qy&+8M~s1+X;sTIcRNWEmIS(qOyxq~|r~Lp$Kdo+H5p92oL};3%&XD0^^V z?OSjVUEGZ;HH!=)e%_E(q}4&SR-_5(EgnT!bK*AnNBDI@aILM8{>9Y=ixs_N#ym}G z5MYJBcmtshs}6u}doM6|!A{fNW!-69$JDz_dW9~kPBWNUM~6j}GMy}k+_jtbP5=F~ zyUf204ITD3O`1F#p>y&$vIM5hLMHNfCji_*LdKNgHnxfDbwq`tV$v(jnJr#z%wdc5 zl{fx(fBoc`aqH`Mhj!wpjn7zD@5a&DMO=xLJ{zeRhoiS##sgft9{Xko2~KsFNzGQ; z6&|@x%@2!JNn7V`poGM>yeoI0$!w#q{j%wWzv{Lg`v-oUL}=iDM+WoIOT3#o2)>>V zYJ>!@AY(CP-Og;MJErCq3p|C7I|Z&5Z6FLmOJ*H9v7zIJYt6|2vcm}>nVss{@lBC+!p44zs0jDP52<04s3PPC%G;nk$viT6_ zZNb}w1y8u%a?C>-cyhQ=v>u{h!SsC!sh$_a4-n%br=p-@@ZvnDHkq*$O}QQ~8({xH z=heS^&ffTY{c{(-c_uZ5GVQy4xCUM;7RlLUh|&*Su1Iw8N@58ewYq)jv|Vj0s-qkO z)8OI@<1WQ*3w6!>ws$0l_7}3kKb*h3KeGBeGIoM5!cZ8Y&WYwL1bA%254X$>KlO(7x(r@6Mk;nmc^f1(?o0jgY^>wF$V(kU9?b zM!&)&DjmyDk}9K-VNA(VetoxCma-O&9SRNb)d0PKjlN|!FCv>yo%@qfv;UmD8B3FR zaeO0BSyjueqX6${Fpex!F+lVhwHAN2Q=|;BgAzkgAJXYTETN9=;tbQ8wE!f zZO0SG;cgN+4i-nvXXJ$edoU1o8DvIB#sJ1Di9t``#|H#WYc^jdjGo2+ihMT4{{q*{ zXU_!Ve8GH-I^G!okON+d5{4?rp@YP{zSH4gTD3)%Q}1v&^gOvc>$&YP_1QMN^Us9` z6pQT{-3b9RTuneW5H4YsOv}-5Wd(P0Mc88W?Yb^qp>)oR|k6xdhzc#CW9E*SL$ik^)g9^)`bjcCiY(d$8j*_{MqX%R7F}f4Hpa=Qi%lHCqXE(F-tn0rMYH+Aocr-K2P4CiFauEX{#}93z`Or*b0lVBS!dzo7gzx{dt;m z-z|GbWE`A_(AGh;YC^5ZScBe6Cc<)3iEUzH2F!MMA=G7vMB{F=MrUFt9J1R4vMD#K z%}e@b{y4684!QNOuoKJPdBZ>#sg4`QqdqFBvI@I*!k%^;mk||a5+Zj&VDJXQ8C?hP zQ3SMsrTuptzwq$;o9^7#KW)~%i#q!u+Q*be_Fh~aKZM7EAE}wcdYc5sC=ml{yU-iY z=iPa!BhJvsv(65yL})%N~VxDUH5xPGYWKg1^fK@_4W0qRBqEG$BY zlqaz1LdB#n=kS~J95LI+@KLc~jBVb|#(VUyYmAVULHy(I&-_c7Y7OT4KH(j9ld-XYrRWM7kGl`mypt1N=$rGv}e(zlilCzyDuQ zO9KQH0000801H-?O8?vdewa@H00wOU04V?*0CHt;Z!c+XWNBk%b1!gfXH{r$M0Qb8 zHfnQdOm8tZRBvfPFEB4(Utv{L2>=7O8~Rwb8~Rvvcnbgl1oZ&`00a~O008a0eM}?k znkP2T`&Ln1r>3^1sN-9=wo>b@cKiBxJdUT1uh(lij^pF&cziux$H(J!EY`6YhGo4h z%Q7q>2(c_-2+I(JI9!G>2oWxqAcjSV%dl(}>VF*GD+aaA6Lai1ey`JJ!%72%04t?5~a=p*T@56rICwlRZRPA@WnwtM{bKtlCi}uccTK&f@oy9+_ z{?q0^{7HM$ui@8k{Odo+SYQ1gJPQQuK!2I->?hbFh@h_6S$_bUK*$RE;y5;Fatxx!Liwj7LYx=;3?*L zp~2$M6N|J0i5$!3vz>%Wi?ZN&80lU8=1>kxSaH zVG9n~1$qX9v@if0Acihr9vZhHo}B^pcZtij1ahL=^&`@$H3a(uY2Gl0fM17GFa}@% zw5lP+eBolOPz)FKw0{KjR^J@h=7Ci`T55!_8eN7S*6GgZt{`QTer_35P0{wjk%Ht0 zy3ZbwX~?+v9U54H=gd4jA=9+D2)mFPqQzyrrwJ0j0G%Gd1n|^82uZwpHsf1{R-UtG z1D&1=A{3K3&N~h(@Bwt*<89+OK9Z(t%4l>&U50TF-YJMXM7|+3M4A$2TQD;NSw8C) zR>AF@BB6cKcT2cJ+E^#?F*-nI$Smpcc#p^?eNP6+3uu^R)K23iWSW||wCS@Ib|LYn zgovsm7+wHD5GoPD-!KcqAj*pnYjw_pTO9?ikQ5@l|A26+#?}*g15DAb+YTTck8mr{ z7-<+Lfk?z)xlq4H9fFc>d|)d1Il|sh?TU`G@*E67B(O^E*>!3}qPY=rPx71KKf%W} zku&_!fZXcSj(}2{2o8d8h;Uxj{r8A844y%VTOT1=650dhlAH(&K3(G1!1T*5)zo~Y z=Hx2r3C%%u0!G2pob2BL%2TS=5r1?QLSv+70j6lgxlcO#?4!&sGX>@<+(T=><2|v4 zU=Y^0d_#ZAXN1!3lwEb%Pr)+@4l!jL0K=gclAB=l>9k8UL@^yozlWfnmw7cWDb%Ok zfz;|xT7C!n0;tXHki8zxA#_(|oukehXfYcFEqD!vZs|g>iF$hqjUlhGM1(QW?Hf$N zB~8-_#yO3(ctZAJmljQa0D`^uguP=tMOkd$B_(bVWEyTsC?~)TK`IuA`t;BzFf^I9 zT&8mv5)fZUnT4KA6g$<| ztlFBxoe@rODYjhLQ1Y>32zZ50G(QBvS#S$)u#_E`P3sycUI%VoE%W8~MecRCF=xZNO>2^SDgQVe4VzS77%pLEIf#i5sP;n0&8>ryi0BSa&j`6@K9B>j1=gYU z493BE3r8f>UdcPU3hU$^+>c?448mLT242AljrG+A##KCqQEt#2SM{co={80A$5Wdj~P9zBGqOE%}6a!tN3-pwcP-r(_Aj zjx;yVgyIO-g$<^-Nc_FK2*$%?k-Q*h^aXKTfurJQJ32F##FZyOkXNA+lUOs$*^ETi zYnQCjDn$Pj$V-SMBVx)ONT`D#$D0-qQ280KpFtGsjrM{P&Q=!E>mzFOXwsZxs(oxNuA- zkR`Ngq%lG*Pk$;bIP7c*iJ@5jGaz}V-zf*2F1x|o6I}^elPz8nCV?K1S*qDN^$TK) zwQxP08|ea9^swaVWn5&Y4_>B@9wec7&up5>LQ+2n+BL z))9RS4EslDnrEB19XiAKN61@p1ge>f@*&E;r0(1fBzUVi<2KtfWnFRzX``b!4ayQ! zoYjb6ca>blaC^j7n5c1IitXg>R!Qp>)}dl}v!Q7agML?TAB-TM@+ldi)!n#)a1(qD z9jOMkDgA-YKxbkLUIJfsy0@9o2JF=pvXN2n*%EEj3F@ugf_lIPN!5Sf5t zJT(cqQpn8)sW<|egj#*g%rm_MOtW!-S_U8G*u9N`6R95g*uWo;>$W;OmE_>`C!2UHbtQHgX!177Doq<0~2^yE@^NW*!| zTCn>Fgn3ZxLmJWwBZ7Za&4XsBD5R zoPZ-_3pTN)ASPddt0^XmMK#M}3%jCy;vl%HPL-a*3ik3X8TrD!17pNX-_TjCn**}M z2+kR@OJ5PqR^q72@mYNmRHtCs`_y7+44T8#a}^BpF>kfbNy$;U6{tQ6kO^Agj+mnMCGw^&5*+vbGilA)ZF%YaQL)W5MfE1b-9syD0-J@iJ`VQa{`t$){Jr4O6B$SwW z{s0R8ti$t&2=74Jf?Am`fFl?@gl2=)EHT0k0wTDrOP>Xav)fdAFl}_P)1ZcmGax(0 z!4oc5QXP9>oMS0Z5JhVAULXtR8^WiovO@GF5Oov=FCe`NoAeoY7QyEqU}wlRc})a| zJ_DjNfncb`*8z26!j^ZqT38wlM7>RUE7G_{u&ga*1e!DJK$Um+JS(rfw#F~V}bqPNf$oFLfv zo8%+1$sH*tQ9N{9g3~+#c90AcUekQ$4@uUE8m*}q2^4t4XAEQGG)M#Rd&DRpz<6lI z*H?^2>CfX zasb|xg(;c^o6@ljWCYs5pQz2)srf=luPTh?2g5Tx%xi4)hwdYY%=%6qm&deqVdu zW#k1;<(`A1ungxARr`dpO`~iR@>r-lrX_v$Fg3(maH&_w?$tGV&hly@n`NVmm~v&2 z;9ZW8butPy)p-K;wgsYyGZ5beB;^&5qq=10*GU3@)GDQd)V~6~3()772QPa8-ZxNm zv#D^bPl}j=Ep2)9CuocWQ>Ai2X$o8WUc&2tUf1`Lw8 zK9L)GVc9|7Xvu~5iE;d)gG0OdUM2h$pd5$sCIzw+D zJwnex97mvt-_yo3(ntRrA{PXMr^$qA3#kX-%CtyDH8%phlXd$VB|$d0g2;K#pzSw^ zBPM1XBjAcB-0l^iPSYUYnZ*)7--$Ykweg>{xgoamR*W6*6h zG=_qCM{CvIbOqCNiugzC=3$+VkW+F2{ec}iL4?>S>3>OvV2cJmK;75&D@kp&+%h#MVy^1-ASg{4Yh9)Xu&eK#f%C_Q1^@+bw#qE%dU!0eS#F$349!*KkjrPhk(dUA-d+^lzwp7z)0Kfu6=ezaUoV9n8QQ z1W+4r1&Mcak$G&lM9!u3+5^}F6W}oeqM3KoT%u7#UelgI&ixLYNj9%{az3rm#urAx z)1#<~lpN@7NQOC$--9wA-(uVK1+bxi`^dH>Gq1p$A@RGqhD*9bqt2Ec#Pe=N2?k^S zShOleY#zr92ub-K${x}88>A&`m82xws4KD*4(g5p^j@M@ehv7jqE{u5S$)^ zHTXpkx4|<5nV{WGV|H)QiZ+x4`xaQmC$I{`Fiv{3A!ZwePWuhx*?533KSxk<-9T8I zf+i_O+xMw!0Cp(%cM#N0KxOA4ry@hzx~xn1S3r_6kI`q|qPso^Jx>#gWOGNo>N9BM z2S~^*3=s{h+O-RQJzu7Fr`vD_f;MN!az#zv5qJTdARusYF@@t;<&<(Yc|FSJ6?TiW z7T`5mMgOJ*7Ga&09K$rA_}W~j#2uk+Nuj@}3rbw(*)!lg0iP1L+oCM*x{9ET>1*}HX zv5oqrxk>e9T6C$qmpahDcgOw8H0bBx3N}0K_Rdx6TZI<8$vZ99@~{*|r%jekD-jK6 zQ5e(zoOlFI4?COuiG5)c^K@4C7(Cf{%@W&-jfdcsuQ8L|ui+W(n}%t^QH^Iyldufz zYw`|6%Zgwj79zm`@b(-)rD&S$45}W{uKA*Q1?7OSJ${J~#q`9i|2{#}lgU5B#Bt>DgT~@H>4@Ku;yPl-B6-H>$#r-}+T{f}WTwIG z?{kS#F2yI~&ZzD!d_!_W5cCFKQC4=loobv-j1n7l^LJ3%CRO`3h;Dye6f2^pq<(;M zbQnVSpqLF}o>hefC}&Nl)Lcir+EWOY{2oII^Sa)a3x&t%0@(t68C^!-1<<0j1|Nuf z5v7oApDERVa+JDmV4h}fkw-rPr`w|u!E`E>q!Q{5W}H%s;0UEyL`99yi8{RjQA@C< zFRp|e{K1sp@~l82LtkRn_pn?7%|cpLb$i@-0y6J^521>o=ugNF@co@_ys$w`-L^zL zQ_L(hZ9#OE6biOc<}ubs;s~NfPsz+6`?E821I1f;2zn(^Y~ow(>yT!LA>J|t?J zr=*&r@)Eeb5e=!zC|SJv1iBO45ZVUnb<)BNB&4(u^H9fc6aNV2D`094OG_qa!MO@y zE>=ZUEVjD3{xNa(hZV`NA&HZ?d5i$|80AFVI)`2636WpI8SUAG0dmEjK$pW4AA07d6-jlfT;Jaz-R*i> zO}Mz)T5weUm zo|@)9LVHCPJGfB@g=Mo?&GFf$cGXb$*3>&N&CUvvv=TPPAiMz0kz$)Ok#zf8&T8+O zcV7e7#%hw97zeghOg)9z9Td9-j01ta#qn=9lxRxo1en z1$y>g+ZBkdpJ*GJNrc}+h*aX@A?!lQG8ZUM+lC~EQiP2>8b=n z5ql}$ISlSzmX{o^po+pLuQ!Mz;7^TV3lD%~b~Qv^LO}8xrtFo5G3&C+>E@D&T+u-G za`>bu$GyS^ngiAM67zEc2)fcCy3ljx3d{p24N)%UyQ5{-8aiqFQxZ_^oL3J|fXd%M z6ZIGYu}5XZxq^UjfKM_LhQ5Wy!(wUMFbL7mT?B%pn&glwFLE|ZiJw0|-@#<<{qiOc}&oEh? z*bFv-e?+)UaS$d*WsXfW&`ic(IAQK+(;98uCL%BOmq+~x z-eIUCMrYN9Nc5Xz7<&AUXMlIvE~?&e92LzaHZ6Anfp{0&U-I{gy4X8Ll&s7N88+L{ zbcwvp1;*fz>PP5b@*?-1EW;JeJ;gOXr^RTKSQ>%06|&AOe$m*sN<=$?`g7>Br8zy0 zu1i$IVsZsoF($tPtagL{{;^TZ?Pc1=g0>ZvFmcGT7LM_3E=OM`Y>})_$ zi({adY$uexQ*b8j7xp``jft&^ZJQH26WbHp<`dhtohP=FiEZ1?&ig;uRl91}SM~K# z*Hza^S6AP6_gcMvmmukZ!ao#N`wsoKs!tI6U^%|!CF*@{ba|r&fimg_}z4zkQSOegpvxz3b1Ic@fQ#q=}6k2yCoH{{6e1XF=|F!_$4Rog>`mV_lpkypGc zD71&Q+(y-kMxw$&|K|qPU)b}?Q=BHUy8-cLB^i}KbALYiG_&3=^_X&w1jSk6 z8`YjZP+H-S(a(BvG^5O&)wxK&tXXrZ2c_*2*&Z^R$F7md3cnEtV($~guXuXM=VI5k znxleqDCpqI_C0IwuM(vllx!651{om`up!cJT*4tQKZ4coYD=9kiFm*^ExGJ+&r z$tuzJPvwyYLv?bfMzEk5y_TZ(9hAyG$cM-0=+7z^wAV!cWL!K$CG11t2#+jrk|;iR zz_ys?z^mi^Zn&oXs8rM10wuS$i7)$cE#BXoFVHztCC$Xlc+=+x#YTb989xlsN2X|= zYl4Ll_~I%G5Blf7wc|Xj0*X+Y8`CY4rN7Bq=w}tCdd*DIlJwA;&=G;85HC#j8xoDj z{E&3WR|gZnyZWcaKkz+_k=B0+W%+4zjPKmDzaFDktM576NfyAAoH}zE*I945_iO>4 z5!OFFHrESFCGIw4jQty}m73JW*X~i4*nePKrquX3@K73M*;wE!;#PVeVe?jw2o zQ92XONFFdqZb6b(Fk%{(G z^X!gc8T^ILt~5nZTwFR?;LwQP1kw#y<-+=h%Nl50;R{_D(%`)%Ns zNC>B~EdSivqzP{g4V4UO$?veThaW6s$J|0B6C>mVT<&;!+izXCfglsy_l&Z$#tMrB zO5@*JE-7}WD0w^_hPJB)`|7ws{E!e5rwk;C{i4Y&F+Z+_;K|iLFD_F4fO$0pJ}8=w z2)u=59nTDC_gWh$bOsuVfGpc3veK9^Iszv54whZ`^cd$_&t36|Ey(6xE9_ZP1qyR@ z`_fCka2qgb#VuOPY^3WDl~z~dAGW6QNY#U~iq7wjgf@tX>A2tY2EI8%k8DK>)oK<6i3xkNstc<=AJ9W0%g zO`}o|UWk%vo>D86xf7T$k9aJiMNIRB$OUZS$VwhI2%V8RpiO{YlU6@-Mt4AEM;2nB z{SO$43{ARFny2NWT>Qs{xvs``jfIsz+b(k_! zzjRhVFE(M;rJZO7g^*(alYb3n93UQ)rm+BvdngIuIog9}4E{@GUxv6(8%)Y)na zs%SRp>AU;RAoEZVlazIB(-In$0%l6VnU3QMG^m=I#T#@w{W7(ad+V};4yah*U#v9&mT-DY_& z>z%@)jz>8wICgtZ*UuM%S%-Dm5xO#2jLi9dIY+RjWQc}T_Bx~*?S6;QAnqXwv~QI* zX^7|za~GH={t1SiN3{Aznx^a7o|@4APMn&o&+^6}kg6N(D~{eoR~?ZTB;kP5%&aRr zI;!GQi1CZ}2Vdi^H7{6(Tq8$)SK+?VhL?Cehv0Og*YCmUNS8HuETArz`8W*y=&g*7 zs+#9(OKljb>jY{R@evhT2IMHS1zrIk`v=~oR10oWt&f8GZ?X&aBU%HSdz!w->s`qE z*N$N#-9|js98#N`Rl_2n+*FF%VyQryQ6NZCWUw*~(E(_=fGhIB z|Kr>)-fgH?O>>yD5tlf}Sj5y3yRj@ad=0|bVB<`TGx==^i}pALi9g=E*)eznRHfVe z+M-{%)nqaSV5^gTN@yE0YK;AM6ceAUV%hD?9>$BdR=GtMhaG@qPBh{G-RrV|NxvT| zWMDFxke9iS3M=Z3* zTi&&l87z6)I`Lk+ywn7Z=;%aSia5%o>p-26%&(mj5u-bfP;$F;nO1eUpTV1e%4&Y1^`{; zw(`s+HnKviMyj0a*OM`l&irPkWy32#Ok1-e>mvs=014);V<4NJjg2Pj3!hxAP_-Q* zgEmQd3jV6V+6gWXW0Inrm4gHH5{8PWv9#)6)N&<#&|mq)yP}9vsdJUe#QI164sQ5h z8bj*_$xY`u#QaNC^q5nvY=96MOF!+n29JXp#*2)(3;EHtxRNkuJ>2_`AUa+2&R>yD zYn^B_SJ-|4=O~yKGi{9(3eMe_6b0TN%4nWs4f=xt`1ROvFNnGh^-7{!;-h{#_V8IG zErYZqV(WeM?qXVZEdbAGh@%X#5QI+1MkrT}c4fh>A7AM8w-DEJ2(xIV)WhM2^iX)% zi?)rhKiynE;tR;0OtfNZA8={Zu;i33E`++)cGrD6)CAX!Un(ocDa{}X;!K>=Vx3lP z0`;HKGL!Baqrjff:^`>7_IR$uKDjt+_m)78Ai$eQPKwxLH)v6-RAOu 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 AhX4Qo literal 0 HcmV?d00001 From badb2be06682a14afbd898145368b0b30e151182 Mon Sep 17 00:00:00 2001 From: Boaz Leskes Date: Fri, 21 Apr 2017 17:31:50 +0200 Subject: [PATCH 032/619] Peer Recovery: remove maxUnsafeAutoIdTimestamp hand off (#24243) With #24149 , it is now stored in the Lucene commit and is implicitly transferred in the file phase of the recovery. --- .../index/engine/EngineConfig.java | 14 +-- .../index/engine/InternalEngine.java | 9 +- .../elasticsearch/index/shard/IndexShard.java | 38 ++++++-- .../index/shard/StoreRecovery.java | 5 +- .../recovery/PeerRecoveryTargetService.java | 2 +- ...ryPrepareForTranslogOperationsRequest.java | 17 ++-- .../recovery/RecoverySourceHandler.java | 6 +- .../indices/recovery/RecoveryTarget.java | 8 +- .../recovery/RecoveryTargetHandler.java | 4 +- .../recovery/RemoteRecoveryTargetHandler.java | 4 +- .../index/engine/InternalEngineTests.java | 95 ++++++++----------- .../RecoveryDuringReplicationTests.java | 6 +- .../index/shard/IndexShardTests.java | 4 +- .../index/shard/RefreshListenersTests.java | 3 +- .../recovery/RecoverySourceHandlerTests.java | 4 +- 15 files changed, 102 insertions(+), 117 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/engine/EngineConfig.java b/core/src/main/java/org/elasticsearch/index/engine/EngineConfig.java index 7852d2c2db089..d22a93273c7e1 100644 --- a/core/src/main/java/org/elasticsearch/index/engine/EngineConfig.java +++ b/core/src/main/java/org/elasticsearch/index/engine/EngineConfig.java @@ -27,7 +27,6 @@ import org.apache.lucene.search.ReferenceManager; import org.apache.lucene.search.Sort; import org.apache.lucene.search.similarities.Similarity; -import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; @@ -67,7 +66,6 @@ public final class EngineConfig { private final Engine.EventListener eventListener; private final QueryCache queryCache; private final QueryCachingPolicy queryCachingPolicy; - private final long maxUnsafeAutoIdTimestamp; @Nullable private final ReferenceManager.RefreshListener refreshListeners; @Nullable @@ -116,7 +114,7 @@ public EngineConfig(OpenMode openMode, ShardId shardId, ThreadPool threadPool, Similarity similarity, CodecService codecService, Engine.EventListener eventListener, TranslogRecoveryPerformer translogRecoveryPerformer, QueryCache queryCache, QueryCachingPolicy queryCachingPolicy, TranslogConfig translogConfig, TimeValue flushMergesAfter, ReferenceManager.RefreshListener refreshListeners, - long maxUnsafeAutoIdTimestamp, Sort indexSort) { + Sort indexSort) { if (openMode == null) { throw new IllegalArgumentException("openMode must not be null"); } @@ -143,9 +141,6 @@ public EngineConfig(OpenMode openMode, ShardId shardId, ThreadPool threadPool, this.flushMergesAfter = flushMergesAfter; this.openMode = openMode; this.refreshListeners = refreshListeners; - assert maxUnsafeAutoIdTimestamp >= IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP : - "maxUnsafeAutoIdTimestamp must be >= -1 but was " + maxUnsafeAutoIdTimestamp; - this.maxUnsafeAutoIdTimestamp = maxUnsafeAutoIdTimestamp; this.indexSort = indexSort; } @@ -333,11 +328,10 @@ public ReferenceManager.RefreshListener getRefreshListeners() { } /** - * Returns the max timestamp that is used to de-optimize documents with auto-generated IDs in the engine. - * This is used to ensure we don't add duplicate documents when we assume an append only case based on auto-generated IDs + * returns true if the engine is allowed to optimize indexing operations with an auto-generated ID */ - public long getMaxUnsafeAutoIdTimestamp() { - return indexSettings.getValue(INDEX_OPTIMIZE_AUTO_GENERATED_IDS) ? maxUnsafeAutoIdTimestamp : Long.MAX_VALUE; + public boolean isAutoGeneratedIDsOptimizationEnabled() { + return indexSettings.getValue(INDEX_OPTIMIZE_AUTO_GENERATED_IDS); } /** diff --git a/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java b/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java index 3e5d3453cacf9..8e1eb059caa5b 100644 --- a/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java +++ b/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java @@ -128,7 +128,7 @@ public class InternalEngine extends Engine { private final AtomicInteger throttleRequestCount = new AtomicInteger(); private final EngineConfig.OpenMode openMode; private final AtomicBoolean pendingTranslogRecovery = new AtomicBoolean(false); - private static final String MAX_UNSAFE_AUTO_ID_TIMESTAMP_COMMIT_ID = "max_unsafe_auto_id_timestamp"; + public static final String MAX_UNSAFE_AUTO_ID_TIMESTAMP_COMMIT_ID = "max_unsafe_auto_id_timestamp"; private final AtomicLong maxUnsafeAutoIdTimestamp = new AtomicLong(-1); private final CounterMetric numVersionLookups = new CounterMetric(); private final CounterMetric numIndexVersionsLookups = new CounterMetric(); @@ -136,11 +136,8 @@ public class InternalEngine extends Engine { public InternalEngine(EngineConfig engineConfig) throws EngineException { super(engineConfig); openMode = engineConfig.getOpenMode(); - if (engineConfig.getIndexSettings().getIndexVersionCreated().before(Version.V_5_0_0_beta1)) { - // no optimization for pre 5.0.0.alpha6 since translog might not have all information needed + if (engineConfig.isAutoGeneratedIDsOptimizationEnabled() == false) { maxUnsafeAutoIdTimestamp.set(Long.MAX_VALUE); - } else { - maxUnsafeAutoIdTimestamp.set(engineConfig.getMaxUnsafeAutoIdTimestamp()); } this.versionMap = new LiveVersionMap(); store.incRef(); @@ -1836,7 +1833,7 @@ public void onSettingsChanged() { mergeScheduler.refreshConfig(); // config().isEnableGcDeletes() or config.getGcDeletesInMillis() may have changed: maybePruneDeletedTombstones(); - if (engineConfig.getMaxUnsafeAutoIdTimestamp() == Long.MAX_VALUE) { + if (engineConfig.isAutoGeneratedIDsOptimizationEnabled() == false) { // this is an anti-viral settings you can only opt out for the entire index // only if a shard starts up again due to relocation or if the index is closed // the setting will be re-interpreted if it's set to true diff --git a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java index d1ca4f13a42da..1da5e6763bc66 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -27,6 +27,7 @@ import org.apache.lucene.index.IndexFormatTooOldException; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.KeepOnlyLastCommitDeletionPolicy; +import org.apache.lucene.index.SegmentInfos; import org.apache.lucene.index.SnapshotDeletionPolicy; import org.apache.lucene.index.Term; import org.apache.lucene.search.Query; @@ -38,11 +39,11 @@ import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.ThreadInterruptedException; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.flush.FlushRequest; import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest; import org.elasticsearch.action.admin.indices.upgrade.post.UpgradeRequest; -import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.cluster.routing.RecoverySource; @@ -79,6 +80,7 @@ import org.elasticsearch.index.engine.EngineConfig; import org.elasticsearch.index.engine.EngineException; import org.elasticsearch.index.engine.EngineFactory; +import org.elasticsearch.index.engine.InternalEngine; import org.elasticsearch.index.engine.InternalEngineFactory; import org.elasticsearch.index.engine.RefreshFailedEngineException; import org.elasticsearch.index.engine.Segment; @@ -1040,11 +1042,11 @@ public void performTranslogRecovery(boolean indexExists) throws IOException { translogStats.totalOperations(0); translogStats.totalOperationsOnStart(0); } - internalPerformTranslogRecovery(false, indexExists, IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP); + internalPerformTranslogRecovery(false, indexExists); assert recoveryState.getStage() == RecoveryState.Stage.TRANSLOG : "TRANSLOG stage expected but was: " + recoveryState.getStage(); } - private void internalPerformTranslogRecovery(boolean skipTranslogRecovery, boolean indexExists, long maxUnsafeAutoIdTimestamp) throws IOException { + private void internalPerformTranslogRecovery(boolean skipTranslogRecovery, boolean indexExists) throws IOException { if (state != IndexShardState.RECOVERING) { throw new IndexShardNotRecoveringException(shardId, state); } @@ -1073,7 +1075,10 @@ private void internalPerformTranslogRecovery(boolean skipTranslogRecovery, boole } else { openMode = EngineConfig.OpenMode.OPEN_INDEX_AND_TRANSLOG; } - final EngineConfig config = newEngineConfig(openMode, maxUnsafeAutoIdTimestamp); + + assert indexExists == false || assertMaxUnsafeAutoIdInCommit(); + + final EngineConfig config = newEngineConfig(openMode); // we disable deletes since we allow for operations to be executed against the shard while recovering // but we need to make sure we don't loose deletes until we are done recovering config.setEnableGcDeletes(false); @@ -1087,6 +1092,22 @@ private void internalPerformTranslogRecovery(boolean skipTranslogRecovery, boole } } + private boolean assertMaxUnsafeAutoIdInCommit() throws IOException { + final Map userData = SegmentInfos.readLatestCommit(store.directory()).getUserData(); + if (recoveryState().getRecoverySource().getType() == RecoverySource.Type.PEER) { + // as of 5.5.0, the engine stores the maxUnsafeAutoIdTimestamp in the commit point. + // This should have baked into the commit by the primary we recover from, regardless of the index age. + assert userData.containsKey(InternalEngine.MAX_UNSAFE_AUTO_ID_TIMESTAMP_COMMIT_ID) : + "recovery from remote but " + InternalEngine.MAX_UNSAFE_AUTO_ID_TIMESTAMP_COMMIT_ID + " is not found in commit"; + } else if (recoveryState().getRecoverySource().getType() == RecoverySource.Type.EXISTING_STORE && + indexSettings.getIndexVersionCreated().onOrAfter(Version.V_5_5_0_UNRELEASED)) { + assert userData.containsKey(InternalEngine.MAX_UNSAFE_AUTO_ID_TIMESTAMP_COMMIT_ID) : + "opening index which was created post 5.5.0 but " + InternalEngine.MAX_UNSAFE_AUTO_ID_TIMESTAMP_COMMIT_ID + + " is not found in commit"; + } + return true; + } + protected void onNewEngine(Engine newEngine) { refreshListeners.setTranslog(newEngine.getTranslog()); } @@ -1096,9 +1117,9 @@ protected void onNewEngine(Engine newEngine) { * the replay of the transaction log which is required in cases where we restore a previous index or recover from * a remote peer. */ - public void skipTranslogRecovery(long maxUnsafeAutoIdTimestamp) throws IOException { + public void skipTranslogRecovery() throws IOException { assert getEngineOrNull() == null : "engine was already created"; - internalPerformTranslogRecovery(true, true, maxUnsafeAutoIdTimestamp); + internalPerformTranslogRecovery(true, true); assert recoveryState.getTranslog().recoveredOperations() == 0; } @@ -1795,14 +1816,13 @@ private DocumentMapperForType docMapper(String type) { return mapperService.documentMapperWithAutoCreate(type); } - private EngineConfig newEngineConfig(EngineConfig.OpenMode openMode, long maxUnsafeAutoIdTimestamp) { + private EngineConfig newEngineConfig(EngineConfig.OpenMode openMode) { final IndexShardRecoveryPerformer translogRecoveryPerformer = new IndexShardRecoveryPerformer(shardId, mapperService, logger); Sort indexSort = indexSortSupplier.get(); return new EngineConfig(openMode, shardId, threadPool, indexSettings, warmer, store, deletionPolicy, indexSettings.getMergePolicy(), mapperService.indexAnalyzer(), similarityService.similarity(mapperService), codecService, shardEventListener, translogRecoveryPerformer, indexCache.query(), cachingPolicy, translogConfig, - IndexingMemoryController.SHARD_INACTIVE_TIME_SETTING.get(indexSettings.getSettings()), refreshListeners, - maxUnsafeAutoIdTimestamp, indexSort); + IndexingMemoryController.SHARD_INACTIVE_TIME_SETTING.get(indexSettings.getSettings()), refreshListeners, indexSort); } /** diff --git a/core/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java b/core/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java index 6cfaca8c45b4b..032c033f175b1 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java +++ b/core/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java @@ -31,7 +31,6 @@ import org.apache.lucene.store.IOContext; import org.apache.lucene.store.IndexInput; import org.elasticsearch.ExceptionsHelper; -import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.cluster.routing.RecoverySource; @@ -353,7 +352,7 @@ private void internalRecoverFromStore(IndexShard indexShard) throws IndexShardRe recoveryState.getIndex().updateVersion(version); if (recoveryState.getRecoverySource().getType() == RecoverySource.Type.LOCAL_SHARDS) { assert indexShouldExists; - indexShard.skipTranslogRecovery(IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP); + indexShard.skipTranslogRecovery(); } else { // since we recover from local, just fill the files and size try { @@ -405,7 +404,7 @@ private void restore(final IndexShard indexShard, final Repository repository, f } final IndexId indexId = repository.getRepositoryData().resolveIndexId(indexName); repository.restoreShard(indexShard, restoreSource.snapshot().getSnapshotId(), restoreSource.version(), indexId, snapshotShardId, indexShard.recoveryState()); - indexShard.skipTranslogRecovery(IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP); + indexShard.skipTranslogRecovery(); indexShard.finalizeRecovery(); indexShard.postRecovery("restore done"); } catch (Exception e) { diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java b/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java index a93cdd51e3842..f449f9ffbe42d 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java @@ -377,7 +377,7 @@ class PrepareForTranslogOperationsRequestHandler implements TransportRequestHand public void messageReceived(RecoveryPrepareForTranslogOperationsRequest request, TransportChannel channel) throws Exception { try (RecoveryRef recoveryRef = onGoingRecoveries.getRecoverySafe(request.recoveryId(), request.shardId() )) { - recoveryRef.target().prepareForTranslogOperations(request.totalTranslogOps(), request.getMaxUnsafeAutoIdTimestamp()); + recoveryRef.target().prepareForTranslogOperations(request.totalTranslogOps()); } channel.sendResponse(TransportResponse.Empty.INSTANCE); } diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryPrepareForTranslogOperationsRequest.java b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryPrepareForTranslogOperationsRequest.java index 94425f627994c..155aa53e71a5d 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryPrepareForTranslogOperationsRequest.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryPrepareForTranslogOperationsRequest.java @@ -19,6 +19,7 @@ package org.elasticsearch.indices.recovery; +import org.elasticsearch.Version; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -29,7 +30,6 @@ public class RecoveryPrepareForTranslogOperationsRequest extends TransportRequest { - private long maxUnsafeAutoIdTimestamp = IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP; private long recoveryId; private ShardId shardId; private int totalTranslogOps = RecoveryState.Translog.UNKNOWN; @@ -37,11 +37,10 @@ public class RecoveryPrepareForTranslogOperationsRequest extends TransportReques public RecoveryPrepareForTranslogOperationsRequest() { } - RecoveryPrepareForTranslogOperationsRequest(long recoveryId, ShardId shardId, int totalTranslogOps, long maxUnsafeAutoIdTimestamp) { + RecoveryPrepareForTranslogOperationsRequest(long recoveryId, ShardId shardId, int totalTranslogOps) { this.recoveryId = recoveryId; this.shardId = shardId; this.totalTranslogOps = totalTranslogOps; - this.maxUnsafeAutoIdTimestamp = maxUnsafeAutoIdTimestamp; } public long recoveryId() { @@ -56,17 +55,15 @@ public int totalTranslogOps() { return totalTranslogOps; } - public long getMaxUnsafeAutoIdTimestamp() { - return maxUnsafeAutoIdTimestamp; - } - @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); recoveryId = in.readLong(); shardId = ShardId.readShardId(in); totalTranslogOps = in.readVInt(); - maxUnsafeAutoIdTimestamp = in.readLong(); + if (in.getVersion().before(Version.V_6_0_0_alpha1_UNRELEASED)) { + in.readLong(); // maxUnsafeAutoIdTimestamp + } } @Override @@ -75,6 +72,8 @@ public void writeTo(StreamOutput out) throws IOException { out.writeLong(recoveryId); shardId.writeTo(out); out.writeVInt(totalTranslogOps); - out.writeLong(maxUnsafeAutoIdTimestamp); + if (out.getVersion().before(Version.V_6_0_0_alpha1_UNRELEASED)) { + out.writeLong(IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP); // maxUnsafeAutoIdTimestamp + } } } diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java b/core/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java index a6aa47492e1c4..40f9f7f74f895 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java @@ -157,7 +157,7 @@ public RecoveryResponse recoverToTarget() throws IOException { } try { - prepareTargetForTranslog(translogView.totalOperations(), shard.segmentStats(false).getMaxUnsafeAutoIdTimestamp()); + prepareTargetForTranslog(translogView.totalOperations()); } catch (final Exception e) { throw new RecoveryEngineException(shard.shardId(), 1, "prepare target for translog failed", e); } @@ -389,13 +389,13 @@ public void phase1(final IndexCommit snapshot, final Translog.View translogView) } } - void prepareTargetForTranslog(final int totalTranslogOps, final long maxUnsafeAutoIdTimestamp) throws IOException { + void prepareTargetForTranslog(final int totalTranslogOps) throws IOException { StopWatch stopWatch = new StopWatch().start(); logger.trace("recovery [phase1]: prepare remote engine for translog"); final long startEngineStart = stopWatch.totalTime().millis(); // Send a request preparing the new shard's translog to receive operations. This ensures the shard engine is started and disables // garbage collection (not the JVM's GC!) of tombstone deletes. - cancellableThreads.executeIO(() -> recoveryTarget.prepareForTranslogOperations(totalTranslogOps, maxUnsafeAutoIdTimestamp)); + cancellableThreads.executeIO(() -> recoveryTarget.prepareForTranslogOperations(totalTranslogOps)); stopWatch.stop(); response.startTime = stopWatch.totalTime().millis() - startEngineStart; diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java index d9886efa07b21..b12006bbd3ce5 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java @@ -36,7 +36,6 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.lucene.Lucene; -import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.Callback; import org.elasticsearch.common.util.CancellableThreads; import org.elasticsearch.common.util.concurrent.AbstractRefCounted; @@ -49,7 +48,6 @@ import org.elasticsearch.index.translog.Translog; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; @@ -58,8 +56,6 @@ import java.util.Map.Entry; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; @@ -360,9 +356,9 @@ private void ensureRefCount() { /*** Implementation of {@link RecoveryTargetHandler } */ @Override - public void prepareForTranslogOperations(int totalTranslogOps, long maxUnsafeAutoIdTimestamp) throws IOException { + public void prepareForTranslogOperations(int totalTranslogOps) throws IOException { state().getTranslog().totalOperations(totalTranslogOps); - indexShard().skipTranslogRecovery(maxUnsafeAutoIdTimestamp); + indexShard().skipTranslogRecovery(); } @Override diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTargetHandler.java b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTargetHandler.java index 831181c631159..bdace02d218ba 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTargetHandler.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTargetHandler.java @@ -33,10 +33,8 @@ public interface RecoveryTargetHandler { * Prepares the target to receive translog operations, after all file have been copied * * @param totalTranslogOps total translog operations expected to be sent - * @param maxUnsafeAutoIdTimestamp the max timestamp that is used to de-optimize documents with auto-generated IDs in the engine. - * This is used to ensure we don't add duplicate documents when we assume an append only case based on auto-generated IDs */ - void prepareForTranslogOperations(int totalTranslogOps, long maxUnsafeAutoIdTimestamp) throws IOException; + void prepareForTranslogOperations(int totalTranslogOps) throws IOException; /** * The finalize request refreshes the engine now that new segments are available, enables garbage collection of tombstone files, and diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RemoteRecoveryTargetHandler.java b/core/src/main/java/org/elasticsearch/indices/recovery/RemoteRecoveryTargetHandler.java index 5fa1ca22c7065..959522d297db2 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RemoteRecoveryTargetHandler.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RemoteRecoveryTargetHandler.java @@ -78,9 +78,9 @@ public RemoteRecoveryTargetHandler(long recoveryId, ShardId shardId, String targ } @Override - public void prepareForTranslogOperations(int totalTranslogOps, long maxUnsafeAutoIdTimestamp) throws IOException { + public void prepareForTranslogOperations(int totalTranslogOps) throws IOException { transportService.submitRequest(targetNode, PeerRecoveryTargetService.Actions.PREPARE_TRANSLOG, - new RecoveryPrepareForTranslogOperationsRequest(recoveryId, shardId, totalTranslogOps, maxUnsafeAutoIdTimestamp), + new RecoveryPrepareForTranslogOperationsRequest(recoveryId, shardId, totalTranslogOps), TransportRequestOptions.builder().withTimeout(recoverySettings.internalActionTimeout()).build(), EmptyTransportResponseHandler.INSTANCE_SAME).txGet(); } diff --git a/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java b/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java index fe9e75f304ac7..3792872c9e349 100644 --- a/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java +++ b/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java @@ -56,10 +56,10 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.ReferenceManager; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortedSetSortField; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TotalHitCountCollector; import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.store.Directory; @@ -262,7 +262,7 @@ public EngineConfig copy(EngineConfig config, EngineConfig.OpenMode openMode, An config.getStore(), config.getDeletionPolicy(), config.getMergePolicy(), analyzer, config.getSimilarity(), new CodecService(null, logger), config.getEventListener(), config.getTranslogRecoveryPerformer(), config.getQueryCache(), config.getQueryCachingPolicy(), config.getTranslogConfig(), config.getFlushMergesAfter(), config.getRefreshListeners(), - config.getMaxUnsafeAutoIdTimestamp(), config.getIndexSort()); + config.getIndexSort()); } @Override @@ -371,7 +371,7 @@ protected InternalEngine createEngine( @Nullable IndexWriterFactory indexWriterFactory, @Nullable Supplier sequenceNumbersServiceSupplier, @Nullable Sort indexSort) throws IOException { - EngineConfig config = config(indexSettings, store, translogPath, mergePolicy, IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, null, indexSort); + EngineConfig config = config(indexSettings, store, translogPath, mergePolicy, null, indexSort); InternalEngine internalEngine = createInternalEngine(indexWriterFactory, sequenceNumbersServiceSupplier, config); if (config.getOpenMode() == EngineConfig.OpenMode.OPEN_INDEX_AND_TRANSLOG) { internalEngine.recoverFromTranslog(); @@ -404,25 +404,22 @@ public SequenceNumbersService seqNoService() { } public EngineConfig config(IndexSettings indexSettings, Store store, Path translogPath, MergePolicy mergePolicy, - long maxUnsafeAutoIdTimestamp, ReferenceManager.RefreshListener refreshListener) { - return config(indexSettings, store, translogPath, mergePolicy, createSnapshotDeletionPolicy(), - maxUnsafeAutoIdTimestamp, refreshListener, null); + ReferenceManager.RefreshListener refreshListener) { + return config(indexSettings, store, translogPath, mergePolicy, createSnapshotDeletionPolicy(), refreshListener, null); } public EngineConfig config(IndexSettings indexSettings, Store store, Path translogPath, MergePolicy mergePolicy, - long maxUnsafeAutoIdTimestamp, ReferenceManager.RefreshListener refreshListener, Sort indexSort) { - return config(indexSettings, store, translogPath, mergePolicy, createSnapshotDeletionPolicy(), - maxUnsafeAutoIdTimestamp, refreshListener, indexSort); + ReferenceManager.RefreshListener refreshListener, Sort indexSort) { + return config(indexSettings, store, translogPath, mergePolicy, createSnapshotDeletionPolicy(), refreshListener, indexSort); } public EngineConfig config(IndexSettings indexSettings, Store store, Path translogPath, MergePolicy mergePolicy, - SnapshotDeletionPolicy deletionPolicy, long maxUnsafeAutoIdTimestamp, - ReferenceManager.RefreshListener refreshListener) { - return config(indexSettings, store, translogPath, mergePolicy, deletionPolicy, maxUnsafeAutoIdTimestamp, refreshListener, null); + SnapshotDeletionPolicy deletionPolicy, ReferenceManager.RefreshListener refreshListener) { + return config(indexSettings, store, translogPath, mergePolicy, deletionPolicy, refreshListener, null); } public EngineConfig config(IndexSettings indexSettings, Store store, Path translogPath, MergePolicy mergePolicy, - SnapshotDeletionPolicy deletionPolicy, long maxUnsafeAutoIdTimestamp, + SnapshotDeletionPolicy deletionPolicy, ReferenceManager.RefreshListener refreshListener, Sort indexSort) { IndexWriterConfig iwc = newIndexWriterConfig(); TranslogConfig translogConfig = new TranslogConfig(shardId, translogPath, indexSettings, BigArrays.NON_RECYCLING_INSTANCE); @@ -445,8 +442,7 @@ public void onFailedEngine(String reason, @Nullable Exception e) { EngineConfig config = new EngineConfig(openMode, shardId, threadPool, indexSettings, null, store, deletionPolicy, mergePolicy, iwc.getAnalyzer(), iwc.getSimilarity(), new CodecService(null, logger), listener, new TranslogHandler(xContentRegistry(), shardId.getIndexName(), logger), IndexSearcher.getDefaultQueryCache(), - IndexSearcher.getDefaultQueryCachingPolicy(), translogConfig, TimeValue.timeValueMinutes(5), refreshListener, - maxUnsafeAutoIdTimestamp, indexSort); + IndexSearcher.getDefaultQueryCachingPolicy(), translogConfig, TimeValue.timeValueMinutes(5), refreshListener, indexSort); return config; } @@ -1170,8 +1166,7 @@ public void testSearchResultRelease() throws Exception { public void testSyncedFlush() throws IOException { try (Store store = createStore(); - Engine engine = new InternalEngine(config(defaultSettings, store, createTempDir(), - new LogByteSizeMergePolicy(), IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, null))) { + Engine engine = new InternalEngine(config(defaultSettings, store, createTempDir(), new LogByteSizeMergePolicy(), null))) { final String syncId = randomUnicodeOfCodepointLengthBetween(10, 20); ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null); engine.index(indexForDoc(doc)); @@ -1198,7 +1193,7 @@ public void testRenewSyncFlush() throws Exception { for (int i = 0; i < iters; i++) { try (Store store = createStore(); InternalEngine engine = new InternalEngine(config(defaultSettings, store, createTempDir(), - new LogDocMergePolicy(), IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, null))) { + new LogDocMergePolicy(), null))) { final String syncId = randomUnicodeOfCodepointLengthBetween(10, 20); Engine.Index doc1 = indexForDoc(testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null)); engine.index(doc1); @@ -1317,7 +1312,7 @@ public void testVersioningNewIndex() throws IOException { public void testForceMerge() throws IOException { try (Store store = createStore(); Engine engine = new InternalEngine(config(defaultSettings, store, createTempDir(), - new LogByteSizeMergePolicy(), IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, null))) { // use log MP here we test some behavior in ESMP + new LogByteSizeMergePolicy(), null))) { // use log MP here we test some behavior in ESMP int numDocs = randomIntBetween(10, 100); for (int i = 0; i < numDocs; i++) { ParsedDocument doc = testParsedDocument(Integer.toString(i), "test", null, testDocument(), B_1, null); @@ -2132,8 +2127,7 @@ public void testSeqNoAndCheckpoints() throws IOException { public void testConcurrentWritesAndCommits() throws Exception { try (Store store = createStore(); InternalEngine engine = new InternalEngine(config(defaultSettings, store, createTempDir(), newMergePolicy(), - new SnapshotDeletionPolicy(NoDeletionPolicy.INSTANCE), - IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, null))) { + new SnapshotDeletionPolicy(NoDeletionPolicy.INSTANCE), null))) { final int numIndexingThreads = scaledRandomIntBetween(3, 6); final int numDocsPerThread = randomIntBetween(500, 1000); @@ -2274,7 +2268,7 @@ public void testIndexWriterIFDInfoStream() throws IllegalAccessException, IOExce public void testEnableGcDeletes() throws Exception { try (Store store = createStore(); - Engine engine = new InternalEngine(config(defaultSettings, store, createTempDir(), newMergePolicy(), IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, null))) { + Engine engine = new InternalEngine(config(defaultSettings, store, createTempDir(), newMergePolicy(), null))) { engine.config().setEnableGcDeletes(false); // Add document @@ -2421,7 +2415,8 @@ public void testMissingTranslog() throws IOException { // expected } // now it should be OK. - EngineConfig config = copy(config(defaultSettings, store, primaryTranslogDir, newMergePolicy(), IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, null), EngineConfig.OpenMode.OPEN_INDEX_CREATE_TRANSLOG); + EngineConfig config = copy(config(defaultSettings, store, primaryTranslogDir, newMergePolicy(), null), + EngineConfig.OpenMode.OPEN_INDEX_CREATE_TRANSLOG); engine = new InternalEngine(config); } @@ -2736,7 +2731,7 @@ public void testRecoverFromForeignTranslog() throws IOException { config.getIndexSettings(), null, store, createSnapshotDeletionPolicy(), newMergePolicy(), config.getAnalyzer(), config.getSimilarity(), new CodecService(null, logger), config.getEventListener(), config.getTranslogRecoveryPerformer(), IndexSearcher.getDefaultQueryCache(), IndexSearcher.getDefaultQueryCachingPolicy(), translogConfig, - TimeValue.timeValueMinutes(5), config.getRefreshListeners(), IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, null); + TimeValue.timeValueMinutes(5), config.getRefreshListeners(), null); try { InternalEngine internalEngine = new InternalEngine(brokenConfig); @@ -2788,7 +2783,7 @@ public void run() { public void testCurrentTranslogIDisCommitted() throws IOException { try (Store store = createStore()) { - EngineConfig config = config(defaultSettings, store, createTempDir(), newMergePolicy(), IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, null); + EngineConfig config = config(defaultSettings, store, createTempDir(), newMergePolicy(), null); // create { @@ -3284,47 +3279,36 @@ public void testRetryConcurrently() throws InterruptedException, IOException { } public void testEngineMaxTimestampIsInitialized() throws IOException { - try (Store store = createStore(); - Engine engine = new InternalEngine(config(defaultSettings, store, createTempDir(), NoMergePolicy.INSTANCE, - IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, null))) { - assertEquals(IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); - - } - final long timestamp1 = Math.abs(randomLong()); + final long timestamp1 = Math.abs(randomNonNegativeLong()); final Path storeDir = createTempDir(); final Path translogDir = createTempDir(); + final long timestamp2 = randomNonNegativeLong(); + final long maxTimestamp12 = Math.max(timestamp1, timestamp2); try (Store store = createStore(newFSDirectory(storeDir)); - Engine engine = new InternalEngine( - config(defaultSettings, store, translogDir, NoMergePolicy.INSTANCE, timestamp1, null))) { + Engine engine = new InternalEngine(config(defaultSettings, store, translogDir, NoMergePolicy.INSTANCE, null))) { + assertEquals(IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); + final ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), + new BytesArray("{}".getBytes(Charset.defaultCharset())), null); + engine.index(appendOnlyPrimary(doc, true, timestamp1)); assertEquals(timestamp1, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); } - final long timestamp2 = randomNonNegativeLong(); - final long timestamp3 = randomNonNegativeLong(); - final long maxTimestamp12 = Math.max(timestamp1, timestamp2); - final long maxTimestamp123 = Math.max(maxTimestamp12, timestamp3); try (Store store = createStore(newFSDirectory(storeDir)); - Engine engine = new InternalEngine( - copy(config(defaultSettings, store, translogDir, NoMergePolicy.INSTANCE, timestamp2, null), - randomFrom(EngineConfig.OpenMode.OPEN_INDEX_AND_TRANSLOG, EngineConfig.OpenMode.OPEN_INDEX_CREATE_TRANSLOG)))) { - assertEquals(maxTimestamp12, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); - if (engine.config().getOpenMode() == EngineConfig.OpenMode.OPEN_INDEX_AND_TRANSLOG) { - // recover from translog and commit maxTimestamp12 - engine.recoverFromTranslog(); - // force flush as the were no ops performed - engine.flush(true, false); - } + Engine engine = new InternalEngine(config(defaultSettings, store, translogDir, NoMergePolicy.INSTANCE, null))) { + assertEquals(IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); + engine.recoverFromTranslog(); + assertEquals(timestamp1, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); final ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); - engine.index(appendOnlyPrimary(doc, true, timestamp3)); - assertEquals(maxTimestamp123, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); + engine.index(appendOnlyPrimary(doc, true, timestamp2)); + assertEquals(maxTimestamp12, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); + engine.flush(); } try (Store store = createStore(newFSDirectory(storeDir)); Engine engine = new InternalEngine( - config(defaultSettings, store, translogDir, NoMergePolicy.INSTANCE, IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, null))) { + copy(config(defaultSettings, store, translogDir, NoMergePolicy.INSTANCE, null), + randomFrom(EngineConfig.OpenMode.OPEN_INDEX_AND_TRANSLOG, EngineConfig.OpenMode.OPEN_INDEX_CREATE_TRANSLOG)))) { assertEquals(maxTimestamp12, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); - engine.recoverFromTranslog(); - assertEquals(maxTimestamp123, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); } } @@ -3394,8 +3378,7 @@ public void testFailEngineOnRandomIO() throws IOException, InterruptedException CyclicBarrier join = new CyclicBarrier(2); CountDownLatch start = new CountDownLatch(1); AtomicInteger controller = new AtomicInteger(0); - EngineConfig config = config(defaultSettings, store, translogPath, newMergePolicy(), - IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, new ReferenceManager.RefreshListener() { + EngineConfig config = config(defaultSettings, store, translogPath, newMergePolicy(), new ReferenceManager.RefreshListener() { @Override public void beforeRefresh() throws IOException { } diff --git a/core/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java b/core/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java index 139c7f500d8d7..349258785f03a 100644 --- a/core/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java +++ b/core/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java @@ -29,11 +29,11 @@ import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.ShardRouting; +import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.engine.EngineConfig; import org.elasticsearch.index.engine.EngineFactory; import org.elasticsearch.index.engine.InternalEngineTests; -import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.store.Store; import org.elasticsearch.index.translog.Translog; @@ -289,9 +289,9 @@ public long addDocument(Iterable doc) throws IOExcepti return new RecoveryTarget(indexShard, node, recoveryListener, l -> { }) { @Override - public void prepareForTranslogOperations(int totalTranslogOps, long maxUnsafeAutoIdTimestamp) throws IOException { + public void prepareForTranslogOperations(int totalTranslogOps) throws IOException { preparedForTranslog.set(true); - super.prepareForTranslogOperations(totalTranslogOps, maxUnsafeAutoIdTimestamp); + super.prepareForTranslogOperations(totalTranslogOps); } }; }); diff --git a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java index 629a8af3e0d3c..eccd958c36e20 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java +++ b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java @@ -1281,8 +1281,8 @@ public void testShardActiveDuringPeerRecovery() throws IOException { new RecoveryTarget(shard, discoveryNode, recoveryListener, aLong -> { }) { @Override - public void prepareForTranslogOperations(int totalTranslogOps, long maxUnsafeAutoIdTimestamp) throws IOException { - super.prepareForTranslogOperations(totalTranslogOps, maxUnsafeAutoIdTimestamp); + public void prepareForTranslogOperations(int totalTranslogOps) throws IOException { + super.prepareForTranslogOperations(totalTranslogOps); // Shard is still inactive since we haven't started recovering yet assertFalse(replica.isActive()); diff --git a/core/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java b/core/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java index 3e5a34c3921fe..aa3b9b1ee85c2 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java +++ b/core/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java @@ -29,7 +29,6 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.store.Directory; import org.apache.lucene.util.IOUtils; -import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; @@ -123,7 +122,7 @@ public void onFailedEngine(String reason, @Nullable Exception e) { store, new SnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy()), newMergePolicy(), iwc.getAnalyzer(), iwc.getSimilarity(), new CodecService(null, logger), eventListener, translogHandler, IndexSearcher.getDefaultQueryCache(), IndexSearcher.getDefaultQueryCachingPolicy(), translogConfig, - TimeValue.timeValueMinutes(5), listeners, IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, null); + TimeValue.timeValueMinutes(5), listeners, null); engine = new InternalEngine(config); listeners.setTranslog(engine.getTranslog()); } diff --git a/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java b/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java index 40a92b11e7372..e424eb399329e 100644 --- a/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java +++ b/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java @@ -393,7 +393,7 @@ public void phase1(final IndexCommit snapshot, final Translog.View translogView) } @Override - void prepareTargetForTranslog(final int totalTranslogOps, final long maxUnsafeAutoIdTimestamp) throws IOException { + void prepareTargetForTranslog(final int totalTranslogOps) throws IOException { prepareTargetForTranslogCalled.set(true); } @@ -483,7 +483,7 @@ public void phase1(final IndexCommit snapshot, final Translog.View translogView) } @Override - void prepareTargetForTranslog(final int totalTranslogOps, final long maxUnsafeAutoIdTimestamp) throws IOException { + void prepareTargetForTranslog(final int totalTranslogOps) throws IOException { prepareTargetForTranslogCalled.set(true); } From ba4867469500b464c67da17226de745f34253163 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 21 Apr 2017 09:25:58 -0700 Subject: [PATCH 033/619] Build: Move plugin cli and tests to distribution tool (#24220) The plugin cli currently resides inside the elasticsearch jar. This commit moves it into a plugin-cli jar. This is change alone is a no-op; it does not change anything about what is loaded at runtime. But it will allow easier testing (with fixtures in the future to test ES or maven installation), as well as eventually not loading these classes when starting elasticsearch. --- .../elasticsearch/plugins/PluginSecurity.java | 1 - distribution/build.gradle | 1 + distribution/tools/plugin-cli/build.gradle | 32 +++++++++++++++++++ .../plugins/InstallPluginCommand.java | 2 +- .../plugins/ListPluginsCommand.java | 0 .../org/elasticsearch/plugins/PluginCli.java | 0 .../plugins/ProgressInputStream.java | 0 .../plugins/RemovePluginCommand.java | 0 .../plugins/InstallPluginCommandTests.java | 0 .../plugins/ListPluginsCommandTests.java | 0 .../plugins/ProgressInputStreamTests.java | 0 .../plugins/RemovePluginCommandTests.java | 0 settings.gradle | 1 + 13 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 distribution/tools/plugin-cli/build.gradle rename {core => distribution/tools/plugin-cli}/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java (100%) rename {core => distribution/tools/plugin-cli}/src/main/java/org/elasticsearch/plugins/ListPluginsCommand.java (100%) rename {core => distribution/tools/plugin-cli}/src/main/java/org/elasticsearch/plugins/PluginCli.java (100%) rename {core => distribution/tools/plugin-cli}/src/main/java/org/elasticsearch/plugins/ProgressInputStream.java (100%) rename {core => distribution/tools/plugin-cli}/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java (100%) rename {qa/evil-tests => distribution/tools/plugin-cli}/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java (100%) rename {qa/evil-tests => distribution/tools/plugin-cli}/src/test/java/org/elasticsearch/plugins/ListPluginsCommandTests.java (100%) rename {core => distribution/tools/plugin-cli}/src/test/java/org/elasticsearch/plugins/ProgressInputStreamTests.java (100%) rename {qa/evil-tests => distribution/tools/plugin-cli}/src/test/java/org/elasticsearch/plugins/RemovePluginCommandTests.java (100%) diff --git a/core/src/main/java/org/elasticsearch/plugins/PluginSecurity.java b/core/src/main/java/org/elasticsearch/plugins/PluginSecurity.java index 55a3c6069e7ef..d09acbe1f3c23 100644 --- a/core/src/main/java/org/elasticsearch/plugins/PluginSecurity.java +++ b/core/src/main/java/org/elasticsearch/plugins/PluginSecurity.java @@ -22,7 +22,6 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.Terminal.Verbosity; -import org.elasticsearch.env.Environment; import java.io.IOException; import java.nio.file.Files; diff --git a/distribution/build.gradle b/distribution/build.gradle index b02c4ed880266..75b40d57b1730 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -165,6 +165,7 @@ configure(distributions) { from project(':core').configurations.runtime // delay add tools using closures, since they have not yet been configured, so no jar task exists yet from { project(':distribution:tools:java-version-checker').jar } + from { project(':distribution:tools:plugin-cli').jar } } modulesFiles = copySpec { diff --git a/distribution/tools/plugin-cli/build.gradle b/distribution/tools/plugin-cli/build.gradle new file mode 100644 index 0000000000000..ae3dca9ef87af --- /dev/null +++ b/distribution/tools/plugin-cli/build.gradle @@ -0,0 +1,32 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +apply plugin: 'elasticsearch.build' + +dependencies { + provided "org.elasticsearch:elasticsearch:${version}" + testCompile "org.elasticsearch.test:framework:${version}" + testCompile 'com.google.jimfs:jimfs:1.1' + testCompile 'com.google.guava:guava:18.0' +} + +test { + // TODO: find a way to add permissions for the tests in this module + systemProperty 'tests.security.manager', 'false' +} diff --git a/core/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java similarity index 100% rename from core/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java rename to distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java index a597fdcc5a2d3..afe4593e62779 100644 --- a/core/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java @@ -26,8 +26,8 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.Version; import org.elasticsearch.bootstrap.JarHell; -import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.EnvironmentAwareCommand; +import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; import org.elasticsearch.common.SuppressForbidden; diff --git a/core/src/main/java/org/elasticsearch/plugins/ListPluginsCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/ListPluginsCommand.java similarity index 100% rename from core/src/main/java/org/elasticsearch/plugins/ListPluginsCommand.java rename to distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/ListPluginsCommand.java diff --git a/core/src/main/java/org/elasticsearch/plugins/PluginCli.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/PluginCli.java similarity index 100% rename from core/src/main/java/org/elasticsearch/plugins/PluginCli.java rename to distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/PluginCli.java diff --git a/core/src/main/java/org/elasticsearch/plugins/ProgressInputStream.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/ProgressInputStream.java similarity index 100% rename from core/src/main/java/org/elasticsearch/plugins/ProgressInputStream.java rename to distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/ProgressInputStream.java diff --git a/core/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java similarity index 100% rename from core/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java rename to distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java similarity index 100% rename from qa/evil-tests/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java rename to distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/plugins/ListPluginsCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/ListPluginsCommandTests.java similarity index 100% rename from qa/evil-tests/src/test/java/org/elasticsearch/plugins/ListPluginsCommandTests.java rename to distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/ListPluginsCommandTests.java diff --git a/core/src/test/java/org/elasticsearch/plugins/ProgressInputStreamTests.java b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/ProgressInputStreamTests.java similarity index 100% rename from core/src/test/java/org/elasticsearch/plugins/ProgressInputStreamTests.java rename to distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/ProgressInputStreamTests.java diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/plugins/RemovePluginCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/RemovePluginCommandTests.java similarity index 100% rename from qa/evil-tests/src/test/java/org/elasticsearch/plugins/RemovePluginCommandTests.java rename to distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/RemovePluginCommandTests.java diff --git a/settings.gradle b/settings.gradle index 36f9c23e7c5b7..519dca27f8045 100644 --- a/settings.gradle +++ b/settings.gradle @@ -21,6 +21,7 @@ List projects = [ 'distribution:deb', 'distribution:rpm', 'distribution:tools:java-version-checker', + 'distribution:tools:plugin-cli', 'test:framework', 'test:fixtures:example-fixture', 'test:fixtures:hdfs-fixture', From 2dd924bc15a8d112e62408c098c9be856663e400 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Fri, 21 Apr 2017 12:51:14 -0400 Subject: [PATCH 034/619] Add Wildfly integration test An important use case for our users is deploying our clients inside of applications containers like Wildly. Sometimes, we make changes that unintentionally break this use case. We need to know before we ship a release that we have broken such use cases. As Wildfly is one of the bigger application containers, this commit starts by adding an integration test that deploys an application using the transport client to Wildfly and ensures that all is well. Future work can add similar integration tests for the low-level and high-level REST clients. Relates #24147 --- qa/wildfly/build.gradle | 198 ++++++++++++++++++ .../elasticsearch/wildfly/model/Employee.java | 87 ++++++++ .../transport/TransportClientActivator.java | 36 ++++ .../TransportClientEmployeeResource.java | 106 ++++++++++ .../transport/TransportClientProducer.java | 72 +++++++ .../TransportJacksonJsonProvider.java | 28 +++ .../src/main/resources/log4j2.properties | 9 + qa/wildfly/src/main/webapp/WEB-INF/beans.xml | 26 +++ .../WEB-INF/jboss-deployment-structure.xml | 10 + .../org/elasticsearch/wildfly/WildflyIT.java | 101 +++++++++ settings.gradle | 1 + 11 files changed, 674 insertions(+) create mode 100644 qa/wildfly/build.gradle create mode 100644 qa/wildfly/src/main/java/org/elasticsearch/wildfly/model/Employee.java create mode 100644 qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientActivator.java create mode 100644 qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientEmployeeResource.java create mode 100644 qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientProducer.java create mode 100644 qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportJacksonJsonProvider.java create mode 100644 qa/wildfly/src/main/resources/log4j2.properties create mode 100644 qa/wildfly/src/main/webapp/WEB-INF/beans.xml create mode 100644 qa/wildfly/src/main/webapp/WEB-INF/jboss-deployment-structure.xml create mode 100644 qa/wildfly/src/test/java/org/elasticsearch/wildfly/WildflyIT.java diff --git a/qa/wildfly/build.gradle b/qa/wildfly/build.gradle new file mode 100644 index 0000000000000..66a141f359765 --- /dev/null +++ b/qa/wildfly/build.gradle @@ -0,0 +1,198 @@ +import org.elasticsearch.gradle.LoggedExec +import org.elasticsearch.gradle.VersionProperties +import org.apache.tools.ant.taskdefs.condition.Os + +import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.util.stream.Stream + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +apply plugin: 'war' +apply plugin: 'elasticsearch.build' +apply plugin: 'elasticsearch.rest-test' + +final String wildflyVersion = '10.0.0.Final' +final String wildflyDir = "${buildDir}/wildfly" +final String wildflyInstall = "${buildDir}/wildfly/wildfly-${wildflyVersion}" +// TODO: use ephemeral ports +final int portOffset = 30000 +final int managementPort = 9990 + portOffset +// we skip these tests on Windows so we do no need to worry about compatibility here +final String stopWildflyCommand = "${wildflyInstall}/bin/jboss-cli.sh --controller=localhost:${managementPort} --connect command=:shutdown" + +repositories { + mavenCentral() + // the Wildfly distribution is not available via a repository, so we fake an Ivy repository on top of the download site + ivy { + url "http://download.jboss.org" + layout 'pattern', { + artifact 'wildfly/[revision]/[module]-[revision].[ext]' + } + } +} + +configurations { + wildfly +} + +dependencies { + providedCompile 'javax.enterprise:cdi-api:1.2' + providedCompile 'org.jboss.spec.javax.annotation:jboss-annotations-api_1.2_spec:1.0.0.Final' + providedCompile 'org.jboss.spec.javax.ws.rs:jboss-jaxrs-api_2.0_spec:1.0.0.Final' + compile ('org.jboss.resteasy:resteasy-jackson2-provider:3.0.19.Final') { + exclude module: 'jackson-annotations' + exclude module: 'jackson-core' + exclude module: 'jackson-databind' + exclude module: 'jackson-jaxrs-json-provider' + } + compile "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}" + compile "com.fasterxml.jackson.core:jackson-core:${versions.jackson}" + compile "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" + compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:${versions.jackson}" + compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:${versions.jackson}" + compile "com.fasterxml.jackson.module:jackson-module-jaxb-annotations:${versions.jackson}" + compile "org.apache.logging.log4j:log4j-api:${versions.log4j}" + compile "org.apache.logging.log4j:log4j-core:${versions.log4j}" + compile project(path: ':client:transport', configuration: 'runtime') + wildfly "org.jboss:wildfly:${wildflyVersion}@zip" + testCompile "org.elasticsearch.test:framework:${VersionProperties.elasticsearch}" +} + +task unzipWildfly(type: Sync) { + into wildflyDir + from { zipTree(configurations.wildfly.singleFile) } +} + +task deploy(type: Copy) { + dependsOn unzipWildfly, war + from war + into "${wildflyInstall}/standalone/deployments" +} + +task writeElasticsearchProperties { + onlyIf { !Os.isFamily(Os.FAMILY_WINDOWS) } + dependsOn 'integTestCluster#wait', deploy + doLast { + final File elasticsearchProperties = + file("${wildflyInstall}/standalone/configuration/elasticsearch.properties") + elasticsearchProperties.write( + [ + "transport.uri=${-> integTest.getNodes().get(0).transportUri()}", + "cluster.name=${-> integTest.getNodes().get(0).clusterName}" + ].join("\n")) + } +} + +// the default configuration ships with IPv6 disabled but our cluster could be bound to IPv6 if the host supports it +task enableIPv6 { + dependsOn unzipWildfly + doLast { + final File standaloneConf = file("${wildflyInstall}/bin/standalone.conf") + final List lines = + Files.readAllLines(standaloneConf.toPath()) + .collect { line -> line.replace("-Djava.net.preferIPv4Stack=true", "-Djava.net.preferIPv4Stack=false") } + standaloneConf.write(lines.join("\n")) + } +} + +task startWildfly { + dependsOn enableIPv6, writeElasticsearchProperties + doFirst { + // we skip these tests on Windows so we do no need to worry about compatibility here + final File script = new File(project.buildDir, "wildfly/wildfly.killer.sh") + script.setText( + ["function shutdown {", + " ${stopWildflyCommand}", + "}", + "trap shutdown EXIT", + // will wait indefinitely for input, but we never pass input, and the pipe is only closed when the build dies + "read line"].join('\n'), 'UTF-8') + final ProcessBuilder pb = new ProcessBuilder("bash", script.absolutePath) + pb.start() + } + doLast { + // we skip these tests on Windows so we do no need to worry about compatibility here + final ProcessBuilder pb = + new ProcessBuilder("${wildflyInstall}/bin/standalone.sh", "-Djboss.socket.binding.port-offset=${portOffset}") + final Process process = pb.start() + new BufferedReader(new InputStreamReader(process.getInputStream())).withReader { br -> + String line + while ((line = br.readLine()) != null) { + if (line.matches(".*WildFly Full \\d+\\.\\d+\\.\\d+\\.Final \\(WildFly Core \\d+\\.\\d+\\.\\d+\\.Final\\) started.*")) { + break + } + } + } + } +} + +task configureTransportClient(type: LoggedExec) { + dependsOn startWildfly + // we skip these tests on Windows so we do no need to worry about compatibility here + commandLine "${wildflyInstall}/bin/jboss-cli.sh", + "--controller=localhost:${managementPort}", + "--connect", + "--command=/system-property=elasticsearch.properties:add(value=\${jboss.server.config.dir}/elasticsearch.properties)" +} + +task stopWildfly(type: LoggedExec) { + commandLine stopWildflyCommand.split(' ') +} + +if (!Os.isFamily(Os.FAMILY_WINDOWS)) { + integTestRunner.dependsOn(configureTransportClient) + final TaskExecutionAdapter logDumpListener = new TaskExecutionAdapter() { + @Override + void afterExecute(final Task task, final TaskState state) { + if (state.failure != null) { + final File logFile = new File(wildflyInstall, "standalone/log/server.log") + println("\nWildfly server log (from ${logFile}):") + println('-----------------------------------------') + final Stream stream = Files.lines(logFile.toPath(), StandardCharsets.UTF_8) + try { + for (String line : stream) { + println(line) + } + } finally { + stream.close() + } + println('=========================================') + } + } + } + integTestRunner.doFirst { + project.gradle.addListener(logDumpListener) + } + integTestRunner.doLast { + project.gradle.removeListener(logDumpListener) + } + integTestRunner.finalizedBy(stopWildfly) +} else { + integTest.enabled = false +} + +check.dependsOn(integTest) + +test.enabled = false + +dependencyLicenses.enabled = false + +thirdPartyAudit.enabled = false diff --git a/qa/wildfly/src/main/java/org/elasticsearch/wildfly/model/Employee.java b/qa/wildfly/src/main/java/org/elasticsearch/wildfly/model/Employee.java new file mode 100644 index 0000000000000..43f2e5faf5e92 --- /dev/null +++ b/qa/wildfly/src/main/java/org/elasticsearch/wildfly/model/Employee.java @@ -0,0 +1,87 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.wildfly.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import java.util.List; + +@Consumes(MediaType.APPLICATION_JSON) +public class Employee { + + @JsonProperty(value = "first_name") + private String firstName; + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + @JsonProperty(value = "last_name") + private String lastName; + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + @JsonProperty(value = "age") + private int age; + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @JsonProperty(value = "about") + private String about; + + public String getAbout() { + return about; + } + + public void setAbout(String about) { + this.about = about; + } + + @JsonProperty(value = "interests") + private List interests; + + public List getInterests() { + return interests; + } + + public void setInterests(List interests) { + this.interests = interests; + } + +} diff --git a/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientActivator.java b/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientActivator.java new file mode 100644 index 0000000000000..881b263f35b97 --- /dev/null +++ b/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientActivator.java @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.wildfly.transport; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +import java.util.Collections; +import java.util.Set; + +@ApplicationPath("/transport") +public class TransportClientActivator extends Application { + + @Override + public Set> getClasses() { + return Collections.singleton(TransportClientEmployeeResource.class); + } + +} diff --git a/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientEmployeeResource.java b/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientEmployeeResource.java new file mode 100644 index 0000000000000..4008bf8801a55 --- /dev/null +++ b/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientEmployeeResource.java @@ -0,0 +1,106 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.wildfly.transport; + +import org.elasticsearch.action.get.GetResponse; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.client.transport.TransportClient; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.wildfly.model.Employee; + +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; + +@Path("/employees") +public class TransportClientEmployeeResource { + + @Inject + private TransportClient client; + + @GET + @Path("/{id}") + @Produces(MediaType.APPLICATION_JSON) + public Response getEmployeeById(final @PathParam("id") Long id) { + Objects.requireNonNull(id); + final GetResponse response = client.prepareGet("megacorp", "employee", Long.toString(id)).get(); + if (response.isExists()) { + final Map source = response.getSource(); + final Employee employee = new Employee(); + employee.setFirstName((String) source.get("first_name")); + employee.setLastName((String) source.get("last_name")); + employee.setAge((Integer) source.get("age")); + employee.setAbout((String) source.get("about")); + @SuppressWarnings("unchecked") final List interests = (List) source.get("interests"); + employee.setInterests(interests); + return Response.ok(employee).build(); + } else { + return Response.status(Response.Status.NOT_FOUND).build(); + } + } + + @PUT + @Path("/{id}") + @Produces(MediaType.APPLICATION_JSON) + public Response putEmployeeById(final @PathParam("id") Long id, final Employee employee) throws URISyntaxException, IOException { + Objects.requireNonNull(id); + Objects.requireNonNull(employee); + try (XContentBuilder builder = jsonBuilder()) { + builder.startObject(); + { + builder.field("first_name", employee.getFirstName()); + builder.field("last_name", employee.getLastName()); + builder.field("age", employee.getAge()); + builder.field("about", employee.getAbout()); + if (employee.getInterests() != null) { + builder.startArray("interests"); + { + for (final String interest : employee.getInterests()) { + builder.value(interest); + } + } + builder.endArray(); + } + } + builder.endObject(); + final IndexResponse response = client.prepareIndex("megacorp", "employee", Long.toString(id)).setSource(builder).get(); + if (response.status().getStatus() == 201) { + return Response.created(new URI("/employees/" + id)).build(); + } else { + return Response.ok().build(); + } + } + } + +} diff --git a/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientProducer.java b/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientProducer.java new file mode 100644 index 0000000000000..f46359db8f0f0 --- /dev/null +++ b/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientProducer.java @@ -0,0 +1,72 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.wildfly.transport; + +import org.elasticsearch.client.transport.TransportClient; +import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.common.SuppressForbidden; +import org.elasticsearch.common.io.PathUtils; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.transport.TransportAddress; +import org.elasticsearch.transport.client.PreBuiltTransportClient; + +import javax.enterprise.inject.Produces; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Properties; + +@SuppressWarnings("unused") +public final class TransportClientProducer { + + @Produces + public TransportClient createTransportClient() throws IOException { + final String elasticsearchProperties = System.getProperty("elasticsearch.properties"); + final Properties properties = new Properties(); + + final String transportUri; + final String clusterName; + try (InputStream is = Files.newInputStream(getPath(elasticsearchProperties))) { + properties.load(is); + transportUri = properties.getProperty("transport.uri"); + clusterName = properties.getProperty("cluster.name"); + } + + final int lastColon = transportUri.lastIndexOf(':'); + final String host = transportUri.substring(0, lastColon); + final int port = Integer.parseInt(transportUri.substring(lastColon + 1)); + final Settings settings = Settings.builder().put("cluster.name", clusterName).build(); + final TransportClient transportClient = new PreBuiltTransportClient(settings, Collections.emptyList()); + transportClient.addTransportAddress(new TransportAddress(InetAddress.getByName(host), port)); + return transportClient; + } + + @SuppressForbidden(reason = "get path not configured in environment") + private Path getPath(final String elasticsearchProperties) { + return PathUtils.get(elasticsearchProperties); + } + +} diff --git a/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportJacksonJsonProvider.java b/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportJacksonJsonProvider.java new file mode 100644 index 0000000000000..07585780c0665 --- /dev/null +++ b/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportJacksonJsonProvider.java @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.wildfly.transport; + +import org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider; + +import javax.ws.rs.ext.Provider; + +@Provider +public class TransportJacksonJsonProvider extends ResteasyJackson2Provider { +} diff --git a/qa/wildfly/src/main/resources/log4j2.properties b/qa/wildfly/src/main/resources/log4j2.properties new file mode 100644 index 0000000000000..46877d0de32a0 --- /dev/null +++ b/qa/wildfly/src/main/resources/log4j2.properties @@ -0,0 +1,9 @@ +status = error + +appender.console.type = Console +appender.console.name = console +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] %marker%m%n + +rootLogger.level = info +rootLogger.appenderRef.console.ref = console diff --git a/qa/wildfly/src/main/webapp/WEB-INF/beans.xml b/qa/wildfly/src/main/webapp/WEB-INF/beans.xml new file mode 100644 index 0000000000000..50582333a4220 --- /dev/null +++ b/qa/wildfly/src/main/webapp/WEB-INF/beans.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/qa/wildfly/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/qa/wildfly/src/main/webapp/WEB-INF/jboss-deployment-structure.xml new file mode 100644 index 0000000000000..7191bfe1268aa --- /dev/null +++ b/qa/wildfly/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/qa/wildfly/src/test/java/org/elasticsearch/wildfly/WildflyIT.java b/qa/wildfly/src/test/java/org/elasticsearch/wildfly/WildflyIT.java new file mode 100644 index 0000000000000..90090c35fa9ee --- /dev/null +++ b/qa/wildfly/src/test/java/org/elasticsearch/wildfly/WildflyIT.java @@ -0,0 +1,101 @@ +package org.elasticsearch.wildfly;/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.lucene.util.LuceneTestCase; +import org.elasticsearch.Build; +import org.elasticsearch.Version; +import org.elasticsearch.cluster.ClusterModule; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.json.JsonXContent; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; + +public class WildflyIT extends LuceneTestCase { + + public void testTransportClient() throws URISyntaxException, IOException { + try (CloseableHttpClient client = HttpClientBuilder.create().build()) { + final String str = String.format( + Locale.ROOT, + "http://localhost:38080/wildfly-%s%s/transport/employees/1", + Version.CURRENT, + Build.CURRENT.isSnapshot() ? "-SNAPSHOT" : ""); + final HttpPut put = new HttpPut(new URI(str)); + final String body; + try (XContentBuilder builder = jsonBuilder()) { + builder.startObject(); + { + builder.field("first_name", "John"); + builder.field("last_name", "Smith"); + builder.field("age", 25); + builder.field("about", "I love to go rock climbing"); + builder.startArray("interests"); + { + builder.value("sports"); + builder.value("music"); + } + builder.endArray(); + } + builder.endObject(); + body = builder.string(); + } + put.setEntity(new StringEntity(body, ContentType.APPLICATION_JSON)); + try (CloseableHttpResponse response = client.execute(put)) { + assertThat(response.getStatusLine().getStatusCode(), equalTo(201)); + } + + final HttpGet get = new HttpGet(new URI(str)); + try ( + CloseableHttpResponse response = client.execute(get); + XContentParser parser = + JsonXContent.jsonXContent.createParser( + new NamedXContentRegistry(ClusterModule.getNamedXWriteables()), + response.getEntity().getContent())) { + final Map map = parser.map(); + assertThat(map.get("first_name"), equalTo("John")); + assertThat(map.get("last_name"), equalTo("Smith")); + assertThat(map.get("age"), equalTo(25)); + assertThat(map.get("about"), equalTo("I love to go rock climbing")); + final Object interests = map.get("interests"); + assertThat(interests, instanceOf(List.class)); + @SuppressWarnings("unchecked") final List interestsAsList = (List) interests; + assertThat(interestsAsList, containsInAnyOrder("sports", "music")); + } + } + } + +} diff --git a/settings.gradle b/settings.gradle index 519dca27f8045..2f1fcfaed3504 100644 --- a/settings.gradle +++ b/settings.gradle @@ -71,6 +71,7 @@ List projects = [ 'qa:smoke-test-reindex-with-painless', 'qa:smoke-test-tribe-node', 'qa:vagrant', + 'qa:wildfly' ] boolean isEclipse = System.getProperty("eclipse.launcher") != null || gradle.startParameter.taskNames.contains('eclipse') || gradle.startParameter.taskNames.contains('cleanEclipse') From 2ca7072b2447d374eb9c58cf81477c2aa01c351c Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Fri, 21 Apr 2017 20:28:00 +0200 Subject: [PATCH 035/619] Fill missing sequence IDs up to max sequence ID when recovering from store (#24238) Today we might promote a primary and recover from store where after translog recovery the local checkpoint is still behind the maximum sequence ID seen. To fill the holes in the sequence ID history this PR adds a utility method that fills up all missing sequence IDs up to the maximum seen sequence ID with no-ops. Relates to #10708 --- .../action/bulk/TransportShardBulkAction.java | 6 +- .../elasticsearch/index/engine/Engine.java | 8 ++ .../index/engine/InternalEngine.java | 23 ++++++ .../index/mapper/SourceToParse.java | 18 +--- .../index/shard/StoreRecovery.java | 2 + .../index/engine/InternalEngineTests.java | 82 ++++++++++++++++++- .../index/shard/IndexShardTests.java | 42 ++++++++++ .../PeerRecoveryTargetServiceTests.java | 2 +- .../index/shard/IndexShardTestCase.java | 4 +- 9 files changed, 163 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java b/core/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java index 30f38230bc94f..21dd799122e39 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java @@ -415,7 +415,7 @@ public enum ReplicaItemExecutionMode { static { assert Version.CURRENT.minimumCompatibilityVersion().after(Version.V_5_0_0) == false: "Remove logic handling NoOp result from primary response; see TODO in replicaItemExecutionMode" + - " as the current minimum compatible version [" + + " as the current minimum compatible version [" + Version.CURRENT.minimumCompatibilityVersion() + "] is after 5.0"; } @@ -565,7 +565,7 @@ static Engine.Index prepareIndexOperationOnReplica( final long version = primaryResponse.getVersion(); final long seqNo = primaryResponse.getSeqNo(); final SourceToParse sourceToParse = - SourceToParse.source(SourceToParse.Origin.REPLICA, shardId.getIndexName(), + SourceToParse.source(shardId.getIndexName(), request.type(), request.id(), request.source(), request.getContentType()) .routing(request.routing()).parent(request.parent()); final VersionType versionType = request.versionType().versionTypeForReplicationAndRecovery(); @@ -578,7 +578,7 @@ static Engine.Index prepareIndexOperationOnReplica( /** Utility method to prepare an index operation on primary shards */ private static Engine.Index prepareIndexOperationOnPrimary(IndexRequest request, IndexShard primary) { final SourceToParse sourceToParse = - SourceToParse.source(SourceToParse.Origin.PRIMARY, request.index(), request.type(), + SourceToParse.source(request.index(), request.type(), request.id(), request.source(), request.getContentType()) .routing(request.routing()).parent(request.parent()); return primary.prepareIndexOnPrimary(sourceToParse, request.version(), request.versionType(), diff --git a/core/src/main/java/org/elasticsearch/index/engine/Engine.java b/core/src/main/java/org/elasticsearch/index/engine/Engine.java index 122587949e319..755d9db68b027 100644 --- a/core/src/main/java/org/elasticsearch/index/engine/Engine.java +++ b/core/src/main/java/org/elasticsearch/index/engine/Engine.java @@ -1415,6 +1415,14 @@ public interface Warmer { */ public abstract void deactivateThrottling(); + /** + * Fills up the local checkpoints history with no-ops until the local checkpoint + * and the max seen sequence ID are identical. + * @param primaryTerm the shards primary term this engine was created for + * @return the number of no-ops added + */ + public abstract int fillSequenceNumberHistory(long primaryTerm) throws IOException; + /** * Performs recovery from the transaction log. * This operation will close the engine if the recovery fails. diff --git a/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java b/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java index 8e1eb059caa5b..5d494ef7ff705 100644 --- a/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java +++ b/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java @@ -225,6 +225,28 @@ public InternalEngine(EngineConfig engineConfig) throws EngineException { logger.trace("created new InternalEngine"); } + @Override + public int fillSequenceNumberHistory(long primaryTerm) throws IOException { + try (ReleasableLock lock = writeLock.acquire()) { + ensureOpen(); + final long localCheckpoint = seqNoService.getLocalCheckpoint(); + final long maxSeqId = seqNoService.getMaxSeqNo(); + int numNoOpsAdded = 0; + for (long seqNo = localCheckpoint + 1; seqNo <= maxSeqId; + // the local checkpoint might have been advanced so we are leap-frogging + // to the next seq ID we need to process and create a noop for + seqNo = seqNoService.getLocalCheckpoint()+1) { + final NoOp noOp = new NoOp(seqNo, primaryTerm, Operation.Origin.PRIMARY, System.nanoTime(), "filling up seqNo history"); + innerNoOp(noOp); + numNoOpsAdded++; + assert seqNo <= seqNoService.getLocalCheckpoint() : "localCheckpoint didn't advanced used to be " + seqNo + " now it's on:" + + seqNoService.getLocalCheckpoint(); + + } + return numNoOpsAdded; + } + } + private void updateMaxUnsafeAutoIdTimestampFromWriter(IndexWriter writer) { long commitMaxUnsafeAutoIdTimestamp = Long.MIN_VALUE; for (Map.Entry entry : writer.getLiveCommitData()) { @@ -1071,6 +1093,7 @@ public NoOpResult noOp(final NoOp noOp) { } private NoOpResult innerNoOp(final NoOp noOp) throws IOException { + assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread(); assert noOp.seqNo() > SequenceNumbersService.NO_OPS_PERFORMED; final long seqNo = noOp.seqNo(); try { diff --git a/core/src/main/java/org/elasticsearch/index/mapper/SourceToParse.java b/core/src/main/java/org/elasticsearch/index/mapper/SourceToParse.java index a8a983ecde832..52e3001da84cd 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/SourceToParse.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/SourceToParse.java @@ -23,22 +23,15 @@ import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; public class SourceToParse { - public static SourceToParse source(String index, String type, String id, BytesReference source, XContentType contentType) { - return source(Origin.PRIMARY, index, type, id, source, contentType); - } - - public static SourceToParse source(Origin origin, String index, String type, String id, BytesReference source, + public static SourceToParse source(String index, String type, String id, BytesReference source, XContentType contentType) { - return new SourceToParse(origin, index, type, id, source, contentType); + return new SourceToParse(index, type, id, source, contentType); } - private final Origin origin; - private final BytesReference source; private final String index; @@ -53,8 +46,7 @@ public static SourceToParse source(Origin origin, String index, String type, Str private XContentType xContentType; - private SourceToParse(Origin origin, String index, String type, String id, BytesReference source, XContentType xContentType) { - this.origin = Objects.requireNonNull(origin); + private SourceToParse(String index, String type, String id, BytesReference source, XContentType xContentType) { this.index = Objects.requireNonNull(index); this.type = Objects.requireNonNull(type); this.id = Objects.requireNonNull(id); @@ -64,10 +56,6 @@ private SourceToParse(Origin origin, String index, String type, String id, Bytes this.xContentType = Objects.requireNonNull(xContentType); } - public Origin origin() { - return origin; - } - public BytesReference source() { return this.source; } diff --git a/core/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java b/core/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java index 032c033f175b1..5d5e17c19291b 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java +++ b/core/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java @@ -364,6 +364,8 @@ private void internalRecoverFromStore(IndexShard indexShard) throws IndexShardRe logger.debug("failed to list file details", e); } indexShard.performTranslogRecovery(indexShouldExists); + assert indexShard.shardRouting.primary() : "only primary shards can recover from store"; + indexShard.getEngine().fillSequenceNumberHistory(indexShard.getPrimaryTerm()); } indexShard.finalizeRecovery(); indexShard.postRecovery("post recovery from shard_store"); diff --git a/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java b/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java index 3792872c9e349..f1b981e14d220 100644 --- a/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java +++ b/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java @@ -114,6 +114,7 @@ import org.elasticsearch.index.mapper.RootObjectMapper; import org.elasticsearch.index.mapper.SeqNoFieldMapper; import org.elasticsearch.index.mapper.SourceFieldMapper; +import org.elasticsearch.index.mapper.SourceToParse; import org.elasticsearch.index.mapper.Uid; import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.seqno.SequenceNumbers; @@ -191,7 +192,7 @@ public class InternalEngineTests extends ESTestCase { - protected final ShardId shardId = new ShardId(new Index("index", "_na_"), 1); + protected final ShardId shardId = new ShardId(new Index("index", "_na_"), 0); private static final IndexSettings INDEX_SETTINGS = IndexSettingsModule.newIndexSettings("index", Settings.EMPTY); protected ThreadPool threadPool; @@ -1956,7 +1957,7 @@ private static class MockAppender extends AbstractAppender { @Override public void append(LogEvent event) { final String formattedMessage = event.getMessage().getFormattedMessage(); - if (event.getLevel() == Level.TRACE && event.getMarker().getName().contains("[index][1] ")) { + if (event.getLevel() == Level.TRACE && event.getMarker().getName().contains("[index][0] ")) { if (event.getLoggerName().endsWith(".IW") && formattedMessage.contains("IW: apply all deletes during flush")) { sawIndexWriterMessage = true; @@ -2335,7 +2336,7 @@ private Engine.Index indexForDoc(ParsedDocument doc) { private Engine.Index replicaIndexForDoc(ParsedDocument doc, long version, long seqNo, boolean isRetry) { - return new Engine.Index(newUid(doc), doc, seqNo, 1, version, VersionType.EXTERNAL, + return new Engine.Index(newUid(doc), doc, seqNo, 1, version, VersionType.EXTERNAL, Engine.Operation.Origin.REPLICA, System.nanoTime(), IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, isRetry); } @@ -3836,4 +3837,79 @@ private Tuple getSequenceID(Engine engine, Engine.Get get) throws En } } + public void testFillUpSequenceIdGapsOnRecovery() throws IOException { + final int docs = randomIntBetween(1, 32); + int numDocsOnReplica = 0; + long maxSeqIDOnReplica = -1; + long checkpointOnReplica; + try { + for (int i = 0; i < docs; i++) { + final String docId = Integer.toString(i); + final ParsedDocument doc = + testParsedDocument(docId, "test", null, testDocumentWithTextField(), SOURCE, null); + Engine.Index primaryResponse = indexForDoc(doc); + Engine.IndexResult indexResult = engine.index(primaryResponse); + if (randomBoolean()) { + numDocsOnReplica++; + maxSeqIDOnReplica = indexResult.getSeqNo(); + replicaEngine.index(replicaIndexForDoc(doc, 1, indexResult.getSeqNo(), false)); + } + } + checkpointOnReplica = replicaEngine.seqNoService().getLocalCheckpoint(); + } finally { + IOUtils.close(replicaEngine); + } + + + boolean flushed = false; + Engine recoveringEngine = null; + try { + assertEquals(docs-1, engine.seqNoService().getMaxSeqNo()); + assertEquals(docs-1, engine.seqNoService().getLocalCheckpoint()); + assertEquals(maxSeqIDOnReplica, replicaEngine.seqNoService().getMaxSeqNo()); + assertEquals(checkpointOnReplica, replicaEngine.seqNoService().getLocalCheckpoint()); + recoveringEngine = new InternalEngine(copy(replicaEngine.config(), EngineConfig.OpenMode.OPEN_INDEX_AND_TRANSLOG)); + assertEquals(numDocsOnReplica, recoveringEngine.getTranslog().totalOperations()); + recoveringEngine.recoverFromTranslog(); + assertEquals(maxSeqIDOnReplica, recoveringEngine.seqNoService().getMaxSeqNo()); + assertEquals(checkpointOnReplica, recoveringEngine.seqNoService().getLocalCheckpoint()); + assertEquals((maxSeqIDOnReplica+1) - numDocsOnReplica, recoveringEngine.fillSequenceNumberHistory(2)); + + // now snapshot the tlog and ensure the primary term is updated + Translog.Snapshot snapshot = recoveringEngine.getTranslog().newSnapshot(); + assertTrue((maxSeqIDOnReplica+1) - numDocsOnReplica <= snapshot.totalOperations()); + Translog.Operation operation; + while((operation = snapshot.next()) != null) { + if (operation.opType() == Translog.Operation.Type.NO_OP) { + assertEquals(2, operation.primaryTerm()); + } else { + assertEquals(1, operation.primaryTerm()); + } + + } + assertEquals(maxSeqIDOnReplica, recoveringEngine.seqNoService().getMaxSeqNo()); + assertEquals(maxSeqIDOnReplica, recoveringEngine.seqNoService().getLocalCheckpoint()); + if ((flushed = randomBoolean())) { + recoveringEngine.flush(true, true); + } + } finally { + IOUtils.close(recoveringEngine); + } + + // now do it again to make sure we preserve values etc. + try { + recoveringEngine = new InternalEngine(copy(replicaEngine.config(), EngineConfig.OpenMode.OPEN_INDEX_AND_TRANSLOG)); + if (flushed) { + assertEquals(0, recoveringEngine.getTranslog().totalOperations()); + } + recoveringEngine.recoverFromTranslog(); + assertEquals(maxSeqIDOnReplica, recoveringEngine.seqNoService().getMaxSeqNo()); + assertEquals(maxSeqIDOnReplica, recoveringEngine.seqNoService().getLocalCheckpoint()); + assertEquals(0, recoveringEngine.fillSequenceNumberHistory(3)); + assertEquals(maxSeqIDOnReplica, recoveringEngine.seqNoService().getMaxSeqNo()); + assertEquals(maxSeqIDOnReplica, recoveringEngine.seqNoService().getLocalCheckpoint()); + } finally { + IOUtils.close(recoveringEngine); + } + } } diff --git a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java index eccd958c36e20..3f01a0c0a9ae2 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java +++ b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java @@ -38,6 +38,7 @@ import org.elasticsearch.action.admin.indices.stats.CommonStats; import org.elasticsearch.action.admin.indices.stats.CommonStatsFlags; import org.elasticsearch.action.admin.indices.stats.ShardStats; +import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MappingMetaData; @@ -77,6 +78,7 @@ import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.SeqNoFieldMapper; +import org.elasticsearch.index.mapper.SourceToParse; import org.elasticsearch.index.mapper.Uid; import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.seqno.SequenceNumbersService; @@ -896,6 +898,46 @@ public void testRecoverFromStore() throws IOException { closeShards(newShard); } + /* This test just verifies that we fill up local checkpoint up to max seen seqID on primary recovery */ + public void testRecoverFromStoreWithNoOps() throws IOException { + final IndexShard shard = newStartedShard(true); + indexDoc(shard, "test", "0"); + Engine.Index test = indexDoc(shard, "test", "1"); + // start a replica shard and index the second doc + final IndexShard otherShard = newStartedShard(false); + test = otherShard.prepareIndexOnReplica( + SourceToParse.source(shard.shardId().getIndexName(), test.type(), test.id(), test.source(), + XContentType.JSON), + 1, 1, VersionType.EXTERNAL, IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, false); + otherShard.index(test); + + final ShardRouting primaryShardRouting = shard.routingEntry(); + IndexShard newShard = reinitShard(otherShard, ShardRoutingHelper.initWithSameId(primaryShardRouting, + RecoverySource.StoreRecoverySource.EXISTING_STORE_INSTANCE)); + DiscoveryNode localNode = new DiscoveryNode("foo", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT); + newShard.markAsRecovering("store", new RecoveryState(newShard.routingEntry(), localNode, null)); + assertTrue(newShard.recoverFromStore()); + assertEquals(1, newShard.recoveryState().getTranslog().recoveredOperations()); + assertEquals(1, newShard.recoveryState().getTranslog().totalOperations()); + assertEquals(1, newShard.recoveryState().getTranslog().totalOperationsOnStart()); + assertEquals(100.0f, newShard.recoveryState().getTranslog().recoveredPercent(), 0.01f); + Translog.Snapshot snapshot = newShard.getTranslog().newSnapshot(); + Translog.Operation operation; + int numNoops = 0; + while((operation = snapshot.next()) != null) { + if (operation.opType() == Translog.Operation.Type.NO_OP) { + numNoops++; + assertEquals(1, operation.primaryTerm()); + assertEquals(0, operation.seqNo()); + } + } + assertEquals(1, numNoops); + newShard.updateRoutingEntry(newShard.routingEntry().moveToStarted()); + assertDocCount(newShard, 1); + assertDocCount(shard, 2); + closeShards(newShard, shard); + } + public void testRecoverFromCleanStore() throws IOException { final IndexShard shard = newStartedShard(true); indexDoc(shard, "test", "0"); diff --git a/core/src/test/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetServiceTests.java b/core/src/test/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetServiceTests.java index bbe4d8ed12e80..261e53064fe01 100644 --- a/core/src/test/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetServiceTests.java +++ b/core/src/test/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetServiceTests.java @@ -44,7 +44,7 @@ public void testGetStartingSeqNo() throws Exception { long seqNo = 0; for (int i = 0; i < docs; i++) { Engine.Index indexOp = replica.prepareIndexOnReplica( - SourceToParse.source(SourceToParse.Origin.REPLICA, index, "type", "doc_" + i, new BytesArray("{}"), XContentType.JSON), + SourceToParse.source(index, "type", "doc_" + i, new BytesArray("{}"), XContentType.JSON), seqNo++, 1, VersionType.EXTERNAL, IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, false); replica.index(indexOp); if (rarely()) { diff --git a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java index 4062666ddbbf8..65576dcf0a2b5 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java @@ -484,7 +484,7 @@ protected Engine.Index indexDoc(IndexShard shard, String type, String id, String final Engine.Index index; if (shard.routingEntry().primary()) { index = shard.prepareIndexOnPrimary( - SourceToParse.source(SourceToParse.Origin.PRIMARY, shard.shardId().getIndexName(), type, id, new BytesArray(source), + SourceToParse.source(shard.shardId().getIndexName(), type, id, new BytesArray(source), xContentType), Versions.MATCH_ANY, VersionType.INTERNAL, @@ -492,7 +492,7 @@ protected Engine.Index indexDoc(IndexShard shard, String type, String id, String false); } else { index = shard.prepareIndexOnReplica( - SourceToParse.source(SourceToParse.Origin.PRIMARY, shard.shardId().getIndexName(), type, id, new BytesArray(source), + SourceToParse.source(shard.shardId().getIndexName(), type, id, new BytesArray(source), xContentType), randomInt(1 << 10), 1, VersionType.EXTERNAL, IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, false); } From fe91c721519ffe706ee44e375adcad8d5a303e66 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Fri, 21 Apr 2017 15:50:44 -0400 Subject: [PATCH 036/619] Use a marker file when removing a plugin Today when removing a plugin, we attempt to move the plugin directory to a temporary directory and then delete that directory from the filesystem. We do this to avoid a plugin being in a half-removed state. We previously tried an atomic move, and fell back to a non-atomic move if that failed. Atomic moves can fail on union filesystems when the plugin directory is not in the top layer of the filesystem. Interestingly, the regular move can fail as well. This is because when the JDK is executing such a move, it first tries to rename the source directory to the target directory and if this fails with EXDEV (as in the case of an atomic move failing), it falls back to copying the source to the target, and then attempts to rmdir the source. The bug here is that the JDK never deleted the contents of the source so the rmdir will always fail (except in the case of an empty directory). Given all this silliness, we were inspired to find a different strategy. The strategy is simple. We will add a marker file to the plugin directory that indicates the plugin is in a state of removal. This file will be the last file out the door during removal. If this file exists during startup, we fail startup. Relates #24252 --- .../elasticsearch/plugins/PluginsService.java | 20 +++++--- .../plugins/PluginsServiceTests.java | 30 ++++++++++++ .../plugins/RemovePluginCommand.java | 49 +++++++++++-------- .../plugins/RemovePluginCommandTests.java | 9 +++- 4 files changed, 80 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/plugins/PluginsService.java b/core/src/main/java/org/elasticsearch/plugins/PluginsService.java index 874c338ff8964..ae2f330b71785 100644 --- a/core/src/main/java/org/elasticsearch/plugins/PluginsService.java +++ b/core/src/main/java/org/elasticsearch/plugins/PluginsService.java @@ -20,8 +20,6 @@ package org.elasticsearch.plugins; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.apache.logging.log4j.util.Supplier; import org.apache.lucene.analysis.util.CharFilterFactory; import org.apache.lucene.analysis.util.TokenFilterFactory; import org.apache.lucene.analysis.util.TokenizerFactory; @@ -36,7 +34,6 @@ import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.common.inject.Module; -import org.elasticsearch.common.io.FileSystemUtils; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; @@ -45,21 +42,19 @@ import org.elasticsearch.threadpool.ExecutorBuilder; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -310,6 +305,19 @@ static Set getPluginBundles(Path pluginsDirectory) throws IOException { throw new IllegalStateException("Could not load plugin descriptor for existing plugin [" + plugin.getFileName() + "]. Was the plugin built before 2.0?", e); } + /* + * Check for the existence of a marker file that indicates the plugin is in a garbage state from a failed attempt to remove + * the plugin. + */ + final Path removing = plugin.resolve(".removing-" + info.getName()); + if (Files.exists(removing)) { + final String message = String.format( + Locale.ROOT, + "found file [%s] from a failed attempt to remove the plugin [%s]; execute [elasticsearch-plugin remove %2$s]", + removing, + info.getName()); + throw new IllegalStateException(message); + } Set urls = new LinkedHashSet<>(); try (DirectoryStream jarStream = Files.newDirectoryStream(plugin, "*.jar")) { diff --git a/core/src/test/java/org/elasticsearch/plugins/PluginsServiceTests.java b/core/src/test/java/org/elasticsearch/plugins/PluginsServiceTests.java index f4aae5232ce81..89c65ad2c8d56 100644 --- a/core/src/test/java/org/elasticsearch/plugins/PluginsServiceTests.java +++ b/core/src/test/java/org/elasticsearch/plugins/PluginsServiceTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.plugins; import org.apache.lucene.util.LuceneTestCase; +import org.elasticsearch.Version; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexModule; @@ -30,6 +31,7 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.List; +import java.util.Locale; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasToString; @@ -121,4 +123,32 @@ public void testHiddenFiles() throws IOException { assertThat(e, hasToString(containsString(expected))); } + public void testStartupWithRemovingMarker() throws IOException { + final Path home = createTempDir(); + final Settings settings = + Settings.builder() + .put(Environment.PATH_HOME_SETTING.getKey(), home) + .build(); + final Path fake = home.resolve("plugins").resolve("fake"); + Files.createDirectories(fake); + Files.createFile(fake.resolve("plugin.jar")); + final Path removing = fake.resolve(".removing-fake"); + Files.createFile(fake.resolve(".removing-fake")); + PluginTestUtil.writeProperties( + fake, + "description", "fake", + "name", "fake", + "version", "1.0.0", + "elasticsearch.version", Version.CURRENT.toString(), + "java.version", System.getProperty("java.specification.version"), + "classname", "Fake", + "has.native.controller", "false"); + final IllegalStateException e = expectThrows(IllegalStateException.class, () -> newPluginsService(settings)); + final String expected = String.format( + Locale.ROOT, + "found file [%s] from a failed attempt to remove the plugin [fake]; execute [elasticsearch-plugin remove fake]", + removing); + assertThat(e, hasToString(containsString(expected))); + } + } diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java index 8e81f97d84ce4..2d2fd8efe58e3 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java @@ -19,15 +19,6 @@ package org.elasticsearch.plugins; -import java.io.IOException; -import java.nio.file.AtomicMoveNotSupportedException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - import joptsimple.OptionSet; import joptsimple.OptionSpec; import org.apache.lucene.util.IOUtils; @@ -38,6 +29,14 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.env.Environment; +import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + import static org.elasticsearch.cli.Terminal.Verbosity.VERBOSE; /** @@ -101,20 +100,31 @@ void execute(final Terminal terminal, final String pluginName, final Environment } terminal.println(VERBOSE, "removing [" + pluginDir + "]"); - final Path tmpPluginDir = env.pluginsFile().resolve(".removing-" + pluginName); + /* + * We are going to create a marker file in the plugin directory that indicates that this plugin is a state of removal. If the + * removal fails, the existence of this marker file indicates that the plugin is in a garbage state. We check for existence of this + * marker file during startup so that we do not startup with plugins in such a garbage state. + */ + final Path removing = pluginDir.resolve(".removing-" + pluginName); + /* + * Add the contents of the plugin directory before creating the marker file and adding it to the list of paths to be deleted so + * that the marker file is the last file to be deleted. + */ + Files.list(pluginDir).forEach(pluginPaths::add); try { - Files.move(pluginDir, tmpPluginDir, StandardCopyOption.ATOMIC_MOVE); - } catch (final AtomicMoveNotSupportedException e) { + Files.createFile(removing); + } catch (final FileAlreadyExistsException e) { /* - * On a union file system if the plugin that we are removing is not installed on the - * top layer then atomic move will not be supported. In this case, we fall back to a - * non-atomic move. + * We need to suppress the marker file already existing as we could be in this state if a previous removal attempt failed and + * the user is attempting to remove the plugin again. */ - Files.move(pluginDir, tmpPluginDir); + terminal.println(VERBOSE, "marker file [" + removing + "] already exists"); } - pluginPaths.add(tmpPluginDir); - + // now add the marker file + pluginPaths.add(removing); IOUtils.rm(pluginPaths.toArray(new Path[pluginPaths.size()])); + // at this point, the plugin directory is empty and we can execute a simple directory removal + Files.delete(pluginDir); /* * We preserve the config files in case the user is upgrading the plugin, but we print a @@ -124,8 +134,7 @@ void execute(final Terminal terminal, final String pluginName, final Environment if (Files.exists(pluginConfigDir)) { final String message = String.format( Locale.ROOT, - "-> preserving plugin config files [%s] in case of upgrade; " - + "delete manually if not needed", + "-> preserving plugin config files [%s] in case of upgrade; delete manually if not needed", pluginConfigDir); terminal.println(message); } diff --git a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/RemovePluginCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/RemovePluginCommandTests.java index a42e66fe872f7..25fbf60fa4e9f 100644 --- a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/RemovePluginCommandTests.java +++ b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/RemovePluginCommandTests.java @@ -34,8 +34,6 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; @@ -159,6 +157,13 @@ public void testMissingPluginName() throws Exception { assertEquals("plugin name is required", e.getMessage()); } + public void testRemoveWhenRemovingMarker() throws Exception { + Files.createDirectory(env.pluginsFile().resolve("fake")); + Files.createFile(env.pluginsFile().resolve("fake").resolve("plugin.jar")); + Files.createFile(env.pluginsFile().resolve("fake").resolve(".removing-fake")); + removePlugin("fake", home); + } + private String expectedConfigDirPreservedMessage(final Path configDir) { return "-> preserving plugin config files [" + configDir + "] in case of upgrade; delete manually if not needed"; } From 8c0329d915191961344d972227bc7ae8e70f3658 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Fri, 21 Apr 2017 16:46:35 -0400 Subject: [PATCH 037/619] Temporarily change Wildfly artifact location This commit temporarily changes the Wildfly artifact location to prevent builds from failing due to a Red Hat datacenter outage. --- qa/wildfly/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/wildfly/build.gradle b/qa/wildfly/build.gradle index 66a141f359765..080e460e4f6d4 100644 --- a/qa/wildfly/build.gradle +++ b/qa/wildfly/build.gradle @@ -42,7 +42,7 @@ repositories { mavenCentral() // the Wildfly distribution is not available via a repository, so we fake an Ivy repository on top of the download site ivy { - url "http://download.jboss.org" + url "http://download.elastic.co" layout 'pattern', { artifact 'wildfly/[revision]/[module]-[revision].[ext]' } From 108d8905e290e6c8c9c7fbaaf13925792abd74fd Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Fri, 21 Apr 2017 18:22:55 -0400 Subject: [PATCH 038/619] Add note to docs regarding JAVA_HOME on Windows For the Windows service, JAVA_HOME should be set to the path to the JDK. We should make this clear in the docs to help users avoid frustrating startup problems. Relates #24260 --- docs/reference/setup/install/windows.asciidoc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/reference/setup/install/windows.asciidoc b/docs/reference/setup/install/windows.asciidoc index f56e4cf57c38a..5595a38ff61aa 100644 --- a/docs/reference/setup/install/windows.asciidoc +++ b/docs/reference/setup/install/windows.asciidoc @@ -121,7 +121,12 @@ The service 'elasticsearch-service-x64' has been installed. NOTE: While a JRE can be used for the Elasticsearch service, due to its use of a client VM (as opposed to a server JVM which offers better performance for long-running applications) its usage is discouraged and a warning will be issued. -NOTE: Upgrading (or downgrading) JVM versions does not require the service to be reinstalled. However, upgrading across JVM types (e.g. JRE versus SE) is not supported, and does require the service to be reinstalled. +NOTE: The system environment variable `JAVA_HOME` should be set to the path to +the JDK installation that you want the service to use. If you upgrade the JDK, +you are not required to the reinstall the service but you must set the value of +the system environment variable `JAVA_HOME` to the path to the new JDK +installation. However, upgrading across JVM types (e.g. JRE versus SE) is not +supported, and does require the service to be reinstalled. [[windows-service-settings]] [float] From 447f307ebb41cf8acdb387005c56f9ce540e120b Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Fri, 21 Apr 2017 18:56:04 -0400 Subject: [PATCH 039/619] Fix _bulk response when it can't create an index (#24048) Before #22488 when an index couldn't be created during a `_bulk` operation we'd do all the *other* actions and return the index creation error on each failing action. In #22488 we accidentally changed it so that we now reject the entire bulk request if a single action cannot create an index that it must create to run. This gets reverts to the old behavior while still keeping the nicer error messages. Instead of failing the entire request we now only fail the portions of the request that can't work because the index doesn't exist. Closes #24028 --- .../action/bulk/TransportBulkAction.java | 118 ++++++++++------- ...ActionIndicesThatCannotBeCreatedTests.java | 125 ++++++++++++++++++ .../bulk/TransportBulkActionIngestTests.java | 10 +- .../bulk/TransportBulkActionTookTests.java | 12 +- .../mapper/DynamicMappingDisabledTests.java | 15 ++- settings.gradle | 3 +- 6 files changed, 220 insertions(+), 63 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIndicesThatCannotBeCreatedTests.java diff --git a/core/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java b/core/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java index a9b82c514a600..16c22524e2d60 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java @@ -52,19 +52,19 @@ import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.common.util.concurrent.AtomicArray; -import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.IndexClosedException; -import org.elasticsearch.node.NodeClosedException; import org.elasticsearch.ingest.IngestService; +import org.elasticsearch.node.NodeClosedException; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -75,6 +75,8 @@ import java.util.function.LongSupplier; import java.util.stream.Collectors; +import static java.util.Collections.emptyMap; + /** * Groups bulk request items by shard, optionally creating non-existent indices and * delegates to {@link TransportShardBulkAction} for shard-level bulk execution @@ -139,30 +141,46 @@ protected void doExecute(Task task, BulkRequest bulkRequest, ActionListener responses = new AtomicArray<>(bulkRequest.requests.size()); if (needToCheck()) { - // Keep track of all unique indices and all unique types per index for the create index requests: - final Set autoCreateIndices = bulkRequest.requests.stream() + // Attempt to create all the indices that we're going to need during the bulk before we start. + // Step 1: collect all the indices in the request + final Set indices = bulkRequest.requests.stream() .map(DocWriteRequest::index) .collect(Collectors.toSet()); - final AtomicInteger counter = new AtomicInteger(autoCreateIndices.size()); + /* Step 2: filter that to indices that don't exist and we can create. At the same time build a map of indices we can't create + * that we'll use when we try to run the requests. */ + final Map indicesThatCannotBeCreated = new HashMap<>(); + Set autoCreateIndices = new HashSet<>(); ClusterState state = clusterService.state(); - for (String index : autoCreateIndices) { - if (shouldAutoCreate(index, state)) { - CreateIndexRequest createIndexRequest = new CreateIndexRequest(); - createIndexRequest.index(index); - createIndexRequest.cause("auto(bulk api)"); - createIndexRequest.masterNodeTimeout(bulkRequest.timeout()); - createIndexAction.execute(createIndexRequest, new ActionListener() { + for (String index : indices) { + boolean shouldAutoCreate; + try { + shouldAutoCreate = shouldAutoCreate(index, state); + } catch (IndexNotFoundException e) { + shouldAutoCreate = false; + indicesThatCannotBeCreated.put(index, e); + } + if (shouldAutoCreate) { + autoCreateIndices.add(index); + } + } + // Step 3: create all the indices that are missing, if there are any missing. start the bulk after all the creates come back. + if (autoCreateIndices.isEmpty()) { + executeBulk(task, bulkRequest, startTime, listener, responses, indicesThatCannotBeCreated); + } else { + final AtomicInteger counter = new AtomicInteger(autoCreateIndices.size()); + for (String index : autoCreateIndices) { + createIndex(index, bulkRequest.timeout(), new ActionListener() { @Override public void onResponse(CreateIndexResponse result) { if (counter.decrementAndGet() == 0) { - executeBulk(task, bulkRequest, startTime, listener, responses); + executeBulk(task, bulkRequest, startTime, listener, responses, indicesThatCannotBeCreated); } } @Override public void onFailure(Exception e) { if (!(ExceptionsHelper.unwrapCause(e) instanceof ResourceAlreadyExistsException)) { - // fail all requests involving this index, if create didnt work + // fail all requests involving this index, if create didn't work for (int i = 0; i < bulkRequest.requests.size(); i++) { DocWriteRequest request = bulkRequest.requests.get(i); if (request != null && setResponseFailureIfIndexMatches(responses, i, request, index, e)) { @@ -174,18 +192,14 @@ public void onFailure(Exception e) { executeBulk(task, bulkRequest, startTime, ActionListener.wrap(listener::onResponse, inner -> { inner.addSuppressed(e); listener.onFailure(inner); - }), responses); + }), responses, indicesThatCannotBeCreated); } } }); - } else { - if (counter.decrementAndGet() == 0) { - executeBulk(task, bulkRequest, startTime, listener, responses); - } } } } else { - executeBulk(task, bulkRequest, startTime, listener, responses); + executeBulk(task, bulkRequest, startTime, listener, responses, emptyMap()); } } @@ -197,6 +211,14 @@ boolean shouldAutoCreate(String index, ClusterState state) { return autoCreateIndex.shouldAutoCreate(index, state); } + void createIndex(String index, TimeValue timeout, ActionListener listener) { + CreateIndexRequest createIndexRequest = new CreateIndexRequest(); + createIndexRequest.index(index); + createIndexRequest.cause("auto(bulk api)"); + createIndexRequest.masterNodeTimeout(timeout); + createIndexAction.execute(createIndexRequest, listener); + } + private boolean setResponseFailureIfIndexMatches(AtomicArray responses, int idx, DocWriteRequest request, String index, Exception e) { if (index.equals(request.index())) { responses.set(idx, new BulkItemResponse(idx, request.opType(), new BulkItemResponse.Failure(request.index(), request.type(), request.id(), e))); @@ -220,14 +242,16 @@ private final class BulkOperation extends AbstractRunnable { private final AtomicArray responses; private final long startTimeNanos; private final ClusterStateObserver observer; + private final Map indicesThatCannotBeCreated; - BulkOperation(Task task, BulkRequest bulkRequest, ActionListener listener, - AtomicArray responses, long startTimeNanos) { + BulkOperation(Task task, BulkRequest bulkRequest, ActionListener listener, AtomicArray responses, + long startTimeNanos, Map indicesThatCannotBeCreated) { this.task = task; this.bulkRequest = bulkRequest; this.listener = listener; this.responses = responses; this.startTimeNanos = startTimeNanos; + this.indicesThatCannotBeCreated = indicesThatCannotBeCreated; this.observer = new ClusterStateObserver(clusterService, bulkRequest.timeout(), logger, threadPool.getThreadContext()); } @@ -250,7 +274,7 @@ protected void doRun() throws Exception { if (docWriteRequest == null) { continue; } - if (addFailureIfIndexIsUnavailable(docWriteRequest, bulkRequest, responses, i, concreteIndices, metaData)) { + if (addFailureIfIndexIsUnavailable(docWriteRequest, i, concreteIndices, metaData)) { continue; } Index concreteIndex = concreteIndices.resolveIfAbsent(docWriteRequest); @@ -393,42 +417,44 @@ public void onTimeout(TimeValue timeout) { } }); } - } - - void executeBulk(Task task, final BulkRequest bulkRequest, final long startTimeNanos, final ActionListener listener, final AtomicArray responses ) { - new BulkOperation(task, bulkRequest, listener, responses, startTimeNanos).run(); - } - private boolean addFailureIfIndexIsUnavailable(DocWriteRequest request, BulkRequest bulkRequest, AtomicArray responses, int idx, - final ConcreteIndices concreteIndices, - final MetaData metaData) { - Index concreteIndex = concreteIndices.getConcreteIndex(request.index()); - Exception unavailableException = null; - if (concreteIndex == null) { - try { - concreteIndex = concreteIndices.resolveIfAbsent(request); - } catch (IndexClosedException | IndexNotFoundException ex) { - // Fix for issue where bulk request references an index that - // cannot be auto-created see issue #8125 - unavailableException = ex; + private boolean addFailureIfIndexIsUnavailable(DocWriteRequest request, int idx, final ConcreteIndices concreteIndices, + final MetaData metaData) { + IndexNotFoundException cannotCreate = indicesThatCannotBeCreated.get(request.index()); + if (cannotCreate != null) { + addFailure(request, idx, cannotCreate); + return true; + } + Index concreteIndex = concreteIndices.getConcreteIndex(request.index()); + if (concreteIndex == null) { + try { + concreteIndex = concreteIndices.resolveIfAbsent(request); + } catch (IndexClosedException | IndexNotFoundException ex) { + addFailure(request, idx, ex); + return true; + } } - } - if (unavailableException == null) { IndexMetaData indexMetaData = metaData.getIndexSafe(concreteIndex); if (indexMetaData.getState() == IndexMetaData.State.CLOSE) { - unavailableException = new IndexClosedException(concreteIndex); + addFailure(request, idx, new IndexClosedException(concreteIndex)); + return true; } + return false; } - if (unavailableException != null) { + + private void addFailure(DocWriteRequest request, int idx, Exception unavailableException) { BulkItemResponse.Failure failure = new BulkItemResponse.Failure(request.index(), request.type(), request.id(), unavailableException); BulkItemResponse bulkItemResponse = new BulkItemResponse(idx, request.opType(), failure); responses.set(idx, bulkItemResponse); // make sure the request gets never processed again bulkRequest.requests.set(idx, null); - return true; } - return false; + } + + void executeBulk(Task task, final BulkRequest bulkRequest, final long startTimeNanos, final ActionListener listener, + final AtomicArray responses, Map indicesThatCannotBeCreated) { + new BulkOperation(task, bulkRequest, listener, responses, startTimeNanos, indicesThatCannotBeCreated).run(); } private static class ConcreteIndices { diff --git a/core/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIndicesThatCannotBeCreatedTests.java b/core/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIndicesThatCannotBeCreatedTests.java new file mode 100644 index 0000000000000..a02939ad2063a --- /dev/null +++ b/core/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIndicesThatCannotBeCreatedTests.java @@ -0,0 +1,125 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.action.bulk; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; +import org.elasticsearch.action.delete.DeleteRequest; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.update.UpdateRequest; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.util.concurrent.AtomicArray; +import org.elasticsearch.index.IndexNotFoundException; +import org.elasticsearch.tasks.Task; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.transport.TransportService; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; +import static org.mockito.Mockito.mock; + +public class TransportBulkActionIndicesThatCannotBeCreatedTests extends ESTestCase { + public void testNonExceptional() { + BulkRequest bulkRequest = new BulkRequest(); + bulkRequest.add(new IndexRequest(randomAlphaOfLength(5))); + bulkRequest.add(new IndexRequest(randomAlphaOfLength(5))); + bulkRequest.add(new DeleteRequest(randomAlphaOfLength(5))); + bulkRequest.add(new UpdateRequest(randomAlphaOfLength(5), randomAlphaOfLength(5), randomAlphaOfLength(5))); + // Test emulating auto_create_index=false + indicesThatCannotBeCreatedTestCase(emptySet(), bulkRequest, null); + // Test emulating auto_create_index=true + indicesThatCannotBeCreatedTestCase(emptySet(), bulkRequest, index -> true); + // Test emulating all indices already created + indicesThatCannotBeCreatedTestCase(emptySet(), bulkRequest, index -> false); + // Test emulating auto_create_index=true with some indices already created. + indicesThatCannotBeCreatedTestCase(emptySet(), bulkRequest, index -> randomBoolean()); + } + + public void testAllFail() { + BulkRequest bulkRequest = new BulkRequest(); + bulkRequest.add(new IndexRequest("no")); + bulkRequest.add(new IndexRequest("can't")); + bulkRequest.add(new DeleteRequest("do")); + bulkRequest.add(new UpdateRequest("nothin", randomAlphaOfLength(5), randomAlphaOfLength(5))); + indicesThatCannotBeCreatedTestCase(new HashSet<>(Arrays.asList("no", "can't", "do", "nothin")), bulkRequest, index -> { + throw new IndexNotFoundException("Can't make it because I say so"); + }); + } + + public void testSomeFail() { + BulkRequest bulkRequest = new BulkRequest(); + bulkRequest.add(new IndexRequest("ok")); + bulkRequest.add(new IndexRequest("bad")); + // Emulate auto_create_index=-bad,+* + indicesThatCannotBeCreatedTestCase(singleton("bad"), bulkRequest, index -> { + if (index.equals("bad")) { + throw new IndexNotFoundException("Can't make it because I say so"); + } + return true; + }); + // Emulate auto_create_index=false but the "ok" index already exists + indicesThatCannotBeCreatedTestCase(singleton("bad"), bulkRequest, index -> { + if (index.equals("bad")) { + throw new IndexNotFoundException("Can't make it because I say so"); + } + return false; + }); + } + + + private void indicesThatCannotBeCreatedTestCase(Set expected, + BulkRequest bulkRequest, Function shouldAutoCreate) { + TransportBulkAction action = new TransportBulkAction(Settings.EMPTY, null, mock(TransportService.class), mock(ClusterService.class), + null, null, null, mock(ActionFilters.class), null, null) { + @Override + void executeBulk(Task task, BulkRequest bulkRequest, long startTimeNanos, ActionListener listener, + AtomicArray responses, Map indicesThatCannotBeCreated) { + assertEquals(expected, indicesThatCannotBeCreated.keySet()); + } + + @Override + boolean needToCheck() { + return null != shouldAutoCreate; // Use "null" to mean "no indices can be created so don't bother checking" + } + + @Override + boolean shouldAutoCreate(String index, ClusterState state) { + return shouldAutoCreate.apply(index); + } + + @Override + void createIndex(String index, TimeValue timeout, ActionListener listener) { + // If we try to create an index just immediately assume it worked + listener.onResponse(new CreateIndexResponse(true, true) {}); + } + }; + action.doExecute(null, bulkRequest, null); + } +} diff --git a/core/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java b/core/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java index b2483bd930658..e6e18fb567da3 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java @@ -28,15 +28,13 @@ import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateApplier; -import org.elasticsearch.cluster.action.shard.ShardStateAction; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.AtomicArray; -import org.elasticsearch.indices.IndicesService; +import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.ingest.IngestService; import org.elasticsearch.ingest.PipelineExecutionService; import org.elasticsearch.tasks.Task; @@ -51,10 +49,10 @@ import java.util.Collections; import java.util.Iterator; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiConsumer; import java.util.function.Consumer; -import java.util.function.Supplier; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.sameInstance; @@ -114,8 +112,8 @@ protected boolean needToCheck() { return false; } @Override - void executeBulk(Task task, final BulkRequest bulkRequest, final long startTimeNanos, - final ActionListener listener, final AtomicArray responses) { + void executeBulk(Task task, final BulkRequest bulkRequest, final long startTimeNanos, final ActionListener listener, + final AtomicArray responses, Map indicesThatCannotBeCreated) { isExecuted = true; } } diff --git a/core/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTookTests.java b/core/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTookTests.java index 0e3d96b5e8eb3..e35f98e220e03 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTookTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTookTests.java @@ -36,6 +36,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.AtomicArray; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.tasks.Task; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.transport.CapturingTransport; @@ -49,6 +50,7 @@ import java.nio.charset.StandardCharsets; import java.util.HashSet; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.function.LongSupplier; @@ -125,9 +127,10 @@ void executeBulk( BulkRequest bulkRequest, long startTimeNanos, ActionListener listener, - AtomicArray responses) { + AtomicArray responses, + Map indicesThatCannotBeCreated) { expected.set(1000000); - super.executeBulk(task, bulkRequest, startTimeNanos, listener, responses); + super.executeBulk(task, bulkRequest, startTimeNanos, listener, responses, indicesThatCannotBeCreated); } }; } else { @@ -149,10 +152,11 @@ void executeBulk( BulkRequest bulkRequest, long startTimeNanos, ActionListener listener, - AtomicArray responses) { + AtomicArray responses, + Map indicesThatCannotBeCreated) { long elapsed = spinForAtLeastOneMillisecond(); expected.set(elapsed); - super.executeBulk(task, bulkRequest, startTimeNanos, listener, responses); + super.executeBulk(task, bulkRequest, startTimeNanos, listener, responses, indicesThatCannotBeCreated); } }; } diff --git a/core/src/test/java/org/elasticsearch/index/mapper/DynamicMappingDisabledTests.java b/core/src/test/java/org/elasticsearch/index/mapper/DynamicMappingDisabledTests.java index 46749e792ed7d..3928dc78c8435 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/DynamicMappingDisabledTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/DynamicMappingDisabledTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.index.mapper; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.bulk.BulkItemResponse; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.bulk.TransportBulkAction; @@ -114,22 +115,24 @@ public void testDynamicDisabled() { request.source(Requests.INDEX_CONTENT_TYPE, "foo", 3); BulkRequest bulkRequest = new BulkRequest(); bulkRequest.add(request); - final AtomicBoolean onFailureCalled = new AtomicBoolean(); + final AtomicBoolean gotResponse = new AtomicBoolean(); transportBulkAction.execute(bulkRequest, new ActionListener() { @Override public void onResponse(BulkResponse bulkResponse) { - fail("onResponse shouldn't be called"); + BulkItemResponse itemResponse = bulkResponse.getItems()[0]; + assertTrue(itemResponse.isFailed()); + assertThat(itemResponse.getFailure().getCause(), instanceOf(IndexNotFoundException.class)); + assertEquals("no such index and [index.mapper.dynamic] is [false]", itemResponse.getFailure().getCause().getMessage()); + gotResponse.set(true); } @Override public void onFailure(Exception e) { - onFailureCalled.set(true); - assertThat(e, instanceOf(IndexNotFoundException.class)); - assertEquals("no such index and [index.mapper.dynamic] is [false]", e.getMessage()); + fail("unexpected failure in bulk action, expected failed bulk item"); } }); - assertTrue(onFailureCalled.get()); + assertTrue(gotResponse.get()); } } diff --git a/settings.gradle b/settings.gradle index 2f1fcfaed3504..c67c44dd2fff4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -57,11 +57,12 @@ List projects = [ 'plugins:repository-s3', 'plugins:jvm-example', 'plugins:store-smb', + 'qa:auto-create-index', 'qa:backwards-5.0', 'qa:evil-tests', + 'qa:multi-cluster-search', 'qa:no-bootstrap-tests', 'qa:rolling-upgrade', - 'qa:multi-cluster-search', 'qa:smoke-test-client', 'qa:smoke-test-http', 'qa:smoke-test-ingest-with-all-dependencies', From aadc33d260f2ae4b9ba3bf1395c29f22f02885ce Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 21 Apr 2017 17:50:22 -0700 Subject: [PATCH 040/619] Scripts: Remove unwrap method from executable scripts (#24263) The unwrap method was leftover from support javascript and python. Since those languages are removed in 6.0, this commit removes the unwrap feature from scripts. --- .../AbstractAsyncBulkByScrollAction.java | 21 +++++++++---------- .../action/update/UpdateHelper.java | 2 -- .../script/ExecutableScript.java | 10 --------- .../subphase/ScriptFieldsFetchSubPhase.java | 2 +- .../search/aggregations/metrics/SumIT.java | 5 ----- .../aggregations/metrics/ValueCountIT.java | 5 ----- .../support/ScriptValuesTests.java | 5 ----- .../index/reindex/SimpleExecutableScript.java | 12 ----------- .../bucket/script/TestScript.java | 5 ----- 9 files changed, 11 insertions(+), 56 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollAction.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollAction.java index eac901248b686..3ba07ea5538f5 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollAction.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollAction.java @@ -799,8 +799,7 @@ public RequestWrapper apply(RequestWrapper request, ScrollableHitSource.Hi executable.setNextVar("ctx", context); executable.run(); - Map resultCtx = (Map) executable.unwrap(context); - String newOp = (String) resultCtx.remove("op"); + String newOp = (String) context.remove("op"); if (newOp == null) { throw new IllegalArgumentException("Script cleared operation type"); } @@ -809,25 +808,25 @@ public RequestWrapper apply(RequestWrapper request, ScrollableHitSource.Hi * It'd be lovely to only set the source if we know its been modified * but it isn't worth keeping two copies of it around just to check! */ - request.setSource((Map) resultCtx.remove(SourceFieldMapper.NAME)); + request.setSource((Map) context.remove(SourceFieldMapper.NAME)); - Object newValue = resultCtx.remove(IndexFieldMapper.NAME); + Object newValue = context.remove(IndexFieldMapper.NAME); if (false == doc.getIndex().equals(newValue)) { scriptChangedIndex(request, newValue); } - newValue = resultCtx.remove(TypeFieldMapper.NAME); + newValue = context.remove(TypeFieldMapper.NAME); if (false == doc.getType().equals(newValue)) { scriptChangedType(request, newValue); } - newValue = resultCtx.remove(IdFieldMapper.NAME); + newValue = context.remove(IdFieldMapper.NAME); if (false == doc.getId().equals(newValue)) { scriptChangedId(request, newValue); } - newValue = resultCtx.remove(VersionFieldMapper.NAME); + newValue = context.remove(VersionFieldMapper.NAME); if (false == Objects.equals(oldVersion, newValue)) { scriptChangedVersion(request, newValue); } - newValue = resultCtx.remove(ParentFieldMapper.NAME); + newValue = context.remove(ParentFieldMapper.NAME); if (false == Objects.equals(oldParent, newValue)) { scriptChangedParent(request, newValue); } @@ -835,7 +834,7 @@ public RequestWrapper apply(RequestWrapper request, ScrollableHitSource.Hi * Its important that routing comes after parent in case you want to * change them both. */ - newValue = resultCtx.remove(RoutingFieldMapper.NAME); + newValue = context.remove(RoutingFieldMapper.NAME); if (false == Objects.equals(oldRouting, newValue)) { scriptChangedRouting(request, newValue); } @@ -845,8 +844,8 @@ public RequestWrapper apply(RequestWrapper request, ScrollableHitSource.Hi return scriptChangedOpType(request, oldOpType, newOpType); } - if (false == resultCtx.isEmpty()) { - throw new IllegalArgumentException("Invalid fields added to context [" + String.join(",", resultCtx.keySet()) + ']'); + if (false == context.isEmpty()) { + throw new IllegalArgumentException("Invalid fields added to context [" + String.join(",", context.keySet()) + ']'); } return request; } diff --git a/core/src/main/java/org/elasticsearch/action/update/UpdateHelper.java b/core/src/main/java/org/elasticsearch/action/update/UpdateHelper.java index 756ff80ddad60..b1621bc118e39 100644 --- a/core/src/main/java/org/elasticsearch/action/update/UpdateHelper.java +++ b/core/src/main/java/org/elasticsearch/action/update/UpdateHelper.java @@ -216,8 +216,6 @@ private Map executeScript(Script script, Map ctx ExecutableScript executableScript = scriptService.executable(script, ScriptContext.Standard.UPDATE); executableScript.setNextVar("ctx", ctx); executableScript.run(); - // we need to unwrap the ctx... - ctx = (Map) executableScript.unwrap(ctx); } } catch (Exception e) { throw new IllegalArgumentException("failed to execute script", e); diff --git a/core/src/main/java/org/elasticsearch/script/ExecutableScript.java b/core/src/main/java/org/elasticsearch/script/ExecutableScript.java index 70f42def216ca..e3f8eb4744f2c 100644 --- a/core/src/main/java/org/elasticsearch/script/ExecutableScript.java +++ b/core/src/main/java/org/elasticsearch/script/ExecutableScript.java @@ -38,14 +38,4 @@ public interface ExecutableScript { * Executes the script. */ Object run(); - - /** - * Unwraps a possible script value. For example, when passing vars and - * expecting the returned value to be part of the vars. Javascript and - * Python need this but other scripting engines just return the values - * passed in. - */ - default Object unwrap(Object value) { - return value; - } } diff --git a/core/src/main/java/org/elasticsearch/search/fetch/subphase/ScriptFieldsFetchSubPhase.java b/core/src/main/java/org/elasticsearch/search/fetch/subphase/ScriptFieldsFetchSubPhase.java index c272ab6dbf0d2..6bed20e6b3ec2 100644 --- a/core/src/main/java/org/elasticsearch/search/fetch/subphase/ScriptFieldsFetchSubPhase.java +++ b/core/src/main/java/org/elasticsearch/search/fetch/subphase/ScriptFieldsFetchSubPhase.java @@ -50,7 +50,7 @@ public void hitExecute(SearchContext context, HitContext hitContext) { final Object value; try { - value = leafScript.unwrap(leafScript.run()); + value = leafScript.run(); } catch (RuntimeException e) { if (scriptField.ignoreException()) { continue; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java index 631e579ac9cbb..1591e6df9313d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java @@ -564,11 +564,6 @@ public LeafSearchScript getLeafSearchScript(LeafReaderContext context) throws IO return new LeafSearchScript() { - @Override - public Object unwrap(Object value) { - throw new UnsupportedOperationException(); - } - @Override public void setNextVar(String name, Object value) { vars.put(name, value); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java index 7bd3ebd88473f..784635bb1d61d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java @@ -305,11 +305,6 @@ public LeafSearchScript getLeafSearchScript(LeafReaderContext context) throws IO return new LeafSearchScript() { - @Override - public Object unwrap(Object value) { - throw new UnsupportedOperationException(); - } - @Override public void setNextVar(String name, Object value) { vars.put(name, value); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/support/ScriptValuesTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/support/ScriptValuesTests.java index 11e03a969d0e0..38111654bbdcf 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/support/ScriptValuesTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/support/ScriptValuesTests.java @@ -58,11 +58,6 @@ public Object run() { return randomBoolean() ? values : Arrays.asList(values); } - @Override - public Object unwrap(Object value) { - throw new UnsupportedOperationException(); - } - @Override public void setScorer(Scorer scorer) { } diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/SimpleExecutableScript.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/SimpleExecutableScript.java index af2312da7545d..7437a5512dfd0 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/SimpleExecutableScript.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/SimpleExecutableScript.java @@ -50,16 +50,4 @@ public void setNextVar(String name, Object value) { throw new IllegalArgumentException("Unsupported var [" + name + "]"); } } - - @Override - public Object unwrap(Object value) { - // Some script engines (javascript) copy any maps they unwrap - if (randomBoolean()) { - if (value instanceof Map) { - return new HashMap<>((Map) value); - } - } - // Others just return the objects plain (painless) - return value; - } } diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/script/TestScript.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/script/TestScript.java index 13c1340de313c..8eb707226413a 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/script/TestScript.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/script/TestScript.java @@ -56,9 +56,4 @@ protected final void checkParams() { Objects.requireNonNull(_superset_freq, "_superset_freq"); Objects.requireNonNull(_superset_size, "_superset_size"); } - - @Override - public Double unwrap(Object value) { - return ((Number) value).doubleValue(); - } } From 473e98981bcbac246ade6f27da13aa0238b0c8f5 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 21 Apr 2017 17:53:03 -0700 Subject: [PATCH 041/619] Scripts: Remove unnecessary executable shortcut (#24264) ScriptService has two executable methods, one which takes a CompiledScript, which is similar to search, and one that takes a raw Script and both compiles and returns an ExecutableScript for it. The latter is not needed, and the call sites which used one or the other were mixed. This commit removes the extra executable method in favor of callers first calling compile, then executable. --- .../elasticsearch/action/update/UpdateHelper.java | 4 +++- .../index/query/QueryRewriteContext.java | 4 +++- .../index/query/QueryShardContext.java | 3 ++- .../org/elasticsearch/script/ScriptService.java | 7 ------- .../significant/heuristics/ScriptHeuristic.java | 4 +++- .../org/elasticsearch/script/NativeScriptTests.java | 5 +++-- .../elasticsearch/script/ScriptServiceTests.java | 13 ++++++++----- .../ingest/common/ScriptProcessor.java | 4 +++- .../ingest/common/ScriptProcessorTests.java | 3 ++- .../mustache/TransportSearchTemplateAction.java | 4 +++- 10 files changed, 30 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/update/UpdateHelper.java b/core/src/main/java/org/elasticsearch/action/update/UpdateHelper.java index b1621bc118e39..09fc0bccbf35e 100644 --- a/core/src/main/java/org/elasticsearch/action/update/UpdateHelper.java +++ b/core/src/main/java/org/elasticsearch/action/update/UpdateHelper.java @@ -43,6 +43,7 @@ import org.elasticsearch.index.mapper.RoutingFieldMapper; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptContext; @@ -213,7 +214,8 @@ protected Result prepare(ShardId shardId, UpdateRequest request, final GetResult private Map executeScript(Script script, Map ctx) { try { if (scriptService != null) { - ExecutableScript executableScript = scriptService.executable(script, ScriptContext.Standard.UPDATE); + CompiledScript compiledScript = scriptService.compile(script, ScriptContext.Standard.UPDATE); + ExecutableScript executableScript = scriptService.executable(compiledScript, script.getParams()); executableScript.setNextVar("ctx", ctx); executableScript.run(); } diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java b/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java index 80726496a739c..183ab690ce2ad 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptContext; @@ -104,7 +105,8 @@ public long nowInMillis() { } public BytesReference getTemplateBytes(Script template) { - ExecutableScript executable = scriptService.executable(template, ScriptContext.Standard.SEARCH); + CompiledScript compiledTemplate = scriptService.compile(template, ScriptContext.Standard.SEARCH); + ExecutableScript executable = scriptService.executable(compiledTemplate, template.getParams()); return (BytesReference) executable.run(); } } diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java b/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java index 2b5e69947f373..a6a7108f7a971 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java @@ -351,7 +351,8 @@ public final Function, SearchScript> getLazySearchScript(Scr */ public final ExecutableScript getExecutableScript(Script script, ScriptContext context) { failIfFrozen(); - return scriptService.executable(script, context); + CompiledScript compiledScript = scriptService.compile(script, context); + return scriptService.executable(compiledScript, script.getParams()); } /** diff --git a/core/src/main/java/org/elasticsearch/script/ScriptService.java b/core/src/main/java/org/elasticsearch/script/ScriptService.java index 692e081a7ba1a..e0c7b3c63de08 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptService.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptService.java @@ -469,13 +469,6 @@ public StoredScriptSource getStoredScript(ClusterState state, GetStoredScriptReq } } - /** - * Compiles (or retrieves from cache) and executes the provided script - */ - public ExecutableScript executable(Script script, ScriptContext scriptContext) { - return executable(compile(script, scriptContext), script.getParams()); - } - /** * Executes a previously compiled script provided as an argument */ diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/heuristics/ScriptHeuristic.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/heuristics/ScriptHeuristic.java index b73f2f0987edf..7618839d4966a 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/heuristics/ScriptHeuristic.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/heuristics/ScriptHeuristic.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryShardException; +import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptContext; @@ -92,7 +93,8 @@ public void writeTo(StreamOutput out) throws IOException { @Override public SignificanceHeuristic rewrite(InternalAggregation.ReduceContext context) { - return new ExecutableScriptHeuristic(script, context.scriptService().executable(script, ScriptContext.Standard.AGGS)); + CompiledScript compiledScript = context.scriptService().compile(script, ScriptContext.Standard.AGGS); + return new ExecutableScriptHeuristic(script, context.scriptService().executable(compiledScript, script.getParams())); } @Override diff --git a/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java b/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java index bf5c7e0daa782..aa2e260c7c2e1 100644 --- a/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java +++ b/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java @@ -52,8 +52,9 @@ public void testNativeScript() throws InterruptedException { List> scriptSettings = scriptModule.getSettings(); scriptSettings.add(InternalSettingsPlugin.VERSION_CREATED); - ExecutableScript executable = scriptModule.getScriptService().executable( - new Script(ScriptType.INLINE, NativeScriptEngineService.NAME, "my", Collections.emptyMap()), ScriptContext.Standard.SEARCH); + Script script = new Script(ScriptType.INLINE, NativeScriptEngineService.NAME, "my", Collections.emptyMap()); + CompiledScript compiledScript = scriptModule.getScriptService().compile(script, ScriptContext.Standard.SEARCH); + ExecutableScript executable = scriptModule.getScriptService().executable(compiledScript, script.getParams()); assertThat(executable.run().toString(), equalTo("test")); } diff --git a/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java b/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java index da205a9292eb0..6c2bd6f1be2c0 100644 --- a/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java +++ b/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java @@ -346,7 +346,9 @@ public void testCompileCountedInCompilationStats() throws IOException { public void testExecutableCountedInCompilationStats() throws IOException { buildScriptService(Settings.EMPTY); - scriptService.executable(new Script(ScriptType.INLINE, "test", "1+1", Collections.emptyMap()), randomFrom(scriptContexts)); + Script script = new Script(ScriptType.INLINE, "test", "1+1", Collections.emptyMap()); + CompiledScript compiledScript = scriptService.compile(script, randomFrom(scriptContexts)); + scriptService.executable(compiledScript, script.getParams()); assertEquals(1L, scriptService.stats().getCompilations()); } @@ -371,8 +373,9 @@ public void testCompilationStatsOnCacheHit() throws IOException { builder.put(ScriptService.SCRIPT_CACHE_SIZE_SETTING.getKey(), 1); builder.put("script.inline", "true"); buildScriptService(builder.build()); - scriptService.executable(new Script(ScriptType.INLINE, "test", "1+1", Collections.emptyMap()), randomFrom(scriptContexts)); - scriptService.executable(new Script(ScriptType.INLINE, "test", "1+1", Collections.emptyMap()), randomFrom(scriptContexts)); + Script script = new Script(ScriptType.INLINE, "test", "1+1", Collections.emptyMap()); + scriptService.compile(script, randomFrom(scriptContexts)); + scriptService.compile(script, randomFrom(scriptContexts)); assertEquals(1L, scriptService.stats().getCompilations()); } @@ -394,8 +397,8 @@ public void testCacheEvictionCountedInCacheEvictionsStats() throws IOException { builder.put(ScriptService.SCRIPT_CACHE_SIZE_SETTING.getKey(), 1); builder.put("script.inline", "true"); buildScriptService(builder.build()); - scriptService.executable(new Script(ScriptType.INLINE, "test", "1+1", Collections.emptyMap()), randomFrom(scriptContexts)); - scriptService.executable(new Script(ScriptType.INLINE, "test", "2+2", Collections.emptyMap()), randomFrom(scriptContexts)); + scriptService.compile(new Script(ScriptType.INLINE, "test", "1+1", Collections.emptyMap()), randomFrom(scriptContexts)); + scriptService.compile(new Script(ScriptType.INLINE, "test", "2+2", Collections.emptyMap()), randomFrom(scriptContexts)); assertEquals(2L, scriptService.stats().getCompilations()); assertEquals(1L, scriptService.stats().getCacheEvictions()); } diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/ScriptProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/ScriptProcessor.java index 33c06a3804a38..a5bc8034027ee 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/ScriptProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/ScriptProcessor.java @@ -23,6 +23,7 @@ import org.elasticsearch.ingest.AbstractProcessor; import org.elasticsearch.ingest.IngestDocument; import org.elasticsearch.ingest.Processor; +import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptContext; @@ -70,7 +71,8 @@ public final class ScriptProcessor extends AbstractProcessor { */ @Override public void execute(IngestDocument document) { - ExecutableScript executableScript = scriptService.executable(script, ScriptContext.Standard.INGEST); + CompiledScript compiledScript = scriptService.compile(script, ScriptContext.Standard.INGEST); + ExecutableScript executableScript = scriptService.executable(compiledScript, script.getParams()); executableScript.setNextVar("ctx", document.getSourceAndMetadata()); executableScript.run(); } diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ScriptProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ScriptProcessorTests.java index 94430622d1a46..e76f3016ddadc 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ScriptProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ScriptProcessorTests.java @@ -24,6 +24,7 @@ import org.elasticsearch.ingest.IngestDocument; import org.elasticsearch.ingest.RandomDocumentPicks; +import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptService; @@ -46,7 +47,7 @@ public void testScripting() throws Exception { ScriptService scriptService = mock(ScriptService.class); Script script = new Script("_script"); ExecutableScript executableScript = mock(ExecutableScript.class); - when(scriptService.executable(any(Script.class), any())).thenReturn(executableScript); + when(scriptService.executable(any(CompiledScript.class), any())).thenReturn(executableScript); Map document = new HashMap<>(); document.put("bytes_in", randomInt()); diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java index d7b0406238278..61f099f6c24cd 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java @@ -34,6 +34,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptService; @@ -71,7 +72,8 @@ protected void doExecute(SearchTemplateRequest request, ActionListener Date: Fri, 21 Apr 2017 22:15:29 -0400 Subject: [PATCH 042/619] Fix delete of plugin directory on remove plugin This commit fixes an issue when deleting the plugin directory while executing the remove plugin command. Namely, we take out a file descriptor on the plugin directory to traverse its contents to obtain the list of files to delete. We leaked this file descriptor. On Unix-based filesystems, this is not a problem, deleting the plugin directory deletes the plugin directory. On Windows though, a delete is not executed until the last file descriptor is closed. Since we leaked this file descriptor, the plugin was not actually deleted. This led to test failures that tried to cleanup left behind temporary directories but these test failures were just exposing this bug. This commit fixes this issue by ensuring that we close the file descriptor to the plugin directory when we are finished with it. Relates #24266 --- .../elasticsearch/plugins/RemovePluginCommand.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java index 2d2fd8efe58e3..3e44cbbf45500 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java @@ -36,6 +36,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.elasticsearch.cli.Terminal.Verbosity.VERBOSE; @@ -110,7 +112,9 @@ void execute(final Terminal terminal, final String pluginName, final Environment * Add the contents of the plugin directory before creating the marker file and adding it to the list of paths to be deleted so * that the marker file is the last file to be deleted. */ - Files.list(pluginDir).forEach(pluginPaths::add); + try (Stream paths = Files.list(pluginDir)) { + pluginPaths.addAll(paths.collect(Collectors.toList())); + } try { Files.createFile(removing); } catch (final FileAlreadyExistsException e) { @@ -122,9 +126,10 @@ void execute(final Terminal terminal, final String pluginName, final Environment } // now add the marker file pluginPaths.add(removing); + // finally, add the plugin directory + pluginPaths.add(pluginDir); IOUtils.rm(pluginPaths.toArray(new Path[pluginPaths.size()])); - // at this point, the plugin directory is empty and we can execute a simple directory removal - Files.delete(pluginDir); + /* * We preserve the config files in case the user is upgrading the plugin, but we print a From 08f52d1a187c59857f37c642108db05ee3f43c97 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Fri, 21 Apr 2017 22:18:33 -0400 Subject: [PATCH 043/619] Remove extra newline in RemovePluginCommand.java This commit removes an extra newline in RemovePluginCommand.java because it drives the OCD among us insane. --- .../main/java/org/elasticsearch/plugins/RemovePluginCommand.java | 1 - 1 file changed, 1 deletion(-) diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java index 3e44cbbf45500..1653d663a2f8a 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/RemovePluginCommand.java @@ -130,7 +130,6 @@ void execute(final Terminal terminal, final String pluginName, final Environment pluginPaths.add(pluginDir); IOUtils.rm(pluginPaths.toArray(new Path[pluginPaths.size()])); - /* * We preserve the config files in case the user is upgrading the plugin, but we print a * message so the user knows in case they want to remove manually. From c5396839c4bd720980308a9d532d9f955323f489 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Sat, 22 Apr 2017 10:41:57 -0400 Subject: [PATCH 044/619] Skip Wildfly tests on JDK 9 It seems that Wildfly 10 can not be made to start in a fully-functional form on JDK 9, so this commit skips running the Wildfly integration tests on JDK 9. --- qa/wildfly/build.gradle | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/qa/wildfly/build.gradle b/qa/wildfly/build.gradle index 080e460e4f6d4..fe5e9d47a5f87 100644 --- a/qa/wildfly/build.gradle +++ b/qa/wildfly/build.gradle @@ -35,7 +35,7 @@ final String wildflyInstall = "${buildDir}/wildfly/wildfly-${wildflyVersion}" // TODO: use ephemeral ports final int portOffset = 30000 final int managementPort = 9990 + portOffset -// we skip these tests on Windows so we do no need to worry about compatibility here +// we skip these tests on Windows so we do not need to worry about compatibility here final String stopWildflyCommand = "${wildflyInstall}/bin/jboss-cli.sh --controller=localhost:${managementPort} --connect command=:shutdown" repositories { @@ -88,11 +88,10 @@ task deploy(type: Copy) { } task writeElasticsearchProperties { - onlyIf { !Os.isFamily(Os.FAMILY_WINDOWS) } + onlyIf { !Os.isFamily(Os.FAMILY_WINDOWS) && project.rootProject.ext.javaVersion == JavaVersion.VERSION_1_8 } dependsOn 'integTestCluster#wait', deploy doLast { - final File elasticsearchProperties = - file("${wildflyInstall}/standalone/configuration/elasticsearch.properties") + final File elasticsearchProperties = file("${wildflyInstall}/standalone/configuration/elasticsearch.properties") elasticsearchProperties.write( [ "transport.uri=${-> integTest.getNodes().get(0).transportUri()}", @@ -146,7 +145,7 @@ task startWildfly { task configureTransportClient(type: LoggedExec) { dependsOn startWildfly - // we skip these tests on Windows so we do no need to worry about compatibility here + // we skip these tests on Windows so we do not need to worry about compatibility here commandLine "${wildflyInstall}/bin/jboss-cli.sh", "--controller=localhost:${managementPort}", "--connect", @@ -157,7 +156,7 @@ task stopWildfly(type: LoggedExec) { commandLine stopWildflyCommand.split(' ') } -if (!Os.isFamily(Os.FAMILY_WINDOWS)) { +if (!Os.isFamily(Os.FAMILY_WINDOWS) && project.rootProject.ext.javaVersion == JavaVersion.VERSION_1_8) { integTestRunner.dependsOn(configureTransportClient) final TaskExecutionAdapter logDumpListener = new TaskExecutionAdapter() { @Override From d4a6ba8ec9c0eaf092d4e93fc27d7528b8687b27 Mon Sep 17 00:00:00 2001 From: Colin Goodheart-Smithe Date: Sat, 22 Apr 2017 18:37:04 +0100 Subject: [PATCH 045/619] No longer add illegal content type option to stored search templates (#24251) When parsing StoredSearchScript we were adding a Content type option that was forbidden (by a check that threw an exception) by the parser thats used to parse the template when we read it from the cluster state. This was stopping Elastisearch from starting after stored search templates had been added. This change no longer adds the content type option to the StoredScriptSource object when parsing from the put search template request. This is safe because the StoredScriptSource content is always JSON when its stored in the cluster state since we do a conversion to JSON before this point. Also removes the check for the content type in the options when parsing StoredScriptSource so users who already have stored scripts can start Elasticsearch. Closes #24227 --- .../script/StoredScriptSource.java | 13 +--- .../script/StoredScriptSourceTests.java | 68 +++++++++++++++++++ .../script/StoredScriptTests.java | 23 ++----- 3 files changed, 75 insertions(+), 29 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/script/StoredScriptSourceTests.java diff --git a/core/src/main/java/org/elasticsearch/script/StoredScriptSource.java b/core/src/main/java/org/elasticsearch/script/StoredScriptSource.java index 11b7821390847..11215fded349e 100644 --- a/core/src/main/java/org/elasticsearch/script/StoredScriptSource.java +++ b/core/src/main/java/org/elasticsearch/script/StoredScriptSource.java @@ -123,10 +123,6 @@ private void setCode(XContentParser parser) { * Appends the user-defined compiler options with the internal compiler options. */ private void setOptions(Map options) { - if (options.containsKey(Script.CONTENT_TYPE_OPTION)) { - throw new IllegalArgumentException(Script.CONTENT_TYPE_OPTION + " cannot be user-specified"); - } - this.options.putAll(options); } @@ -266,8 +262,7 @@ public static StoredScriptSource parse(String lang, BytesReference content, XCon //this is really for search templates, that need to be converted to json format try (XContentBuilder builder = XContentFactory.jsonBuilder()) { builder.copyCurrentStructure(parser); - return new StoredScriptSource(lang, builder.string(), - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType())); + return new StoredScriptSource(lang, builder.string(), Collections.emptyMap()); } } @@ -283,8 +278,7 @@ public static StoredScriptSource parse(String lang, BytesReference content, XCon token = parser.nextToken(); if (token == Token.VALUE_STRING) { - return new StoredScriptSource(lang, parser.text(), - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType())); + return new StoredScriptSource(lang, parser.text(), Collections.emptyMap()); } } @@ -297,8 +291,7 @@ public static StoredScriptSource parse(String lang, BytesReference content, XCon builder.copyCurrentStructure(parser); } - return new StoredScriptSource(lang, builder.string(), - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType())); + return new StoredScriptSource(lang, builder.string(), Collections.emptyMap()); } } } catch (IOException ioe) { diff --git a/core/src/test/java/org/elasticsearch/script/StoredScriptSourceTests.java b/core/src/test/java/org/elasticsearch/script/StoredScriptSourceTests.java new file mode 100644 index 0000000000000..a99c897ec3445 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/script/StoredScriptSourceTests.java @@ -0,0 +1,68 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.script; + +import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.test.AbstractSerializingTestCase; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class StoredScriptSourceTests extends AbstractSerializingTestCase { + + @Override + protected StoredScriptSource createTestInstance() { + String lang = randomAlphaOfLengthBetween(1, 20); + XContentType xContentType = randomFrom(XContentType.JSON, XContentType.YAML); + try { + XContentBuilder template = XContentBuilder.builder(xContentType.xContent()); + template.startObject(); + template.startObject("query"); + template.startObject("match"); + template.field("title", "{{query_string}}"); + template.endObject(); + template.endObject(); + template.endObject(); + Map options = new HashMap<>(); + if (randomBoolean()) { + options.put(Script.CONTENT_TYPE_OPTION, xContentType.mediaType()); + } + return StoredScriptSource.parse(lang, template.bytes(), xContentType); + } catch (IOException e) { + throw new AssertionError("Failed to create test instance", e); + } + } + + @Override + protected StoredScriptSource doParseInstance(XContentParser parser) throws IOException { + return StoredScriptSource.fromXContent(parser); + } + + @Override + protected Reader instanceReader() { + return StoredScriptSource::new; + } + + +} diff --git a/core/src/test/java/org/elasticsearch/script/StoredScriptTests.java b/core/src/test/java/org/elasticsearch/script/StoredScriptTests.java index 96a9a417bbec5..af54afbf77dee 100644 --- a/core/src/test/java/org/elasticsearch/script/StoredScriptTests.java +++ b/core/src/test/java/org/elasticsearch/script/StoredScriptTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.script; import org.elasticsearch.ResourceNotFoundException; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; @@ -198,8 +197,7 @@ public void testSourceParsing() throws Exception { builder.startObject().field("template", "code").endObject(); StoredScriptSource parsed = StoredScriptSource.parse("lang", builder.bytes(), XContentType.JSON); - StoredScriptSource source = new StoredScriptSource("lang", "code", - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, builder.contentType().mediaType())); + StoredScriptSource source = new StoredScriptSource("lang", "code", Collections.emptyMap()); assertThat(parsed, equalTo(source)); } @@ -214,8 +212,7 @@ public void testSourceParsing() throws Exception { } StoredScriptSource parsed = StoredScriptSource.parse("lang", builder.bytes(), XContentType.JSON); - StoredScriptSource source = new StoredScriptSource("lang", code, - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, builder.contentType().mediaType())); + StoredScriptSource source = new StoredScriptSource("lang", code, Collections.emptyMap()); assertThat(parsed, equalTo(source)); } @@ -230,8 +227,7 @@ public void testSourceParsing() throws Exception { } StoredScriptSource parsed = StoredScriptSource.parse("lang", builder.bytes(), XContentType.JSON); - StoredScriptSource source = new StoredScriptSource("lang", code, - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, builder.contentType().mediaType())); + StoredScriptSource source = new StoredScriptSource("lang", code, Collections.emptyMap()); assertThat(parsed, equalTo(source)); } @@ -246,8 +242,7 @@ public void testSourceParsing() throws Exception { } StoredScriptSource parsed = StoredScriptSource.parse("lang", builder.bytes(), XContentType.JSON); - StoredScriptSource source = new StoredScriptSource("lang", code, - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, builder.contentType().mediaType())); + StoredScriptSource source = new StoredScriptSource("lang", code, Collections.emptyMap()); assertThat(parsed, equalTo(source)); } @@ -328,16 +323,6 @@ public void testSourceParsingErrors() throws Exception { StoredScriptSource.parse(null, builder.bytes(), XContentType.JSON)); assertThat(iae.getMessage(), equalTo("illegal compiler options [{option=option}] specified")); } - - // check for illegal use of content type option - try (XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON)) { - builder.startObject().field("script").startObject().field("lang", "lang").field("code", "code") - .startObject("options").field("content_type", "option").endObject().endObject().endObject(); - - ParsingException pe = expectThrows(ParsingException.class, () -> - StoredScriptSource.parse(null, builder.bytes(), XContentType.JSON)); - assertThat(pe.getRootCause().getMessage(), equalTo("content_type cannot be user-specified")); - } } @Override From 4423e1b78fa7b08129ff05a10141b6b934c6e15e Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Sat, 22 Apr 2017 13:37:13 -0400 Subject: [PATCH 046/619] Test search templates during rolling upgrade test (#24258) In #24251 we fix an issue with stored search templates that this test would have discovered: stored search templates cause the node to refuse to start. Technically a "restart" test would have caught this as well and would have caught it more quickly. But we already *have* an upgrade test and we don't have restart tests. And testing this on upgrade is a good thing too. --- .../test/mixed_cluster/10_basic.yaml | 10 +++++ .../test/old_cluster/10_basic.yaml | 39 +++++++++++++------ .../test/upgraded_cluster/10_basic.yaml | 10 +++++ 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yaml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yaml index f9475057bc447..4def6fa5da589 100644 --- a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yaml +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yaml @@ -59,6 +59,16 @@ - match: { hits.total: 10 } +--- +"Verify that we can still find things with the template": + - do: + search_template: + body: + id: test_search_template + params: + f1: v5_old + - match: { hits.total: 1 } + --- "Verify custom cluster metadata still exists during upgrade": - do: diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yaml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yaml index 98627e03419cd..a5e9a26a9a164 100644 --- a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yaml +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yaml @@ -1,15 +1,15 @@ --- -"Index data and search on the old cluster": - - do: - indices.create: +"Index data, search, and create things in the cluster state that we'll validate are there after the ugprade": + - do: + indices.create: index: test_index body: settings: index: number_of_replicas: 0 - - do: - bulk: + - do: + bulk: refresh: true body: - '{"index": {"_index": "test_index", "_type": "test_type"}}' @@ -23,18 +23,16 @@ - '{"index": {"_index": "test_index", "_type": "test_type"}}' - '{"f1": "v5_old", "f2": 4}' - - do: - indices.flush: + - do: + indices.flush: index: test_index - - do: - search: + - do: + search: index: test_index - - match: { hits.total: 5 } + - match: { hits.total: 5 } ---- -"Add stuff to cluster state so that we can verify that it remains to exist during and after the rolling upgrade": - do: snapshot.create_repository: repository: my_repo @@ -54,3 +52,20 @@ ] } - match: { "acknowledged": true } + + - do: + put_template: + id: test_search_template + body: + query: + match: + f1: "{{f1}}" + - match: { acknowledged: true } + + - do: + search_template: + body: + id: test_search_template + params: + f1: v5_old + - match: { hits.total: 1 } diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yaml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yaml index ef352a5e50d88..93c85582c9083 100644 --- a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yaml +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yaml @@ -36,6 +36,16 @@ - match: { hits.total: 15 } # 10 docs from previous clusters plus 5 new docs +--- +"Verify that we can still find things with the template": + - do: + search_template: + body: + id: test_search_template + params: + f1: v5_old + - match: { hits.total: 1 } + --- "Verify custom cluster metadata still exists after rolling upgrade": - do: From d93d955563b3e115001ed40e089f625171de8822 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Sun, 23 Apr 2017 11:38:16 -0400 Subject: [PATCH 047/619] Revert "Temporarily change Wildfly artifact location" This reverts commit 8c0329d915191961344d972227bc7ae8e70f3658. --- qa/wildfly/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/wildfly/build.gradle b/qa/wildfly/build.gradle index fe5e9d47a5f87..df9720d4f77e0 100644 --- a/qa/wildfly/build.gradle +++ b/qa/wildfly/build.gradle @@ -42,7 +42,7 @@ repositories { mavenCentral() // the Wildfly distribution is not available via a repository, so we fake an Ivy repository on top of the download site ivy { - url "http://download.elastic.co" + url "http://download.jboss.org" layout 'pattern', { artifact 'wildfly/[revision]/[module]-[revision].[ext]' } From ca9aebac726c5d943e89d27bccd443e42f16090f Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Sun, 23 Apr 2017 20:40:24 +0200 Subject: [PATCH 048/619] Update docs to include index sorting link --- docs/reference/index-modules.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference/index-modules.asciidoc b/docs/reference/index-modules.asciidoc index 2d2b6600fafd8..afbd4105663fa 100644 --- a/docs/reference/index-modules.asciidoc +++ b/docs/reference/index-modules.asciidoc @@ -204,3 +204,5 @@ include::index-modules/slowlog.asciidoc[] include::index-modules/store.asciidoc[] include::index-modules/translog.asciidoc[] + +include::index-modules/index-sorting.asciidoc[] From 6d6a230f70e7a93a39b3b39a6c8149066c4aacf6 Mon Sep 17 00:00:00 2001 From: Colin Goodheart-Smithe Date: Mon, 24 Apr 2017 10:20:15 +0100 Subject: [PATCH 049/619] Makes StoredScriptSource implement ToXContentObject --- .../org/elasticsearch/script/StoredScriptSource.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/script/StoredScriptSource.java b/core/src/main/java/org/elasticsearch/script/StoredScriptSource.java index 11215fded349e..b4e6e257eb718 100644 --- a/core/src/main/java/org/elasticsearch/script/StoredScriptSource.java +++ b/core/src/main/java/org/elasticsearch/script/StoredScriptSource.java @@ -34,7 +34,7 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ObjectParser.ValueType; -import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; @@ -52,7 +52,7 @@ * {@link StoredScriptSource} represents user-defined parameters for a script * saved in the {@link ClusterState}. */ -public class StoredScriptSource extends AbstractDiffable implements Writeable, ToXContent { +public class StoredScriptSource extends AbstractDiffable implements Writeable, ToXContentObject { /** * Standard {@link ParseField} for outer level of stored script source. @@ -426,11 +426,6 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } - @Override - public boolean isFragment() { - return false; - } - /** * @return The language used for compiling this script. */ From 7c395070e20807926086e2e572e78d1346c8dddb Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Mon, 24 Apr 2017 12:24:51 +0200 Subject: [PATCH 050/619] [TEST] Wait for tribe node to be fully connected before shutting it down The tribe was being shutdown by the test while a publishing round (that adds the tribe node to a cluster) is not completed yet (i.e. the node itself knows that it became part of the cluster, and the test shuts the tribe node down, but another node has not applied the cluster state yet, which makes that node hang while trying to connect to the node that is shutting down (due to connect_timeout being 30 seconds), delaying publishing for 30 seconds, and subsequently tripping an assertion when another tribe instance wants to join. Relates to #23695 --- .../java/org/elasticsearch/tribe/TribeIT.java | 61 +++++++------------ 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/tribe/TribeIT.java b/core/src/test/java/org/elasticsearch/tribe/TribeIT.java index 9e1d37681be93..7c58622e5cced 100644 --- a/core/src/test/java/org/elasticsearch/tribe/TribeIT.java +++ b/core/src/test/java/org/elasticsearch/tribe/TribeIT.java @@ -211,6 +211,27 @@ private Releasable startTribeNode() throws Exception { private Releasable startTribeNode(Predicate filter, Settings settings) throws Exception { final String node = internalCluster().startNode(createTribeSettings(filter).put(settings).build()); + + // wait for node to be connected to all tribe clusters + final Set expectedNodes = Sets.newHashSet(internalCluster().getNodeNames()); + doWithAllClusters(filter, c -> { + // Adds the tribe client node dedicated to this remote cluster + for (String tribeNode : internalCluster().getNodeNames()) { + expectedNodes.add(tribeNode + "/" + c.getClusterName()); + } + // Adds the remote clusters nodes names + Collections.addAll(expectedNodes, c.getNodeNames()); + }); + assertBusy(() -> { + ClusterState state = client().admin().cluster().prepareState().setNodes(true).get().getState(); + Set nodes = StreamSupport.stream(state.getNodes().spliterator(), false).map(DiscoveryNode::getName).collect(toSet()); + assertThat(nodes, containsInAnyOrder(expectedNodes.toArray())); + }); + // wait for join to be fully applied on all nodes in the tribe clusters, see https://github.com/elastic/elasticsearch/issues/23695 + doWithAllClusters(filter, c -> { + assertFalse(c.client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).get().isTimedOut()); + }); + return () -> { try { while(internalCluster().getNodeNames().length > 0) { @@ -256,9 +277,6 @@ public void testGlobalReadWriteBlocks() throws Exception { assertAcked(cluster2.client().admin().indices().prepareCreate("test2")); ensureGreen(cluster2.client()); - // Wait for the tribe node to connect to the two remote clusters - assertNodes(ALL); - // Wait for the tribe node to retrieve the indices into its cluster state assertIndicesExist(client(), "test1", "test2"); @@ -294,9 +312,6 @@ public void testIndexWriteBlocks() throws Exception { assertAcked(cluster2.client().admin().indices().prepareCreate("block_test2")); ensureGreen(cluster2.client()); - // Wait for the tribe node to connect to the two remote clusters - assertNodes(ALL); - // Wait for the tribe node to retrieve the indices into its cluster state assertIndicesExist(client(), "test1", "test2", "block_test1", "block_test2"); @@ -328,9 +343,6 @@ public void testOnConflictDrop() throws Exception { assertAcked(cluster2.client().admin().indices().prepareCreate("conflict")); ensureGreen(cluster2.client()); - // Wait for the tribe node to connect to the two remote clusters - assertNodes(ALL); - // Wait for the tribe node to retrieve the indices into its cluster state assertIndicesExist(client(), "test1", "test2"); @@ -358,9 +370,6 @@ public void testOnConflictPrefer() throws Exception { assertAcked(cluster2.client().admin().indices().prepareCreate("shared")); ensureGreen(cluster2.client()); - // Wait for the tribe node to connect to the two remote clusters - assertNodes(ALL); - // Wait for the tribe node to retrieve the indices into its cluster state assertIndicesExist(client(), "test1", "test2", "shared"); @@ -383,9 +392,6 @@ public void testTribeOnOneCluster() throws Exception { assertAcked(cluster2.client().admin().indices().prepareCreate("test2")); ensureGreen(cluster2.client()); - // Wait for the tribe node to connect to the two remote clusters - assertNodes(ALL); - // Wait for the tribe node to retrieve the indices into its cluster state assertIndicesExist(client(), "test1", "test2"); @@ -444,9 +450,6 @@ public void testCloseAndOpenIndex() throws Exception { assertTrue(cluster1.client().admin().indices().prepareClose("first").get().isAcknowledged()); try (Releasable tribeNode = startTribeNode()) { - // Wait for the tribe node to connect to the two remote clusters - assertNodes(ALL); - // The closed index is not part of the tribe node cluster state ClusterState clusterState = client().admin().cluster().prepareState().get().getState(); assertFalse(clusterState.getMetaData().hasIndex("first")); @@ -481,7 +484,6 @@ public void testClusterStateNodes() throws Exception { for (Predicate predicate : predicates) { try (Releasable tribeNode = startTribeNode(predicate, Settings.EMPTY)) { - assertNodes(predicate); } } } @@ -492,7 +494,6 @@ public void testMergingRemovedCustomMetaData() throws Exception { MergableCustomMetaData1 customMetaData1 = new MergableCustomMetaData1("a"); MergableCustomMetaData1 customMetaData2 = new MergableCustomMetaData1("b"); try (Releasable tribeNode = startTribeNode()) { - assertNodes(ALL); putCustomMetaData(cluster1, customMetaData1); putCustomMetaData(cluster2, customMetaData2); assertCustomMetaDataUpdated(internalCluster(), customMetaData2); @@ -510,7 +511,6 @@ public void testMergingCustomMetaData() throws Exception { Collections.sort(customMetaDatas, (cm1, cm2) -> cm2.getData().compareTo(cm1.getData())); final MergableCustomMetaData1 tribeNodeCustomMetaData = customMetaDatas.get(0); try (Releasable tribeNode = startTribeNode()) { - assertNodes(ALL); putCustomMetaData(cluster1, customMetaData1); assertCustomMetaDataUpdated(internalCluster(), customMetaData1); putCustomMetaData(cluster2, customMetaData2); @@ -530,7 +530,6 @@ public void testMergingMultipleCustomMetaData() throws Exception { Collections.sort(mergedCustomMetaDataType1, (cm1, cm2) -> cm2.getData().compareTo(cm1.getData())); Collections.sort(mergedCustomMetaDataType2, (cm1, cm2) -> cm2.getData().compareTo(cm1.getData())); try (Releasable tribeNode = startTribeNode()) { - assertNodes(ALL); // test putting multiple custom md types propagates to tribe putCustomMetaData(cluster1, firstCustomMetaDataType1); putCustomMetaData(cluster1, firstCustomMetaDataType2); @@ -631,24 +630,6 @@ private void ensureGreen(Client client) throws Exception { }); } - private static void assertNodes(Predicate filter) throws Exception { - final Set expectedNodes = Sets.newHashSet(internalCluster().getNodeNames()); - doWithAllClusters(filter, c -> { - // Adds the tribe client node dedicated to this remote cluster - for (String tribeNode : internalCluster().getNodeNames()) { - expectedNodes.add(tribeNode + "/" + c.getClusterName()); - } - // Adds the remote clusters nodes names - Collections.addAll(expectedNodes, c.getNodeNames()); - }); - - assertBusy(() -> { - ClusterState state = client().admin().cluster().prepareState().setNodes(true).get().getState(); - Set nodes = StreamSupport.stream(state.getNodes().spliterator(), false).map(DiscoveryNode::getName).collect(toSet()); - assertThat(nodes, containsInAnyOrder(expectedNodes.toArray())); - }); - } - private static void doWithAllClusters(Consumer consumer) { doWithAllClusters(cluster -> cluster != null, consumer); } From 94b079ed423a9e45bf002ed40c0acd7a90819b56 Mon Sep 17 00:00:00 2001 From: Michael Despotopoulos Date: Mon, 24 Apr 2017 14:25:54 +0300 Subject: [PATCH 051/619] Docs: Replace deprecated pluginList with Arrays.asList (#24270) ESIntegTestCase#pluginList was remove removed in ES 5.0. We are using Arrays.asList instead. --- docs/reference/testing/testing-framework.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/testing/testing-framework.asciidoc b/docs/reference/testing/testing-framework.asciidoc index 94aa6d4b42bd8..d1fe769f3c108 100644 --- a/docs/reference/testing/testing-framework.asciidoc +++ b/docs/reference/testing/testing-framework.asciidoc @@ -171,7 +171,7 @@ As elasticsearch is using JUnit 4, using the `@Before` and `@After` annotations ----------------------------------------- @Override protected Collection> nodePlugins() { - return pluginList(CustomSuggesterPlugin.class); + return Arrays.asList(CustomSuggesterPlugin.class); } ----------------------------------------- From a7947b404b3f32df9ebdaa35da836bd4bc0fa760 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 24 Apr 2017 09:06:36 -0400 Subject: [PATCH 052/619] Fix hash code for AliasFilter This commit fixes the hash code for AliasFilter as the previous implementation was neglecting to take into consideration the fact that the aliases field is an array and thus a deep hash code of it should be computed rather than a shallow hash code on the reference. Relates #24286 --- .../search/internal/AliasFilter.java | 2 +- .../search/internal/AliasFilterTests.java | 62 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 core/src/test/java/org/elasticsearch/search/internal/AliasFilterTests.java diff --git a/core/src/main/java/org/elasticsearch/search/internal/AliasFilter.java b/core/src/main/java/org/elasticsearch/search/internal/AliasFilter.java index 915e745ddf456..f053b3d48fee5 100644 --- a/core/src/main/java/org/elasticsearch/search/internal/AliasFilter.java +++ b/core/src/main/java/org/elasticsearch/search/internal/AliasFilter.java @@ -126,7 +126,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(aliases, filter, reparseAliases); + return Objects.hash(reparseAliases, Arrays.hashCode(aliases), filter); } @Override diff --git a/core/src/test/java/org/elasticsearch/search/internal/AliasFilterTests.java b/core/src/test/java/org/elasticsearch/search/internal/AliasFilterTests.java new file mode 100644 index 0000000000000..ba21a63f683f5 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/internal/AliasFilterTests.java @@ -0,0 +1,62 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.internal; + +import org.elasticsearch.common.io.stream.BytesStreamOutput; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.query.TermQueryBuilder; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.EqualsHashCodeTestUtils; + +import java.util.Arrays; + +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.instanceOf; + +public class AliasFilterTests extends ESTestCase { + + public void testEqualsAndHashCode() { + final QueryBuilder filter = QueryBuilders.termQuery("field", "value"); + final String[] aliases = new String[] { "alias_0", "alias_1" }; + final AliasFilter aliasFilter = new AliasFilter(filter, aliases); + final EqualsHashCodeTestUtils.CopyFunction aliasFilterCopyFunction = x -> { + assertThat(x.getQueryBuilder(), instanceOf(TermQueryBuilder.class)); + final BytesStreamOutput out = new BytesStreamOutput(); + x.getQueryBuilder().writeTo(out); + final QueryBuilder otherFilter = new TermQueryBuilder(out.bytes().streamInput()); + final String[] otherAliases = Arrays.copyOf(x.getAliases(), x.getAliases().length); + return new AliasFilter(otherFilter, otherAliases); + }; + + final EqualsHashCodeTestUtils.MutateFunction aliasFilterMutationFunction = x -> { + assertThat(x.getQueryBuilder(), instanceOf(TermQueryBuilder.class)); + final BytesStreamOutput out = new BytesStreamOutput(); + x.getQueryBuilder().writeTo(out); + final QueryBuilder otherFilter = new TermQueryBuilder(out.bytes().streamInput()); + assertThat(x.getAliases().length, greaterThan(0)); + final String[] otherAliases = Arrays.copyOf(x.getAliases(), x.getAliases().length - 1); + return new AliasFilter(otherFilter, otherAliases); + }; + + EqualsHashCodeTestUtils.checkEqualsAndHashCode(aliasFilter, aliasFilterCopyFunction, aliasFilterMutationFunction); + } + +} From 1500beafc7baad4e8cbce0682ed76e429498ea13 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 24 Apr 2017 09:31:54 -0400 Subject: [PATCH 053/619] Check for default.path.data included in path.data If the user explicitly configured path.data to include default.path.data, then we should not fail the node if we find indices in default.path.data. This commit addresses this. Relates #24285 --- .../java/org/elasticsearch/node/Node.java | 21 ++++- .../org/elasticsearch/node/NodeTests.java | 0 .../org/elasticsearch/node/EvilNodeTests.java | 77 +++++++++++++++++++ 3 files changed, 95 insertions(+), 3 deletions(-) rename {test/framework/src/main => core/src/test}/java/org/elasticsearch/node/NodeTests.java (100%) create mode 100644 qa/evil-tests/src/test/java/org/elasticsearch/node/EvilNodeTests.java diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index 2508872eed14a..103add6280983 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -523,15 +523,21 @@ static void checkForIndexDataInDefaultPathData( boolean clean = true; for (final String defaultPathData : Environment.DEFAULT_PATH_DATA_SETTING.get(settings)) { - final Path nodeDirectory = NodeEnvironment.resolveNodePath(getPath(defaultPathData), nodeEnv.getNodeLockId()); - if (Files.exists(nodeDirectory) == false) { + final Path defaultNodeDirectory = NodeEnvironment.resolveNodePath(getPath(defaultPathData), nodeEnv.getNodeLockId()); + if (Files.exists(defaultNodeDirectory) == false) { continue; } - final NodeEnvironment.NodePath nodePath = new NodeEnvironment.NodePath(nodeDirectory); + + if (isDefaultPathDataInPathData(nodeEnv, defaultNodeDirectory)) { + continue; + } + + final NodeEnvironment.NodePath nodePath = new NodeEnvironment.NodePath(defaultNodeDirectory); final Set availableIndexFolders = nodeEnv.availableIndexFoldersForPath(nodePath); if (availableIndexFolders.isEmpty()) { continue; } + clean = false; logger.error("detected index data in default.path.data [{}] where there should not be any", nodePath.indicesPath); for (final String availableIndexFolder : availableIndexFolders) { @@ -554,6 +560,15 @@ static void checkForIndexDataInDefaultPathData( throw new IllegalStateException(message); } + private static boolean isDefaultPathDataInPathData(final NodeEnvironment nodeEnv, final Path defaultNodeDirectory) throws IOException { + for (final NodeEnvironment.NodePath dataPath : nodeEnv.nodePaths()) { + if (Files.isSameFile(dataPath.path, defaultNodeDirectory)) { + return true; + } + } + return false; + } + @SuppressForbidden(reason = "read path that is not configured in environment") private static Path getPath(final String path) { return PathUtils.get(path); diff --git a/test/framework/src/main/java/org/elasticsearch/node/NodeTests.java b/core/src/test/java/org/elasticsearch/node/NodeTests.java similarity index 100% rename from test/framework/src/main/java/org/elasticsearch/node/NodeTests.java rename to core/src/test/java/org/elasticsearch/node/NodeTests.java diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/node/EvilNodeTests.java b/qa/evil-tests/src/test/java/org/elasticsearch/node/EvilNodeTests.java new file mode 100644 index 0000000000000..ac29902627dae --- /dev/null +++ b/qa/evil-tests/src/test/java/org/elasticsearch/node/EvilNodeTests.java @@ -0,0 +1,77 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.node; + +import org.apache.logging.log4j.Logger; +import org.elasticsearch.common.UUIDs; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.env.NodeEnvironment; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +public class EvilNodeTests extends ESTestCase { + + public void testDefaultPathDataIncludedInPathData() throws IOException { + final Path zero = createTempDir().toAbsolutePath(); + final Path one = createTempDir().toAbsolutePath(); + final int random = randomIntBetween(0, 2); + final Path defaultPathData; + final Path choice = randomFrom(zero, one); + switch (random) { + case 0: + defaultPathData = choice; + break; + case 1: + defaultPathData = createTempDir().toAbsolutePath().resolve("link"); + Files.createSymbolicLink(defaultPathData, choice); + break; + case 2: + defaultPathData = createTempDir().toAbsolutePath().resolve("link"); + Files.createLink(defaultPathData, choice); + break; + default: + throw new AssertionError(Integer.toString(random)); + } + final Settings settings = Settings.builder() + .put("path.home", createTempDir().toAbsolutePath()) + .put("path.data.0", zero) + .put("path.data.1", one) + .put("default.path.data", defaultPathData) + .build(); + try (NodeEnvironment nodeEnv = new NodeEnvironment(settings, new Environment(settings))) { + final Path defaultPathDataWithNodesAndId = defaultPathData.resolve("nodes/0"); + Files.createDirectories(defaultPathDataWithNodesAndId); + final NodeEnvironment.NodePath defaultNodePath = new NodeEnvironment.NodePath(defaultPathDataWithNodesAndId); + Files.createDirectories(defaultNodePath.indicesPath.resolve(UUIDs.randomBase64UUID())); + final Logger mock = mock(Logger.class); + // nothing should happen here + Node.checkForIndexDataInDefaultPathData(settings, nodeEnv, mock); + verifyNoMoreInteractions(mock); + } + } + +} From 931198688c2d34555fb7d82a0d2cf34feea70f1d Mon Sep 17 00:00:00 2001 From: farisk Date: Mon, 24 Apr 2017 15:28:46 +0100 Subject: [PATCH 054/619] Document that painless doesn't support the "advanced text scoring" (#24271) I just spent ages debugging a script I wrote after following the documentation. It was not clear to me that _index is not defined when using painless; if it was mentioned on this page I would have saved myself a lot of time. --- docs/reference/modules/scripting/advanced-scripting.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference/modules/scripting/advanced-scripting.asciidoc b/docs/reference/modules/scripting/advanced-scripting.asciidoc index b4053d765980a..a5fcc12777dec 100644 --- a/docs/reference/modules/scripting/advanced-scripting.asciidoc +++ b/docs/reference/modules/scripting/advanced-scripting.asciidoc @@ -10,6 +10,8 @@ script inside a <>. Statistics over the document collection are computed *per shard*, not per index. +It should be noted that the `_index` variable is not supported in the painless language, but `_index` is defined when using the groovy language. + [float] === Nomenclature: From 373edee29a676444c0f423b9ee202fb0735d3ccd Mon Sep 17 00:00:00 2001 From: Nilabh Sagar Date: Mon, 24 Apr 2017 20:05:14 +0530 Subject: [PATCH 055/619] Provide informative error message in case of unknown suggestion context. (#24241) Provide a list of available contexts when you send an unknown context to the completion suggester. --- .../completion/context/ContextMappings.java | 4 ++- .../CategoryContextMappingTests.java | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/search/suggest/completion/context/ContextMappings.java b/core/src/main/java/org/elasticsearch/search/suggest/completion/context/ContextMappings.java index 0cac17dbb8176..bbefbf41b8907 100644 --- a/core/src/main/java/org/elasticsearch/search/suggest/completion/context/ContextMappings.java +++ b/core/src/main/java/org/elasticsearch/search/suggest/completion/context/ContextMappings.java @@ -81,7 +81,9 @@ public int size() { public ContextMapping get(String name) { ContextMapping contextMapping = contextNameMap.get(name); if (contextMapping == null) { - throw new IllegalArgumentException("Unknown context name[" + name + "], must be one of " + contextNameMap.size()); + List keys = new ArrayList<>(contextNameMap.keySet()); + Collections.sort(keys); + throw new IllegalArgumentException("Unknown context name [" + name + "], must be one of " + keys.toString()); } return contextMapping; } diff --git a/core/src/test/java/org/elasticsearch/search/suggest/completion/CategoryContextMappingTests.java b/core/src/test/java/org/elasticsearch/search/suggest/completion/CategoryContextMappingTests.java index a9639c7b465ac..b9b15341da021 100644 --- a/core/src/test/java/org/elasticsearch/search/suggest/completion/CategoryContextMappingTests.java +++ b/core/src/test/java/org/elasticsearch/search/suggest/completion/CategoryContextMappingTests.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.index.mapper.CompletionFieldMapper.CompletionFieldType; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; @@ -673,6 +674,31 @@ public void testQueryContextParsingMixedHavingNULL() throws Exception { Exception e = expectThrows(ElasticsearchParseException.class, () -> mapping.parseQueryContext(createParseContext(parser))); assertEquals("category context must be an object, string, number or boolean", e.getMessage()); } + + public void testUnknownQueryContextParsing() throws Exception { + String mapping = jsonBuilder().startObject().startObject("type1") + .startObject("properties").startObject("completion") + .field("type", "completion") + .startArray("contexts") + .startObject() + .field("name", "ctx") + .field("type", "category") + .endObject() + .startObject() + .field("name", "type") + .field("type", "category") + .endObject() + .endArray() + .endObject().endObject() + .endObject().endObject().string(); + + DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping)); + FieldMapper fieldMapper = defaultMapper.mappers().getMapper("completion"); + CompletionFieldType completionFieldType = (CompletionFieldType) fieldMapper.fieldType(); + + Exception e = expectThrows(IllegalArgumentException.class, () -> completionFieldType.getContextMappings().get("brand")); + assertEquals("Unknown context name [brand], must be one of [ctx, type]", e.getMessage()); + } public void testParsingContextFromDocument() throws Exception { CategoryContextMapping mapping = ContextBuilder.category("cat").field("category").build(); From dabbf5d4f44c6bd98d1fad1ca71edfb15ca4b94f Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Wed, 19 Apr 2017 10:23:27 +0200 Subject: [PATCH 056/619] [TEST] Added unittests for InternalGeoCentroid Relates to #22278 --- .../geocentroid/InternalGeoCentroid.java | 21 ++++++ .../geocentroid/InternalGeoCentroidTests.java | 66 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java index bd65cd28afff6..da69115ac6b34 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.util.List; import java.util.Map; +import java.util.Objects; /** * Serialization and merge logic for {@link GeoCentroidAggregator}. @@ -154,4 +155,24 @@ public XContentBuilder doXContentBody(XContentBuilder builder, Params params) th } return builder; } + + @Override + public boolean doEquals(Object o) { + InternalGeoCentroid that = (InternalGeoCentroid) o; + return count == that.count && + Objects.equals(centroid, that.centroid); + } + + @Override + protected int doHashCode() { + return Objects.hash(centroid, count); + } + + @Override + public String toString() { + return "InternalGeoCentroid{" + + "centroid=" + centroid + + ", count=" + count + + '}'; + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java new file mode 100644 index 0000000000000..c409d2aa7950e --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java @@ -0,0 +1,66 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations.metrics.geocentroid; + +import org.apache.lucene.geo.GeoEncodingUtils; +import org.elasticsearch.common.geo.GeoPoint; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.geo.RandomGeoGenerator; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class InternalGeoCentroidTests extends InternalAggregationTestCase { + + @Override + protected InternalGeoCentroid createTestInstance(String name, List pipelineAggregators, + Map metaData) { + GeoPoint centroid = RandomGeoGenerator.randomPoint(random()); + + // Re-encode lat/longs to avoid rounding issue when testing InternalGeoCentroid#hashCode() and + // InternalGeoCentroid#equals() + int encodedLon = GeoEncodingUtils.encodeLongitude(centroid.lon()); + centroid.resetLon(GeoEncodingUtils.decodeLongitude(encodedLon)); + int encodedLat = GeoEncodingUtils.encodeLatitude(centroid.lat()); + centroid.resetLat(GeoEncodingUtils.decodeLatitude(encodedLat)); + + return new InternalGeoCentroid("_name", centroid, 1, Collections.emptyList(), Collections.emptyMap()); + } + + @Override + protected Writeable.Reader instanceReader() { + return InternalGeoCentroid::new; + } + + @Override + protected void assertReduced(InternalGeoCentroid reduced, List inputs) { + GeoPoint expected = new GeoPoint(0, 0); + int i = 0; + for (InternalGeoCentroid input : inputs) { + expected.reset(expected.lat() + (input.centroid().lat() - expected.lat()) / (i+1), + expected.lon() + (input.centroid().lon() - expected.lon()) / (i+1)); + i++; + } + assertEquals(expected.getLat(), reduced.centroid().getLat(), 1E-5D); + assertEquals(expected.getLon(), reduced.centroid().getLon(), 1E-5D); + } +} From c5b6f52eccb2a663770e0eb9fee496432e1a8d5c Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Mon, 24 Apr 2017 10:59:08 -0400 Subject: [PATCH 057/619] Fixes maintaining the shards a snapshot is waiting on (#24289) There was a bug in the calculation of the shards that a snapshot must wait on, due to their relocating or initializing, before the snapshot can proceed safely to snapshot the shard data. In this bug, an incorrect key was used to look up the index of the waiting shards, resulting in the fact that each index would have at most one shard in the waiting state causing the snapshot to pause. This could be problematic if there are more than one shard in the relocating or initializing state, which would result in a snapshot prematurely starting because it thinks its only waiting on one relocating or initializing shard (when in fact there could be more than one). While not a common case and likely rare in practice, it is still problematic. This commit fixes the issue by ensuring the correct key is used to look up the waiting indices map as it is being built up, so the list of waiting shards for each index (those shards that are relocating or initializing) are aggregated for a given index instead of overwritten. --- .../cluster/SnapshotsInProgress.java | 9 ++- .../cluster/SnapshotsInProgressTests.java | 78 +++++++++++++++++++ 2 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/cluster/SnapshotsInProgressTests.java diff --git a/core/src/main/java/org/elasticsearch/cluster/SnapshotsInProgress.java b/core/src/main/java/org/elasticsearch/cluster/SnapshotsInProgress.java index 1e1b61281b4b5..5b8d391889480 100644 --- a/core/src/main/java/org/elasticsearch/cluster/SnapshotsInProgress.java +++ b/core/src/main/java/org/elasticsearch/cluster/SnapshotsInProgress.java @@ -195,14 +195,16 @@ public String toString() { return snapshot.toString(); } - private ImmutableOpenMap> findWaitingIndices(ImmutableOpenMap shards) { + // package private for testing + ImmutableOpenMap> findWaitingIndices(ImmutableOpenMap shards) { Map> waitingIndicesMap = new HashMap<>(); for (ObjectObjectCursor entry : shards) { if (entry.value.state() == State.WAITING) { - List waitingShards = waitingIndicesMap.get(entry.key.getIndex()); + final String indexName = entry.key.getIndexName(); + List waitingShards = waitingIndicesMap.get(indexName); if (waitingShards == null) { waitingShards = new ArrayList<>(); - waitingIndicesMap.put(entry.key.getIndexName(), waitingShards); + waitingIndicesMap.put(indexName, waitingShards); } waitingShards.add(entry.key); } @@ -216,7 +218,6 @@ private ImmutableOpenMap> findWaitingIndices(ImmutableOpen } return waitingIndicesBuilder.build(); } - } /** diff --git a/core/src/test/java/org/elasticsearch/cluster/SnapshotsInProgressTests.java b/core/src/test/java/org/elasticsearch/cluster/SnapshotsInProgressTests.java new file mode 100644 index 0000000000000..4d1a1a6e588e0 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/cluster/SnapshotsInProgressTests.java @@ -0,0 +1,78 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.cluster; + +import org.elasticsearch.cluster.SnapshotsInProgress.Entry; +import org.elasticsearch.cluster.SnapshotsInProgress.ShardSnapshotStatus; +import org.elasticsearch.cluster.SnapshotsInProgress.State; +import org.elasticsearch.common.collect.ImmutableOpenMap; +import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.repositories.IndexId; +import org.elasticsearch.snapshots.Snapshot; +import org.elasticsearch.snapshots.SnapshotId; +import org.elasticsearch.test.ESTestCase; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Unit tests for the {@link SnapshotsInProgress} class and its inner classes. + */ +public class SnapshotsInProgressTests extends ESTestCase { + + /** + * Makes sure that the indices being waited on before snapshotting commences + * are populated with all shards in the relocating or initializing state. + */ + public void testWaitingIndices() { + final Snapshot snapshot = new Snapshot("repo", new SnapshotId("snap", randomAlphaOfLength(5))); + final String idx1Name = "idx1"; + final String idx2Name = "idx2"; + final String idx3Name = "idx3"; + final String idx1UUID = randomAlphaOfLength(5); + final String idx2UUID = randomAlphaOfLength(5); + final String idx3UUID = randomAlphaOfLength(5); + final List indices = Arrays.asList(new IndexId(idx1Name, randomAlphaOfLength(5)), + new IndexId(idx2Name, randomAlphaOfLength(5)), new IndexId(idx3Name, randomAlphaOfLength(5))); + ImmutableOpenMap.Builder shards = ImmutableOpenMap.builder(); + + // test more than one waiting shard in an index + shards.put(new ShardId(idx1Name, idx1UUID, 0), new ShardSnapshotStatus(randomAlphaOfLength(2), State.WAITING)); + shards.put(new ShardId(idx1Name, idx1UUID, 1), new ShardSnapshotStatus(randomAlphaOfLength(2), State.WAITING)); + shards.put(new ShardId(idx1Name, idx1UUID, 2), new ShardSnapshotStatus(randomAlphaOfLength(2), randomNonWaitingState())); + // test exactly one waiting shard in an index + shards.put(new ShardId(idx2Name, idx2UUID, 0), new ShardSnapshotStatus(randomAlphaOfLength(2), State.WAITING)); + shards.put(new ShardId(idx2Name, idx2UUID, 1), new ShardSnapshotStatus(randomAlphaOfLength(2), randomNonWaitingState())); + // test no waiting shards in an index + shards.put(new ShardId(idx3Name, idx3UUID, 0), new ShardSnapshotStatus(randomAlphaOfLength(2), randomNonWaitingState())); + Entry entry = new Entry(snapshot, randomBoolean(), randomBoolean(), State.INIT, + indices, System.currentTimeMillis(), randomLong(), shards.build()); + + ImmutableOpenMap> waitingIndices = entry.waitingIndices(); + assertEquals(2, waitingIndices.get(idx1Name).size()); + assertEquals(1, waitingIndices.get(idx2Name).size()); + assertFalse(waitingIndices.containsKey(idx3Name)); + } + + private State randomNonWaitingState() { + return randomFrom(Arrays.stream(State.values()).filter(s -> s != State.WAITING).collect(Collectors.toSet())); + } +} From c9a6d66bd5f67e9fc65204529234a9f3ee3b2932 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 24 Apr 2017 11:02:21 -0400 Subject: [PATCH 058/619] Only test hard linking to directory on macOS This skips trying to create a hard link to a directory in the evil node tests on non-macOS operating systems. --- .../test/java/org/elasticsearch/node/EvilNodeTests.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/node/EvilNodeTests.java b/qa/evil-tests/src/test/java/org/elasticsearch/node/EvilNodeTests.java index ac29902627dae..341d1227926e8 100644 --- a/qa/evil-tests/src/test/java/org/elasticsearch/node/EvilNodeTests.java +++ b/qa/evil-tests/src/test/java/org/elasticsearch/node/EvilNodeTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.node; import org.apache.logging.log4j.Logger; +import org.apache.lucene.util.Constants; import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; @@ -38,7 +39,13 @@ public class EvilNodeTests extends ESTestCase { public void testDefaultPathDataIncludedInPathData() throws IOException { final Path zero = createTempDir().toAbsolutePath(); final Path one = createTempDir().toAbsolutePath(); - final int random = randomIntBetween(0, 2); + // creating hard links to directories is okay on macOS so we exercise it here + final int random; + if (Constants.MAC_OS_X) { + random = randomFrom(0, 1, 2); + } else { + random = randomFrom(0, 1); + } final Path defaultPathData; final Path choice = randomFrom(zero, one); switch (random) { From 026bf2e3ee1ee5c188e57839d9e902962158138b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Mon, 24 Apr 2017 18:40:57 +0200 Subject: [PATCH 059/619] Remove getCountAsString() from InternalStats and Stats interface (#24291) The `count` value in the stats aggregation represents a simple doc count that doesn't require a formatted version. We didn't render an "as_string" version for count in the rest response, so the method should also be removed in favour of just using String.valueOf(getCount()) if a string version of the count is needed. Closes #24287 --- .../search/aggregations/metrics/stats/InternalStats.java | 5 ----- .../search/aggregations/metrics/stats/Stats.java | 5 ----- .../search/aggregations/metrics/ExtendedStatsIT.java | 1 - .../elasticsearch/search/aggregations/metrics/StatsIT.java | 1 - docs/reference/migration/migrate_6_0/java.asciidoc | 6 ++++++ 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/InternalStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/InternalStats.java index 08c9292d54e62..b0b2ea73d3c9f 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/InternalStats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/InternalStats.java @@ -112,11 +112,6 @@ public double getSum() { return sum; } - @Override - public String getCountAsString() { - return valueAsString(Metrics.count.name()); - } - @Override public String getMinAsString() { return valueAsString(Metrics.min.name()); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/Stats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/Stats.java index 4910dc140026d..46620f51dc2fc 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/Stats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/Stats.java @@ -50,11 +50,6 @@ public interface Stats extends NumericMetricsAggregation.MultiValue { */ double getSum(); - /** - * @return The number of values that were aggregated as a String. - */ - String getCountAsString(); - /** * @return The minimum value of all aggregated values as a String. */ diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java index 6ec3f090d45fa..7de7bb0f31596 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java @@ -214,7 +214,6 @@ public void testSingleValuedField_WithFormatter() throws Exception { assertThat(stats.getSum(), equalTo((double) 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10)); assertThat(stats.getSumAsString(), equalTo("0055.0")); assertThat(stats.getCount(), equalTo(10L)); - assertThat(stats.getCountAsString(), equalTo("0010.0")); assertThat(stats.getSumOfSquares(), equalTo((double) 1 + 4 + 9 + 16 + 25 + 36 + 49 + 64 + 81 + 100)); assertThat(stats.getSumOfSquaresAsString(), equalTo("0385.0")); assertThat(stats.getVariance(), equalTo(variance(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java index 8b849f625c187..85db61663072a 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java @@ -162,7 +162,6 @@ public void testSingleValuedField_WithFormatter() throws Exception { assertThat(stats.getSum(), equalTo((double) 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10)); assertThat(stats.getSumAsString(), equalTo("0055.0")); assertThat(stats.getCount(), equalTo(10L)); - assertThat(stats.getCountAsString(), equalTo("0010.0")); } @Override diff --git a/docs/reference/migration/migrate_6_0/java.asciidoc b/docs/reference/migration/migrate_6_0/java.asciidoc index 991fe165fb231..a8954732657a3 100644 --- a/docs/reference/migration/migrate_6_0/java.asciidoc +++ b/docs/reference/migration/migrate_6_0/java.asciidoc @@ -13,3 +13,9 @@ providing the source in bytes or as a string. In previous versions of Elasticsearch, delete by query requests without an explicit query were accepted, match_all was used as the default query and all documents were deleted as a result. From version 6.0.0, a `DeleteByQueryRequest` requires an explicit query be set. + +=== `InternalStats` and `Stats` getCountAsString() method removed + +The `count` value in the stats aggregation represents a doc count that shouldnn't require a formatted +version. This method was deprecated in 5.4 in favour of just using +`String.valueOf(getCount())` if needed From 30cc33e2e542810a9c893297cdc79c0ab8ea3bdb Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Mon, 24 Apr 2017 09:58:02 -0700 Subject: [PATCH 060/619] Fix Painless Lambdas for Java 9 (#24070) Replaces LambdaMetaFactory with LambdaBootstrap, a custom solution for lambdas in Painless using a design similar to LambdaMetaFactory, but allows for custom adaptation of types which recent changes to LambdaMetaFactory no longer allowed. --- .../org/elasticsearch/painless/Compiler.java | 14 +- .../java/org/elasticsearch/painless/Def.java | 65 +-- .../elasticsearch/painless/FeatureTest.java | 13 +- .../elasticsearch/painless/FunctionRef.java | 210 ++++--- .../painless/LambdaBootstrap.java | 530 ++++++++++++++++++ .../painless/WriterConstants.java | 17 +- .../elasticsearch/painless/antlr/Walker.java | 2 +- .../painless/node/ECapturingFunctionRef.java | 47 +- .../painless/node/EFunctionRef.java | 54 +- .../elasticsearch/painless/node/ELambda.java | 70 ++- .../painless/node/SFunction.java | 2 +- .../painless/org.elasticsearch.txt | 1 + .../painless/AugmentationTests.java | 144 ++--- .../painless/FunctionRefTests.java | 132 ++--- .../elasticsearch/painless/LambdaTests.java | 42 +- .../painless/node/NodeToStringTests.java | 8 +- 16 files changed, 890 insertions(+), 461 deletions(-) create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/LambdaBootstrap.java diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java index 9961dcbe156f9..976ef897aec2f 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java @@ -79,9 +79,19 @@ static final class Loader extends SecureClassLoader { * @param bytes The generated byte code. * @return A Class object extending {@link PainlessScript}. */ - Class define(String name, byte[] bytes) { + Class defineScript(String name, byte[] bytes) { return defineClass(name, bytes, 0, bytes.length, CODESOURCE).asSubclass(PainlessScript.class); } + + /** + * Generates a Class object for a lambda method. + * @param name The name of the class. + * @param bytes The generated byte code. + * @return A Class object. + */ + Class defineLambda(String name, byte[] bytes) { + return defineClass(name, bytes, 0, bytes.length); + } } /** @@ -110,7 +120,7 @@ static T compile(Loader loader, Class iface, String name, String source, root.write(); try { - Class clazz = loader.define(CLASS_NAME, root.getBytes()); + Class clazz = loader.defineScript(CLASS_NAME, root.getBytes()); clazz.getField("$DEFINITION").set(null, definition); java.lang.reflect.Constructor constructor = clazz.getConstructor(String.class, String.class, BitSet.class); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index 5250238c81784..6d9fad7d79e17 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java @@ -23,7 +23,6 @@ import org.elasticsearch.painless.Definition.RuntimeClass; import java.lang.invoke.CallSite; -import java.lang.invoke.LambdaMetafactory; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; @@ -132,7 +131,7 @@ private ArrayLengthHelper() {} } catch (final ReflectiveOperationException roe) { throw new AssertionError(roe); } - + // lookup up the factory for arraylength MethodHandle (intrinsic) from Java 9: // https://bugs.openjdk.java.net/browse/JDK-8156915 MethodHandle arrayLengthMHFactory; @@ -150,7 +149,7 @@ private ArrayLengthHelper() {} static void rethrow(Throwable t) throws T { throw (T) t; } - + /** Returns an array length getter MethodHandle for the given array type */ static MethodHandle arrayLengthGetter(Class arrayType) { if (JAVA9_ARRAY_LENGTH_MH_FACTORY != null) { @@ -206,7 +205,7 @@ static Method lookupMethodInternal(Definition definition, Class receiverClass } } } - + throw new IllegalArgumentException("Unable to find dynamic method [" + name + "] with [" + arity + "] arguments " + "for class [" + receiverClass.getCanonicalName() + "]."); } @@ -239,7 +238,7 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp if (recipeString.isEmpty()) { return lookupMethodInternal(definition, receiverClass, name, numArguments - 1).handle; } - + // convert recipe string to a bitset for convenience (the code below should be refactored...) BitSet lambdaArgs = new BitSet(); for (int i = 0; i < recipeString.length(); i++) { @@ -247,7 +246,7 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp } // otherwise: first we have to compute the "real" arity. This is because we have extra arguments: - // e.g. f(a, g(x), b, h(y), i()) looks like f(a, g, x, b, h, y, i). + // e.g. f(a, g(x), b, h(y), i()) looks like f(a, g, x, b, h, y, i). int arity = callSiteType.parameterCount() - 1; int upTo = 1; for (int i = 1; i < numArguments; i++) { @@ -257,7 +256,7 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp arity -= numCaptures; } } - + // lookup the method with the proper arity, then we know everything (e.g. interface types of parameters). // based on these we can finally link any remaining lambdas that were deferred. Method method = lookupMethodInternal(definition, receiverClass, name, arity); @@ -268,7 +267,7 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp for (int i = 1; i < numArguments; i++) { // its a functional reference, replace the argument with an impl if (lambdaArgs.get(i - 1)) { - // decode signature of form 'type.call,2' + // decode signature of form 'type.call,2' String signature = (String) args[upTo++]; int separator = signature.lastIndexOf('.'); int separator2 = signature.indexOf(','); @@ -313,10 +312,10 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp replaced += numCaptures; } } - + return handle; } - + /** * Returns an implementation of interfaceClass that calls receiverClass.name *

@@ -335,7 +334,7 @@ static MethodHandle lookupReference(Definition definition, Lookup lookup, String return lookupReferenceInternal(definition, lookup, interfaceType, implMethod.owner.name, implMethod.name, receiverClass); } - + /** Returns a method handle to an implementation of clazz, given method reference signature. */ private static MethodHandle lookupReferenceInternal(Definition definition, Lookup lookup, Definition.Type clazz, String type, String call, Class... captures) @@ -351,47 +350,37 @@ private static MethodHandle lookupReferenceInternal(Definition definition, Looku int arity = interfaceMethod.arguments.size() + captures.length; final MethodHandle handle; try { - MethodHandle accessor = lookup.findStaticGetter(lookup.lookupClass(), - getUserFunctionHandleFieldName(call, arity), + MethodHandle accessor = lookup.findStaticGetter(lookup.lookupClass(), + getUserFunctionHandleFieldName(call, arity), MethodHandle.class); - handle = (MethodHandle) accessor.invokeExact(); + handle = (MethodHandle)accessor.invokeExact(); } catch (NoSuchFieldException | IllegalAccessException e) { // is it a synthetic method? If we generated the method ourselves, be more helpful. It can only fail // because the arity does not match the expected interface type. if (call.contains("$")) { - throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.name + + throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.name + "] in [" + clazz.clazz + "]"); } throw new IllegalArgumentException("Unknown call [" + call + "] with [" + arity + "] arguments."); } - ref = new FunctionRef(clazz, interfaceMethod, handle, captures.length); + ref = new FunctionRef(clazz, interfaceMethod, call, handle.type(), captures.length); } else { // whitelist lookup ref = new FunctionRef(definition, clazz, type, call, captures.length); } - final CallSite callSite; - if (ref.needsBridges()) { - callSite = LambdaMetafactory.altMetafactory(lookup, - ref.invokedName, - ref.invokedType, - ref.samMethodType, - ref.implMethod, - ref.samMethodType, - LambdaMetafactory.FLAG_BRIDGES, - 1, - ref.interfaceMethodType); - } else { - callSite = LambdaMetafactory.altMetafactory(lookup, - ref.invokedName, - ref.invokedType, - ref.samMethodType, - ref.implMethod, - ref.samMethodType, - 0); - } + final CallSite callSite = LambdaBootstrap.lambdaBootstrap( + lookup, + ref.interfaceMethodName, + ref.factoryMethodType, + ref.interfaceMethodType, + ref.delegateClassName, + ref.delegateInvokeType, + ref.delegateMethodName, + ref.delegateMethodType + ); return callSite.dynamicInvoker().asType(MethodType.methodType(clazz.clazz, captures)); } - + /** gets the field name used to lookup up the MethodHandle for a function. */ public static String getUserFunctionHandleFieldName(String name, int arity) { return "handle$" + name + "$" + arity; @@ -595,7 +584,7 @@ static MethodHandle lookupArrayLoad(Class receiverClass) { throw new IllegalArgumentException("Attempting to address a non-array type " + "[" + receiverClass.getCanonicalName() + "] as an array."); } - + /** Helper class for isolating MethodHandles and methods to get iterators over arrays * (to emulate "enhanced for loop" using MethodHandles). These cause boxing, and are not as efficient * as they could be, but works. diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FeatureTest.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FeatureTest.java index 603023e61fe1e..1561aeddf83e3 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FeatureTest.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FeatureTest.java @@ -1,5 +1,6 @@ package org.elasticsearch.painless; +import java.util.List; import java.util.function.Function; /* @@ -25,11 +26,11 @@ public class FeatureTest { private int x; private int y; - + /** empty ctor */ public FeatureTest() { } - + /** ctor with params */ public FeatureTest(int x, int y) { this.x = x; @@ -60,14 +61,18 @@ public void setY(int y) { public static boolean overloadedStatic() { return true; } - + /** static method that returns what you ask it */ public static boolean overloadedStatic(boolean whatToReturn) { return whatToReturn; } - + /** method taking two functions! */ public Object twoFunctionsOfX(Function f, Function g) { return f.apply(g.apply(x)); } + + public void listInput(List list) { + + } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java index dddd91663115d..6bfe911d974c7 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java @@ -20,125 +20,138 @@ package org.elasticsearch.painless; import org.elasticsearch.painless.Definition.Method; -import org.objectweb.asm.Handle; -import org.objectweb.asm.Opcodes; +import org.elasticsearch.painless.Definition.Type; +import org.elasticsearch.painless.api.Augmentation; -import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.lang.reflect.Modifier; -/** - * Reference to a function or lambda. +import static org.elasticsearch.painless.WriterConstants.CLASS_NAME; +import static org.objectweb.asm.Opcodes.H_INVOKEINTERFACE; +import static org.objectweb.asm.Opcodes.H_INVOKESTATIC; +import static org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL; +import static org.objectweb.asm.Opcodes.H_NEWINVOKESPECIAL; + +/** + * Reference to a function or lambda. *

- * Once you have created one of these, you have "everything you need" to call LambdaMetaFactory - * either statically from bytecode with invokedynamic, or at runtime from Java. + * Once you have created one of these, you have "everything you need" to call {@link LambdaBootstrap} + * either statically from bytecode with invokedynamic, or at runtime from Java. */ public class FunctionRef { - /** Function Object's method name */ - public final String invokedName; - /** CallSite signature */ - public final MethodType invokedType; - /** Implementation method */ - public final MethodHandle implMethod; - /** Function Object's method signature */ - public final MethodType samMethodType; - /** When bridging is required, request this bridge interface */ + + /** functional interface method name */ + public final String interfaceMethodName; + /** factory (CallSite) method signature */ + public final MethodType factoryMethodType; + /** functional interface method signature */ public final MethodType interfaceMethodType; - - /** ASM "Handle" to the method, for the constant pool */ - public final Handle implMethodASM; - + /** class of the delegate method to be called */ + public final String delegateClassName; + /** the invocation type of the delegate method */ + public final int delegateInvokeType; + /** the name of the delegate method */ + public final String delegateMethodName; + /** delegate method signature */ + public final MethodType delegateMethodType; + + /** interface method */ + public final Method interfaceMethod; + /** delegate method */ + public final Method delegateMethod; + + /** factory method type descriptor */ + public final String factoryDescriptor; + /** functional interface method as type */ + public final org.objectweb.asm.Type interfaceType; + /** delegate method type method as type */ + public final org.objectweb.asm.Type delegateType; + /** * Creates a new FunctionRef, which will resolve {@code type::call} from the whitelist. * @param definition the whitelist against which this script is being compiled - * @param expected interface type to implement. + * @param expected functional interface type to implement. * @param type the left hand side of a method reference expression * @param call the right hand side of a method reference expression * @param numCaptures number of captured arguments - */ - public FunctionRef(Definition definition, Definition.Type expected, String type, String call, - int numCaptures) { - this(expected, expected.struct.getFunctionalMethod(), - lookup(definition, expected, type, call, numCaptures > 0), numCaptures); + */ + public FunctionRef(Definition definition, Type expected, String type, String call, int numCaptures) { + this(expected, expected.struct.getFunctionalMethod(), lookup(definition, expected, type, call, numCaptures > 0), numCaptures); } /** * Creates a new FunctionRef (already resolved) - * @param expected interface type to implement - * @param method functional interface method - * @param impl implementation method + * @param expected functional interface type to implement + * @param interfaceMethod functional interface method + * @param delegateMethod implementation method * @param numCaptures number of captured arguments - */ - public FunctionRef(Definition.Type expected, Definition.Method method, Definition.Method impl, int numCaptures) { - // e.g. compareTo - invokedName = method.name; - // e.g. (Object)Comparator - MethodType implType = impl.getMethodType(); - // only include captured parameters as arguments - invokedType = MethodType.methodType(expected.clazz, - implType.dropParameterTypes(numCaptures, implType.parameterCount())); - // e.g. (Object,Object)int - interfaceMethodType = method.getMethodType().dropParameterTypes(0, 1); - - final int tag; - if ("".equals(impl.name)) { - tag = Opcodes.H_NEWINVOKESPECIAL; - } else if (Modifier.isStatic(impl.modifiers)) { - tag = Opcodes.H_INVOKESTATIC; - } else if (impl.owner.clazz.isInterface()) { - tag = Opcodes.H_INVOKEINTERFACE; + */ + public FunctionRef(Type expected, Method interfaceMethod, Method delegateMethod, int numCaptures) { + MethodType delegateMethodType = delegateMethod.getMethodType(); + + interfaceMethodName = interfaceMethod.name; + factoryMethodType = MethodType.methodType(expected.clazz, + delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); + interfaceMethodType = interfaceMethod.getMethodType().dropParameterTypes(0, 1); + + // the Painless$Script class can be inferred if owner is null + if (delegateMethod.owner == null) { + delegateClassName = CLASS_NAME; + } else if (delegateMethod.augmentation) { + delegateClassName = Augmentation.class.getName(); } else { - tag = Opcodes.H_INVOKEVIRTUAL; + delegateClassName = delegateMethod.owner.clazz.getName(); } - final String owner; - final boolean ownerIsInterface; - if (impl.owner == null) { - // owner == null: script class itself - ownerIsInterface = false; - owner = WriterConstants.CLASS_TYPE.getInternalName(); - } else if (impl.augmentation) { - ownerIsInterface = false; - owner = WriterConstants.AUGMENTATION_TYPE.getInternalName(); + + if ("".equals(delegateMethod.name)) { + delegateInvokeType = H_NEWINVOKESPECIAL; + } else if (Modifier.isStatic(delegateMethod.modifiers)) { + delegateInvokeType = H_INVOKESTATIC; + } else if (delegateMethod.owner.clazz.isInterface()) { + delegateInvokeType = H_INVOKEINTERFACE; } else { - ownerIsInterface = impl.owner.clazz.isInterface(); - owner = impl.owner.type.getInternalName(); + delegateInvokeType = H_INVOKEVIRTUAL; } - implMethodASM = new Handle(tag, owner, impl.name, impl.method.getDescriptor(), ownerIsInterface); - implMethod = impl.handle; - - // remove any prepended captured arguments for the 'natural' signature. - samMethodType = adapt(interfaceMethodType, impl.getMethodType().dropParameterTypes(0, numCaptures)); + + delegateMethodName = delegateMethod.name; + this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures); + + this.interfaceMethod = interfaceMethod; + this.delegateMethod = delegateMethod; + + factoryDescriptor = factoryMethodType.toMethodDescriptorString(); + interfaceType = org.objectweb.asm.Type.getMethodType(interfaceMethodType.toMethodDescriptorString()); + delegateType = org.objectweb.asm.Type.getMethodType(this.delegateMethodType.toMethodDescriptorString()); } /** - * Creates a new FunctionRef (low level). - *

- * This will not set implMethodASM. It is for runtime use only. + * Creates a new FunctionRef (low level). + * It is for runtime use only. */ - public FunctionRef(Definition.Type expected, Definition.Method method, MethodHandle impl, int numCaptures) { - // e.g. compareTo - invokedName = method.name; - // e.g. (Object)Comparator - MethodType implType = impl.type(); - // only include captured parameters as arguments - invokedType = MethodType.methodType(expected.clazz, - implType.dropParameterTypes(numCaptures, implType.parameterCount())); - // e.g. (Object,Object)int - interfaceMethodType = method.getMethodType().dropParameterTypes(0, 1); - - implMethod = impl; - - implMethodASM = null; - - // remove any prepended captured arguments for the 'natural' signature. - samMethodType = adapt(interfaceMethodType, impl.type().dropParameterTypes(0, numCaptures)); + public FunctionRef(Type expected, Method interfaceMethod, String delegateMethodName, MethodType delegateMethodType, int numCaptures) { + interfaceMethodName = interfaceMethod.name; + factoryMethodType = MethodType.methodType(expected.clazz, + delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); + interfaceMethodType = interfaceMethod.getMethodType().dropParameterTypes(0, 1); + + delegateClassName = CLASS_NAME; + delegateInvokeType = H_INVOKESTATIC; + this.delegateMethodName = delegateMethodName; + this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures); + + this.interfaceMethod = null; + delegateMethod = null; + + factoryDescriptor = null; + interfaceType = null; + delegateType = null; } - /** + /** * Looks up {@code type::call} from the whitelist, and returns a matching method. */ private static Definition.Method lookup(Definition definition, Definition.Type expected, - String type, String call, boolean receiverCaptured) { + String type, String call, boolean receiverCaptured) { // check its really a functional interface // for e.g. Comparable Method method = expected.struct.getFunctionalMethod(); @@ -177,27 +190,4 @@ private static Definition.Method lookup(Definition definition, Definition.Type e } return impl; } - - /** Returns true if you should ask LambdaMetaFactory to construct a bridge for the interface signature */ - public boolean needsBridges() { - // currently if the interface differs, we ask for a bridge, but maybe we should do smarter checking? - // either way, stuff will fail if its wrong :) - return interfaceMethodType.equals(samMethodType) == false; - } - - /** - * If the interface expects a primitive type to be returned, we can't return Object, - * But we can set SAM to the wrapper version, and a cast will take place - */ - private MethodType adapt(MethodType expected, MethodType actual) { - // add some checks, now that we've set everything up, to deliver exceptions as early as possible. - if (expected.parameterCount() != actual.parameterCount()) { - throw new IllegalArgumentException("Incorrect number of parameters for [" + invokedName + - "] in [" + invokedType.returnType() + "]"); - } - if (expected.returnType().isPrimitive() && actual.returnType() == Object.class) { - actual = actual.changeReturnType(MethodType.methodType(expected.returnType()).wrap().returnType()); - } - return actual; - } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/LambdaBootstrap.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/LambdaBootstrap.java new file mode 100644 index 0000000000000..746467c454bee --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/LambdaBootstrap.java @@ -0,0 +1,530 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.painless; + +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.GeneratorAdapter; +import org.objectweb.asm.commons.Method; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.LambdaConversionException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.concurrent.atomic.AtomicLong; + +import static java.lang.invoke.MethodHandles.Lookup; +import static org.elasticsearch.painless.Compiler.Loader; +import static org.elasticsearch.painless.WriterConstants.CLASS_VERSION; +import static org.elasticsearch.painless.WriterConstants.DELEGATE_BOOTSTRAP_HANDLE; +import static org.objectweb.asm.Opcodes.ACC_FINAL; +import static org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.objectweb.asm.Opcodes.ACC_SUPER; +import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC; +import static org.objectweb.asm.Opcodes.H_INVOKEINTERFACE; +import static org.objectweb.asm.Opcodes.H_INVOKESTATIC; +import static org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL; +import static org.objectweb.asm.Opcodes.H_NEWINVOKESPECIAL; + +/** + * LambdaBootstrap is used to generate all the code necessary to execute + * lambda functions and method references within Painless. The code generation + * used here is based upon the following article: + * http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html + * However, it is a simplified version as Painless has no concept of generics + * or serialization. LambdaBootstrap is being used as a replacement for + * {@link java.lang.invoke.LambdaMetafactory} since the Painless casting model + * cannot be fully supported through this class. + * + * For each lambda function/method reference used within a Painless script + * a class will be generated at link-time using the + * {@link LambdaBootstrap#lambdaBootstrap} method that contains the following: + * 1. member fields for any captured variables + * 2. a constructor that will take in captured variables and assign them to + * their respective member fields + * 3. if there are captures, a factory method that will take in captured + * variables and delegate them to the constructor + * 4. a method that will load the member fields representing captured variables + * and take in any other necessary values based on the arguments passed into the + * lambda function/reference method; it will then make a delegated call to the + * actual lambda function/reference method + * + * Take for example the following Painless script: + * + * {@code + * List list1 = new ArrayList(); " + * list1.add(2); " + * List list2 = new ArrayList(); " + * list1.forEach(x -> list2.add(x));" + * return list[0]" + * } + * + * The script contains a lambda function with a captured variable. + * The following Lambda class would be generated: + * + * {@code + * public static final class $$Lambda0 implements Consumer { + * private List arg$0; + * + * public $$Lambda0(List arg$0) { + * this.arg$0 = arg$0; + * } + * + * public static $$Lambda0 get$Lambda(List arg$0) { + * return $$Lambda0(arg$0); + * } + * + * public void accept(Object val$0) { + * Painless$Script.lambda$0(this.arg$0, val$0); + * } + * } + * + * public class Painless$Script implements ... { + * ... + * public static lambda$0(List list2, Object x) { + * list2.add(x); + * } + * ... + * } + * } + * + * Note if the above didn't have a captured variable then + * the factory method get$Lambda would not have been generated. + * Also the accept method actually uses an invokedynamic + * instruction to call the lambda$0 method so that + * {@link MethodHandle#asType} can be used to do the necessary + * conversions between argument types without having to hard + * code them. + * + * When the {@link CallSite} is linked the linked method depends + * on whether or not there are captures. If there are no captures + * the same instance of the generated lambda class will be + * returned each time by the factory method as there are no + * changing values other than the arguments. If there are + * captures a new instance of the generated lambda class will + * be returned each time with the captures passed into the + * factory method to be stored in the member fields. + */ +public final class LambdaBootstrap { + + /** + * Metadata for a captured variable used during code generation. + */ + private static final class Capture { + private final String name; + private final Type type; + private final String desc; + + /** + * Converts incoming parameters into the name, type, and + * descriptor for the captured argument. + * @param count The captured argument count + * @param type The class type of the captured argument + */ + private Capture(int count, Class type) { + this.name = "arg$" + count; + this.type = Type.getType(type); + this.desc = this.type.getDescriptor(); + } + } + + /** + * A counter used to generate a unique name + * for each lambda function/reference class. + */ + private static final AtomicLong COUNTER = new AtomicLong(0); + + /** + * Generates a lambda class for a lambda function/method reference + * within a Painless script. Variables with the prefix interface are considered + * to represent values for code generated for the lambda class. Variables with + * the prefix delegate are considered to represent values for code generated + * within the Painless script. The interface method delegates (calls) to the + * delegate method. + * @param lookup Standard {@link MethodHandles#lookup} + * @param interfaceMethodName Name of functional interface method that is called + * @param factoryMethodType The type of method to be linked to this CallSite; note that + * captured types are based on the parameters for this method + * @param interfaceMethodType The type of method representing the functional interface method + * @param delegateClassName The name of the Painless script class + * @param delegateInvokeType The type of method call to be made + * (static, virtual, interface, or constructor) + * @param delegateMethodName The name of the method to be called in the Painless script class + * @param delegateMethodType The type of method call in the Painless script class without + * the captured types + * @return A {@link CallSite} linked to a factory method for creating a lambda class + * that implements the expected functional interface + * @throws LambdaConversionException Thrown when an illegal type conversion occurs at link time + */ + public static CallSite lambdaBootstrap( + Lookup lookup, + String interfaceMethodName, + MethodType factoryMethodType, + MethodType interfaceMethodType, + String delegateClassName, + int delegateInvokeType, + String delegateMethodName, + MethodType delegateMethodType) + throws LambdaConversionException { + String factoryMethodName = "get$lambda"; + String lambdaClassName = lookup.lookupClass().getName().replace('.', '/') + + "$$Lambda" + COUNTER.getAndIncrement(); + Type lambdaClassType = Type.getType("L" + lambdaClassName + ";"); + + validateTypes(interfaceMethodType, delegateMethodType); + + ClassWriter cw = + beginLambdaClass(lambdaClassName, factoryMethodType.returnType().getName()); + Capture[] captures = generateCaptureFields(cw, factoryMethodType); + Method constructorMethod = + generateLambdaConstructor(cw, lambdaClassType, factoryMethodType, captures); + + if (captures.length > 0) { + generateFactoryMethod( + cw, factoryMethodName, factoryMethodType, lambdaClassType, constructorMethod); + } + + generateInterfaceMethod(cw, factoryMethodType, lambdaClassName, lambdaClassType, + interfaceMethodName, interfaceMethodType, delegateClassName, delegateInvokeType, + delegateMethodName, delegateMethodType, captures); + endLambdaClass(cw); + + Class lambdaClass = + createLambdaClass((Loader)lookup.lookupClass().getClassLoader(), cw, lambdaClassName); + + if (captures.length > 0) { + return createCaptureCallSite(lookup, factoryMethodName, factoryMethodType, lambdaClass); + } else { + return createNoCaptureCallSite(factoryMethodType, lambdaClass); + } + } + + /** + * Validates some conversions at link time. Currently, only ensures that the lambda method + * with a return value cannot delegate to a delegate method with no return type. + */ + private static void validateTypes(MethodType interfaceMethodType, MethodType delegateMethodType) + throws LambdaConversionException { + + if (interfaceMethodType.returnType() != void.class && + delegateMethodType.returnType() == void.class) { + throw new LambdaConversionException("lambda expects return type [" + + interfaceMethodType.returnType() + "], but found return type [void]"); + } + } + + /** + * Creates the {@link ClassWriter} to be used for the lambda class generation. + */ + private static ClassWriter beginLambdaClass(String lambdaClassName, String lambdaInterface) { + String baseClass = Object.class.getName().replace('.', '/'); + lambdaInterface = lambdaInterface.replace('.', '/'); + int modifiers = ACC_PUBLIC | ACC_STATIC | ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC; + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + cw.visit(CLASS_VERSION, + modifiers, lambdaClassName, null, baseClass, new String[] {lambdaInterface}); + + return cw; + } + + /** + * Generates member fields for captured variables + * based on the parameters for the factory method. + * @return An array of captured variable metadata + * for generating method arguments later on + */ + private static Capture[] generateCaptureFields(ClassWriter cw, MethodType factoryMethodType) { + int captureTotal = factoryMethodType.parameterCount(); + Capture[] captures = new Capture[captureTotal]; + + for (int captureCount = 0; captureCount < captureTotal; ++captureCount) { + captures[captureCount] = + new Capture(captureCount, factoryMethodType.parameterType(captureCount)); + int modifiers = ACC_PRIVATE + ACC_FINAL; + + FieldVisitor fv = cw.visitField( + modifiers, captures[captureCount].name, captures[captureCount].desc, null, null); + fv.visitEnd(); + } + + return captures; + } + + /** + * Generates a constructor that will take in captured + * arguments if any and store them in their respective + * member fields. + * @return The constructor {@link Method} used to + * call this method from a potential factory method + * if there are captured arguments + */ + private static Method generateLambdaConstructor( + ClassWriter cw, + Type lambdaClassType, + MethodType factoryMethodType, + Capture[] captures) { + + String conName = ""; + String conDesc = factoryMethodType.changeReturnType(void.class).toMethodDescriptorString(); + Method conMeth = new Method(conName, conDesc); + Type baseConType = Type.getType(Object.class); + Method baseConMeth = new Method(conName, + MethodType.methodType(void.class).toMethodDescriptorString()); + int modifiers = ACC_PUBLIC; + + GeneratorAdapter constructor = new GeneratorAdapter(modifiers, conMeth, + cw.visitMethod(modifiers, conName, conDesc, null, null)); + constructor.visitCode(); + constructor.loadThis(); + constructor.invokeConstructor(baseConType, baseConMeth); + + for (int captureCount = 0; captureCount < captures.length; ++captureCount) { + constructor.loadThis(); + constructor.loadArg(captureCount); + constructor.putField( + lambdaClassType, captures[captureCount].name, captures[captureCount].type); + } + + constructor.returnValue(); + constructor.endMethod(); + + return conMeth; + } + + /** + * Generates a factory method that can be used to create the lambda class + * if there are captured variables. + */ + private static void generateFactoryMethod( + ClassWriter cw, + String factoryMethodName, + MethodType factoryMethodType, + Type lambdaClassType, + Method constructorMethod) { + + String facDesc = factoryMethodType.toMethodDescriptorString(); + Method facMeth = new Method(factoryMethodName, facDesc); + int modifiers = ACC_PUBLIC | ACC_STATIC; + + GeneratorAdapter factory = new GeneratorAdapter(modifiers, facMeth, + cw.visitMethod(modifiers, factoryMethodName, facDesc, null, null)); + factory.visitCode(); + factory.newInstance(lambdaClassType); + factory.dup(); + factory.loadArgs(); + factory.invokeConstructor(lambdaClassType, constructorMethod); + factory.returnValue(); + factory.endMethod(); + } + + /** + * Generates the interface method that will delegate (call) to the delegate method. + */ + private static void generateInterfaceMethod( + ClassWriter cw, + MethodType factoryMethodType, + String lambdaClassName, + Type lambdaClassType, + String interfaceMethodName, + MethodType interfaceMethodType, + String delegateClassName, + int delegateInvokeType, + String delegateMethodName, + MethodType delegateMethodType, + Capture[] captures) + throws LambdaConversionException { + + String lamDesc = interfaceMethodType.toMethodDescriptorString(); + Method lamMeth = new Method(lambdaClassName, lamDesc); + int modifiers = ACC_PUBLIC; + + GeneratorAdapter iface = new GeneratorAdapter(modifiers, lamMeth, + cw.visitMethod(modifiers, interfaceMethodName, lamDesc, null, null)); + iface.visitCode(); + + // Handles the case where a reference method refers to a constructor. + // A new instance of the requested type will be created and the + // constructor with no parameters will be called. + // Example: String::new + if (delegateInvokeType == H_NEWINVOKESPECIAL) { + String conName = ""; + String conDesc = MethodType.methodType(void.class).toMethodDescriptorString(); + Method conMeth = new Method(conName, conDesc); + Type conType = Type.getType(delegateMethodType.returnType()); + + iface.newInstance(conType); + iface.dup(); + iface.invokeConstructor(conType, conMeth); + } else { + // Loads any captured variables onto the stack. + for (int captureCount = 0; captureCount < captures.length; ++captureCount) { + iface.loadThis(); + iface.getField( + lambdaClassType, captures[captureCount].name, captures[captureCount].type); + } + + // Loads any passed in arguments onto the stack. + iface.loadArgs(); + + // Handles the case for a lambda function or a static reference method. + // interfaceMethodType and delegateMethodType both have the captured types + // inserted into their type signatures. This later allows the delegate + // method to be invoked dynamically and have the interface method types + // appropriately converted to the delegate method types. + // Example: Integer::parseInt + // Example: something.each(x -> x + 1) + if (delegateInvokeType == H_INVOKESTATIC) { + interfaceMethodType = + interfaceMethodType.insertParameterTypes(0, factoryMethodType.parameterArray()); + delegateMethodType = + delegateMethodType.insertParameterTypes(0, factoryMethodType.parameterArray()); + } else if (delegateInvokeType == H_INVOKEVIRTUAL || + delegateInvokeType == H_INVOKEINTERFACE) { + // Handles the case for a virtual or interface reference method with no captures. + // delegateMethodType drops the 'this' parameter because it will be re-inserted + // when the method handle for the dynamically invoked delegate method is created. + // Example: Object::toString + if (captures.length == 0) { + Class clazz = delegateMethodType.parameterType(0); + delegateClassName = clazz.getName(); + delegateMethodType = delegateMethodType.dropParameterTypes(0, 1); + // Handles the case for a virtual or interface reference method with 'this' + // captured. interfaceMethodType inserts the 'this' type into its + // method signature. This later allows the delegate + // method to be invoked dynamically and have the interface method types + // appropriately converted to the delegate method types. + // Example: something::toString + } else if (captures.length == 1) { + Class clazz = factoryMethodType.parameterType(0); + delegateClassName = clazz.getName(); + interfaceMethodType = interfaceMethodType.insertParameterTypes(0, clazz); + } else { + throw new LambdaConversionException( + "unexpected number of captures [ " + captures.length + "]"); + } + } else { + throw new IllegalStateException( + "unexpected invocation type [" + delegateInvokeType + "]"); + } + + Handle delegateHandle = + new Handle(delegateInvokeType, delegateClassName.replace('.', '/'), + delegateMethodName, delegateMethodType.toMethodDescriptorString(), + delegateInvokeType == H_INVOKEINTERFACE); + iface.invokeDynamic(delegateMethodName, Type.getMethodType(interfaceMethodType + .toMethodDescriptorString()).getDescriptor(), DELEGATE_BOOTSTRAP_HANDLE, + delegateHandle); + } + + iface.returnValue(); + iface.endMethod(); + } + + /** + * Closes the {@link ClassWriter}. + */ + private static void endLambdaClass(ClassWriter cw) { + cw.visitEnd(); + } + + /** + * Defines the {@link Class} for the lambda class using the same {@link Loader} + * that originally defined the class for the Painless script. + */ + private static Class createLambdaClass( + Loader loader, + ClassWriter cw, + String lambdaClassName) { + + byte[] classBytes = cw.toByteArray(); + return AccessController.doPrivileged((PrivilegedAction>)() -> + loader.defineLambda(lambdaClassName.replace('/', '.'), classBytes)); + } + + /** + * Creates an {@link ConstantCallSite} that will return the same instance + * of the generated lambda class every time this linked factory method is called. + */ + private static CallSite createNoCaptureCallSite( + MethodType factoryMethodType, + Class lambdaClass) { + + Constructor constructor = AccessController.doPrivileged( + (PrivilegedAction>)() -> { + try { + return lambdaClass.getConstructor(); + } catch (NoSuchMethodException nsme) { + throw new IllegalStateException("unable to create lambda class", nsme); + } + }); + + try { + return new ConstantCallSite(MethodHandles.constant( + factoryMethodType.returnType(), constructor.newInstance())); + } catch (InstantiationException | + IllegalAccessException | + InvocationTargetException exception) { + throw new IllegalStateException("unable to create lambda class", exception); + } + } + + /** + * Creates an {@link ConstantCallSite} + */ + private static CallSite createCaptureCallSite( + Lookup lookup, + String factoryMethodName, + MethodType factoryMethodType, + Class lambdaClass) { + + try { + return new ConstantCallSite( + lookup.findStatic(lambdaClass, factoryMethodName, factoryMethodType)); + } catch (NoSuchMethodException | IllegalAccessException exception) { + throw new IllegalStateException("unable to create lambda factory class", exception); + } + } + + /** + * Links the delegate method to the returned {@link CallSite}. The linked + * delegate method will use converted types from the interface method. Using + * invokedynamic to make the delegate method call allows + * {@link MethodHandle#asType} to be used to do the type conversion instead + * of either a lot more code or requiring many {@link Definition.Type}s to be looked + * up at link-time. + */ + public static CallSite delegateBootstrap(Lookup lookup, + String delegateMethodName, + MethodType interfaceMethodType, + MethodHandle delegateMethodHandle) { + return new ConstantCallSite(delegateMethodHandle.asType(interfaceMethodType)); + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java index f29afbb74e67f..2772cdcf27521 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java @@ -84,11 +84,10 @@ public final class WriterConstants { public static final Type UTILITY_TYPE = Type.getType(Utility.class); public static final Method STRING_TO_CHAR = getAsmMethod(char.class, "StringTochar", String.class); public static final Method CHAR_TO_STRING = getAsmMethod(String.class, "charToString", char.class); - - public static final Type OBJECT_ARRAY_TYPE = Type.getType("[Ljava/lang/Object;"); + public static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class); - + public static final Type AUGMENTATION_TYPE = Type.getType(Augmentation.class); /** @@ -110,7 +109,6 @@ public final class WriterConstants { public static final Method DEF_BOOTSTRAP_DELEGATE_METHOD = getAsmMethod(CallSite.class, "bootstrap", Definition.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, int.class, Object[].class); - public static final Type DEF_UTIL_TYPE = Type.getType(Def.class); public static final Method DEF_TO_BOOLEAN = getAsmMethod(boolean.class, "DefToboolean" , Object.class); public static final Method DEF_TO_BYTE_IMPLICIT = getAsmMethod(byte.class , "DefTobyteImplicit" , Object.class); @@ -132,10 +130,15 @@ public final class WriterConstants { /** invokedynamic bootstrap for lambda expression/method references */ public static final MethodType LAMBDA_BOOTSTRAP_TYPE = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, - MethodType.class, Object[].class); + MethodType.class, MethodType.class, String.class, int.class, String.class, MethodType.class); public static final Handle LAMBDA_BOOTSTRAP_HANDLE = - new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(LambdaMetafactory.class), - "altMetafactory", LAMBDA_BOOTSTRAP_TYPE.toMethodDescriptorString(), false); + new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(LambdaBootstrap.class), + "lambdaBootstrap", LAMBDA_BOOTSTRAP_TYPE.toMethodDescriptorString(), false); + public static final MethodType DELEGATE_BOOTSTRAP_TYPE = + MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, MethodHandle.class); + public static final Handle DELEGATE_BOOTSTRAP_HANDLE = + new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(LambdaBootstrap.class), + "delegateBootstrap", DELEGATE_BOOTSTRAP_TYPE.toMethodDescriptorString(), false); /** dynamic invokedynamic bootstrap for indy string concats (Java 9+) */ public static final Handle INDY_STRING_CONCAT_BOOTSTRAP_HANDLE; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java index 51f374478219b..0210b7ce151d5 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java @@ -1060,7 +1060,7 @@ public ANode visitLambda(LambdaContext ctx) { for (LamtypeContext lamtype : ctx.lamtype()) { if (lamtype.decltype() == null) { - paramTypes.add("def"); + paramTypes.add(null); } else { paramTypes.add(lamtype.decltype().getText()); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java index 717d2a43b1ea1..f40883186dd7a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java @@ -19,6 +19,7 @@ package org.elasticsearch.painless.node; +import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.DefBootstrap; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.FunctionRef; @@ -30,10 +31,10 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; -import java.lang.invoke.LambdaMetafactory; import java.util.Objects; import java.util.Set; +import static org.elasticsearch.painless.Definition.VOID_TYPE; import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE; /** @@ -77,6 +78,17 @@ void analyze(Locals locals) { if (captured.type.sort != Definition.Sort.DEF) { try { ref = new FunctionRef(locals.getDefinition(), expected, captured.type.name, call, 1); + + // check casts between the interface method and the delegate method are legal + for (int i = 0; i < ref.interfaceMethod.arguments.size(); ++i) { + Definition.Type from = ref.interfaceMethod.arguments.get(i); + Definition.Type to = ref.delegateMethod.arguments.get(i); + AnalyzerCaster.getLegalCast(location, from, to, false, true); + } + + if (ref.interfaceMethod.rtn != VOID_TYPE) { + AnalyzerCaster.getLegalCast(location, ref.delegateMethod.rtn, ref.interfaceMethod.rtn, false, true); + } } catch (IllegalArgumentException e) { throw createError(e); } @@ -101,29 +113,16 @@ void write(MethodWriter writer, Globals globals) { } else { // typed interface, typed implementation writer.visitVarInsn(captured.type.type.getOpcode(Opcodes.ILOAD), captured.getSlot()); - // convert MethodTypes to asm Type for the constant pool. - String invokedType = ref.invokedType.toMethodDescriptorString(); - Type samMethodType = Type.getMethodType(ref.samMethodType.toMethodDescriptorString()); - Type interfaceType = Type.getMethodType(ref.interfaceMethodType.toMethodDescriptorString()); - if (ref.needsBridges()) { - writer.invokeDynamic(ref.invokedName, - invokedType, - LAMBDA_BOOTSTRAP_HANDLE, - samMethodType, - ref.implMethodASM, - samMethodType, - LambdaMetafactory.FLAG_BRIDGES, - 1, - interfaceType); - } else { - writer.invokeDynamic(ref.invokedName, - invokedType, - LAMBDA_BOOTSTRAP_HANDLE, - samMethodType, - ref.implMethodASM, - samMethodType, - 0); - } + writer.invokeDynamic( + ref.interfaceMethodName, + ref.factoryDescriptor, + LAMBDA_BOOTSTRAP_HANDLE, + ref.interfaceType, + ref.delegateClassName, + ref.delegateInvokeType, + ref.delegateMethodName, + ref.delegateType + ); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java index 0fe11400269b4..c9b2a5d91d232 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java @@ -19,6 +19,7 @@ package org.elasticsearch.painless.node; +import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Definition.Method; import org.elasticsearch.painless.Definition.MethodKey; @@ -29,10 +30,10 @@ import org.elasticsearch.painless.MethodWriter; import org.objectweb.asm.Type; -import java.lang.invoke.LambdaMetafactory; import java.util.Objects; import java.util.Set; +import static org.elasticsearch.painless.Definition.VOID_TYPE; import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE; /** @@ -71,16 +72,28 @@ void analyze(Locals locals) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + "to [" + expected.name + "], not a functional interface"); } - Method implMethod = locals.getMethod(new MethodKey(call, interfaceMethod.arguments.size())); - if (implMethod == null) { + Method delegateMethod = locals.getMethod(new MethodKey(call, interfaceMethod.arguments.size())); + if (delegateMethod == null) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + "to [" + expected.name + "], function not found"); } - ref = new FunctionRef(expected, interfaceMethod, implMethod, 0); + ref = new FunctionRef(expected, interfaceMethod, delegateMethod, 0); + + // check casts between the interface method and the delegate method are legal + for (int i = 0; i < interfaceMethod.arguments.size(); ++i) { + Definition.Type from = interfaceMethod.arguments.get(i); + Definition.Type to = delegateMethod.arguments.get(i); + AnalyzerCaster.getLegalCast(location, from, to, false, true); + } + + if (interfaceMethod.rtn != VOID_TYPE) { + AnalyzerCaster.getLegalCast(location, delegateMethod.rtn, interfaceMethod.rtn, false, true); + } } else { // whitelist lookup ref = new FunctionRef(locals.getDefinition(), expected, type, call, 0); } + } catch (IllegalArgumentException e) { throw createError(e); } @@ -92,29 +105,16 @@ void analyze(Locals locals) { void write(MethodWriter writer, Globals globals) { if (ref != null) { writer.writeDebugInfo(location); - // convert MethodTypes to asm Type for the constant pool. - String invokedType = ref.invokedType.toMethodDescriptorString(); - Type samMethodType = Type.getMethodType(ref.samMethodType.toMethodDescriptorString()); - Type interfaceType = Type.getMethodType(ref.interfaceMethodType.toMethodDescriptorString()); - if (ref.needsBridges()) { - writer.invokeDynamic(ref.invokedName, - invokedType, - LAMBDA_BOOTSTRAP_HANDLE, - samMethodType, - ref.implMethodASM, - samMethodType, - LambdaMetafactory.FLAG_BRIDGES, - 1, - interfaceType); - } else { - writer.invokeDynamic(ref.invokedName, - invokedType, - LAMBDA_BOOTSTRAP_HANDLE, - samMethodType, - ref.implMethodASM, - samMethodType, - 0); - } + writer.invokeDynamic( + ref.interfaceMethodName, + ref.factoryDescriptor, + LAMBDA_BOOTSTRAP_HANDLE, + ref.interfaceType, + ref.delegateClassName, + ref.delegateInvokeType, + ref.delegateMethodName, + ref.delegateType + ); } else { // TODO: don't do this: its just to cutover :) writer.push((String)null); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java index ca086561d6989..ad843f86af200 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java @@ -19,6 +19,7 @@ package org.elasticsearch.painless.node; +import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Definition.Method; import org.elasticsearch.painless.Definition.Type; @@ -31,7 +32,6 @@ import org.elasticsearch.painless.node.SFunction.FunctionReserved; import org.objectweb.asm.Opcodes; -import java.lang.invoke.LambdaMetafactory; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -39,7 +39,10 @@ import java.util.Objects; import java.util.Set; +import static org.elasticsearch.painless.Definition.VOID_TYPE; +import static org.elasticsearch.painless.WriterConstants.CLASS_NAME; import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE; +import static org.objectweb.asm.Opcodes.H_INVOKESTATIC; /** * Lambda expression node. @@ -109,8 +112,15 @@ void analyze(Locals locals) { interfaceMethod = null; // we don't know anything: treat as def returnType = Definition.DEF_TYPE; - // don't infer any types - actualParamTypeStrs = paramTypeStrs; + // don't infer any types, replace any null types with def + actualParamTypeStrs = new ArrayList<>(); + for (String type : paramTypeStrs) { + if (type == null) { + actualParamTypeStrs.add("def"); + } else { + actualParamTypeStrs.add(type); + } + } } else { // we know the method statically, infer return type and any unknown/def types interfaceMethod = expected.struct.getFunctionalMethod(); @@ -128,11 +138,11 @@ void analyze(Locals locals) { } else { returnType = interfaceMethod.rtn; } - // replace any def types with the actual type (which could still be def) - actualParamTypeStrs = new ArrayList(); + // replace any null types with the actual type + actualParamTypeStrs = new ArrayList<>(); for (int i = 0; i < paramTypeStrs.size(); i++) { String paramType = paramTypeStrs.get(i); - if (paramType.equals(Definition.DEF_TYPE.name)) { + if (paramType == null) { actualParamTypeStrs.add(interfaceMethod.arguments.get(i).name); } else { actualParamTypeStrs.add(paramType); @@ -180,6 +190,18 @@ void analyze(Locals locals) { } catch (IllegalArgumentException e) { throw createError(e); } + + // check casts between the interface method and the delegate method are legal + for (int i = 0; i < interfaceMethod.arguments.size(); ++i) { + Type from = interfaceMethod.arguments.get(i); + Type to = desugared.parameters.get(i + captures.size()).type; + AnalyzerCaster.getLegalCast(location, from, to, false, true); + } + + if (interfaceMethod.rtn != VOID_TYPE) { + AnalyzerCaster.getLegalCast(location, desugared.rtnType, interfaceMethod.rtn, false, true); + } + actual = expected; } } @@ -194,31 +216,17 @@ void write(MethodWriter writer, Globals globals) { for (Variable capture : captures) { writer.visitVarInsn(capture.type.type.getOpcode(Opcodes.ILOAD), capture.getSlot()); } - // convert MethodTypes to asm Type for the constant pool. - String invokedType = ref.invokedType.toMethodDescriptorString(); - org.objectweb.asm.Type samMethodType = - org.objectweb.asm.Type.getMethodType(ref.samMethodType.toMethodDescriptorString()); - org.objectweb.asm.Type interfaceType = - org.objectweb.asm.Type.getMethodType(ref.interfaceMethodType.toMethodDescriptorString()); - if (ref.needsBridges()) { - writer.invokeDynamic(ref.invokedName, - invokedType, - LAMBDA_BOOTSTRAP_HANDLE, - samMethodType, - ref.implMethodASM, - samMethodType, - LambdaMetafactory.FLAG_BRIDGES, - 1, - interfaceType); - } else { - writer.invokeDynamic(ref.invokedName, - invokedType, - LAMBDA_BOOTSTRAP_HANDLE, - samMethodType, - ref.implMethodASM, - samMethodType, - 0); - } + + writer.invokeDynamic( + ref.interfaceMethodName, + ref.factoryDescriptor, + LAMBDA_BOOTSTRAP_HANDLE, + ref.interfaceType, + ref.delegateClassName, + ref.delegateInvokeType, + ref.delegateMethodName, + ref.delegateType + ); } else { // placeholder writer.push((String)null); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java index 3ef20b023ce75..257f2975c939a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java @@ -174,7 +174,7 @@ void analyze(Locals locals) { /** Writes the function to given ClassVisitor. */ void write (ClassVisitor writer, CompilerSettings settings, Globals globals) { - int access = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC; + int access = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC; if (synthetic) { access |= Opcodes.ACC_SYNTHETIC; } diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/org.elasticsearch.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/org.elasticsearch.txt index 8c4f104a0c13b..68d86356e9500 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/org.elasticsearch.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/org.elasticsearch.txt @@ -155,4 +155,5 @@ class org.elasticsearch.painless.FeatureTest -> org.elasticsearch.painless.Featu boolean overloadedStatic() boolean overloadedStatic(boolean) Object twoFunctionsOfX(Function,Function) + void listInput(List) } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/AugmentationTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/AugmentationTests.java index 311a6b775ddd1..acf698e2fc7be 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/AugmentationTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/AugmentationTests.java @@ -19,35 +19,29 @@ package org.elasticsearch.painless; -import org.apache.lucene.util.Constants; - import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class AugmentationTests extends ScriptTestCase { - + public void testStatic() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(1, exec("ArrayList l = new ArrayList(); l.add(1); return l.getLength();")); assertEquals(1, exec("ArrayList l = new ArrayList(); l.add(1); return l.length;")); } - + public void testSubclass() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(1, exec("List l = new ArrayList(); l.add(1); return l.getLength();")); assertEquals(1, exec("List l = new ArrayList(); l.add(1); return l.length;")); } - + public void testDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(1, exec("def l = new ArrayList(); l.add(1); return l.getLength();")); assertEquals(1, exec("def l = new ArrayList(); l.add(1); return l.length;")); } - + public void testCapturingReference() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(1, exec("int foo(Supplier t) { return t.get() }" + "ArrayList l = new ArrayList(); l.add(1);" + "return foo(l::getLength);")); @@ -58,164 +52,140 @@ public void testCapturingReference() { "def l = new ArrayList(); l.add(1);" + "return foo(l::getLength);")); } - + public void testIterable_Any() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(true, + assertEquals(true, exec("List l = new ArrayList(); l.add(1); l.any(x -> x == 1)")); } - + public void testIterable_AsCollection() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(true, + assertEquals(true, exec("List l = new ArrayList(); return l.asCollection() === l")); } - + public void testIterable_AsList() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(true, + assertEquals(true, exec("List l = new ArrayList(); return l.asList() === l")); - assertEquals(5, + assertEquals(5, exec("Set l = new HashSet(); l.add(5); return l.asList()[0]")); } - + public void testIterable_Each() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(1, + assertEquals(1, exec("List l = new ArrayList(); l.add(1); List l2 = new ArrayList(); l.each(l2::add); return l2.size()")); } - + public void testIterable_EachWithIndex() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(0, + assertEquals(0, exec("List l = new ArrayList(); l.add(2); Map m = new HashMap(); l.eachWithIndex(m::put); return m.get(2)")); } - + public void testIterable_Every() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(false, exec("List l = new ArrayList(); l.add(1); l.add(2); l.every(x -> x == 1)")); } - + public void testIterable_FindResults() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(1, + assertEquals(1, exec("List l = new ArrayList(); l.add(1); l.add(2); l.findResults(x -> x == 1 ? x : null).size()")); } - + public void testIterable_GroupBy() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(2, + assertEquals(2, exec("List l = new ArrayList(); l.add(1); l.add(-1); l.groupBy(x -> x < 0 ? 'negative' : 'positive').size()")); } - + public void testIterable_Join() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals("test,ing", + assertEquals("test,ing", exec("List l = new ArrayList(); l.add('test'); l.add('ing'); l.join(',')")); } - + public void testIterable_Sum() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(3.0D, exec("def l = [1,2]; return l.sum()")); - assertEquals(5.0D, + assertEquals(5.0D, exec("List l = new ArrayList(); l.add(1); l.add(2); l.sum(x -> x + 1)")); } - + public void testCollection_Collect() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(Arrays.asList(2, 3), + assertEquals(Arrays.asList(2, 3), exec("List l = new ArrayList(); l.add(1); l.add(2); l.collect(x -> x + 1)")); - assertEquals(asSet(2, 3), + assertEquals(asSet(2, 3), exec("List l = new ArrayList(); l.add(1); l.add(2); l.collect(new HashSet(), x -> x + 1)")); } - + public void testCollection_Find() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(2, + assertEquals(2, exec("List l = new ArrayList(); l.add(1); l.add(2); return l.find(x -> x == 2)")); } - + public void testCollection_FindAll() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(Arrays.asList(2), + assertEquals(Arrays.asList(2), exec("List l = new ArrayList(); l.add(1); l.add(2); return l.findAll(x -> x == 2)")); } - + public void testCollection_FindResult() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals("found", + assertEquals("found", exec("List l = new ArrayList(); l.add(1); l.add(2); return l.findResult(x -> x > 1 ? 'found' : null)")); - assertEquals("notfound", + assertEquals("notfound", exec("List l = new ArrayList(); l.add(1); l.add(2); return l.findResult('notfound', x -> x > 10 ? 'found' : null)")); } - + public void testCollection_Split() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(Arrays.asList(Arrays.asList(2), Arrays.asList(1)), + assertEquals(Arrays.asList(Arrays.asList(2), Arrays.asList(1)), exec("List l = new ArrayList(); l.add(1); l.add(2); return l.split(x -> x == 2)")); } - + public void testMap_Collect() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(Arrays.asList("one1", "two2"), + assertEquals(Arrays.asList("one1", "two2"), exec("Map m = new TreeMap(); m.one = 1; m.two = 2; m.collect((key,value) -> key + value)")); - assertEquals(asSet("one1", "two2"), + assertEquals(asSet("one1", "two2"), exec("Map m = new TreeMap(); m.one = 1; m.two = 2; m.collect(new HashSet(), (key,value) -> key + value)")); } - + public void testMap_Count() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(1, + assertEquals(1, exec("Map m = new TreeMap(); m.one = 1; m.two = 2; m.count((key,value) -> value == 2)")); } - + public void testMap_Each() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(2, + assertEquals(2, exec("Map m = new TreeMap(); m.one = 1; m.two = 2; Map m2 = new TreeMap(); m.each(m2::put); return m2.size()")); } - + public void testMap_Every() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(false, + assertEquals(false, exec("Map m = new TreeMap(); m.one = 1; m.two = 2; m.every((key,value) -> value == 2)")); } - + public void testMap_Find() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals("two", + assertEquals("two", exec("Map m = new TreeMap(); m.one = 1; m.two = 2; return m.find((key,value) -> value == 2).key")); } - + public void testMap_FindAll() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(Collections.singletonMap("two", 2), + assertEquals(Collections.singletonMap("two", 2), exec("Map m = new TreeMap(); m.one = 1; m.two = 2; return m.findAll((key,value) -> value == 2)")); } - + public void testMap_FindResult() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals("found", + assertEquals("found", exec("Map m = new TreeMap(); m.one = 1; m.two = 2; return m.findResult((key,value) -> value == 2 ? 'found' : null)")); - assertEquals("notfound", - exec("Map m = new TreeMap(); m.one = 1; m.two = 2; " + + assertEquals("notfound", + exec("Map m = new TreeMap(); m.one = 1; m.two = 2; " + "return m.findResult('notfound', (key,value) -> value == 10 ? 'found' : null)")); } - + public void testMap_FindResults() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(Arrays.asList("negative", "positive"), - exec("Map m = new TreeMap(); m.a = -1; m.b = 1; " + + exec("Map m = new TreeMap(); m.a = -1; m.b = 1; " + "return m.findResults((key,value) -> value < 0 ? 'negative' : 'positive')")); } - + public void testMap_GroupBy() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); Map> expected = new HashMap<>(); expected.put("negative", Collections.singletonMap("a", -1)); expected.put("positive", Collections.singletonMap("b", 1)); assertEquals(expected, - exec("Map m = new TreeMap(); m.a = -1; m.b = 1; " + + exec("Map m = new TreeMap(); m.a = -1; m.b = 1; " + "return m.groupBy((key,value) -> value < 0 ? 'negative' : 'positive')")); } } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java index 5dd2481c0f177..4bd687a72058d 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.painless; -import org.apache.lucene.util.Constants; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -33,39 +32,32 @@ public class FunctionRefTests extends ScriptTestCase { public void testStaticMethodReference() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(1, exec("List l = new ArrayList(); l.add(2); l.add(1); l.sort(Integer::compare); return l.get(0);")); } - + public void testStaticMethodReferenceDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(1, exec("def l = new ArrayList(); l.add(2); l.add(1); l.sort(Integer::compare); return l.get(0);")); } public void testVirtualMethodReference() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(2, exec("List l = new ArrayList(); l.add(1); l.add(1); return l.stream().mapToInt(Integer::intValue).sum();")); } - + public void testVirtualMethodReferenceDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(2, exec("def l = new ArrayList(); l.add(1); l.add(1); return l.stream().mapToInt(Integer::intValue).sum();")); } public void testQualifiedStaticMethodReference() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(true, exec("List l = [true]; l.stream().map(org.elasticsearch.painless.FeatureTest::overloadedStatic).findFirst().get()")); } public void testQualifiedStaticMethodReferenceDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(true, exec("def l = [true]; l.stream().map(org.elasticsearch.painless.FeatureTest::overloadedStatic).findFirst().get()")); } public void testQualifiedVirtualMethodReference() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); long instant = randomLong(); assertEquals(instant, exec( "List l = [params.d]; return l.stream().mapToLong(org.joda.time.ReadableDateTime::getMillis).sum()", @@ -73,7 +65,6 @@ public void testQualifiedVirtualMethodReference() { } public void testQualifiedVirtualMethodReferenceDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); long instant = randomLong(); assertEquals(instant, exec( "def l = [params.d]; return l.stream().mapToLong(org.joda.time.ReadableDateTime::getMillis).sum()", @@ -81,129 +72,112 @@ public void testQualifiedVirtualMethodReferenceDef() { } public void testCtorMethodReference() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(3.0D, - exec("List l = new ArrayList(); l.add(1.0); l.add(2.0); " + - "DoubleStream doubleStream = l.stream().mapToDouble(Double::doubleValue);" + + assertEquals(3.0D, + exec("List l = new ArrayList(); l.add(1.0); l.add(2.0); " + + "DoubleStream doubleStream = l.stream().mapToDouble(Double::doubleValue);" + "DoubleSummaryStatistics stats = doubleStream.collect(DoubleSummaryStatistics::new, " + "DoubleSummaryStatistics::accept, " + - "DoubleSummaryStatistics::combine); " + + "DoubleSummaryStatistics::combine); " + "return stats.getSum()")); } - + public void testCtorMethodReferenceDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(3.0D, - exec("def l = new ArrayList(); l.add(1.0); l.add(2.0); " + - "def doubleStream = l.stream().mapToDouble(Double::doubleValue);" + + assertEquals(3.0D, + exec("def l = new ArrayList(); l.add(1.0); l.add(2.0); " + + "def doubleStream = l.stream().mapToDouble(Double::doubleValue);" + "def stats = doubleStream.collect(DoubleSummaryStatistics::new, " + "DoubleSummaryStatistics::accept, " + - "DoubleSummaryStatistics::combine); " + + "DoubleSummaryStatistics::combine); " + "return stats.getSum()")); } public void testArrayCtorMethodRef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(1.0D, - exec("List l = new ArrayList(); l.add(1.0); l.add(2.0); " + - "def[] array = l.stream().toArray(Double[]::new);" + + assertEquals(1.0D, + exec("List l = new ArrayList(); l.add(1.0); l.add(2.0); " + + "def[] array = l.stream().toArray(Double[]::new);" + "return array[0];")); } public void testArrayCtorMethodRefDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(1.0D, - exec("def l = new ArrayList(); l.add(1.0); l.add(2.0); " + - "def[] array = l.stream().toArray(Double[]::new);" + + assertEquals(1.0D, + exec("def l = new ArrayList(); l.add(1.0); l.add(2.0); " + + "def[] array = l.stream().toArray(Double[]::new);" + "return array[0];")); } public void testCapturingMethodReference() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals("5", exec("Integer x = Integer.valueOf(5); return Optional.empty().orElseGet(x::toString);")); assertEquals("[]", exec("List l = new ArrayList(); return Optional.empty().orElseGet(l::toString);")); } - + public void testCapturingMethodReferenceDefImpl() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals("5", exec("def x = Integer.valueOf(5); return Optional.empty().orElseGet(x::toString);")); assertEquals("[]", exec("def l = new ArrayList(); return Optional.empty().orElseGet(l::toString);")); } - + public void testCapturingMethodReferenceDefInterface() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals("5", exec("Integer x = Integer.valueOf(5); def opt = Optional.empty(); return opt.orElseGet(x::toString);")); assertEquals("[]", exec("List l = new ArrayList(); def opt = Optional.empty(); return opt.orElseGet(l::toString);")); } - + public void testCapturingMethodReferenceDefEverywhere() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals("5", exec("def x = Integer.valueOf(5); def opt = Optional.empty(); return opt.orElseGet(x::toString);")); assertEquals("[]", exec("def l = new ArrayList(); def opt = Optional.empty(); return opt.orElseGet(l::toString);")); } - + public void testCapturingMethodReferenceMultipleLambdas() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals("testingcdefg", exec( "String x = 'testing';" + - "String y = 'abcdefg';" + - "org.elasticsearch.painless.FeatureTest test = new org.elasticsearch.painless.FeatureTest(2,3);" + + "String y = 'abcdefg';" + + "org.elasticsearch.painless.FeatureTest test = new org.elasticsearch.painless.FeatureTest(2,3);" + "return test.twoFunctionsOfX(x::concat, y::substring);")); } - + public void testCapturingMethodReferenceMultipleLambdasDefImpls() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals("testingcdefg", exec( "def x = 'testing';" + - "def y = 'abcdefg';" + - "org.elasticsearch.painless.FeatureTest test = new org.elasticsearch.painless.FeatureTest(2,3);" + + "def y = 'abcdefg';" + + "org.elasticsearch.painless.FeatureTest test = new org.elasticsearch.painless.FeatureTest(2,3);" + "return test.twoFunctionsOfX(x::concat, y::substring);")); } - + public void testCapturingMethodReferenceMultipleLambdasDefInterface() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals("testingcdefg", exec( "String x = 'testing';" + - "String y = 'abcdefg';" + - "def test = new org.elasticsearch.painless.FeatureTest(2,3);" + + "String y = 'abcdefg';" + + "def test = new org.elasticsearch.painless.FeatureTest(2,3);" + "return test.twoFunctionsOfX(x::concat, y::substring);")); } - + public void testCapturingMethodReferenceMultipleLambdasDefEverywhere() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals("testingcdefg", exec( "def x = 'testing';" + - "def y = 'abcdefg';" + - "def test = new org.elasticsearch.painless.FeatureTest(2,3);" + + "def y = 'abcdefg';" + + "def test = new org.elasticsearch.painless.FeatureTest(2,3);" + "return test.twoFunctionsOfX(x::concat, y::substring);")); } - + public void testOwnStaticMethodReference() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(2, exec("int mycompare(int i, int j) { j - i } " + "List l = new ArrayList(); l.add(2); l.add(1); l.sort(this::mycompare); return l.get(0);")); } - + public void testOwnStaticMethodReferenceDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(2, exec("int mycompare(int i, int j) { j - i } " + "def l = new ArrayList(); l.add(2); l.add(1); l.sort(this::mycompare); return l.get(0);")); } public void testInterfaceDefaultMethod() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals("bar", exec("String f(BiFunction function) { function.apply('foo', 'bar') }" + + assertEquals("bar", exec("String f(BiFunction function) { function.apply('foo', 'bar') }" + "Map map = new HashMap(); f(map::getOrDefault)")); } - + public void testInterfaceDefaultMethodDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals("bar", exec("String f(BiFunction function) { function.apply('foo', 'bar') }" + + assertEquals("bar", exec("String f(BiFunction function) { function.apply('foo', 'bar') }" + "def map = new HashMap(); f(map::getOrDefault)")); } public void testMethodMissing() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); Exception e = expectScriptThrows(IllegalArgumentException.class, () -> { exec("List l = [2, 1]; l.sort(Integer::bogus); return l.get(0);"); }); @@ -211,7 +185,6 @@ public void testMethodMissing() { } public void testQualifiedMethodMissing() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); Exception e = expectScriptThrows(IllegalArgumentException.class, () -> { exec("List l = [2, 1]; l.sort(org.joda.time.ReadableDateTime::bogus); return l.get(0);", false); }); @@ -219,7 +192,6 @@ public void testQualifiedMethodMissing() { } public void testClassMissing() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); Exception e = expectScriptThrows(IllegalArgumentException.class, () -> { exec("List l = [2, 1]; l.sort(Bogus::bogus); return l.get(0);", false); }); @@ -227,7 +199,6 @@ public void testClassMissing() { } public void testQualifiedClassMissing() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); Exception e = expectScriptThrows(IllegalArgumentException.class, () -> { exec("List l = [2, 1]; l.sort(org.joda.time.BogusDateTime::bogus); return l.get(0);", false); }); @@ -237,7 +208,6 @@ public void testQualifiedClassMissing() { } public void testNotFunctionalInterface() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("List l = new ArrayList(); l.add(2); l.add(1); l.add(Integer::bogus); return l.get(0);"); }); @@ -245,38 +215,33 @@ public void testNotFunctionalInterface() { } public void testIncompatible() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); expectScriptThrows(BootstrapMethodError.class, () -> { exec("List l = new ArrayList(); l.add(2); l.add(1); l.sort(String::startsWith); return l.get(0);"); }); } - + public void testWrongArity() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("Optional.empty().orElseGet(String::startsWith);"); }); assertThat(expected.getMessage(), containsString("Unknown reference")); } - + public void testWrongArityNotEnough() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("List l = new ArrayList(); l.add(2); l.add(1); l.sort(String::isEmpty);"); }); assertTrue(expected.getMessage().contains("Unknown reference")); } - + public void testWrongArityDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("def y = Optional.empty(); return y.orElseGet(String::startsWith);"); }); assertThat(expected.getMessage(), containsString("Unknown reference")); } - + public void testWrongArityNotEnoughDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("def l = new ArrayList(); l.add(2); l.add(1); l.sort(String::isEmpty);"); }); @@ -284,29 +249,26 @@ public void testWrongArityNotEnoughDef() { } public void testReturnVoid() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - Throwable expected = expectScriptThrows(BootstrapMethodError.class, () -> { - exec("StringBuilder b = new StringBuilder(); List l = [1, 2]; l.stream().mapToLong(b::setLength);"); + Throwable expected = expectScriptThrows(ClassCastException.class, () -> { + exec("StringBuilder b = new StringBuilder(); List l = [1, 2]; l.stream().mapToLong(b::setLength).sum();"); }); - assertThat(expected.getCause().getMessage(), - containsString("Type mismatch for lambda expected return: void is not convertible to long")); + assertThat(expected.getMessage(), containsString("Cannot cast from [void] to [long].")); } public void testReturnVoidDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); Exception expected = expectScriptThrows(LambdaConversionException.class, () -> { exec("StringBuilder b = new StringBuilder(); def l = [1, 2]; l.stream().mapToLong(b::setLength);"); }); - assertThat(expected.getMessage(), containsString("Type mismatch for lambda expected return: void is not convertible to long")); + assertThat(expected.getMessage(), containsString("lambda expects return type [long], but found return type [void]")); expected = expectScriptThrows(LambdaConversionException.class, () -> { exec("def b = new StringBuilder(); def l = [1, 2]; l.stream().mapToLong(b::setLength);"); }); - assertThat(expected.getMessage(), containsString("Type mismatch for lambda expected return: void is not convertible to long")); + assertThat(expected.getMessage(), containsString("lambda expects return type [long], but found return type [void]")); expected = expectScriptThrows(LambdaConversionException.class, () -> { exec("def b = new StringBuilder(); List l = [1, 2]; l.stream().mapToLong(b::setLength);"); }); - assertThat(expected.getMessage(), containsString("Type mismatch for lambda expected return: void is not convertible to long")); + assertThat(expected.getMessage(), containsString("lambda expects return type [long], but found return type [void]")); } } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/LambdaTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/LambdaTests.java index bcb92a527d9e6..20e257e574709 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/LambdaTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/LambdaTests.java @@ -19,8 +19,6 @@ package org.elasticsearch.painless; -import org.apache.lucene.util.Constants; - import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -30,87 +28,72 @@ public class LambdaTests extends ScriptTestCase { public void testNoArgLambda() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(1, exec("Optional.empty().orElseGet(() -> 1);")); } public void testNoArgLambdaDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(1, exec("def x = Optional.empty(); x.orElseGet(() -> 1);")); } public void testLambdaWithArgs() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals("short", exec("List l = new ArrayList(); l.add('looooong'); l.add('short'); " + "l.sort((a, b) -> a.length() - b.length()); return l.get(0)")); } public void testLambdaWithTypedArgs() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals("short", exec("List l = new ArrayList(); l.add('looooong'); l.add('short'); " + "l.sort((String a, String b) -> a.length() - b.length()); return l.get(0)")); } public void testPrimitiveLambdas() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(4, exec("List l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(x -> x + 1).sum();")); } public void testPrimitiveLambdasWithTypedArgs() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(4, exec("List l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(int x -> x + 1).sum();")); } public void testPrimitiveLambdasDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(4, exec("def l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(x -> x + 1).sum();")); } public void testPrimitiveLambdasWithTypedArgsDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(4, exec("def l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(int x -> x + 1).sum();")); } public void testPrimitiveLambdasConvertible() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); - assertEquals(2, exec("List l = new ArrayList(); l.add(1); l.add(1); " - + "return l.stream().mapToInt(byte x -> x).sum();")); + assertEquals(2, exec("List l = new ArrayList(); l.add((short)1); l.add(1); " + + "return l.stream().mapToInt(long x -> (int)1).sum();")); } public void testPrimitiveArgs() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(2, exec("int applyOne(IntFunction arg) { arg.apply(1) } applyOne(x -> x + 1)")); } public void testPrimitiveArgsTyped() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(2, exec("int applyOne(IntFunction arg) { arg.apply(1) } applyOne(int x -> x + 1)")); } public void testPrimitiveArgsTypedOddly() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(2L, exec("long applyOne(IntFunction arg) { arg.apply(1) } applyOne(long x -> x + 1)")); } public void testMultipleStatements() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(2, exec("int applyOne(IntFunction arg) { arg.apply(1) } applyOne(x -> { def y = x + 1; return y })")); } public void testUnneededCurlyStatements() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(2, exec("int applyOne(IntFunction arg) { arg.apply(1) } applyOne(x -> { x + 1 })")); } /** interface ignores return value */ public void testVoidReturn() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(2, exec("List list = new ArrayList(); " + "list.add(2); " + "List list2 = new ArrayList(); " @@ -120,7 +103,6 @@ public void testVoidReturn() { /** interface ignores return value */ public void testVoidReturnDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(2, exec("def list = new ArrayList(); " + "list.add(2); " + "List list2 = new ArrayList(); " @@ -129,19 +111,16 @@ public void testVoidReturnDef() { } public void testTwoLambdas() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals("testingcdefg", exec( "org.elasticsearch.painless.FeatureTest test = new org.elasticsearch.painless.FeatureTest(2,3);" + "return test.twoFunctionsOfX(x -> 'testing'.concat(x), y -> 'abcdefg'.substring(y))")); } public void testNestedLambdas() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(1, exec("Optional.empty().orElseGet(() -> Optional.empty().orElseGet(() -> 1));")); } public void testLambdaInLoop() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(100, exec("int sum = 0; " + "for (int i = 0; i < 100; i++) {" + " sum += Optional.empty().orElseGet(() -> 1);" + @@ -150,17 +129,14 @@ public void testLambdaInLoop() { } public void testCapture() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(5, exec("int x = 5; return Optional.empty().orElseGet(() -> x);")); } public void testTwoCaptures() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals("1test", exec("int x = 1; String y = 'test'; return Optional.empty().orElseGet(() -> x + y);")); } public void testCapturesAreReadOnly() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("List l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(x -> { l = null; return x + 1 }).sum();"); @@ -170,14 +146,12 @@ public void testCapturesAreReadOnly() { @AwaitsFix(bugUrl = "def type tracking") public void testOnlyCapturesAreReadOnly() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(4, exec("List l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(x -> { x += 1; return x }).sum();")); } /** Lambda parameters shouldn't be able to mask a variable already in scope */ public void testNoParamMasking() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("int x = 0; List l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(x -> { x += 1; return x }).sum();"); @@ -186,24 +160,20 @@ public void testNoParamMasking() { } public void testCaptureDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(5, exec("int x = 5; def y = Optional.empty(); y.orElseGet(() -> x);")); } public void testNestedCapture() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(1, exec("boolean x = false; int y = 1;" + "return Optional.empty().orElseGet(() -> x ? 5 : Optional.empty().orElseGet(() -> y));")); } public void testNestedCaptureParams() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(2, exec("int foo(Function f) { return f.apply(1) }" + "return foo(x -> foo(y -> x + 1))")); } public void testWrongArity() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, false, () -> { exec("Optional.empty().orElseGet(x -> x);"); }); @@ -211,7 +181,6 @@ public void testWrongArity() { } public void testWrongArityDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("def y = Optional.empty(); return y.orElseGet(x -> x);"); }); @@ -219,7 +188,6 @@ public void testWrongArityDef() { } public void testWrongArityNotEnough() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, false, () -> { exec("List l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(() -> 5).sum();"); @@ -228,7 +196,6 @@ public void testWrongArityNotEnough() { } public void testWrongArityNotEnoughDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("def l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(() -> 5).sum();"); @@ -237,17 +204,14 @@ public void testWrongArityNotEnoughDef() { } public void testLambdaInFunction() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(5, exec("def foo() { Optional.empty().orElseGet(() -> 5) } return foo();")); } public void testLambdaCaptureFunctionParam() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); assertEquals(5, exec("def foo(int x) { Optional.empty().orElseGet(() -> x) } return foo(5);")); } public void testReservedCapture() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); String compare = "boolean compare(Supplier s, def v) {s.get() == v}"; assertEquals(true, exec(compare + "compare(() -> new ArrayList(), new ArrayList())")); assertEquals(true, exec(compare + "compare(() -> { new ArrayList() }, new ArrayList())")); @@ -272,7 +236,6 @@ public void testReservedCapture() { } public void testReturnVoid() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); Throwable expected = expectScriptThrows(ClassCastException.class, () -> { exec("StringBuilder b = new StringBuilder(); List l = [1, 2]; l.stream().mapToLong(i -> b.setLength(i))"); }); @@ -280,7 +243,6 @@ public void testReturnVoid() { } public void testReturnVoidDef() { - assumeFalse("JDK is JDK 9", Constants.JRE_IS_MINIMUM_JAVA9); // If we can catch the error at compile time we do Exception expected = expectScriptThrows(ClassCastException.class, () -> { exec("StringBuilder b = new StringBuilder(); def l = [1, 2]; l.stream().mapToLong(i -> b.setLength(i))"); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java index df9d0c0f4eaab..13d48bd2a0119 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java @@ -240,7 +240,7 @@ public void testELambda() { + "}).sum()"); assertToString( "(SSource (SReturn (PCallInvoke (PCallInvoke (PCallInvoke (EListInit (ENumeric 1) (ENumeric 2) (ENumeric 3)) stream) " - + "mapToInt (Args (ELambda (Pair def x)\n" + + "mapToInt (Args (ELambda (Pair null x)\n" + " (SReturn (EBinary (EVariable x) + (ENumeric 1)))))) sum)))", "return [1, 2, 3].stream().mapToInt(x -> x + 1).sum()"); assertToString( @@ -250,7 +250,7 @@ public void testELambda() { + " return a.length() - b.length()\n" + "})"); assertToString( - "(SSource (SReturn (PCallInvoke (EListInit (EString 'a') (EString 'b')) sort (Args (ELambda (Pair def a) (Pair def b)\n" + "(SSource (SReturn (PCallInvoke (EListInit (EString 'a') (EString 'b')) sort (Args (ELambda (Pair null a) (Pair null b)\n" + " (SReturn (EBinary (PCallInvoke (EVariable a) length) - (PCallInvoke (EVariable b) length))))))))", "return ['a', 'b'].sort((a, b) -> a.length() - b.length())"); assertToString( @@ -371,14 +371,14 @@ public void testPField() { assertToString( "(SSource\n" + " (SDeclBlock (SDeclaration int[] a (ENewArray int dims (Args (ENumeric 10)))))\n" - + " (SReturn (PField (EVariable a) length)))", + + " (SReturn (PField (EVariable a) length)))", "int[] a = new int[10];\n" + "return a.length"); assertToString( "(SSource\n" + " (SDeclBlock (SDeclaration org.elasticsearch.painless.FeatureTest a (ENewObj org.elasticsearch.painless.FeatureTest)))\n" + " (SExpression (EAssignment (PField (EVariable a) x) = (ENumeric 10)))\n" - + " (SReturn (PField (EVariable a) x)))", + + " (SReturn (PField (EVariable a) x)))", "org.elasticsearch.painless.FeatureTest a = new org.elasticsearch.painless.FeatureTest();\n" + "a.x = 10;\n" + "return a.x"); From 5fbc86e2aa1cb84ad835a3dd9f13d6c03bd6b509 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Mon, 24 Apr 2017 14:22:39 -0400 Subject: [PATCH 061/619] Allow painless to load stored fields (#24290) We document that painless can load stored fields but it can't because the classes that make that work aren't whitelisted. --- docs/reference/modules/scripting/fields.asciidoc | 9 ++++----- .../org/elasticsearch/painless/org.elasticsearch.txt | 6 ++++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/reference/modules/scripting/fields.asciidoc b/docs/reference/modules/scripting/fields.asciidoc index 9b71866d98957..0dbb5b6b42e6d 100644 --- a/docs/reference/modules/scripting/fields.asciidoc +++ b/docs/reference/modules/scripting/fields.asciidoc @@ -48,13 +48,13 @@ relevance `_score` of each document: [source,js] ------------------------------------- -PUT my_index/my_type/1 +PUT my_index/my_type/1?refresh { "text": "quick brown fox", "popularity": 1 } -PUT my_index/my_type/2 +PUT my_index/my_type/2?refresh { "text": "quick fox", "popularity": 5 @@ -93,7 +93,7 @@ store, enabled by default on all fields except for < org.elasticsearch.painless.Featu Object twoFunctionsOfX(Function,Function) void listInput(List) } + +class org.elasticsearch.search.lookup.FieldLookup -> org.elasticsearch.search.lookup.FieldLookup extends Object { + def getValue() + List getValues() + boolean isEmpty() +} From e429d66956c88bc0b968eb39ec492a32ab61f316 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Mon, 24 Apr 2017 16:08:19 -0400 Subject: [PATCH 062/619] CONSOLEify some more docs Relates to #18160 --- docs/build.gradle | 7 ----- .../mapping/fields/all-field.asciidoc | 2 ++ .../mapping/params/analyzer.asciidoc | 5 +--- docs/reference/mapping/types/binary.asciidoc | 3 +- docs/reference/mapping/types/ip.asciidoc | 7 +++-- docs/reference/mapping/types/nested.asciidoc | 1 + docs/reference/mapping/types/object.asciidoc | 2 +- .../mapping/types/percolator.asciidoc | 30 +++++++++++++------ 8 files changed, 32 insertions(+), 25 deletions(-) diff --git a/docs/build.gradle b/docs/build.gradle index 63d9d12c2dafc..b75a88fc6ce87 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -80,13 +80,6 @@ buildRestTests.expectedUnconvertedCandidates = [ 'reference/indices/shard-stores.asciidoc', 'reference/ingest/ingest-node.asciidoc', 'reference/mapping/dynamic/templates.asciidoc', - 'reference/mapping/fields/all-field.asciidoc', - 'reference/mapping/params/analyzer.asciidoc', - 'reference/mapping/types/binary.asciidoc', - 'reference/mapping/types/ip.asciidoc', - 'reference/mapping/types/nested.asciidoc', - 'reference/mapping/types/object.asciidoc', - 'reference/mapping/types/percolator.asciidoc', 'reference/modules/cross-cluster-search.asciidoc', // this is hard to test since we need 2 clusters -- maybe we can trick it into referencing itself... 'reference/search/field-stats.asciidoc', 'reference/search/profile.asciidoc', diff --git a/docs/reference/mapping/fields/all-field.asciidoc b/docs/reference/mapping/fields/all-field.asciidoc index 068fb45f7ec82..f6cf8237e1c70 100644 --- a/docs/reference/mapping/fields/all-field.asciidoc +++ b/docs/reference/mapping/fields/all-field.asciidoc @@ -101,6 +101,8 @@ requests>> (which is rewritten to a `query_string` query internally): -------------------------------- GET _search?q=john+smith+new+york -------------------------------- +// TEST[skip:_all is no longer allowed] +// CONSOLE Other queries, such as the <> and <> queries require you to specify the `_all` field diff --git a/docs/reference/mapping/params/analyzer.asciidoc b/docs/reference/mapping/params/analyzer.asciidoc index 0b60451e02af2..358be142373ca 100644 --- a/docs/reference/mapping/params/analyzer.asciidoc +++ b/docs/reference/mapping/params/analyzer.asciidoc @@ -135,11 +135,7 @@ PUT my_index } } } --------------------------------------------------- -// CONSOLE -[source,js] --------------------------------------------------- PUT my_index/my_type/1 { "title":"The Quick Brown Fox" @@ -159,6 +155,7 @@ GET my_index/my_type/_search } } -------------------------------------------------- +// CONSOLE <1> `my_analyzer` analyzer which tokens all terms including stop words <2> `my_stop_analyzer` analyzer which removes stop words <3> `analyzer` setting that points to the `my_analyzer` analyzer which will be used at index time diff --git a/docs/reference/mapping/types/binary.asciidoc b/docs/reference/mapping/types/binary.asciidoc index 7f82523416f14..556fc55b7f0f0 100644 --- a/docs/reference/mapping/types/binary.asciidoc +++ b/docs/reference/mapping/types/binary.asciidoc @@ -29,6 +29,7 @@ PUT my_index/my_type/1 "blob": "U29tZSBiaW5hcnkgYmxvYg==" <1> } -------------------------------------------------- +// CONSOLE <1> The Base64 encoded binary value must not have embedded newlines `\n`. [[binary-params]] @@ -49,5 +50,3 @@ The following parameters are accepted by `binary` fields: Whether the field value should be stored and retrievable separately from the <> field. Accepts `true` or `false` (default). - - diff --git a/docs/reference/mapping/types/ip.asciidoc b/docs/reference/mapping/types/ip.asciidoc index 423b501cbcd0f..afb482c454b48 100644 --- a/docs/reference/mapping/types/ip.asciidoc +++ b/docs/reference/mapping/types/ip.asciidoc @@ -34,7 +34,7 @@ GET my_index/_search } -------------------------------------------------- // CONSOLE - +// TESTSETUP [[ip-params]] ==== Parameters for `ip` fields @@ -86,6 +86,7 @@ GET my_index/_search } } -------------------------------------------------- +// CONSOLE or @@ -100,6 +101,7 @@ GET my_index/_search } } -------------------------------------------------- +// CONSOLE Also beware that colons are special characters to the <> query, so ipv6 addresses will @@ -108,7 +110,7 @@ searched value: [source,js] -------------------------------------------------- -GET t/_search +GET my_index/_search { "query": { "query_string" : { @@ -117,3 +119,4 @@ GET t/_search } } -------------------------------------------------- +// CONSOLE diff --git a/docs/reference/mapping/types/nested.asciidoc b/docs/reference/mapping/types/nested.asciidoc index 67896f07e22ff..8047193f934f4 100644 --- a/docs/reference/mapping/types/nested.asciidoc +++ b/docs/reference/mapping/types/nested.asciidoc @@ -42,6 +42,7 @@ would be transformed internally into a document that looks more like this: "user.last" : [ "smith", "white" ] } -------------------------------------------------- +// NOTCONSOLE The `user.first` and `user.last` fields are flattened into multi-value fields, and the association between `alice` and `white` is lost. This document would diff --git a/docs/reference/mapping/types/object.asciidoc b/docs/reference/mapping/types/object.asciidoc index e35f159712e29..31f728d1ae0fd 100644 --- a/docs/reference/mapping/types/object.asciidoc +++ b/docs/reference/mapping/types/object.asciidoc @@ -35,6 +35,7 @@ pairs, something like this: "manager.name.last": "Smith" } -------------------------------------------------- +// NOTCONSOLE An explicit mapping for the above document could look like this: @@ -96,4 +97,3 @@ The following parameters are accepted by `object` fields: IMPORTANT: If you need to index arrays of objects instead of single objects, read <> first. - diff --git a/docs/reference/mapping/types/percolator.asciidoc b/docs/reference/mapping/types/percolator.asciidoc index 6197c1b9d0bb1..1a5121ae30789 100644 --- a/docs/reference/mapping/types/percolator.asciidoc +++ b/docs/reference/mapping/types/percolator.asciidoc @@ -15,27 +15,39 @@ If the following mapping configures the `percolator` field type for the [source,js] -------------------------------------------------- +PUT my_index { - "properties": { - "query": { - "type": "percolator" + "mappings": { + "doc": { + "properties": { + "query": { + "type": "percolator" + }, + "field": { + "type": "text" + } + } } } } -------------------------------------------------- +// CONSOLE +// TESTSETUP -Then the following json snippet can be indexed as a native query: +Then you can index a query: [source,js] -------------------------------------------------- +PUT my_index/doc/match_value { "query" : { - "match" : { - "field" : "value" - } - } + "match" : { + "field" : "value" + } + } } -------------------------------------------------- +// CONSOLE [IMPORTANT] ===================================== @@ -82,4 +94,4 @@ queries are indexed by the `percolator` field type then the get call is executed query evaluates these queries, the fetches terms, shapes etc. as the were upon index time will be used. Important to note is that fetching of terms that these queries do, happens both each time the percolator query gets indexed on both primary and replica shards, so the terms that are actually indexed can be different between shard copies, if the source index -changed while indexing. \ No newline at end of file +changed while indexing. From db93735321c3df5938c90d6575b2680c54677c9f Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Mon, 24 Apr 2017 17:06:54 -0400 Subject: [PATCH 063/619] CONSOLEify some of the docs documentation delete, index, and update. Relates to #18160 --- docs/build.gradle | 3 -- docs/reference/docs/delete.asciidoc | 33 ++++++++++++++++++---- docs/reference/docs/index_.asciidoc | 44 +++++++++++++++-------------- docs/reference/docs/update.asciidoc | 10 ++++--- 4 files changed, 57 insertions(+), 33 deletions(-) diff --git a/docs/build.gradle b/docs/build.gradle index b75a88fc6ce87..a587cec658f0c 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -64,11 +64,8 @@ buildRestTests.expectedUnconvertedCandidates = [ 'reference/cluster/stats.asciidoc', 'reference/cluster/tasks.asciidoc', 'reference/docs/delete-by-query.asciidoc', - 'reference/docs/delete.asciidoc', - 'reference/docs/index_.asciidoc', 'reference/docs/reindex.asciidoc', 'reference/docs/update-by-query.asciidoc', - 'reference/docs/update.asciidoc', 'reference/index-modules/similarity.asciidoc', 'reference/index-modules/store.asciidoc', 'reference/index-modules/translog.asciidoc', diff --git a/docs/reference/docs/delete.asciidoc b/docs/reference/docs/delete.asciidoc index dd5c1de148526..403c05679aff1 100644 --- a/docs/reference/docs/delete.asciidoc +++ b/docs/reference/docs/delete.asciidoc @@ -8,8 +8,10 @@ from an index called twitter, under a type called tweet, with id valued [source,js] -------------------------------------------------- -$ curl -XDELETE 'http://localhost:9200/twitter/tweet/1' +DELETE /twitter/tweet/1 -------------------------------------------------- +// CONSOLE +// TEST[setup:twitter] The result of the above delete operation is: @@ -17,18 +19,21 @@ The result of the above delete operation is: -------------------------------------------------- { "_shards" : { - "total" : 10, + "total" : 2, "failed" : 0, - "successful" : 10 + "successful" : 2 }, "found" : true, "_index" : "twitter", "_type" : "tweet", "_id" : "1", "_version" : 2, + "_primary_term": 1, + "_seq_no": 5, "result": "deleted" } -------------------------------------------------- +// TESTRESPONSE[s/"successful" : 2/"successful" : 1/] [float] [[delete-versioning]] @@ -48,10 +53,26 @@ When indexing using the ability to control the routing, in order to delete a document, the routing value should also be provided. For example: +//// +Example to delete with routing + +[source,js] +-------------------------------------------------- +PUT /twitter/tweet/1?routing=kimhcy +{ + "test": "test" +} +-------------------------------------------------- +// CONSOLE +//// + + [source,js] -------------------------------------------------- -$ curl -XDELETE 'http://localhost:9200/twitter/tweet/1?routing=kimchy' +DELETE /twitter/tweet/1?routing=kimchy -------------------------------------------------- +// CONSOLE +// TEST[continued] The above will delete a tweet with id 1, but will be routed based on the user. Note, issuing a delete without the correct routing, will cause the @@ -130,5 +151,7 @@ to 5 minutes: [source,js] -------------------------------------------------- -$ curl -XDELETE 'http://localhost:9200/twitter/tweet/1?timeout=5m' +DELETE /twitter/tweet/1?timeout=5m -------------------------------------------------- +// CONSOLE +// TEST[setup:twitter] diff --git a/docs/reference/docs/index_.asciidoc b/docs/reference/docs/index_.asciidoc index 2af9cf0a0c529..bcbcee2f907a8 100644 --- a/docs/reference/docs/index_.asciidoc +++ b/docs/reference/docs/index_.asciidoc @@ -46,9 +46,9 @@ The `_shards` header provides information about the replication process of the i The index operation is successful in the case `successful` is at least 1. -NOTE: Replica shards may not all be started when an indexing operation successfully returns (by default, only the - primary is required, but this behavior can be <>). In that case, - `total` will be equal to the total shards based on the `number_of_replicas` setting and `successful` will be +NOTE: Replica shards may not all be started when an indexing operation successfully returns (by default, only the + primary is required, but this behavior can be <>). In that case, + `total` will be equal to the total shards based on the `number_of_replicas` setting and `successful` will be equal to the number of shards started (primary plus replicas). If there were no failures, the `failed` will be 0. [float] @@ -101,6 +101,7 @@ PUT twitter/tweet/1?version=2 } -------------------------------------------------- // CONSOLE +// TEST[continued] // TEST[catch: conflict] *NOTE:* versioning is completely real time, and is not affected by the @@ -312,46 +313,46 @@ if needed, the update is distributed to applicable replicas. [[index-wait-for-active-shards]] === Wait For Active Shards -To improve the resiliency of writes to the system, indexing operations -can be configured to wait for a certain number of active shard copies +To improve the resiliency of writes to the system, indexing operations +can be configured to wait for a certain number of active shard copies before proceeding with the operation. If the requisite number of active -shard copies are not available, then the write operation must wait and -retry, until either the requisite shard copies have started or a timeout -occurs. By default, write operations only wait for the primary shards +shard copies are not available, then the write operation must wait and +retry, until either the requisite shard copies have started or a timeout +occurs. By default, write operations only wait for the primary shards to be active before proceeding (i.e. `wait_for_active_shards=1`). This default can be overridden in the index settings dynamically -by setting `index.write.wait_for_active_shards`. To alter this behavior +by setting `index.write.wait_for_active_shards`. To alter this behavior per operation, the `wait_for_active_shards` request parameter can be used. Valid values are `all` or any positive integer up to the total number of configured copies per shard in the index (which is `number_of_replicas+1`). -Specifying a negative value or a number greater than the number of +Specifying a negative value or a number greater than the number of shard copies will throw an error. For example, suppose we have a cluster of three nodes, `A`, `B`, and `C` and -we create an index `index` with the number of replicas set to 3 (resulting in -4 shard copies, one more copy than there are nodes). If we +we create an index `index` with the number of replicas set to 3 (resulting in +4 shard copies, one more copy than there are nodes). If we attempt an indexing operation, by default the operation will only ensure the primary copy of each shard is available before proceeding. This means that even if `B` and `C` went down, and `A` hosted the primary shard copies, -the indexing operation would still proceed with only one copy of the data. +the indexing operation would still proceed with only one copy of the data. If `wait_for_active_shards` is set on the request to `3` (and all 3 nodes -are up), then the indexing operation will require 3 active shard copies +are up), then the indexing operation will require 3 active shard copies before proceeding, a requirement which should be met because there are 3 active nodes in the cluster, each one holding a copy of the shard. However, -if we set `wait_for_active_shards` to `all` (or to `4`, which is the same), -the indexing operation will not proceed as we do not have all 4 copies of -each shard active in the index. The operation will timeout +if we set `wait_for_active_shards` to `all` (or to `4`, which is the same), +the indexing operation will not proceed as we do not have all 4 copies of +each shard active in the index. The operation will timeout unless a new node is brought up in the cluster to host the fourth copy of the shard. -It is important to note that this setting greatly reduces the chances of -the write operation not writing to the requisite number of shard copies, +It is important to note that this setting greatly reduces the chances of +the write operation not writing to the requisite number of shard copies, but it does not completely eliminate the possibility, because this check occurs before the write operation commences. Once the write operation -is underway, it is still possible for replication to fail on any number of +is underway, it is still possible for replication to fail on any number of shard copies but still succeed on the primary. The `_shards` section of the -write operation's response reveals the number of shard copies on which +write operation's response reveals the number of shard copies on which replication succeeded/failed. [source,js] @@ -364,6 +365,7 @@ replication succeeded/failed. } } -------------------------------------------------- +// NOTCONSOLE [float] [[index-refresh]] diff --git a/docs/reference/docs/update.asciidoc b/docs/reference/docs/update.asciidoc index c904326f2d47f..47711830df62e 100644 --- a/docs/reference/docs/update.asciidoc +++ b/docs/reference/docs/update.asciidoc @@ -75,7 +75,7 @@ We can also add a new field to the document: -------------------------------------------------- POST test/type1/1/_update { - "script" : "ctx._source.new_field = \"value_of_new_field\"" + "script" : "ctx._source.new_field = 'value_of_new_field'" } -------------------------------------------------- // CONSOLE @@ -87,7 +87,7 @@ Or remove a field from the document: -------------------------------------------------- POST test/type1/1/_update { - "script" : "ctx._source.remove(\"new_field\")" + "script" : "ctx._source.remove('new_field')" } -------------------------------------------------- // CONSOLE @@ -102,7 +102,7 @@ the doc if the `tags` field contain `green`, otherwise it does nothing POST test/type1/1/_update { "script" : { - "inline": "if (ctx._source.tags.contains(params.tag)) { ctx.op = \"delete\" } else { ctx.op = \"none\" }", + "inline": "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'none' }", "lang": "painless", "params" : { "tag" : "green" @@ -242,6 +242,9 @@ POST sessions/session/dh3sgudg8gsrgl/_update "upsert" : {} } -------------------------------------------------- +// CONSOLE +// TEST[s/"id": "my_web_session_summariser"/"inline": "ctx._source.page_view_event = params.pageViewEvent"/] +// TEST[continued] [float] ==== `doc_as_upsert` @@ -263,7 +266,6 @@ POST test/type1/1/_update // CONSOLE // TEST[continued] - [float] === Parameters From 3ae671aaf3dfdedf7871641c4a4617106b903999 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Mon, 24 Apr 2017 17:22:06 -0400 Subject: [PATCH 064/619] Docs test: Be ok with different _seq_nos The test wanted specific _primary_terms and _seq_nos but there is no need to specify that. --- docs/reference/docs/delete.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference/docs/delete.asciidoc b/docs/reference/docs/delete.asciidoc index 403c05679aff1..6fbe5e33ce27a 100644 --- a/docs/reference/docs/delete.asciidoc +++ b/docs/reference/docs/delete.asciidoc @@ -34,6 +34,8 @@ The result of the above delete operation is: } -------------------------------------------------- // TESTRESPONSE[s/"successful" : 2/"successful" : 1/] +// TESTRESPONSE[s/"_primary_term" : 1/"_primary_term" : $body._primary_term/] +// TESTRESPONSE[s/"_seq_no" : 5/"_seq_no" : $body._seq_no/] [float] [[delete-versioning]] From 6ebf08759bbb224401fb44a08665aa2c4c428f5e Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 24 Apr 2017 15:45:20 -0700 Subject: [PATCH 065/619] Templates: Add compileTemplate method to ScriptService for template consumers (#24280) This commit adds a compileTemplate method to the ScriptService. Eventually this will be used to easily cutover all consumers to a new TemplateService. relates #16314 --- .../index/query/QueryRewriteContext.java | 6 ++-- .../ingest/InternalTemplateService.java | 19 ++++------ .../elasticsearch/script/ScriptService.java | 8 +++++ .../template/CompiledTemplate.java | 35 +++++++++++++++++++ .../TransportSearchTemplateAction.java | 8 ++--- 5 files changed, 57 insertions(+), 19 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/template/CompiledTemplate.java diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java b/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java index 183ab690ce2ad..036421159767c 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java @@ -30,6 +30,7 @@ import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptService; +import org.elasticsearch.template.CompiledTemplate; import java.util.function.LongSupplier; @@ -105,8 +106,7 @@ public long nowInMillis() { } public BytesReference getTemplateBytes(Script template) { - CompiledScript compiledTemplate = scriptService.compile(template, ScriptContext.Standard.SEARCH); - ExecutableScript executable = scriptService.executable(compiledTemplate, template.getParams()); - return (BytesReference) executable.run(); + CompiledTemplate compiledTemplate = scriptService.compileTemplate(template, ScriptContext.Standard.SEARCH); + return compiledTemplate.run(template.getParams()); } } diff --git a/core/src/main/java/org/elasticsearch/ingest/InternalTemplateService.java b/core/src/main/java/org/elasticsearch/ingest/InternalTemplateService.java index 26d6737706bcb..b5aa2dbc51a8a 100644 --- a/core/src/main/java/org/elasticsearch/ingest/InternalTemplateService.java +++ b/core/src/main/java/org/elasticsearch/ingest/InternalTemplateService.java @@ -19,16 +19,16 @@ package org.elasticsearch.ingest; +import java.util.Collections; +import java.util.Map; + +import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.script.CompiledScript; -import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptType; - -import java.util.Collections; -import java.util.Map; +import org.elasticsearch.template.CompiledTemplate; public class InternalTemplateService implements TemplateService { @@ -44,16 +44,11 @@ public Template compile(String template) { int mustacheEnd = template.indexOf("}}"); if (mustacheStart != -1 && mustacheEnd != -1 && mustacheStart < mustacheEnd) { Script script = new Script(ScriptType.INLINE, "mustache", template, Collections.emptyMap()); - CompiledScript compiledScript = scriptService.compile(script, ScriptContext.Standard.INGEST); + CompiledTemplate compiledTemplate = scriptService.compileTemplate(script, ScriptContext.Standard.INGEST); return new Template() { @Override public String execute(Map model) { - ExecutableScript executableScript = scriptService.executable(compiledScript, model); - Object result = executableScript.run(); - if (result instanceof BytesReference) { - return ((BytesReference) result).utf8ToString(); - } - return String.valueOf(result); + return compiledTemplate.run(model).utf8ToString(); } @Override diff --git a/core/src/main/java/org/elasticsearch/script/ScriptService.java b/core/src/main/java/org/elasticsearch/script/ScriptService.java index e0c7b3c63de08..7a8691656a4c5 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptService.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptService.java @@ -38,6 +38,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Strings; import org.elasticsearch.common.breaker.CircuitBreakingException; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.cache.Cache; import org.elasticsearch.common.cache.CacheBuilder; import org.elasticsearch.common.cache.RemovalListener; @@ -56,6 +57,7 @@ import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.env.Environment; import org.elasticsearch.search.lookup.SearchLookup; +import org.elasticsearch.template.CompiledTemplate; import org.elasticsearch.watcher.FileChangesListener; import org.elasticsearch.watcher.FileWatcher; import org.elasticsearch.watcher.ResourceWatcherService; @@ -320,6 +322,12 @@ public CompiledScript compile(Script script, ScriptContext scriptContext) { } } + /** Compiles a template. Note this will be moved to a separate TemplateService in the future. */ + public CompiledTemplate compileTemplate(Script script, ScriptContext scriptContext) { + CompiledScript compiledScript = compile(script, scriptContext); + return params -> (BytesReference)executable(compiledScript, params).run(); + } + /** * Check whether there have been too many compilations within the last minute, throwing a circuit breaking exception if so. * This is a variant of the token bucket algorithm: https://en.wikipedia.org/wiki/Token_bucket diff --git a/core/src/main/java/org/elasticsearch/template/CompiledTemplate.java b/core/src/main/java/org/elasticsearch/template/CompiledTemplate.java new file mode 100644 index 0000000000000..52f46202eb8ba --- /dev/null +++ b/core/src/main/java/org/elasticsearch/template/CompiledTemplate.java @@ -0,0 +1,35 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.template; + +import java.util.Map; + +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.script.CompiledScript; +import org.elasticsearch.script.ScriptType; + +/** + * A template that may be executed. + */ +public interface CompiledTemplate { + + /** Run a template and return the resulting string, encoded in utf8 bytes. */ + BytesReference run(Map params); +} diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java index 61f099f6c24cd..60435e72a4beb 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java @@ -19,6 +19,7 @@ package org.elasticsearch.script.mustache; +import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; @@ -39,6 +40,7 @@ import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.template.CompiledTemplate; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -72,10 +74,8 @@ protected void doExecute(SearchTemplateRequest request, ActionListener Date: Tue, 25 Apr 2017 10:30:08 +0100 Subject: [PATCH 066/619] [TEST] mute failing docs test --- docs/reference/docs/delete.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/docs/delete.asciidoc b/docs/reference/docs/delete.asciidoc index 6fbe5e33ce27a..73632557b6280 100644 --- a/docs/reference/docs/delete.asciidoc +++ b/docs/reference/docs/delete.asciidoc @@ -11,7 +11,7 @@ from an index called twitter, under a type called tweet, with id valued DELETE /twitter/tweet/1 -------------------------------------------------- // CONSOLE -// TEST[setup:twitter] +// TEST[skip:https://github.com/elastic/elasticsearch/issues/24303] The result of the above delete operation is: From 508b774d76bc7e3787ac1545c8e6fae35a35fe4a Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 25 Apr 2017 06:04:02 -0400 Subject: [PATCH 067/619] Revert "[TEST] mute failing docs test" This reverts commit f2e31cdeefc4e03f5202951643b1c6e746817d10. --- docs/reference/docs/delete.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/docs/delete.asciidoc b/docs/reference/docs/delete.asciidoc index 73632557b6280..6fbe5e33ce27a 100644 --- a/docs/reference/docs/delete.asciidoc +++ b/docs/reference/docs/delete.asciidoc @@ -11,7 +11,7 @@ from an index called twitter, under a type called tweet, with id valued DELETE /twitter/tweet/1 -------------------------------------------------- // CONSOLE -// TEST[skip:https://github.com/elastic/elasticsearch/issues/24303] +// TEST[setup:twitter] The result of the above delete operation is: From 88de33d43d48dc4cc185306ebae72eedf1a97b4f Mon Sep 17 00:00:00 2001 From: Koen De Groote Date: Tue, 25 Apr 2017 13:13:55 +0200 Subject: [PATCH 068/619] Minor changes to collection creation from enums (#24274) These changes are mainly cosmetic with minor perf advantages drawn from checkstyle. --- .../org/elasticsearch/cluster/node/DiscoveryNode.java | 2 +- .../routing/allocation/AllocateUnassignedDecision.java | 4 ++-- .../org/elasticsearch/common/settings/SecureSetting.java | 9 ++------- .../java/org/elasticsearch/common/unit/TimeValue.java | 3 ++- .../java/org/elasticsearch/ingest/IngestDocument.java | 3 ++- .../java/org/elasticsearch/script/ScriptSettings.java | 3 ++- .../org/elasticsearch/search/internal/SearchContext.java | 4 ++-- .../java/org/elasticsearch/AnalysisFactoryTestCase.java | 7 ++++--- .../org/elasticsearch/cluster/ESAllocationTestCase.java | 4 ++-- .../java/org/elasticsearch/test/ClusterServiceUtils.java | 4 ++-- .../org/elasticsearch/test/PosixPermissionsResetter.java | 6 +++--- 11 files changed, 24 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java b/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java index 3eea37e2c898d..0e8435d0f8e62 100644 --- a/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java +++ b/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java @@ -198,7 +198,7 @@ public static DiscoveryNode createLocal(Settings settings, TransportAddress publ /** extract node roles from the given settings */ public static Set getRolesFromSettings(Settings settings) { - Set roles = new HashSet<>(); + Set roles = EnumSet.noneOf(Role.class); if (Node.NODE_INGEST_SETTING.get(settings)) { roles.add(Role.INGEST); } diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocateUnassignedDecision.java b/core/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocateUnassignedDecision.java index 49b9604e3459e..decdafd724c7d 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocateUnassignedDecision.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocateUnassignedDecision.java @@ -31,7 +31,7 @@ import java.io.IOException; import java.util.Collections; -import java.util.HashMap; +import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -49,7 +49,7 @@ public class AllocateUnassignedDecision extends AbstractAllocationDecision { */ private static final Map CACHED_DECISIONS; static { - Map cachedDecisions = new HashMap<>(); + Map cachedDecisions = new EnumMap<>(AllocationStatus.class); cachedDecisions.put(AllocationStatus.FETCHING_SHARD_DATA, new AllocateUnassignedDecision(AllocationStatus.FETCHING_SHARD_DATA, null, null, null, false, 0L, 0L)); cachedDecisions.put(AllocationStatus.NO_VALID_SHARD_COPY, diff --git a/core/src/main/java/org/elasticsearch/common/settings/SecureSetting.java b/core/src/main/java/org/elasticsearch/common/settings/SecureSetting.java index 2efb36696c5f7..e93240b9a32ba 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/SecureSetting.java +++ b/core/src/main/java/org/elasticsearch/common/settings/SecureSetting.java @@ -21,12 +21,9 @@ import java.io.InputStream; import java.security.GeneralSecurityException; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Objects; +import java.util.EnumSet; import java.util.Set; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.util.ArrayUtils; @@ -36,9 +33,7 @@ * This class allows access to settings from the Elasticsearch keystore. */ public abstract class SecureSetting extends Setting { - private static final Set ALLOWED_PROPERTIES = new HashSet<>( - Arrays.asList(Property.Deprecated, Property.Shared) - ); + private static final Set ALLOWED_PROPERTIES = EnumSet.of(Property.Deprecated, Property.Shared); private static final Property[] FIXED_PROPERTIES = { Property.NodeScope diff --git a/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java b/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java index 4ab91aac5b529..4c3344eb9d880 100644 --- a/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java +++ b/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.util.Collections; +import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; @@ -48,7 +49,7 @@ public class TimeValue implements Writeable, Comparable { private static Map BYTE_TIME_UNIT_MAP; static { - final Map timeUnitByteMap = new HashMap<>(); + final Map timeUnitByteMap = new EnumMap<>(TimeUnit.class); timeUnitByteMap.put(TimeUnit.NANOSECONDS, (byte)0); timeUnitByteMap.put(TimeUnit.MICROSECONDS, (byte)1); timeUnitByteMap.put(TimeUnit.MILLISECONDS, (byte)2); diff --git a/core/src/main/java/org/elasticsearch/ingest/IngestDocument.java b/core/src/main/java/org/elasticsearch/ingest/IngestDocument.java index 02d3988c4a83e..fcf49ef6992ad 100644 --- a/core/src/main/java/org/elasticsearch/ingest/IngestDocument.java +++ b/core/src/main/java/org/elasticsearch/ingest/IngestDocument.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Base64; import java.util.Date; +import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -554,7 +555,7 @@ private Map createTemplateModel() { * Metadata fields that used to be accessible as ordinary top level fields will be removed as part of this call. */ public Map extractMetadata() { - Map metadataMap = new HashMap<>(); + Map metadataMap = new EnumMap<>(MetaData.class); for (MetaData metaData : MetaData.values()) { metadataMap.put(metaData, cast(metaData.getFieldName(), sourceAndMetadata.remove(metaData.getFieldName()), String.class)); } diff --git a/core/src/main/java/org/elasticsearch/script/ScriptSettings.java b/core/src/main/java/org/elasticsearch/script/ScriptSettings.java index 447097a488404..e4387aa52dd8e 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptSettings.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptSettings.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -35,7 +36,7 @@ public class ScriptSettings { private static final Map> SCRIPT_TYPE_SETTING_MAP; static { - Map> scriptTypeSettingMap = new HashMap<>(); + Map> scriptTypeSettingMap = new EnumMap<>(ScriptType.class); for (ScriptType scriptType : ScriptType.values()) { scriptTypeSettingMap.put(scriptType, Setting.boolSetting( ScriptModes.sourceKey(scriptType), diff --git a/core/src/main/java/org/elasticsearch/search/internal/SearchContext.java b/core/src/main/java/org/elasticsearch/search/internal/SearchContext.java index 59d3646f18fa2..ebb2157d981e7 100644 --- a/core/src/main/java/org/elasticsearch/search/internal/SearchContext.java +++ b/core/src/main/java/org/elasticsearch/search/internal/SearchContext.java @@ -63,7 +63,7 @@ import org.elasticsearch.search.suggest.SuggestionSearchContext; import java.util.ArrayList; -import java.util.HashMap; +import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @@ -336,7 +336,7 @@ public SearchLookup lookup() { */ public void addReleasable(Releasable releasable, Lifetime lifetime) { if (clearables == null) { - clearables = new HashMap<>(); + clearables = new EnumMap<>(Lifetime.class); } List releasables = clearables.get(lifetime); if (releasables == null) { diff --git a/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java b/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java index 7f60058788a18..d49a1b4cae5bf 100644 --- a/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java @@ -98,6 +98,7 @@ import org.elasticsearch.test.ESTestCase; import java.util.Collection; +import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -152,7 +153,7 @@ private static String toCamelCase(String s) { static final Map> PREBUILT_TOKENIZERS; static { - PREBUILT_TOKENIZERS = new HashMap<>(); + PREBUILT_TOKENIZERS = new EnumMap<>(PreBuiltTokenizers.class); for (PreBuiltTokenizers tokenizer : PreBuiltTokenizers.values()) { Class luceneFactoryClazz; switch (tokenizer) { @@ -289,7 +290,7 @@ private static String toCamelCase(String s) { static final Map> PREBUILT_TOKENFILTERS; static { - PREBUILT_TOKENFILTERS = new HashMap<>(); + PREBUILT_TOKENFILTERS = new EnumMap<>(PreBuiltTokenFilters.class); for (PreBuiltTokenFilters tokenizer : PreBuiltTokenFilters.values()) { Class luceneFactoryClazz; switch (tokenizer) { @@ -335,7 +336,7 @@ private static String toCamelCase(String s) { static final Map> PREBUILT_CHARFILTERS; static { - PREBUILT_CHARFILTERS = new HashMap<>(); + PREBUILT_CHARFILTERS = new EnumMap<>(PreBuiltCharFilters.class); for (PreBuiltCharFilters tokenizer : PreBuiltCharFilters.values()) { Class luceneFactoryClazz; switch (tokenizer) { diff --git a/test/framework/src/main/java/org/elasticsearch/cluster/ESAllocationTestCase.java b/test/framework/src/main/java/org/elasticsearch/cluster/ESAllocationTestCase.java index a0cc0130bfc73..02f8896be4d37 100644 --- a/test/framework/src/main/java/org/elasticsearch/cluster/ESAllocationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/cluster/ESAllocationTestCase.java @@ -43,7 +43,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; +import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Random; @@ -95,7 +95,7 @@ public static AllocationDeciders randomAllocationDeciders(Settings settings, Clu } protected static Set MASTER_DATA_ROLES = - Collections.unmodifiableSet(new HashSet<>(Arrays.asList(DiscoveryNode.Role.MASTER, DiscoveryNode.Role.DATA))); + Collections.unmodifiableSet(EnumSet.of(DiscoveryNode.Role.MASTER, DiscoveryNode.Role.DATA)); protected static DiscoveryNode newNode(String nodeId) { return newNode(nodeId, Version.CURRENT); diff --git a/test/framework/src/main/java/org/elasticsearch/test/ClusterServiceUtils.java b/test/framework/src/main/java/org/elasticsearch/test/ClusterServiceUtils.java index 01f626a1e2b35..b2eced6fd3545 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ClusterServiceUtils.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ClusterServiceUtils.java @@ -33,7 +33,7 @@ import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; +import java.util.EnumSet; import java.util.concurrent.CountDownLatch; import static junit.framework.TestCase.fail; @@ -42,7 +42,7 @@ public class ClusterServiceUtils { public static ClusterService createClusterService(ThreadPool threadPool) { DiscoveryNode discoveryNode = new DiscoveryNode("node", ESTestCase.buildNewFakeTransportAddress(), Collections.emptyMap(), - new HashSet<>(Arrays.asList(DiscoveryNode.Role.values())),Version.CURRENT); + EnumSet.allOf(DiscoveryNode.Role.class), Version.CURRENT); return createClusterService(threadPool, discoveryNode); } diff --git a/test/framework/src/main/java/org/elasticsearch/test/PosixPermissionsResetter.java b/test/framework/src/main/java/org/elasticsearch/test/PosixPermissionsResetter.java index a644205bad96a..19bea3802f07d 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/PosixPermissionsResetter.java +++ b/test/framework/src/main/java/org/elasticsearch/test/PosixPermissionsResetter.java @@ -18,6 +18,8 @@ */ package org.elasticsearch.test; +import java.util.EnumSet; +import java.util.Set; import org.junit.Assert; import java.io.IOException; @@ -25,8 +27,6 @@ import java.nio.file.Path; import java.nio.file.attribute.PosixFileAttributeView; import java.nio.file.attribute.PosixFilePermission; -import java.util.HashSet; -import java.util.Set; /** Stores the posix attributes for a path and resets them on close. */ public class PosixPermissionsResetter implements AutoCloseable { @@ -46,6 +46,6 @@ public void setPermissions(Set newPermissions) throws IOExc } public Set getCopyPermissions() { - return new HashSet<>(permissions); + return EnumSet.copyOf(permissions); } } From 768420db554479d7853a3d63548b2cb8cd507f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Tue, 25 Apr 2017 13:42:15 +0200 Subject: [PATCH 069/619] Add parsing for InternalStats (#24239) --- .../aggregations/ParsedAggregation.java | 15 ++ ...dSingleValueNumericMetricsAggregation.java | 53 +++---- .../metrics/stats/InternalStats.java | 29 ++-- .../metrics/stats/ParsedStats.java | 147 ++++++++++++++++++ .../stats/extended/InternalExtendedStats.java | 2 +- .../pipeline/derivative/ParsedDerivative.java | 2 +- .../InternalAggregationTestCase.java | 3 + .../metrics/InternalStatsTests.java | 27 +++- 8 files changed, 230 insertions(+), 48 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java index 452fe9dcb0853..6942b6aec5d1f 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java @@ -22,6 +22,8 @@ import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParser.Token; import java.io.IOException; import java.util.Collections; @@ -77,4 +79,17 @@ public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params par } protected abstract XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException; + + /** + * Parse a token of type XContentParser.Token.VALUE_NUMBER or XContentParser.Token.STRING to a double. + * In other cases the default value is returned instead. + */ + protected static double parseDouble(XContentParser parser, double defaultNullValue) throws IOException { + Token currentToken = parser.currentToken(); + if (currentToken == XContentParser.Token.VALUE_NUMBER || currentToken == XContentParser.Token.VALUE_STRING) { + return parser.doubleValue(); + } else { + return defaultNullValue; + } + } } \ No newline at end of file diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java index 5eb0a7223a11f..3105a785e1903 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java @@ -20,54 +20,41 @@ import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ObjectParser.ValueType; -import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentParser.Token; import org.elasticsearch.search.aggregations.ParsedAggregation; -import java.io.IOException; - public abstract class ParsedSingleValueNumericMetricsAggregation extends ParsedAggregation implements NumericMetricsAggregation.SingleValue { - protected double value; - protected String valueAsString; - - @Override - public String getValueAsString() { - if (valueAsString != null) { - return valueAsString; - } else { - return Double.toString(value); - } - } + protected double value; + protected String valueAsString; - @Override - public double value() { - return value; + @Override + public String getValueAsString() { + if (valueAsString != null) { + return valueAsString; + } else { + return Double.toString(value); } + } - protected void setValue(double value) { - this.value = value; - } + @Override + public double value() { + return value; + } - protected void setValueAsString(String valueAsString) { - this.valueAsString = valueAsString; - } + protected void setValue(double value) { + this.value = value; + } - protected static double parseValue(XContentParser parser, double defaultNullValue) throws IOException { - Token currentToken = parser.currentToken(); - if (currentToken == XContentParser.Token.VALUE_NUMBER || currentToken == XContentParser.Token.VALUE_STRING) { - return parser.doubleValue(); - } else { - return defaultNullValue; - } - } + protected void setValueAsString(String valueAsString) { + this.valueAsString = valueAsString; + } protected static void declareSingleValueFields(ObjectParser objectParser, double defaultNullValue) { declareAggregationFields(objectParser); objectParser.declareField(ParsedSingleValueNumericMetricsAggregation::setValue, - (parser, context) -> parseValue(parser, defaultNullValue), CommonFields.VALUE, ValueType.DOUBLE_OR_NULL); + (parser, context) -> parseDouble(parser, defaultNullValue), CommonFields.VALUE, ValueType.DOUBLE_OR_NULL); objectParser.declareString(ParsedSingleValueNumericMetricsAggregation::setValueAsString, CommonFields.VALUE_AS_STRING); } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/InternalStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/InternalStats.java index b0b2ea73d3c9f..a29754ea559a6 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/InternalStats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/InternalStats.java @@ -177,21 +177,28 @@ static class Fields { @Override public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { builder.field(Fields.COUNT, count); - builder.field(Fields.MIN, count != 0 ? min : null); - builder.field(Fields.MAX, count != 0 ? max : null); - builder.field(Fields.AVG, count != 0 ? getAvg() : null); - builder.field(Fields.SUM, count != 0 ? sum : null); - if (count != 0 && format != DocValueFormat.RAW) { - builder.field(Fields.MIN_AS_STRING, format.format(min)); - builder.field(Fields.MAX_AS_STRING, format.format(max)); - builder.field(Fields.AVG_AS_STRING, format.format(getAvg())); - builder.field(Fields.SUM_AS_STRING, format.format(sum)); + if (count != 0) { + builder.field(Fields.MIN, min); + builder.field(Fields.MAX, max); + builder.field(Fields.AVG, getAvg()); + builder.field(Fields.SUM, sum); + if (format != DocValueFormat.RAW) { + builder.field(Fields.MIN_AS_STRING, format.format(min)); + builder.field(Fields.MAX_AS_STRING, format.format(max)); + builder.field(Fields.AVG_AS_STRING, format.format(getAvg())); + builder.field(Fields.SUM_AS_STRING, format.format(sum)); + } + } else { + builder.nullField(Fields.MIN); + builder.nullField(Fields.MAX); + builder.nullField(Fields.AVG); + builder.nullField(Fields.SUM); } - otherStatsToXCotent(builder, params); + otherStatsToXContent(builder, params); return builder; } - protected XContentBuilder otherStatsToXCotent(XContentBuilder builder, Params params) throws IOException { + protected XContentBuilder otherStatsToXContent(XContentBuilder builder, Params params) throws IOException { return builder; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java new file mode 100644 index 0000000000000..28ca3418a994a --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java @@ -0,0 +1,147 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.stats; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser.ValueType; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.ParsedAggregation; +import org.elasticsearch.search.aggregations.metrics.stats.InternalStats.Fields; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class ParsedStats extends ParsedAggregation implements Stats { + + protected long count; + protected double min; + protected double max; + protected double sum; + protected double avg; + + protected final Map valueAsString = new HashMap<>(); + + @Override + public long getCount() { + return count; + } + + @Override + public double getMin() { + return min; + } + + @Override + public double getMax() { + return max; + } + + @Override + public double getAvg() { + return avg; + } + + @Override + public double getSum() { + return sum; + } + + @Override + public String getMinAsString() { + return valueAsString.getOrDefault(Fields.MIN_AS_STRING, Double.toString(min)); + } + + @Override + public String getMaxAsString() { + return valueAsString.getOrDefault(Fields.MAX_AS_STRING, Double.toString(max)); + } + + @Override + public String getAvgAsString() { + return valueAsString.getOrDefault(Fields.AVG_AS_STRING, Double.toString(avg)); + } + + @Override + public String getSumAsString() { + return valueAsString.getOrDefault(Fields.SUM_AS_STRING, Double.toString(sum)); + } + + @Override + protected String getType() { + return StatsAggregationBuilder.NAME; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + builder.field(Fields.COUNT, count); + if (count != 0) { + builder.field(Fields.MIN, min); + builder.field(Fields.MAX, max); + builder.field(Fields.AVG, avg); + builder.field(Fields.SUM, sum); + if (valueAsString.get(Fields.MIN_AS_STRING) != null) { + builder.field(Fields.MIN_AS_STRING, getMinAsString()); + builder.field(Fields.MAX_AS_STRING, getMaxAsString()); + builder.field(Fields.AVG_AS_STRING, getAvgAsString()); + builder.field(Fields.SUM_AS_STRING, getSumAsString()); + } + } else { + builder.nullField(Fields.MIN); + builder.nullField(Fields.MAX); + builder.nullField(Fields.AVG); + builder.nullField(Fields.SUM); + } + otherStatsToXContent(builder, params); + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedStats.class.getSimpleName(), true, + ParsedStats::new); + + static { + declareAggregationFields(PARSER); + PARSER.declareLong((agg, value) -> agg.count = value, new ParseField(Fields.COUNT)); + PARSER.declareField((agg, value) -> agg.min = value, (parser, context) -> parseDouble(parser, Double.POSITIVE_INFINITY), + new ParseField(Fields.MIN), ValueType.DOUBLE_OR_NULL); + PARSER.declareField((agg, value) -> agg.max = value, (parser, context) -> parseDouble(parser, Double.NEGATIVE_INFINITY), + new ParseField(Fields.MAX), ValueType.DOUBLE_OR_NULL); + PARSER.declareField((agg, value) -> agg.avg = value, (parser, context) -> parseDouble(parser, 0), new ParseField(Fields.AVG), + ValueType.DOUBLE_OR_NULL); + PARSER.declareField((agg, value) -> agg.sum = value, (parser, context) -> parseDouble(parser, 0), new ParseField(Fields.SUM), + ValueType.DOUBLE_OR_NULL); + PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.MIN_AS_STRING, value), new ParseField(Fields.MIN_AS_STRING)); + PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.MAX_AS_STRING, value), new ParseField(Fields.MAX_AS_STRING)); + PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.AVG_AS_STRING, value), new ParseField(Fields.AVG_AS_STRING)); + PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.SUM_AS_STRING, value), new ParseField(Fields.SUM_AS_STRING)); + } + + public static ParsedStats fromXContent(XContentParser parser, final String name) { + ParsedStats parsedStats = PARSER.apply(parser, null); + parsedStats.setName(name); + return parsedStats; + } + + protected XContentBuilder otherStatsToXContent(XContentBuilder builder, Params params) throws IOException { + return builder; + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/InternalExtendedStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/InternalExtendedStats.java index 370399bfbb8db..b769850b817d8 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/InternalExtendedStats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/InternalExtendedStats.java @@ -169,7 +169,7 @@ static class Fields { } @Override - protected XContentBuilder otherStatsToXCotent(XContentBuilder builder, Params params) throws IOException { + protected XContentBuilder otherStatsToXContent(XContentBuilder builder, Params params) throws IOException { builder.field(Fields.SUM_OF_SQRS, count != 0 ? sumOfSqrs : null); builder.field(Fields.VARIANCE, count != 0 ? getVariance() : null); builder.field(Fields.STD_DEVIATION, count != 0 ? getStdDeviation() : null); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java index ed463239b2440..5469b8b65cfe8 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java @@ -54,7 +54,7 @@ protected String getType() { PARSER.declareField((agg, normalized) -> { agg.normalizedValue = normalized; agg.hasNormalizationFactor = true; - }, (parser, context) -> parseValue(parser, Double.NaN), NORMALIZED, ValueType.DOUBLE_OR_NULL); + }, (parser, context) -> parseDouble(parser, Double.NaN), NORMALIZED, ValueType.DOUBLE_OR_NULL); PARSER.declareString((agg, normalAsString) -> agg.normalizedAsString = normalAsString, NORMALIZED_AS_STRING); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index fd6b9ecdc44c0..63211f3db222a 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -50,6 +50,8 @@ import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentiles; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentiles; +import org.elasticsearch.search.aggregations.metrics.stats.ParsedStats; +import org.elasticsearch.search.aggregations.metrics.stats.StatsAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.sum.ParsedSum; import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.valuecount.ParsedValueCount; @@ -99,6 +101,7 @@ static List getNamedXContents() { namedXContents.put(InternalSimpleValue.NAME, (p, c) -> ParsedSimpleValue.fromXContent(p, (String) c)); namedXContents.put(DerivativePipelineAggregationBuilder.NAME, (p, c) -> ParsedDerivative.fromXContent(p, (String) c)); namedXContents.put(InternalBucketMetricValue.NAME, (p, c) -> ParsedBucketMetricValue.fromXContent(p, (String) c)); + namedXContents.put(StatsAggregationBuilder.NAME, (p, c) -> ParsedStats.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java index c99d208581c13..c0c54d3a8a000 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java @@ -21,7 +21,9 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.metrics.stats.InternalStats; +import org.elasticsearch.search.aggregations.metrics.stats.ParsedStats; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.Collections; @@ -29,9 +31,9 @@ import java.util.Map; public class InternalStatsTests extends InternalAggregationTestCase { + @Override - protected InternalStats createTestInstance(String name, List pipelineAggregators, - Map metaData) { + protected InternalStats createTestInstance(String name, List pipelineAggregators, Map metaData) { long count = frequently() ? randomIntBetween(1, Integer.MAX_VALUE) : 0; double min = randomDoubleBetween(-1000000, 1000000, true); double max = randomDoubleBetween(-1000000, 1000000, true); @@ -62,8 +64,29 @@ protected void assertReduced(InternalStats reduced, List inputs) assertEquals(expectedMax, reduced.getMax(), 0d); } + @Override + protected void assertFromXContent(InternalStats aggregation, ParsedAggregation parsedAggregation) { + assertTrue(parsedAggregation instanceof ParsedStats); + ParsedStats parsed = (ParsedStats) parsedAggregation; + long count = aggregation.getCount(); + assertEquals(count, parsed.getCount()); + // for count == 0, fields are rendered as `null`, so we test that we parse to default values used also in the reduce phase + assertEquals(count > 0 ? aggregation.getMin() : Double.POSITIVE_INFINITY , parsed.getMin(), 0); + assertEquals(count > 0 ? aggregation.getMax() : Double.NEGATIVE_INFINITY, parsed.getMax(), 0); + assertEquals(count > 0 ? aggregation.getSum() : 0, parsed.getSum(), 0); + assertEquals(count > 0 ? aggregation.getAvg() : 0, parsed.getAvg(), 0); + // also as_string values are only rendered for count != 0 + if (count > 0) { + assertEquals(aggregation.getMinAsString(), parsed.getMinAsString()); + assertEquals(aggregation.getMaxAsString(), parsed.getMaxAsString()); + assertEquals(aggregation.getSumAsString(), parsed.getSumAsString()); + assertEquals(aggregation.getAvgAsString(), parsed.getAvgAsString()); + } + } + @Override protected Writeable.Reader instanceReader() { return InternalStats::new; } } + From 739cb35d1be6509eeca5200857de6f377270d735 Mon Sep 17 00:00:00 2001 From: Guillaume Le Floch Date: Tue, 25 Apr 2017 13:43:21 +0200 Subject: [PATCH 070/619] Allow passing single scrollID in clear scroll API body (#24242) * Allow single scrollId in string format Closes #24233 --- .../action/search/RestClearScrollAction.java | 13 ++++-- docs/reference/search/request/scroll.asciidoc | 2 +- .../rest-api-spec/test/scroll/11_clear.yaml | 46 ++++++++++++++++++- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/rest/action/search/RestClearScrollAction.java b/core/src/main/java/org/elasticsearch/rest/action/search/RestClearScrollAction.java index 5f39db3a357e1..c7281da23f137 100644 --- a/core/src/main/java/org/elasticsearch/rest/action/search/RestClearScrollAction.java +++ b/core/src/main/java/org/elasticsearch/rest/action/search/RestClearScrollAction.java @@ -78,10 +78,17 @@ public static void buildFromContent(XContentParser parser, ClearScrollRequest cl while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); - } else if ("scroll_id".equals(currentFieldName) && token == XContentParser.Token.START_ARRAY) { - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + } else if ("scroll_id".equals(currentFieldName)){ + if (token == XContentParser.Token.START_ARRAY) { + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + if (token.isValue() == false) { + throw new IllegalArgumentException("scroll_id array element should only contain scroll_id"); + } + clearScrollRequest.addScrollId(parser.text()); + } + } else { if (token.isValue() == false) { - throw new IllegalArgumentException("scroll_id array element should only contain scroll_id"); + throw new IllegalArgumentException("scroll_id element should only contain scroll_id"); } clearScrollRequest.addScrollId(parser.text()); } diff --git a/docs/reference/search/request/scroll.asciidoc b/docs/reference/search/request/scroll.asciidoc index 82a27881720fa..1b78c0bc9bacd 100644 --- a/docs/reference/search/request/scroll.asciidoc +++ b/docs/reference/search/request/scroll.asciidoc @@ -144,7 +144,7 @@ cleared as soon as the scroll is not being used anymore using the --------------------------------------- DELETE /_search/scroll { - "scroll_id" : ["DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ=="] + "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==" } --------------------------------------- // CONSOLE diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/11_clear.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/11_clear.yaml index aa4885825d2ad..c2a026df1d744 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/11_clear.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/11_clear.yaml @@ -38,7 +38,7 @@ scroll_id: $scroll_id1 --- -"Body params override query string": +"Body params with array param override query string": - do: indices.create: index: test_scroll @@ -76,3 +76,47 @@ catch: missing clear_scroll: scroll_id: $scroll_id1 + +--- +"Body params with string param scroll id override query string": + - skip: + version: " - 5.99.99" + reason: this uses a new API that has been added in 6.0 + + - do: + indices.create: + index: test_scroll + - do: + index: + index: test_scroll + type: test + id: 42 + body: { foo: bar } + + - do: + indices.refresh: {} + + - do: + search: + index: test_scroll + scroll: 1m + body: + query: + match_all: {} + + - set: {_scroll_id: scroll_id1} + + - do: + clear_scroll: + scroll_id: "invalid_scroll_id" + body: { "scroll_id": "$scroll_id1" } + + - do: + catch: missing + scroll: + scroll_id: $scroll_id1 + + - do: + catch: missing + clear_scroll: + scroll_id: $scroll_id1 From 92d6b2b60d7f3bb397acbfdad865f5835a558fa9 Mon Sep 17 00:00:00 2001 From: Bodecker DellaMaria Date: Tue, 25 Apr 2017 07:43:55 -0500 Subject: [PATCH 071/619] Add newly open-sourced Scala REST client "escalar" (#24174) Workday recently open-sourced our internal Scala wrapper for the Elasticsearch REST API. We plan to continue maintaining the library and use it in our products. Thought it would be a good idea to link it here in case anyone else is interested in using it! --- docs/community-clients/index.asciidoc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/community-clients/index.asciidoc b/docs/community-clients/index.asciidoc index 99d7e5f768cc4..fbb4a3ddce83c 100644 --- a/docs/community-clients/index.asciidoc +++ b/docs/community-clients/index.asciidoc @@ -238,16 +238,19 @@ The following projects appear to be abandoned: * https://github.com/sksamuel/elastic4s[elastic4s]: Scala DSL. -* https://github.com/scalastuff/esclient[esclient]: - Thin Scala client. - * https://github.com/gphat/wabisabi[wabisabi]: Asynchronous REST API Scala client. + +* https://github.com/workday/escalar[escalar]: + Type-safe Scala wrapper for the REST API. * https://github.com/SumoLogic/elasticsearch-client[elasticsearch-client]: Scala DSL that uses the REST API. Akka and AWS helpers included. -The following project appears to be abandoned: +The following projects appear to be abandoned: + +* https://github.com/scalastuff/esclient[esclient]: + Thin Scala client. * https://github.com/bsadeh/scalastic[scalastic]: Scala client. From e69147a87098a7261586225c9d5dae7f51c01d9f Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Tue, 25 Apr 2017 17:34:25 +0200 Subject: [PATCH 072/619] Add support for `tests.enable_mock_modules` to ESIntegTestCase (#24309) `tests.enable_mock_modules` is a documented but unrespected / unused option to disable all mock modules / pluings during test runs. This will basically site-step mock assertions like check-index on shard closing. This can speed up test-execution dramatically on nodes with slow disks etc. Relates to #24304 --- .../src/main/java/org/elasticsearch/test/ESIntegTestCase.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java index 7c8d8cd1a551f..0240a8c4315e1 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java @@ -299,6 +299,7 @@ public abstract class ESIntegTestCase extends ESTestCase { */ public static final String TESTS_ENABLE_MOCK_MODULES = "tests.enable_mock_modules"; + private static final boolean MOCK_MODULES_ENABLED = "true".equals(System.getProperty(TESTS_ENABLE_MOCK_MODULES, "true")); /** * Threshold at which indexing switches from frequently async to frequently bulk. */ @@ -1882,7 +1883,7 @@ protected Function getClientWrapper() { /** Return the mock plugins the cluster should use */ protected Collection> getMockPlugins() { final ArrayList> mocks = new ArrayList<>(); - if (randomBoolean()) { // sometimes run without those completely + if (MOCK_MODULES_ENABLED && randomBoolean()) { // sometimes run without those completely if (randomBoolean() && addMockTransportService()) { mocks.add(MockTransportService.TestPlugin.class); } From 0adaf9fb4ce2daaefebd1d08a42a7f691d0c3b55 Mon Sep 17 00:00:00 2001 From: Danilo Akamine Date: Tue, 25 Apr 2017 12:48:44 -0400 Subject: [PATCH 073/619] Drop `search_analyzer` parameter from keyword.asciidoc (#24221) `search_analyzer` isn't supported by `keyword` fields so this removes it from the documentation for them. --- docs/reference/mapping/types/keyword.asciidoc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/reference/mapping/types/keyword.asciidoc b/docs/reference/mapping/types/keyword.asciidoc index aba8fb9ea5087..e560f8ae1d0bd 100644 --- a/docs/reference/mapping/types/keyword.asciidoc +++ b/docs/reference/mapping/types/keyword.asciidoc @@ -91,11 +91,6 @@ The following parameters are accepted by `keyword` fields: the <> field. Accepts `true` or `false` (default). -<>:: - - The <> that should be used at search time on - <> fields. Defaults to the `analyzer` setting. - <>:: Which scoring algorithm or _similarity_ should be used. Defaults From 1b660c51275972e01520becafafc82110c8e50a0 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 25 Apr 2017 16:25:52 -0400 Subject: [PATCH 074/619] Fix incorrect logger invocation It looks like auto-complete gave us a nasty surprise here with Logger#equals being invoked instead of Logger#error swallowing the absolute worst-possible level of a log message. This commit fixes the invocation. --- .../elasticsearch/transport/netty4/Netty4InternalESLogger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java index aaa277e34b35f..91bbe1c1a9b3c 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java @@ -161,7 +161,7 @@ public boolean isErrorEnabled() { @Override public void error(String msg) { - logger.equals(msg); + logger.error(msg); } @Override From b744dc3bccda4cf0644dd06661c7886c4b6a4696 Mon Sep 17 00:00:00 2001 From: Till Backhaus Date: Tue, 25 Apr 2017 22:51:58 +0200 Subject: [PATCH 075/619] Link to minimum master nodes docs from Zen docs This commit adds a link to the minimum master nodes section of the important settings docs from the Zen discovery docs to clarify the meaning and importance of setting minimum master nodes to a quorum of master-eligible nodes. Relates #24311 --- docs/reference/modules/discovery/zen.asciidoc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/reference/modules/discovery/zen.asciidoc b/docs/reference/modules/discovery/zen.asciidoc index d65f278d41ae4..349dd1800e9ef 100644 --- a/docs/reference/modules/discovery/zen.asciidoc +++ b/docs/reference/modules/discovery/zen.asciidoc @@ -77,9 +77,10 @@ complete and for the elected node to accept its mastership. The same setting con active master eligible nodes that should be a part of any active cluster. If this requirement is not met the active master node will step down and a new master election will begin. -This setting must be set to a quorum of your master eligible nodes. It is recommended to avoid -having only two master eligible nodes, since a quorum of two is two. Therefore, a loss -of either master eligible node will result in an inoperable cluster. +This setting must be set to a <> of your master +eligible nodes. It is recommended to avoid having only two master eligible +nodes, since a quorum of two is two. Therefore, a loss of either master +eligible node will result in an inoperable cluster. [float] [[fault-detection]] From 0e31a21db2951e5d803269d31a541cf50e0b98b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Tue, 25 Apr 2017 23:55:24 +0200 Subject: [PATCH 076/619] Add parsing for InternalExtendedStats (#24284) --- .../metrics/stats/ParsedStats.java | 28 ++- .../stats/extended/InternalExtendedStats.java | 49 +++-- .../stats/extended/ParsedExtendedStats.java | 186 ++++++++++++++++++ .../InternalAggregationTestCase.java | 3 + .../metrics/InternalExtendedStatsTests.java | 48 +++-- .../metrics/InternalStatsTests.java | 7 +- 6 files changed, 279 insertions(+), 42 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java index 28ca3418a994a..1711e4ee15df2 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java @@ -119,20 +119,28 @@ protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) ParsedStats::new); static { - declareAggregationFields(PARSER); - PARSER.declareLong((agg, value) -> agg.count = value, new ParseField(Fields.COUNT)); - PARSER.declareField((agg, value) -> agg.min = value, (parser, context) -> parseDouble(parser, Double.POSITIVE_INFINITY), + declareStatsFields(PARSER); + } + + protected static void declareStatsFields(ObjectParser objectParser) { + declareAggregationFields(objectParser); + objectParser.declareLong((agg, value) -> agg.count = value, new ParseField(Fields.COUNT)); + objectParser.declareField((agg, value) -> agg.min = value, (parser, context) -> parseDouble(parser, Double.POSITIVE_INFINITY), new ParseField(Fields.MIN), ValueType.DOUBLE_OR_NULL); - PARSER.declareField((agg, value) -> agg.max = value, (parser, context) -> parseDouble(parser, Double.NEGATIVE_INFINITY), + objectParser.declareField((agg, value) -> agg.max = value, (parser, context) -> parseDouble(parser, Double.NEGATIVE_INFINITY), new ParseField(Fields.MAX), ValueType.DOUBLE_OR_NULL); - PARSER.declareField((agg, value) -> agg.avg = value, (parser, context) -> parseDouble(parser, 0), new ParseField(Fields.AVG), + objectParser.declareField((agg, value) -> agg.avg = value, (parser, context) -> parseDouble(parser, 0), new ParseField(Fields.AVG), ValueType.DOUBLE_OR_NULL); - PARSER.declareField((agg, value) -> agg.sum = value, (parser, context) -> parseDouble(parser, 0), new ParseField(Fields.SUM), + objectParser.declareField((agg, value) -> agg.sum = value, (parser, context) -> parseDouble(parser, 0), new ParseField(Fields.SUM), ValueType.DOUBLE_OR_NULL); - PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.MIN_AS_STRING, value), new ParseField(Fields.MIN_AS_STRING)); - PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.MAX_AS_STRING, value), new ParseField(Fields.MAX_AS_STRING)); - PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.AVG_AS_STRING, value), new ParseField(Fields.AVG_AS_STRING)); - PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.SUM_AS_STRING, value), new ParseField(Fields.SUM_AS_STRING)); + objectParser.declareString((agg, value) -> agg.valueAsString.put(Fields.MIN_AS_STRING, value), + new ParseField(Fields.MIN_AS_STRING)); + objectParser.declareString((agg, value) -> agg.valueAsString.put(Fields.MAX_AS_STRING, value), + new ParseField(Fields.MAX_AS_STRING)); + objectParser.declareString((agg, value) -> agg.valueAsString.put(Fields.AVG_AS_STRING, value), + new ParseField(Fields.AVG_AS_STRING)); + objectParser.declareString((agg, value) -> agg.valueAsString.put(Fields.SUM_AS_STRING, value), + new ParseField(Fields.SUM_AS_STRING)); } public static ParsedStats fromXContent(XContentParser parser, final String name) { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/InternalExtendedStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/InternalExtendedStats.java index b769850b817d8..6e06a88cccd32 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/InternalExtendedStats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/InternalExtendedStats.java @@ -170,24 +170,37 @@ static class Fields { @Override protected XContentBuilder otherStatsToXContent(XContentBuilder builder, Params params) throws IOException { - builder.field(Fields.SUM_OF_SQRS, count != 0 ? sumOfSqrs : null); - builder.field(Fields.VARIANCE, count != 0 ? getVariance() : null); - builder.field(Fields.STD_DEVIATION, count != 0 ? getStdDeviation() : null); - builder.startObject(Fields.STD_DEVIATION_BOUNDS) - .field(Fields.UPPER, count != 0 ? getStdDeviationBound(Bounds.UPPER) : null) - .field(Fields.LOWER, count != 0 ? getStdDeviationBound(Bounds.LOWER) : null) - .endObject(); - - if (count != 0 && format != DocValueFormat.RAW) { - builder.field(Fields.SUM_OF_SQRS_AS_STRING, format.format(sumOfSqrs)); - builder.field(Fields.VARIANCE_AS_STRING, format.format(getVariance())); - builder.field(Fields.STD_DEVIATION_AS_STRING, getStdDeviationAsString()); - - builder.startObject(Fields.STD_DEVIATION_BOUNDS_AS_STRING) - .field(Fields.UPPER, getStdDeviationBoundAsString(Bounds.UPPER)) - .field(Fields.LOWER, getStdDeviationBoundAsString(Bounds.LOWER)) - .endObject(); - + if (count != 0) { + builder.field(Fields.SUM_OF_SQRS, sumOfSqrs); + builder.field(Fields.VARIANCE, getVariance()); + builder.field(Fields.STD_DEVIATION, getStdDeviation()); + builder.startObject(Fields.STD_DEVIATION_BOUNDS); + { + builder.field(Fields.UPPER, getStdDeviationBound(Bounds.UPPER)); + builder.field(Fields.LOWER, getStdDeviationBound(Bounds.LOWER)); + } + builder.endObject(); + if (format != DocValueFormat.RAW) { + builder.field(Fields.SUM_OF_SQRS_AS_STRING, format.format(sumOfSqrs)); + builder.field(Fields.VARIANCE_AS_STRING, format.format(getVariance())); + builder.field(Fields.STD_DEVIATION_AS_STRING, getStdDeviationAsString()); + builder.startObject(Fields.STD_DEVIATION_BOUNDS_AS_STRING); + { + builder.field(Fields.UPPER, getStdDeviationBoundAsString(Bounds.UPPER)); + builder.field(Fields.LOWER, getStdDeviationBoundAsString(Bounds.LOWER)); + } + builder.endObject(); + } + } else { + builder.nullField(Fields.SUM_OF_SQRS); + builder.nullField(Fields.VARIANCE); + builder.nullField(Fields.STD_DEVIATION); + builder.startObject(Fields.STD_DEVIATION_BOUNDS); + { + builder.nullField(Fields.UPPER); + builder.nullField(Fields.LOWER); + } + builder.endObject(); } return builder; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java new file mode 100644 index 0000000000000..8947f4c0aac5e --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java @@ -0,0 +1,186 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.stats.extended; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser.ValueType; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.stats.ParsedStats; +import org.elasticsearch.search.aggregations.metrics.stats.extended.InternalExtendedStats.Fields; + +import java.io.IOException; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; + +public class ParsedExtendedStats extends ParsedStats implements ExtendedStats { + + protected double sumOfSquares; + protected double variance; + protected double stdDeviation; + protected double stdDeviationBoundUpper; + protected double stdDeviationBoundLower; + protected double sum; + protected double avg; + + @Override + protected String getType() { + return ExtendedStatsAggregationBuilder.NAME; + } + + @Override + public double getSumOfSquares() { + return sumOfSquares; + } + + @Override + public double getVariance() { + return variance; + } + + @Override + public double getStdDeviation() { + return stdDeviation; + } + + private void setStdDeviationBounds(Tuple bounds) { + this.stdDeviationBoundLower = bounds.v1(); + this.stdDeviationBoundUpper = bounds.v2(); + } + + @Override + public double getStdDeviationBound(Bounds bound) { + return (bound.equals(Bounds.LOWER)) ? stdDeviationBoundLower : stdDeviationBoundUpper; + } + + @Override + public String getStdDeviationAsString() { + return valueAsString.getOrDefault(Fields.STD_DEVIATION_AS_STRING, Double.toString(stdDeviation)); + } + + private void setStdDeviationBoundsAsString(Tuple boundsAsString) { + this.valueAsString.put(Fields.STD_DEVIATION_BOUNDS_AS_STRING + "_lower", boundsAsString.v1()); + this.valueAsString.put(Fields.STD_DEVIATION_BOUNDS_AS_STRING + "_upper", boundsAsString.v2()); + } + + @Override + public String getStdDeviationBoundAsString(Bounds bound) { + if (bound.equals(Bounds.LOWER)) { + return valueAsString.getOrDefault(Fields.STD_DEVIATION_BOUNDS_AS_STRING + "_lower", Double.toString(stdDeviationBoundLower)); + } else { + return valueAsString.getOrDefault(Fields.STD_DEVIATION_BOUNDS_AS_STRING + "_upper", Double.toString(stdDeviationBoundUpper)); + } + } + + @Override + public String getSumOfSquaresAsString() { + return valueAsString.getOrDefault(Fields.SUM_OF_SQRS_AS_STRING, Double.toString(sumOfSquares)); + } + + @Override + public String getVarianceAsString() { + return valueAsString.getOrDefault(Fields.VARIANCE_AS_STRING, Double.toString(variance)); + } + + @Override + protected XContentBuilder otherStatsToXContent(XContentBuilder builder, Params params) throws IOException { + if (count != 0) { + builder.field(Fields.SUM_OF_SQRS, sumOfSquares); + builder.field(Fields.VARIANCE, getVariance()); + builder.field(Fields.STD_DEVIATION, getStdDeviation()); + builder.startObject(Fields.STD_DEVIATION_BOUNDS); + { + builder.field(Fields.UPPER, getStdDeviationBound(Bounds.UPPER)); + builder.field(Fields.LOWER, getStdDeviationBound(Bounds.LOWER)); + } + builder.endObject(); + if (valueAsString.containsKey(Fields.SUM_OF_SQRS_AS_STRING)) { + builder.field(Fields.SUM_OF_SQRS_AS_STRING, getSumOfSquaresAsString()); + builder.field(Fields.VARIANCE_AS_STRING, getVarianceAsString()); + builder.field(Fields.STD_DEVIATION_AS_STRING, getStdDeviationAsString()); + builder.startObject(Fields.STD_DEVIATION_BOUNDS_AS_STRING); + { + builder.field(Fields.UPPER, getStdDeviationBoundAsString(Bounds.UPPER)); + builder.field(Fields.LOWER, getStdDeviationBoundAsString(Bounds.LOWER)); + } + builder.endObject(); + } + } else { + builder.nullField(Fields.SUM_OF_SQRS); + builder.nullField(Fields.VARIANCE); + builder.nullField(Fields.STD_DEVIATION); + builder.startObject(Fields.STD_DEVIATION_BOUNDS); + { + builder.nullField(Fields.UPPER); + builder.nullField(Fields.LOWER); + } + builder.endObject(); + } + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedExtendedStats.class.getSimpleName(), true, + ParsedExtendedStats::new); + + private static final ConstructingObjectParser, Void> STD_BOUNDS_PARSER = new ConstructingObjectParser<>( + ParsedExtendedStats.class.getSimpleName() + "_STD_BOUNDS", true, args -> new Tuple<>((Double) args[0], (Double) args[1])); + static { + STD_BOUNDS_PARSER.declareField(constructorArg(), (parser, context) -> parseDouble(parser, 0), + new ParseField(Fields.LOWER), ValueType.DOUBLE_OR_NULL); + STD_BOUNDS_PARSER.declareField(constructorArg(), (parser, context) -> parseDouble(parser, 0), + new ParseField(Fields.UPPER), ValueType.DOUBLE_OR_NULL); + } + + private static final ConstructingObjectParser, Void> STD_BOUNDS_AS_STRING_PARSER = new ConstructingObjectParser<>( + ParsedExtendedStats.class.getSimpleName() + "_STD_BOUNDS_AS_STRING", true, args -> new Tuple<>((String) args[0], (String) args[1])); + static { + STD_BOUNDS_AS_STRING_PARSER.declareString(constructorArg(), new ParseField(Fields.LOWER)); + STD_BOUNDS_AS_STRING_PARSER.declareString(constructorArg(), new ParseField(Fields.UPPER)); + } + + static { + declareAggregationFields(PARSER); + declareStatsFields(PARSER); + PARSER.declareField((agg, value) -> agg.sumOfSquares = value, (parser, context) -> parseDouble(parser, 0), + new ParseField(Fields.SUM_OF_SQRS), ValueType.DOUBLE_OR_NULL); + PARSER.declareField((agg, value) -> agg.variance = value, (parser, context) -> parseDouble(parser, 0), + new ParseField(Fields.VARIANCE), ValueType.DOUBLE_OR_NULL); + PARSER.declareField((agg, value) -> agg.stdDeviation = value, (parser, context) -> parseDouble(parser, 0), + new ParseField(Fields.STD_DEVIATION), ValueType.DOUBLE_OR_NULL); + PARSER.declareObject(ParsedExtendedStats::setStdDeviationBounds, STD_BOUNDS_PARSER, new ParseField(Fields.STD_DEVIATION_BOUNDS)); + PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.SUM_OF_SQRS_AS_STRING, value), + new ParseField(Fields.SUM_OF_SQRS_AS_STRING)); + PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.VARIANCE_AS_STRING, value), + new ParseField(Fields.VARIANCE_AS_STRING)); + PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.STD_DEVIATION_AS_STRING, value), + new ParseField(Fields.STD_DEVIATION_AS_STRING)); + PARSER.declareObject(ParsedExtendedStats::setStdDeviationBoundsAsString, STD_BOUNDS_AS_STRING_PARSER, + new ParseField(Fields.STD_DEVIATION_BOUNDS_AS_STRING)); + } + + public static ParsedExtendedStats fromXContent(XContentParser parser, final String name) { + ParsedExtendedStats parsedStats = PARSER.apply(parser, null); + parsedStats.setName(name); + return parsedStats; + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index 63211f3db222a..1a8a674a531d8 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -52,6 +52,8 @@ import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentiles; import org.elasticsearch.search.aggregations.metrics.stats.ParsedStats; import org.elasticsearch.search.aggregations.metrics.stats.StatsAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.stats.extended.ExtendedStatsAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.stats.extended.ParsedExtendedStats; import org.elasticsearch.search.aggregations.metrics.sum.ParsedSum; import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.valuecount.ParsedValueCount; @@ -102,6 +104,7 @@ static List getNamedXContents() { namedXContents.put(DerivativePipelineAggregationBuilder.NAME, (p, c) -> ParsedDerivative.fromXContent(p, (String) c)); namedXContents.put(InternalBucketMetricValue.NAME, (p, c) -> ParsedBucketMetricValue.fromXContent(p, (String) c)); namedXContents.put(StatsAggregationBuilder.NAME, (p, c) -> ParsedStats.fromXContent(p, (String) c)); + namedXContents.put(ExtendedStatsAggregationBuilder.NAME, (p, c) -> ParsedExtendedStats.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalExtendedStatsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalExtendedStatsTests.java index 83e1815f3984d..b6d9fd3dd7bd9 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalExtendedStatsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalExtendedStatsTests.java @@ -22,11 +22,13 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; +import org.elasticsearch.search.aggregations.metrics.stats.extended.ExtendedStats.Bounds; import org.elasticsearch.search.aggregations.metrics.stats.extended.InternalExtendedStats; +import org.elasticsearch.search.aggregations.metrics.stats.extended.ParsedExtendedStats; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.junit.Before; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -40,15 +42,14 @@ public void randomSigma() { @Override protected InternalExtendedStats createTestInstance(String name, List pipelineAggregators, - Map metaData) { - long count = randomIntBetween(1, 50); - double[] minMax = new double[2]; - minMax[0] = randomDouble(); - minMax[0] = randomDouble(); - double sum = randomDoubleBetween(0, 100, true); - return new InternalExtendedStats(name, count, sum, minMax[0], minMax[1], - randomDouble(), sigma, DocValueFormat.RAW, - pipelineAggregators, Collections.emptyMap()); + Map metaData) { + long count = frequently() ? randomIntBetween(1, Integer.MAX_VALUE) : 0; + double min = randomDoubleBetween(-1000000, 1000000, true); + double max = randomDoubleBetween(-1000000, 1000000, true); + double sum = randomDoubleBetween(-1000000, 1000000, true); + DocValueFormat format = randomNumericDocValueFormat(); + return new InternalExtendedStats(name, count, sum, min, max, randomDoubleBetween(0, 1000000, true), sigma, format, + pipelineAggregators, metaData); } @Override @@ -72,10 +73,33 @@ protected void assertReduced(InternalExtendedStats reduced, List 0 ? aggregation.getSumOfSquares() : 0 , parsed.getSumOfSquares(), 0); + assertEquals(count > 0 ? aggregation.getVariance() : 0 , parsed.getVariance(), 0); + assertEquals(count > 0 ? aggregation.getStdDeviation() : 0 , parsed.getStdDeviation(), 0); + assertEquals(count > 0 ? aggregation.getStdDeviationBound(Bounds.LOWER) : 0 , parsed.getStdDeviationBound(Bounds.LOWER), 0); + assertEquals(count > 0 ? aggregation.getStdDeviationBound(Bounds.UPPER) : 0 , parsed.getStdDeviationBound(Bounds.UPPER), 0); + // also as_string values are only rendered for count != 0 + if (count > 0) { + assertEquals(aggregation.getSumOfSquaresAsString(), parsed.getSumOfSquaresAsString()); + assertEquals(aggregation.getVarianceAsString(), parsed.getVarianceAsString()); + assertEquals(aggregation.getStdDeviationAsString(), parsed.getStdDeviationAsString()); + assertEquals(aggregation.getStdDeviationBoundAsString(Bounds.LOWER), parsed.getStdDeviationBoundAsString(Bounds.LOWER)); + assertEquals(aggregation.getStdDeviationBoundAsString(Bounds.UPPER), parsed.getStdDeviationBoundAsString(Bounds.UPPER)); + } } @Override diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java index c0c54d3a8a000..354470114a962 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java @@ -26,7 +26,6 @@ import org.elasticsearch.search.aggregations.metrics.stats.ParsedStats; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -39,7 +38,7 @@ protected InternalStats createTestInstance(String name, List double max = randomDoubleBetween(-1000000, 1000000, true); double sum = randomDoubleBetween(-1000000, 1000000, true); DocValueFormat format = randomNumericDocValueFormat(); - return new InternalStats(name, count, sum, min, max, format, pipelineAggregators, Collections.emptyMap()); + return new InternalStats(name, count, sum, min, max, format, pipelineAggregators, metaData); } @Override @@ -68,6 +67,10 @@ protected void assertReduced(InternalStats reduced, List inputs) protected void assertFromXContent(InternalStats aggregation, ParsedAggregation parsedAggregation) { assertTrue(parsedAggregation instanceof ParsedStats); ParsedStats parsed = (ParsedStats) parsedAggregation; + assertStats(aggregation, parsed); + } + + static void assertStats(InternalStats aggregation, ParsedStats parsed) { long count = aggregation.getCount(); assertEquals(count, parsed.getCount()); // for count == 0, fields are rendered as `null`, so we test that we parse to default values used also in the reduce phase From fc97e25b564011bdb1d272a15e5b6b1f22c0c633 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Tue, 25 Apr 2017 21:11:47 -0400 Subject: [PATCH 077/619] Add task to look for tests in src/main (#24298) Creates a new task `namingConventionsMain`, that runs on the `buildSrc` and `test:framework` projects and fails the build if any of the classes in the main artifacts are named like tests or are non-abstract subclasses of ESTestCase. It also fixes the three tests that would cause it to fail. --- buildSrc/build.gradle | 7 + .../precommit/NamingConventionsTask.groovy | 50 ++-- .../test/NamingConventionsCheck.java | 216 ++++++++++++------ .../test/NamingConventionsCheckInMainIT.java | 26 +++ .../NamingConventionsCheckInMainTests.java | 26 +++ test/framework/build.gradle | 5 + .../AnalysisFactoryTestCase.java | 2 +- .../disruption/LongGCDisruptionTests.java} | 2 +- .../test/disruption/NetworkDisruptionIT.java | 0 .../disruption/NetworkDisruptionTests.java | 0 10 files changed, 247 insertions(+), 87 deletions(-) create mode 100644 buildSrc/src/main/java/org/elasticsearch/test/NamingConventionsCheckInMainIT.java create mode 100644 buildSrc/src/main/java/org/elasticsearch/test/NamingConventionsCheckInMainTests.java rename test/framework/src/{main/java/org/elasticsearch/test/disruption/LongGCDisruptionTest.java => test/java/org/elasticsearch/test/disruption/LongGCDisruptionTests.java} (99%) rename test/framework/src/{main => test}/java/org/elasticsearch/test/disruption/NetworkDisruptionIT.java (100%) rename test/framework/src/{main => test}/java/org/elasticsearch/test/disruption/NetworkDisruptionTests.java (100%) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 6536c77e58726..0839b8a22f8fa 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -156,4 +156,11 @@ if (project != rootProject) { testClass = 'org.elasticsearch.test.NamingConventionsCheckBadClasses$UnitTestCase' integTestClass = 'org.elasticsearch.test.NamingConventionsCheckBadClasses$IntegTestCase' } + + task namingConventionsMain(type: org.elasticsearch.gradle.precommit.NamingConventionsTask) { + checkForTestsInMain = true + testClass = namingConventions.testClass + integTestClass = namingConventions.integTestClass + } + precommit.dependsOn namingConventionsMain } diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/NamingConventionsTask.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/NamingConventionsTask.groovy index 52de7dac2d5a3..2711a0e38f23b 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/NamingConventionsTask.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/NamingConventionsTask.groovy @@ -38,17 +38,7 @@ public class NamingConventionsTask extends LoggedExec { * inputs (ie the jars/class files). */ @OutputFile - File successMarker = new File(project.buildDir, 'markers/namingConventions') - - /** - * The classpath to run the naming conventions checks against. Must contain the files in the test - * output directory and everything required to load those classes. - * - * We don't declare the actual test files as a dependency or input because if they change then - * this will change. - */ - @InputFiles - FileCollection classpath = project.sourceSets.test.runtimeClasspath + File successMarker = new File(project.buildDir, "markers/${this.name}") /** * Should we skip the integ tests in disguise tests? Defaults to true because only core names its @@ -69,18 +59,35 @@ public class NamingConventionsTask extends LoggedExec { @Input String integTestClass = 'org.elasticsearch.test.ESIntegTestCase' + /** + * Should the test also check the main classpath for test classes instead of + * doing the usual checks to the test classpath. + */ + @Input + boolean checkForTestsInMain = false; + public NamingConventionsTask() { // Extra classpath contains the actual test - project.configurations.create('namingConventions') - Dependency buildToolsDep = project.dependencies.add('namingConventions', - "org.elasticsearch.gradle:build-tools:${VersionProperties.elasticsearch}") - buildToolsDep.transitive = false // We don't need gradle in the classpath. It conflicts. + if (false == project.configurations.names.contains('namingConventions')) { + project.configurations.create('namingConventions') + Dependency buildToolsDep = project.dependencies.add('namingConventions', + "org.elasticsearch.gradle:build-tools:${VersionProperties.elasticsearch}") + buildToolsDep.transitive = false // We don't need gradle in the classpath. It conflicts. + } FileCollection extraClasspath = project.configurations.namingConventions dependsOn(extraClasspath) - description = "Runs NamingConventionsCheck on ${classpath}" + FileCollection classpath = project.sourceSets.test.runtimeClasspath + inputs.files(classpath) + description = "Tests that test classes aren't misnamed or misplaced" executable = new File(project.javaHome, 'bin/java') - onlyIf { project.sourceSets.test.output.classesDir.exists() } + if (false == checkForTestsInMain) { + /* This task is created by default for all subprojects with this + * setting and there is no point in running it if the files don't + * exist. */ + onlyIf { project.sourceSets.test.output.classesDir.exists() } + } + /* * We build the arguments in a funny afterEvaluate/doFirst closure so that we can wait for the classpath to be * ready for us. Strangely neither one on their own are good enough. @@ -104,7 +111,14 @@ public class NamingConventionsTask extends LoggedExec { if (':build-tools'.equals(project.path)) { args('--self-test') } - args('--', project.sourceSets.test.output.classesDir.absolutePath) + if (checkForTestsInMain) { + args('--main') + args('--') + args(project.sourceSets.main.output.classesDir.absolutePath) + } else { + args('--') + args(project.sourceSets.test.output.classesDir.absolutePath) + } } } doLast { successMarker.setText("", 'UTF-8') } diff --git a/buildSrc/src/main/java/org/elasticsearch/test/NamingConventionsCheck.java b/buildSrc/src/main/java/org/elasticsearch/test/NamingConventionsCheck.java index cbfa31d1aaf5b..9bd14675d34a4 100644 --- a/buildSrc/src/main/java/org/elasticsearch/test/NamingConventionsCheck.java +++ b/buildSrc/src/main/java/org/elasticsearch/test/NamingConventionsCheck.java @@ -28,6 +28,7 @@ import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.util.HashSet; +import java.util.Objects; import java.util.Set; /** @@ -49,6 +50,7 @@ public static void main(String[] args) throws IOException { Path rootPath = null; boolean skipIntegTestsInDisguise = false; boolean selfTest = false; + boolean checkMainClasses = false; for (int i = 0; i < args.length; i++) { String arg = args[i]; switch (arg) { @@ -64,6 +66,9 @@ public static void main(String[] args) throws IOException { case "--self-test": selfTest = true; break; + case "--main": + checkMainClasses = true; + break; case "--": rootPath = Paths.get(args[++i]); break; @@ -73,28 +78,43 @@ public static void main(String[] args) throws IOException { } NamingConventionsCheck check = new NamingConventionsCheck(testClass, integTestClass); - check.check(rootPath, skipIntegTestsInDisguise); + if (checkMainClasses) { + check.checkMain(rootPath); + } else { + check.checkTests(rootPath, skipIntegTestsInDisguise); + } if (selfTest) { - assertViolation("WrongName", check.missingSuffix); - assertViolation("WrongNameTheSecond", check.missingSuffix); - assertViolation("DummyAbstractTests", check.notRunnable); - assertViolation("DummyInterfaceTests", check.notRunnable); - assertViolation("InnerTests", check.innerClasses); - assertViolation("NotImplementingTests", check.notImplementing); - assertViolation("PlainUnit", check.pureUnitTest); + if (checkMainClasses) { + assertViolation(NamingConventionsCheckInMainTests.class.getName(), check.testsInMain); + assertViolation(NamingConventionsCheckInMainIT.class.getName(), check.testsInMain); + } else { + assertViolation("WrongName", check.missingSuffix); + assertViolation("WrongNameTheSecond", check.missingSuffix); + assertViolation("DummyAbstractTests", check.notRunnable); + assertViolation("DummyInterfaceTests", check.notRunnable); + assertViolation("InnerTests", check.innerClasses); + assertViolation("NotImplementingTests", check.notImplementing); + assertViolation("PlainUnit", check.pureUnitTest); + } } // Now we should have no violations - assertNoViolations("Not all subclasses of " + check.testClass.getSimpleName() - + " match the naming convention. Concrete classes must end with [Tests]", check.missingSuffix); + assertNoViolations( + "Not all subclasses of " + check.testClass.getSimpleName() + + " match the naming convention. Concrete classes must end with [Tests]", + check.missingSuffix); assertNoViolations("Classes ending with [Tests] are abstract or interfaces", check.notRunnable); assertNoViolations("Found inner classes that are tests, which are excluded from the test runner", check.innerClasses); assertNoViolations("Pure Unit-Test found must subclass [" + check.testClass.getSimpleName() + "]", check.pureUnitTest); assertNoViolations("Classes ending with [Tests] must subclass [" + check.testClass.getSimpleName() + "]", check.notImplementing); + assertNoViolations( + "Classes ending with [Tests] or [IT] or extending [" + check.testClass.getSimpleName() + "] must be in src/test/java", + check.testsInMain); if (skipIntegTestsInDisguise == false) { - assertNoViolations("Subclasses of " + check.integTestClass.getSimpleName() + - " should end with IT as they are integration tests", check.integTestsInDisguise); + assertNoViolations( + "Subclasses of " + check.integTestClass.getSimpleName() + " should end with IT as they are integration tests", + check.integTestsInDisguise); } } @@ -104,84 +124,76 @@ public static void main(String[] args) throws IOException { private final Set> integTestsInDisguise = new HashSet<>(); private final Set> notRunnable = new HashSet<>(); private final Set> innerClasses = new HashSet<>(); + private final Set> testsInMain = new HashSet<>(); private final Class testClass; private final Class integTestClass; public NamingConventionsCheck(Class testClass, Class integTestClass) { - this.testClass = testClass; + this.testClass = Objects.requireNonNull(testClass, "--test-class is required"); this.integTestClass = integTestClass; } - public void check(Path rootPath, boolean skipTestsInDisguised) throws IOException { - Files.walkFileTree(rootPath, new FileVisitor() { - /** - * The package name of the directory we are currently visiting. Kept as a string rather than something fancy because we load - * just about every class and doing so requires building a string out of it anyway. At least this way we don't need to build the - * first part of the string over and over and over again. - */ - private String packageName; - + public void checkTests(Path rootPath, boolean skipTestsInDisguised) throws IOException { + Files.walkFileTree(rootPath, new TestClassVisitor() { @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - // First we visit the root directory - if (packageName == null) { - // And it package is empty string regardless of the directory name - packageName = ""; - } else { - packageName += dir.getFileName() + "."; + protected void visitTestClass(Class clazz) { + if (skipTestsInDisguised == false && integTestClass.isAssignableFrom(clazz)) { + integTestsInDisguise.add(clazz); + } + if (Modifier.isAbstract(clazz.getModifiers()) || Modifier.isInterface(clazz.getModifiers())) { + notRunnable.add(clazz); + } else if (isTestCase(clazz) == false) { + notImplementing.add(clazz); + } else if (Modifier.isStatic(clazz.getModifiers())) { + innerClasses.add(clazz); } - return FileVisitResult.CONTINUE; } @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - // Go up one package by jumping back to the second to last '.' - packageName = packageName.substring(0, 1 + packageName.lastIndexOf('.', packageName.length() - 2)); - return FileVisitResult.CONTINUE; + protected void visitIntegrationTestClass(Class clazz) { + if (isTestCase(clazz) == false) { + notImplementing.add(clazz); + } } @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - String filename = file.getFileName().toString(); - if (filename.endsWith(".class")) { - String className = filename.substring(0, filename.length() - ".class".length()); - Class clazz = loadClassWithoutInitializing(packageName + className); - if (clazz.getName().endsWith("Tests")) { - if (skipTestsInDisguised == false && integTestClass.isAssignableFrom(clazz)) { - integTestsInDisguise.add(clazz); - } - if (Modifier.isAbstract(clazz.getModifiers()) || Modifier.isInterface(clazz.getModifiers())) { - notRunnable.add(clazz); - } else if (isTestCase(clazz) == false) { - notImplementing.add(clazz); - } else if (Modifier.isStatic(clazz.getModifiers())) { - innerClasses.add(clazz); - } - } else if (clazz.getName().endsWith("IT")) { - if (isTestCase(clazz) == false) { - notImplementing.add(clazz); - } - } else if (Modifier.isAbstract(clazz.getModifiers()) == false && Modifier.isInterface(clazz.getModifiers()) == false) { - if (isTestCase(clazz)) { - missingSuffix.add(clazz); - } else if (junit.framework.Test.class.isAssignableFrom(clazz)) { - pureUnitTest.add(clazz); - } - } + protected void visitOtherClass(Class clazz) { + if (Modifier.isAbstract(clazz.getModifiers()) || Modifier.isInterface(clazz.getModifiers())) { + return; + } + if (isTestCase(clazz)) { + missingSuffix.add(clazz); + } else if (junit.framework.Test.class.isAssignableFrom(clazz)) { + pureUnitTest.add(clazz); } - return FileVisitResult.CONTINUE; + } + }); + } + + public void checkMain(Path rootPath) throws IOException { + Files.walkFileTree(rootPath, new TestClassVisitor() { + @Override + protected void visitTestClass(Class clazz) { + testsInMain.add(clazz); } - private boolean isTestCase(Class clazz) { - return testClass.isAssignableFrom(clazz); + @Override + protected void visitIntegrationTestClass(Class clazz) { + testsInMain.add(clazz); } @Override - public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { - throw exc; + protected void visitOtherClass(Class clazz) { + if (Modifier.isAbstract(clazz.getModifiers()) || Modifier.isInterface(clazz.getModifiers())) { + return; + } + if (isTestCase(clazz)) { + testsInMain.add(clazz); + } } }); + } /** @@ -203,7 +215,7 @@ private static void assertNoViolations(String message, Set> set) { * similar enough. */ private static void assertViolation(String className, Set> set) { - className = "org.elasticsearch.test.NamingConventionsCheckBadClasses$" + className; + className = className.startsWith("org") ? className : "org.elasticsearch.test.NamingConventionsCheckBadClasses$" + className; if (false == set.remove(loadClassWithoutInitializing(className))) { System.err.println("Error in NamingConventionsCheck! Expected [" + className + "] to be a violation but wasn't."); System.exit(1); @@ -229,4 +241,74 @@ static Class loadClassWithoutInitializing(String name) { throw new RuntimeException(e); } } + + abstract class TestClassVisitor implements FileVisitor { + /** + * The package name of the directory we are currently visiting. Kept as a string rather than something fancy because we load + * just about every class and doing so requires building a string out of it anyway. At least this way we don't need to build the + * first part of the string over and over and over again. + */ + private String packageName; + + /** + * Visit classes named like a test. + */ + protected abstract void visitTestClass(Class clazz); + /** + * Visit classes named like an integration test. + */ + protected abstract void visitIntegrationTestClass(Class clazz); + /** + * Visit classes not named like a test at all. + */ + protected abstract void visitOtherClass(Class clazz); + + @Override + public final FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + // First we visit the root directory + if (packageName == null) { + // And it package is empty string regardless of the directory name + packageName = ""; + } else { + packageName += dir.getFileName() + "."; + } + return FileVisitResult.CONTINUE; + } + + @Override + public final FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + // Go up one package by jumping back to the second to last '.' + packageName = packageName.substring(0, 1 + packageName.lastIndexOf('.', packageName.length() - 2)); + return FileVisitResult.CONTINUE; + } + + @Override + public final FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + String filename = file.getFileName().toString(); + if (filename.endsWith(".class")) { + String className = filename.substring(0, filename.length() - ".class".length()); + Class clazz = loadClassWithoutInitializing(packageName + className); + if (clazz.getName().endsWith("Tests")) { + visitTestClass(clazz); + } else if (clazz.getName().endsWith("IT")) { + visitIntegrationTestClass(clazz); + } else { + visitOtherClass(clazz); + } + } + return FileVisitResult.CONTINUE; + } + + /** + * Is this class a test case? + */ + protected boolean isTestCase(Class clazz) { + return testClass.isAssignableFrom(clazz); + } + + @Override + public final FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + throw exc; + } + } } diff --git a/buildSrc/src/main/java/org/elasticsearch/test/NamingConventionsCheckInMainIT.java b/buildSrc/src/main/java/org/elasticsearch/test/NamingConventionsCheckInMainIT.java new file mode 100644 index 0000000000000..46adc7f065b16 --- /dev/null +++ b/buildSrc/src/main/java/org/elasticsearch/test/NamingConventionsCheckInMainIT.java @@ -0,0 +1,26 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.test; + +/** + * This class should fail the naming conventions self test. + */ +public class NamingConventionsCheckInMainIT { +} diff --git a/buildSrc/src/main/java/org/elasticsearch/test/NamingConventionsCheckInMainTests.java b/buildSrc/src/main/java/org/elasticsearch/test/NamingConventionsCheckInMainTests.java new file mode 100644 index 0000000000000..27c0b41eb3f6a --- /dev/null +++ b/buildSrc/src/main/java/org/elasticsearch/test/NamingConventionsCheckInMainTests.java @@ -0,0 +1,26 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.test; + +/** + * This class should fail the naming conventions self test. + */ +public class NamingConventionsCheckInMainTests { +} diff --git a/test/framework/build.gradle b/test/framework/build.gradle index 6756495e0a16c..13a5ef11ce2db 100644 --- a/test/framework/build.gradle +++ b/test/framework/build.gradle @@ -63,3 +63,8 @@ thirdPartyAudit.excludes = [ 'org.easymock.IArgumentMatcher', 'org.jmock.core.Constraint', ] + +task namingConventionsMain(type: org.elasticsearch.gradle.precommit.NamingConventionsTask) { + checkForTestsInMain = true +} +precommit.dependsOn namingConventionsMain diff --git a/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java b/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java index d49a1b4cae5bf..cbabdeef4af3c 100644 --- a/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java @@ -113,7 +113,7 @@ * If we don't want to expose one for a specific reason, just map it to Void. * The deprecated ones can be mapped to Deprecated.class. */ -public class AnalysisFactoryTestCase extends ESTestCase { +public abstract class AnalysisFactoryTestCase extends ESTestCase { private static final Pattern UNDERSCORE_THEN_ANYTHING = Pattern.compile("_(.)"); diff --git a/test/framework/src/main/java/org/elasticsearch/test/disruption/LongGCDisruptionTest.java b/test/framework/src/test/java/org/elasticsearch/test/disruption/LongGCDisruptionTests.java similarity index 99% rename from test/framework/src/main/java/org/elasticsearch/test/disruption/LongGCDisruptionTest.java rename to test/framework/src/test/java/org/elasticsearch/test/disruption/LongGCDisruptionTests.java index a5cd7c307235e..48bd18986c284 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/disruption/LongGCDisruptionTest.java +++ b/test/framework/src/test/java/org/elasticsearch/test/disruption/LongGCDisruptionTests.java @@ -34,7 +34,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; -public class LongGCDisruptionTest extends ESTestCase { +public class LongGCDisruptionTests extends ESTestCase { static class LockedExecutor { ReentrantLock lock = new ReentrantLock(); diff --git a/test/framework/src/main/java/org/elasticsearch/test/disruption/NetworkDisruptionIT.java b/test/framework/src/test/java/org/elasticsearch/test/disruption/NetworkDisruptionIT.java similarity index 100% rename from test/framework/src/main/java/org/elasticsearch/test/disruption/NetworkDisruptionIT.java rename to test/framework/src/test/java/org/elasticsearch/test/disruption/NetworkDisruptionIT.java diff --git a/test/framework/src/main/java/org/elasticsearch/test/disruption/NetworkDisruptionTests.java b/test/framework/src/test/java/org/elasticsearch/test/disruption/NetworkDisruptionTests.java similarity index 100% rename from test/framework/src/main/java/org/elasticsearch/test/disruption/NetworkDisruptionTests.java rename to test/framework/src/test/java/org/elasticsearch/test/disruption/NetworkDisruptionTests.java From 3c845727f80c609275b68eabd1c2d5d2c1fff4ec Mon Sep 17 00:00:00 2001 From: Koen De Groote Date: Wed, 26 Apr 2017 04:15:00 +0200 Subject: [PATCH 078/619] Replace alternating regex with character classes This commit replaces two alternating regular expressions (that is, regular expressions that consist of the form a|b where a and b are characters) with the equivalent regular expression rewritten as a character class (that is, [ab]) The reason this is an improvement is because a|b involves backtracking while [ab] does not. Relates #24316 --- core/src/main/java/org/elasticsearch/Version.java | 2 +- .../org/elasticsearch/common/logging/DeprecationLogger.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/Version.java b/core/src/main/java/org/elasticsearch/Version.java index 199f67bcb0bed..a7f01ef552848 100644 --- a/core/src/main/java/org/elasticsearch/Version.java +++ b/core/src/main/java/org/elasticsearch/Version.java @@ -196,7 +196,7 @@ public static Version fromString(String version) { if (snapshot = version.endsWith("-SNAPSHOT")) { version = version.substring(0, version.length() - 9); } - String[] parts = version.split("\\.|\\-"); + String[] parts = version.split("[.-]"); if (parts.length < 3 || parts.length > 4) { throw new IllegalArgumentException( "the version needs to contain major, minor, and revision, and optionally the build: " + version); diff --git a/core/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/core/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index 7594f96e2dfb8..ee3e3c7490576 100644 --- a/core/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/core/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -311,7 +311,7 @@ public static String formatWarning(final String s) { * @return the escaped string */ public static String escape(String s) { - return s.replaceAll("(\\\\|\")", "\\\\$1"); + return s.replaceAll("([\"\\\\])", "\\\\$1"); } } From 51b33f1fd549b474e901e70a460440d73a1751ce Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 25 Apr 2017 23:43:20 -0700 Subject: [PATCH 079/619] S3 Repository: Deprecate remaining `repositories.s3.*` settings (#24144) Most of these settings should always be pulled from the repository settings. A couple were leftover that should be moved to client settings. The path style access setting should be removed altogether. This commit adds deprecations for all of these existing settings, as well as adding new client specific settings for max retries and throttling. relates #24143 --- .../repositories/s3/InternalAwsS3Service.java | 26 ++++--- .../repositories/s3/S3ClientSettings.java | 37 +++++++--- .../repositories/s3/S3Repository.java | 29 ++++---- .../repositories/s3/S3RepositoryPlugin.java | 2 + .../s3/AwsS3ServiceImplTests.java | 68 +++++++++++++++---- .../repositories/s3/S3RepositoryTests.java | 3 +- .../org/elasticsearch/test/ESTestCase.java | 1 + 7 files changed, 115 insertions(+), 51 deletions(-) diff --git a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/InternalAwsS3Service.java b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/InternalAwsS3Service.java index 1ba0414afe29b..95b746a0a2ba6 100644 --- a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/InternalAwsS3Service.java +++ b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/InternalAwsS3Service.java @@ -75,12 +75,6 @@ public synchronized AmazonS3 client(Settings repositorySettings) { Strings.collectionToDelimitedString(clientsSettings.keySet(), ",")); } - Integer maxRetries = getValue(repositorySettings, settings, - S3Repository.Repository.MAX_RETRIES_SETTING, - S3Repository.Repositories.MAX_RETRIES_SETTING); - boolean useThrottleRetries = getValue(repositorySettings, settings, - S3Repository.Repository.USE_THROTTLE_RETRIES_SETTING, - S3Repository.Repositories.USE_THROTTLE_RETRIES_SETTING); // If the user defined a path style access setting, we rely on it, // otherwise we use the default value set by the SDK Boolean pathStyleAccess = null; @@ -91,12 +85,11 @@ public synchronized AmazonS3 client(Settings repositorySettings) { S3Repository.Repositories.PATH_STYLE_ACCESS_SETTING); } - logger.debug("creating S3 client with client_name [{}], endpoint [{}], max_retries [{}], " + - "use_throttle_retries [{}], path_style_access [{}]", - clientName, clientSettings.endpoint, maxRetries, useThrottleRetries, pathStyleAccess); + logger.debug("creating S3 client with client_name [{}], endpoint [{}], path_style_access [{}]", + clientName, clientSettings.endpoint, pathStyleAccess); AWSCredentialsProvider credentials = buildCredentials(logger, clientSettings); - ClientConfiguration configuration = buildConfiguration(clientSettings, maxRetries, useThrottleRetries); + ClientConfiguration configuration = buildConfiguration(clientSettings, repositorySettings); client = new AmazonS3Client(credentials, configuration); @@ -113,7 +106,7 @@ public synchronized AmazonS3 client(Settings repositorySettings) { } // pkg private for tests - static ClientConfiguration buildConfiguration(S3ClientSettings clientSettings, Integer maxRetries, boolean useThrottleRetries) { + static ClientConfiguration buildConfiguration(S3ClientSettings clientSettings, Settings repositorySettings) { ClientConfiguration clientConfiguration = new ClientConfiguration(); // the response metadata cache is only there for diagnostics purposes, // but can force objects from every response to the old generation. @@ -128,10 +121,13 @@ static ClientConfiguration buildConfiguration(S3ClientSettings clientSettings, I clientConfiguration.setProxyPassword(clientSettings.proxyPassword); } + Integer maxRetries = getRepoValue(repositorySettings, S3Repository.Repository.MAX_RETRIES_SETTING, clientSettings.maxRetries); if (maxRetries != null) { // If not explicitly set, default to 3 with exponential backoff policy clientConfiguration.setMaxErrorRetry(maxRetries); } + boolean useThrottleRetries = getRepoValue(repositorySettings, + S3Repository.Repository.USE_THROTTLE_RETRIES_SETTING, clientSettings.throttleRetries); clientConfiguration.setUseThrottleRetries(useThrottleRetries); clientConfiguration.setSocketTimeout(clientSettings.readTimeoutMillis); @@ -149,6 +145,14 @@ static AWSCredentialsProvider buildCredentials(Logger logger, S3ClientSettings c } } + /** Returns the value for a given setting from the repository, or returns the fallback value. */ + private static T getRepoValue(Settings repositorySettings, Setting repositorySetting, T fallback) { + if (repositorySetting.exists(repositorySettings)) { + return repositorySetting.get(repositorySettings); + } + return fallback; + } + @Override protected void doStart() throws ElasticsearchException { } diff --git a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3ClientSettings.java b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3ClientSettings.java index ece4a5d29ece3..8be6aaff74fb8 100644 --- a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3ClientSettings.java +++ b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3ClientSettings.java @@ -31,6 +31,7 @@ import org.elasticsearch.common.settings.SecureSetting; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; @@ -52,20 +53,19 @@ class S3ClientSettings { /** An override for the s3 endpoint to connect to. */ static final Setting.AffixSetting ENDPOINT_SETTING = Setting.affixKeySetting(PREFIX, "endpoint", - key -> new Setting<>(key, "", s -> s.toLowerCase(Locale.ROOT), - Setting.Property.NodeScope)); + key -> new Setting<>(key, "", s -> s.toLowerCase(Locale.ROOT), Property.NodeScope)); /** The protocol to use to connect to s3. */ static final Setting.AffixSetting PROTOCOL_SETTING = Setting.affixKeySetting(PREFIX, "protocol", - key -> new Setting<>(key, "https", s -> Protocol.valueOf(s.toUpperCase(Locale.ROOT)), Setting.Property.NodeScope)); + key -> new Setting<>(key, "https", s -> Protocol.valueOf(s.toUpperCase(Locale.ROOT)), Property.NodeScope)); /** The host name of a proxy to connect to s3 through. */ static final Setting.AffixSetting PROXY_HOST_SETTING = Setting.affixKeySetting(PREFIX, "proxy.host", - key -> Setting.simpleString(key, Setting.Property.NodeScope)); + key -> Setting.simpleString(key, Property.NodeScope)); /** The port of a proxy to connect to s3 through. */ static final Setting.AffixSetting PROXY_PORT_SETTING = Setting.affixKeySetting(PREFIX, "proxy.port", - key -> Setting.intSetting(key, 80, 0, 1<<16, Setting.Property.NodeScope)); + key -> Setting.intSetting(key, 80, 0, 1<<16, Property.NodeScope)); /** The username of a proxy to connect to s3 through. */ static final Setting.AffixSetting PROXY_USERNAME_SETTING = Setting.affixKeySetting(PREFIX, "proxy.username", @@ -77,8 +77,15 @@ class S3ClientSettings { /** The socket timeout for connecting to s3. */ static final Setting.AffixSetting READ_TIMEOUT_SETTING = Setting.affixKeySetting(PREFIX, "read_timeout", - key -> Setting.timeSetting(key, TimeValue.timeValueMillis(ClientConfiguration.DEFAULT_SOCKET_TIMEOUT), - Setting.Property.NodeScope)); + key -> Setting.timeSetting(key, TimeValue.timeValueMillis(ClientConfiguration.DEFAULT_SOCKET_TIMEOUT), Property.NodeScope)); + + /** The number of retries to use when an s3 request fails. */ + static final Setting.AffixSetting MAX_RETRIES_SETTING = Setting.affixKeySetting(PREFIX, "max_retries", + key -> Setting.intSetting(key, S3Repository.Repositories.MAX_RETRIES_SETTING, 0, Property.NodeScope)); + + /** Whether retries should be throttled (ie use backoff). */ + static final Setting.AffixSetting USE_THROTTLE_RETRIES_SETTING = Setting.affixKeySetting(PREFIX, "use_throttle_retries", + key -> Setting.boolSetting(key, S3Repository.Repositories.USE_THROTTLE_RETRIES_SETTING, Property.NodeScope)); /** Credentials to authenticate with s3. */ final BasicAWSCredentials credentials; @@ -106,9 +113,15 @@ class S3ClientSettings { /** The read timeout for the s3 client. */ final int readTimeoutMillis; + /** The number of retries to use for the s3 client. */ + final int maxRetries; + + /** Whether the s3 client should use an exponential backoff retry policy. */ + final boolean throttleRetries; + private S3ClientSettings(BasicAWSCredentials credentials, String endpoint, Protocol protocol, - String proxyHost, int proxyPort, String proxyUsername, - String proxyPassword, int readTimeoutMillis) { + String proxyHost, int proxyPort, String proxyUsername, String proxyPassword, + int readTimeoutMillis, int maxRetries, boolean throttleRetries) { this.credentials = credentials; this.endpoint = endpoint; this.protocol = protocol; @@ -117,6 +130,8 @@ private S3ClientSettings(BasicAWSCredentials credentials, String endpoint, Proto this.proxyUsername = proxyUsername; this.proxyPassword = proxyPassword; this.readTimeoutMillis = readTimeoutMillis; + this.maxRetries = maxRetries; + this.throttleRetries = throttleRetries; } /** @@ -163,7 +178,9 @@ static S3ClientSettings getClientSettings(Settings settings, String clientName) getConfigValue(settings, clientName, PROXY_PORT_SETTING), proxyUsername.toString(), proxyPassword.toString(), - (int)getConfigValue(settings, clientName, READ_TIMEOUT_SETTING).millis() + (int)getConfigValue(settings, clientName, READ_TIMEOUT_SETTING).millis(), + getConfigValue(settings, clientName, MAX_RETRIES_SETTING), + getConfigValue(settings, clientName, USE_THROTTLE_RETRIES_SETTING) ); } } diff --git a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java index c9f37f24dedbd..b183fd3b81471 100644 --- a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java +++ b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java @@ -61,13 +61,13 @@ public interface Repositories { /** * repositories.s3.bucket: The name of the bucket to be used for snapshots. */ - Setting BUCKET_SETTING = Setting.simpleString("repositories.s3.bucket", Property.NodeScope); + Setting BUCKET_SETTING = Setting.simpleString("repositories.s3.bucket", Property.NodeScope, Property.Deprecated); /** * repositories.s3.server_side_encryption: When set to true files are encrypted on server side using AES256 algorithm. * Defaults to false. */ Setting SERVER_SIDE_ENCRYPTION_SETTING = - Setting.boolSetting("repositories.s3.server_side_encryption", false, Property.NodeScope); + Setting.boolSetting("repositories.s3.server_side_encryption", false, Property.NodeScope, Property.Deprecated); /** * Default is to use 100MB (S3 defaults) for heaps above 2GB and 5% of @@ -89,41 +89,41 @@ public interface Repositories { */ Setting BUFFER_SIZE_SETTING = Setting.byteSizeSetting("repositories.s3.buffer_size", DEFAULT_BUFFER_SIZE, - new ByteSizeValue(5, ByteSizeUnit.MB), new ByteSizeValue(5, ByteSizeUnit.TB), Property.NodeScope); + new ByteSizeValue(5, ByteSizeUnit.MB), new ByteSizeValue(5, ByteSizeUnit.TB), Property.NodeScope, Property.Deprecated); /** * repositories.s3.max_retries: Number of retries in case of S3 errors. Defaults to 3. */ - Setting MAX_RETRIES_SETTING = Setting.intSetting("repositories.s3.max_retries", 3, Property.NodeScope); + Setting MAX_RETRIES_SETTING = Setting.intSetting("repositories.s3.max_retries", 3, Property.NodeScope, Property.Deprecated); /** * repositories.s3.use_throttle_retries: Set to `true` if you want to throttle retries. Defaults to AWS SDK default value (`false`). */ Setting USE_THROTTLE_RETRIES_SETTING = Setting.boolSetting("repositories.s3.use_throttle_retries", - ClientConfiguration.DEFAULT_THROTTLE_RETRIES, Property.NodeScope); + ClientConfiguration.DEFAULT_THROTTLE_RETRIES, Property.NodeScope, Property.Deprecated); /** * repositories.s3.chunk_size: Big files can be broken down into chunks during snapshotting if needed. Defaults to 1g. */ Setting CHUNK_SIZE_SETTING = Setting.byteSizeSetting("repositories.s3.chunk_size", new ByteSizeValue(1, ByteSizeUnit.GB), - new ByteSizeValue(5, ByteSizeUnit.MB), new ByteSizeValue(5, ByteSizeUnit.TB), Property.NodeScope); + new ByteSizeValue(5, ByteSizeUnit.MB), new ByteSizeValue(5, ByteSizeUnit.TB), Property.NodeScope, Property.Deprecated); /** * repositories.s3.compress: When set to true metadata files are stored in compressed format. This setting doesn’t affect index * files that are already compressed by default. Defaults to false. */ - Setting COMPRESS_SETTING = Setting.boolSetting("repositories.s3.compress", false, Property.NodeScope); + Setting COMPRESS_SETTING = Setting.boolSetting("repositories.s3.compress", false, Property.NodeScope, Property.Deprecated); /** * repositories.s3.storage_class: Sets the S3 storage class type for the backup files. Values may be standard, reduced_redundancy, * standard_ia. Defaults to standard. */ - Setting STORAGE_CLASS_SETTING = Setting.simpleString("repositories.s3.storage_class", Property.NodeScope); + Setting STORAGE_CLASS_SETTING = Setting.simpleString("repositories.s3.storage_class", Property.NodeScope, Property.Deprecated); /** * repositories.s3.canned_acl: The S3 repository supports all S3 canned ACLs : private, public-read, public-read-write, * authenticated-read, log-delivery-write, bucket-owner-read, bucket-owner-full-control. Defaults to private. */ - Setting CANNED_ACL_SETTING = Setting.simpleString("repositories.s3.canned_acl", Property.NodeScope); + Setting CANNED_ACL_SETTING = Setting.simpleString("repositories.s3.canned_acl", Property.NodeScope, Property.Deprecated); /** * repositories.s3.base_path: Specifies the path within bucket to repository data. Defaults to root directory. */ - Setting BASE_PATH_SETTING = Setting.simpleString("repositories.s3.base_path", Property.NodeScope); + Setting BASE_PATH_SETTING = Setting.simpleString("repositories.s3.base_path", Property.NodeScope, Property.Deprecated); /** * repositories.s3.path_style_access: When set to true configures the client to use path-style access for all requests. Amazon S3 supports virtual-hosted-style and path-style access in all Regions. The path-style syntax, however, @@ -132,7 +132,8 @@ The default behaviour is to detect which access style to use based on the config in path-style access) and the bucket being accessed (some buckets are not valid DNS names). Setting this flag will result in path-style access being used for all requests. */ - Setting PATH_STYLE_ACCESS_SETTING = Setting.boolSetting("repositories.s3.path_style_access", false, Property.NodeScope); + Setting PATH_STYLE_ACCESS_SETTING = Setting.boolSetting("repositories.s3.path_style_access", false, + Property.NodeScope, Property.Deprecated); } /** @@ -160,13 +161,13 @@ public interface Repository { * max_retries * @see Repositories#MAX_RETRIES_SETTING */ - Setting MAX_RETRIES_SETTING = Setting.intSetting("max_retries", 3); + Setting MAX_RETRIES_SETTING = Setting.intSetting("max_retries", 3, Property.Deprecated); /** * use_throttle_retries * @see Repositories#USE_THROTTLE_RETRIES_SETTING */ Setting USE_THROTTLE_RETRIES_SETTING = Setting.boolSetting("use_throttle_retries", - ClientConfiguration.DEFAULT_THROTTLE_RETRIES); + ClientConfiguration.DEFAULT_THROTTLE_RETRIES, Property.Deprecated); /** * chunk_size * @see Repositories#CHUNK_SIZE_SETTING @@ -198,7 +199,7 @@ public interface Repository { * path_style_access * @see Repositories#PATH_STYLE_ACCESS_SETTING */ - Setting PATH_STYLE_ACCESS_SETTING = Setting.boolSetting("path_style_access", false); + Setting PATH_STYLE_ACCESS_SETTING = Setting.boolSetting("path_style_access", false, Property.Deprecated); } private final S3BlobStore blobStore; diff --git a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java index 1ab0ca3544156..a4512ab815d06 100644 --- a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java +++ b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java @@ -90,6 +90,8 @@ public List> getSettings() { S3ClientSettings.PROXY_USERNAME_SETTING, S3ClientSettings.PROXY_PASSWORD_SETTING, S3ClientSettings.READ_TIMEOUT_SETTING, + S3ClientSettings.MAX_RETRIES_SETTING, + S3ClientSettings.USE_THROTTLE_RETRIES_SETTING, // Register S3 repositories settings: repositories.s3 S3Repository.Repositories.BUCKET_SETTING, diff --git a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java index 18608b83627b8..017e7fbeb3f16 100644 --- a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java +++ b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java @@ -24,6 +24,7 @@ import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; import org.elasticsearch.common.settings.MockSecureSettings; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; @@ -86,21 +87,70 @@ public void testAWSConfigurationWithAwsSettings() { "aws_proxy_password", 3, false, 10000); } - public void testGlobalMaxRetries() { + public void testGlobalMaxRetriesBackcompat() { Settings settings = Settings.builder() .put(S3Repository.Repositories.MAX_RETRIES_SETTING.getKey(), 10) .build(); launchAWSConfigurationTest(settings, Settings.EMPTY, Protocol.HTTPS, null, -1, null, null, 10, false, 50000); + assertSettingDeprecationsAndWarnings(new Setting[]{ + S3Repository.Repositories.MAX_RETRIES_SETTING + }); } public void testRepositoryMaxRetries() { - Settings repositorySettings = generateRepositorySettings(20); + Settings settings = Settings.builder() + .put("s3.client.default.max_retries", 5) + .build(); + launchAWSConfigurationTest(settings, Settings.EMPTY, Protocol.HTTPS, null, -1, null, + null, 5, false, 50000); + } + + public void testRepositoryMaxRetriesBackcompat() { + Settings repositorySettings = Settings.builder() + .put(S3Repository.Repository.MAX_RETRIES_SETTING.getKey(), 20).build(); Settings settings = Settings.builder() .put(S3Repository.Repositories.MAX_RETRIES_SETTING.getKey(), 10) .build(); launchAWSConfigurationTest(settings, repositorySettings, Protocol.HTTPS, null, -1, null, null, 20, false, 50000); + assertSettingDeprecationsAndWarnings(new Setting[]{ + S3Repository.Repositories.MAX_RETRIES_SETTING, + S3Repository.Repository.MAX_RETRIES_SETTING + }); + } + + public void testGlobalThrottleRetriesBackcompat() { + Settings settings = Settings.builder() + .put(S3Repository.Repositories.USE_THROTTLE_RETRIES_SETTING.getKey(), true) + .build(); + launchAWSConfigurationTest(settings, Settings.EMPTY, Protocol.HTTPS, null, -1, null, + null, 3, true, 50000); + assertSettingDeprecationsAndWarnings(new Setting[]{ + S3Repository.Repositories.USE_THROTTLE_RETRIES_SETTING + }); + } + + public void testRepositoryThrottleRetries() { + Settings settings = Settings.builder() + .put("s3.client.default.use_throttle_retries", true) + .build(); + launchAWSConfigurationTest(settings, Settings.EMPTY, Protocol.HTTPS, null, -1, null, + null, 3, true, 50000); + } + + public void testRepositoryThrottleRetriesBackcompat() { + Settings repositorySettings = Settings.builder() + .put(S3Repository.Repository.USE_THROTTLE_RETRIES_SETTING.getKey(), true).build(); + Settings settings = Settings.builder() + .put(S3Repository.Repositories.USE_THROTTLE_RETRIES_SETTING.getKey(), false) + .build(); + launchAWSConfigurationTest(settings, repositorySettings, Protocol.HTTPS, null, -1, null, + null, 3, true, 50000); + assertSettingDeprecationsAndWarnings(new Setting[]{ + S3Repository.Repositories.USE_THROTTLE_RETRIES_SETTING, + S3Repository.Repository.USE_THROTTLE_RETRIES_SETTING + }); } private void launchAWSConfigurationTest(Settings settings, @@ -113,13 +163,9 @@ private void launchAWSConfigurationTest(Settings settings, Integer expectedMaxRetries, boolean expectedUseThrottleRetries, int expectedReadTimeout) { - Integer maxRetries = S3Repository.getValue(singleRepositorySettings, settings, - S3Repository.Repository.MAX_RETRIES_SETTING, S3Repository.Repositories.MAX_RETRIES_SETTING); - Boolean useThrottleRetries = S3Repository.getValue(singleRepositorySettings, settings, - S3Repository.Repository.USE_THROTTLE_RETRIES_SETTING, S3Repository.Repositories.USE_THROTTLE_RETRIES_SETTING); S3ClientSettings clientSettings = S3ClientSettings.getClientSettings(settings, "default"); - ClientConfiguration configuration = InternalAwsS3Service.buildConfiguration(clientSettings, maxRetries, useThrottleRetries); + ClientConfiguration configuration = InternalAwsS3Service.buildConfiguration(clientSettings, singleRepositorySettings); assertThat(configuration.getResponseMetadataCacheSize(), is(0)); assertThat(configuration.getProtocol(), is(expectedProtocol)); @@ -132,14 +178,6 @@ private void launchAWSConfigurationTest(Settings settings, assertThat(configuration.getSocketTimeout(), is(expectedReadTimeout)); } - private static Settings generateRepositorySettings(Integer maxRetries) { - Settings.Builder builder = Settings.builder(); - if (maxRetries != null) { - builder.put(S3Repository.Repository.MAX_RETRIES_SETTING.getKey(), maxRetries); - } - return builder.build(); - } - public void testEndpointSetting() { Settings settings = Settings.builder() .put("s3.client.default.endpoint", "s3.endpoint") diff --git a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java index 11daf7b18ff13..ce20d05a37ee2 100644 --- a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java +++ b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java @@ -108,7 +108,8 @@ public void testBasePathSetting() throws IOException { Settings settings = Settings.builder().put(Repositories.BASE_PATH_SETTING.getKey(), "/foo/bar").build(); s3repo = new S3Repository(metadata, settings, NamedXContentRegistry.EMPTY, new DummyS3Service()); assertEquals("foo/bar/", s3repo.basePath().buildAsString()); // make sure leading `/` is removed and trailing is added - assertWarnings("S3 repository base_path" + + assertSettingDeprecationsAndWarnings(new Setting[] { Repositories.BASE_PATH_SETTING }, + "S3 repository base_path" + " trimming the leading `/`, and leading `/` will not be supported for the S3 repository in future releases"); } diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index fab12d2606b23..29241b4b19d50 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -327,6 +327,7 @@ protected final void assertWarnings(String... expectedWarnings) { } try { final List actualWarnings = threadContext.getResponseHeaders().get("Warning"); + assertNotNull(actualWarnings); final Set actualWarningValues = actualWarnings.stream().map(DeprecationLogger::extractWarningValueFromWarningHeader).collect(Collectors.toSet()); for (String msg : expectedWarnings) { From fede0bab2cd5ad58bcceda1c15ec72bd90fb86e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 26 Apr 2017 11:04:33 +0200 Subject: [PATCH 080/619] Correcting 140 character line length --- .../metrics/stats/extended/ParsedExtendedStats.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java index 8947f4c0aac5e..2c17376e88725 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java @@ -139,8 +139,8 @@ protected XContentBuilder otherStatsToXContent(XContentBuilder builder, Params p return builder; } - private static final ObjectParser PARSER = new ObjectParser<>(ParsedExtendedStats.class.getSimpleName(), true, - ParsedExtendedStats::new); + private static final ObjectParser PARSER = new ObjectParser<>(ParsedExtendedStats.class.getSimpleName(), + true, ParsedExtendedStats::new); private static final ConstructingObjectParser, Void> STD_BOUNDS_PARSER = new ConstructingObjectParser<>( ParsedExtendedStats.class.getSimpleName() + "_STD_BOUNDS", true, args -> new Tuple<>((Double) args[0], (Double) args[1])); @@ -152,7 +152,8 @@ protected XContentBuilder otherStatsToXContent(XContentBuilder builder, Params p } private static final ConstructingObjectParser, Void> STD_BOUNDS_AS_STRING_PARSER = new ConstructingObjectParser<>( - ParsedExtendedStats.class.getSimpleName() + "_STD_BOUNDS_AS_STRING", true, args -> new Tuple<>((String) args[0], (String) args[1])); + ParsedExtendedStats.class.getSimpleName() + "_STD_BOUNDS_AS_STRING", true, + args -> new Tuple<>((String) args[0], (String) args[1])); static { STD_BOUNDS_AS_STRING_PARSER.declareString(constructorArg(), new ParseField(Fields.LOWER)); STD_BOUNDS_AS_STRING_PARSER.declareString(constructorArg(), new ParseField(Fields.UPPER)); From c17de49a6dc1d54fcfee3754211ae67a06bdcec7 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Wed, 19 Apr 2017 20:37:20 +0200 Subject: [PATCH 081/619] [percolator] Fix memory leak when percolator uses bitset or field data cache. The percolator doesn't close the IndexReader of the memory index any more. Prior to 2.x the percolator had its own SearchContext (PercolatorContext) that did this, but that was removed when the percolator was refactored as part of the 5.0 release. I think an alternative way to fix this is to let percolator not use the bitset and fielddata caches, that way we prevent the memory leak. Closes #24108 --- .../index/cache/bitset/BitsetFilterCache.java | 3 +- .../aggregations/AggregatorTestCase.java | 8 ++ .../bucket/nested/NestedAggregatorTests.java | 10 +- .../nested/ReverseNestedAggregatorTests.java | 4 +- .../percolator/PercolateQueryBuilder.java | 47 ++++++- .../percolator/PercolatorQuerySearchIT.java | 122 ++++++++++++++++++ 6 files changed, 181 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/cache/bitset/BitsetFilterCache.java b/core/src/main/java/org/elasticsearch/index/cache/bitset/BitsetFilterCache.java index 04d2ac47d187e..7d3e75f6f5d12 100644 --- a/core/src/main/java/org/elasticsearch/index/cache/bitset/BitsetFilterCache.java +++ b/core/src/main/java/org/elasticsearch/index/cache/bitset/BitsetFilterCache.java @@ -121,8 +121,7 @@ private BitSet getAndLoadIfNotPresent(final Query query, final LeafReaderContext } final IndexReader.CacheKey coreCacheReader = cacheHelper.getKey(); final ShardId shardId = ShardUtils.extractShardId(context.reader()); - if (shardId != null // can't require it because of the percolator - && indexSettings.getIndex().equals(shardId.getIndex()) == false) { + if (indexSettings.getIndex().equals(shardId.getIndex()) == false) { // insanity throw new IllegalStateException("Trying to load bit set for index " + shardId.getIndex() + " with cache of index " + indexSettings.getIndex()); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java index 3dc530204bc88..fd36a03ddf6ce 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java @@ -19,6 +19,7 @@ package org.elasticsearch.search.aggregations; import org.apache.lucene.index.CompositeReaderContext; +import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReaderContext; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.Collector; @@ -31,8 +32,10 @@ import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.lease.Releasable; import org.elasticsearch.common.lease.Releasables; +import org.elasticsearch.common.lucene.index.ElasticsearchDirectoryReader; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.cache.bitset.BitsetFilterCache; import org.elasticsearch.index.cache.bitset.BitsetFilterCache.Listener; @@ -48,6 +51,7 @@ import org.elasticsearch.index.mapper.ObjectMapper.Nested; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.support.NestedScope; +import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.indices.fielddata.cache.IndicesFieldDataCache; @@ -289,4 +293,8 @@ public String toString() { return "ShardSearcher(" + ctx.get(0) + ")"; } } + + protected static DirectoryReader wrap(DirectoryReader directoryReader) throws IOException { + return ElasticsearchDirectoryReader.wrap(directoryReader, new ShardId(new Index("_index", "_na_"), 0)); + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregatorTests.java index cdd2251fb6e61..304b7f03c59ef 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregatorTests.java @@ -65,7 +65,7 @@ public void testNoDocs() throws IOException { try (RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { // intentionally not writing any docs } - try (IndexReader indexReader = DirectoryReader.open(directory)) { + try (IndexReader indexReader = wrap(DirectoryReader.open(directory))) { NestedAggregationBuilder nestedBuilder = new NestedAggregationBuilder(NESTED_AGG, NESTED_OBJECT); MaxAggregationBuilder maxAgg = new MaxAggregationBuilder(MAX_AGG_NAME) @@ -112,7 +112,7 @@ public void testSingleNestingMax() throws IOException { } iw.commit(); } - try (IndexReader indexReader = DirectoryReader.open(directory)) { + try (IndexReader indexReader = wrap(DirectoryReader.open(directory))) { NestedAggregationBuilder nestedBuilder = new NestedAggregationBuilder(NESTED_AGG, NESTED_OBJECT); MaxAggregationBuilder maxAgg = new MaxAggregationBuilder(MAX_AGG_NAME) @@ -160,7 +160,7 @@ public void testDoubleNestingMax() throws IOException { } iw.commit(); } - try (IndexReader indexReader = DirectoryReader.open(directory)) { + try (IndexReader indexReader = wrap(DirectoryReader.open(directory))) { NestedAggregationBuilder nestedBuilder = new NestedAggregationBuilder(NESTED_AGG, NESTED_OBJECT + "." + NESTED_OBJECT2); MaxAggregationBuilder maxAgg = new MaxAggregationBuilder(MAX_AGG_NAME) @@ -213,7 +213,7 @@ public void testOrphanedDocs() throws IOException { iw.addDocuments(documents); iw.commit(); } - try (IndexReader indexReader = DirectoryReader.open(directory)) { + try (IndexReader indexReader = wrap(DirectoryReader.open(directory))) { NestedAggregationBuilder nestedBuilder = new NestedAggregationBuilder(NESTED_AGG, NESTED_OBJECT); SumAggregationBuilder sumAgg = new SumAggregationBuilder(SUM_AGG_NAME) @@ -292,7 +292,7 @@ public void testResetRootDocId() throws Exception { iw.commit(); iw.close(); } - try (IndexReader indexReader = DirectoryReader.open(directory)) { + try (IndexReader indexReader = wrap(DirectoryReader.open(directory))) { NestedAggregationBuilder nestedBuilder = new NestedAggregationBuilder(NESTED_AGG, "nested_field"); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregatorTests.java index ce6ec7794b2af..74fb7ca9ca4d2 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregatorTests.java @@ -54,7 +54,7 @@ public void testNoDocs() throws IOException { try (RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { // intentionally not writing any docs } - try (IndexReader indexReader = DirectoryReader.open(directory)) { + try (IndexReader indexReader = wrap(DirectoryReader.open(directory))) { NestedAggregationBuilder nestedBuilder = new NestedAggregationBuilder(NESTED_AGG, NESTED_OBJECT); ReverseNestedAggregationBuilder reverseNestedBuilder @@ -117,7 +117,7 @@ public void testMaxFromParentDocs() throws IOException { } iw.commit(); } - try (IndexReader indexReader = DirectoryReader.open(directory)) { + try (IndexReader indexReader = wrap(DirectoryReader.open(directory))) { NestedAggregationBuilder nestedBuilder = new NestedAggregationBuilder(NESTED_AGG, NESTED_OBJECT); ReverseNestedAggregationBuilder reverseNestedBuilder diff --git a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java index 9ebb65c32753c..0cd58365bf484 100644 --- a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java +++ b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java @@ -23,16 +23,22 @@ import org.apache.lucene.analysis.DelegatingAnalyzerWrapper; import org.apache.lucene.index.BinaryDocValues; import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReaderContext; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.ReaderUtil; import org.apache.lucene.index.memory.MemoryIndex; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; +import org.apache.lucene.search.join.BitSetProducer; import org.apache.lucene.store.RAMDirectory; +import org.apache.lucene.util.BitDocIdSet; +import org.apache.lucene.util.BitSet; import org.apache.lucene.util.BytesRef; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ResourceNotFoundException; @@ -51,6 +57,8 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.analysis.FieldNameAnalyzer; +import org.elasticsearch.index.fielddata.IndexFieldData; +import org.elasticsearch.index.fielddata.IndexFieldDataCache; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.DocumentMapperForType; import org.elasticsearch.index.mapper.MappedFieldType; @@ -62,6 +70,8 @@ import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardException; +import org.elasticsearch.indices.breaker.CircuitBreakerService; +import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import java.io.IOException; import java.util.Objects; @@ -412,12 +422,9 @@ protected Analyzer getWrappedAnalyzer(String fieldName) { docSearcher.setQueryCache(null); } - Version indexVersionCreated = context.getIndexSettings().getIndexVersionCreated(); boolean mapUnmappedFieldsAsString = context.getIndexSettings() .getValue(PercolatorFieldMapper.INDEX_MAP_UNMAPPED_FIELDS_AS_STRING_SETTING); - // We have to make a copy of the QueryShardContext here so we can have a unfrozen version for parsing the legacy - // percolator queries - QueryShardContext percolateShardContext = new QueryShardContext(context); + QueryShardContext percolateShardContext = wrap(context); MappedFieldType fieldType = context.fieldMapper(field); if (fieldType == null) { throw new QueryShardException(context, "field [" + field + "] does not exist"); @@ -503,4 +510,36 @@ private static PercolateQuery.QueryStore createStore(PercolatorFieldMapper.Field }; } + static QueryShardContext wrap(QueryShardContext shardContext) { + return new QueryShardContext(shardContext) { + + @Override + public BitSetProducer bitsetFilter(Query query) { + return context -> { + final IndexReaderContext topLevelContext = ReaderUtil.getTopLevelContext(context); + final IndexSearcher searcher = new IndexSearcher(topLevelContext); + searcher.setQueryCache(null); + final Weight weight = searcher.createNormalizedWeight(query, false); + final Scorer s = weight.scorer(context); + + if (s != null) { + return new BitDocIdSet(BitSet.of(s.iterator(), context.reader().maxDoc())).bits(); + } else { + return null; + } + }; + } + + @Override + @SuppressWarnings("unchecked") + public > IFD getForField(MappedFieldType fieldType) { + IndexFieldData.Builder builder = fieldType.fielddataBuilder(); + IndexFieldDataCache cache = new IndexFieldDataCache.None(); + CircuitBreakerService circuitBreaker = new NoneCircuitBreakerService(); + return (IFD) builder.build(shardContext.getIndexSettings(), fieldType, cache, circuitBreaker, + shardContext.getMapperService()); + } + }; + } + } diff --git a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java index ebf0c13b20aa0..5760b260b0116 100644 --- a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java +++ b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java @@ -26,9 +26,12 @@ import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.cache.bitset.BitsetFilterCache; +import org.elasticsearch.index.fielddata.ScriptDocValues; import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.query.MatchPhraseQueryBuilder; import org.elasticsearch.index.query.MultiMatchQueryBuilder; @@ -39,6 +42,7 @@ import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; +import org.elasticsearch.search.lookup.LeafDocLookup; import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.ESSingleNodeTestCase; @@ -83,6 +87,11 @@ public static class CustomScriptPlugin extends MockScriptPlugin { protected Map, Object>> pluginScripts() { Map, Object>> scripts = new HashMap<>(); scripts.put("1==1", vars -> Boolean.TRUE); + scripts.put("use_fielddata_please", vars -> { + LeafDocLookup leafDocLookup = (LeafDocLookup) vars.get("_doc"); + ScriptDocValues scriptDocValues = leafDocLookup.get("employees.name"); + return "virginia_potts".equals(scriptDocValues.get(0)); + }); return scripts; } } @@ -606,6 +615,119 @@ public void testPercolateQueryWithNestedDocuments() throws Exception { assertHitCount(response, 0); } + public void testPercolateQueryWithNestedDocuments_doNotLeakBitsetCacheEntries() throws Exception { + XContentBuilder mapping = XContentFactory.jsonBuilder(); + mapping.startObject().startObject("properties").startObject("companyname").field("type", "text").endObject() + .startObject("employee").field("type", "nested").startObject("properties") + .startObject("name").field("type", "text").endObject().endObject().endObject().endObject() + .endObject(); + createIndex("test", client().admin().indices().prepareCreate("test") + // to avoid normal document from being cached by BitsetFilterCache + .setSettings(Settings.builder().put(BitsetFilterCache.INDEX_LOAD_RANDOM_ACCESS_FILTERS_EAGERLY_SETTING.getKey(), false)) + .addMapping("employee", mapping) + .addMapping("queries", "query", "type=percolator") + ); + client().prepareIndex("test", "queries", "q1").setSource(jsonBuilder().startObject() + .field("query", QueryBuilders.nestedQuery("employee", + QueryBuilders.matchQuery("employee.name", "virginia potts").operator(Operator.AND), ScoreMode.Avg) + ).endObject()) + .get(); + client().admin().indices().prepareRefresh().get(); + + for (int i = 0; i < 32; i++) { + SearchResponse response = client().prepareSearch() + .setQuery(new PercolateQueryBuilder("query", "employee", + XContentFactory.jsonBuilder() + .startObject().field("companyname", "stark") + .startArray("employee") + .startObject().field("name", "virginia potts").endObject() + .startObject().field("name", "tony stark").endObject() + .endArray() + .endObject().bytes(), XContentType.JSON)) + .addSort("_doc", SortOrder.ASC) + // size 0, because other wise load bitsets for normal document in FetchPhase#findRootDocumentIfNested(...) + .setSize(0) + .get(); + assertHitCount(response, 1); + } + + // We can't check via api... because BitsetCacheListener requires that it can extract shardId from index reader + // and for percolator it can't do that, but that means we don't keep track of + // memory for BitsetCache in case of percolator + long bitsetSize = client().admin().cluster().prepareClusterStats().get() + .getIndicesStats().getSegments().getBitsetMemoryInBytes(); + assertEquals("The percolator works with in-memory index and therefor shouldn't use bitset cache", 0L, bitsetSize); + } + + public void testPercolateQueryWithNestedDocuments_doLeakFieldDataCacheEntries() throws Exception { + XContentBuilder mapping = XContentFactory.jsonBuilder(); + mapping.startObject(); + { + mapping.startObject("properties"); + { + mapping.startObject("companyname"); + mapping.field("type", "text"); + mapping.endObject(); + } + { + mapping.startObject("employees"); + mapping.field("type", "nested"); + { + mapping.startObject("properties"); + { + mapping.startObject("name"); + mapping.field("type", "text"); + mapping.field("fielddata", true); + mapping.endObject(); + } + mapping.endObject(); + } + mapping.endObject(); + } + mapping.endObject(); + } + mapping.endObject(); + createIndex("test", client().admin().indices().prepareCreate("test") + .addMapping("employee", mapping) + .addMapping("queries", "query", "type=percolator") + ); + Script script = new Script(ScriptType.INLINE, MockScriptPlugin.NAME, "use_fielddata_please", Collections.emptyMap()); + client().prepareIndex("test", "queries", "q1").setSource(jsonBuilder().startObject() + .field("query", QueryBuilders.nestedQuery("employees", + QueryBuilders.scriptQuery(script), ScoreMode.Avg) + ).endObject()).get(); + client().admin().indices().prepareRefresh().get(); + XContentBuilder doc = jsonBuilder(); + doc.startObject(); + { + doc.field("companyname", "stark"); + doc.startArray("employees"); + { + doc.startObject(); + doc.field("name", "virginia_potts"); + doc.endObject(); + } + { + doc.startObject(); + doc.field("name", "tony_stark"); + doc.endObject(); + } + doc.endArray(); + } + doc.endObject(); + for (int i = 0; i < 32; i++) { + SearchResponse response = client().prepareSearch() + .setQuery(new PercolateQueryBuilder("query", "employee", doc.bytes(), XContentType.JSON)) + .addSort("_doc", SortOrder.ASC) + .get(); + assertHitCount(response, 1); + } + + long fieldDataSize = client().admin().cluster().prepareClusterStats().get() + .getIndicesStats().getFieldData().getMemorySizeInBytes(); + assertEquals("The percolator works with in-memory index and therefor shouldn't use field-data cache", 0L, fieldDataSize); + } + public void testPercolatorQueryViaMultiSearch() throws Exception { createIndex("test", client().admin().indices().prepareCreate("test") .addMapping("type", "field1", "type=text") From 7f8fe8b81db5f90f5449638ba8a817f9d3dd5918 Mon Sep 17 00:00:00 2001 From: Jay Modi Date: Wed, 26 Apr 2017 07:23:07 -0400 Subject: [PATCH 082/619] StreamInput throws exceptions instead of using assertions (#24294) StreamInput has methods such as readVInt that perform sanity checks on the data using assertions, which will catch bad data in tests but provide no safety when running as a node without assertions enabled. The use of assertions also make testing with invalid data difficult since we would need to handle assertion errors in the code using the stream input and errors like this should not be something we try to catch. This commit introduces a flag that will throw an IOException instead of using an assertion. --- .../org/elasticsearch/common/io/stream/StreamInput.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java b/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java index e33c3ed840a5d..7d175916c8e88 100644 --- a/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java +++ b/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java @@ -202,7 +202,9 @@ public int readVInt() throws IOException { return i; } b = readByte(); - assert (b & 0x80) == 0; + if ((b & 0x80) != 0) { + throw new IOException("Invalid vInt ((" + Integer.toHexString(b) + " & 0x7f) << 28) | " + Integer.toHexString(i)); + } return i | ((b & 0x7F) << 28); } @@ -367,7 +369,7 @@ public String readString() throws IOException { buffer[i] = ((char) ((c & 0x0F) << 12 | (readByte() & 0x3F) << 6 | (readByte() & 0x3F) << 0)); break; default: - new AssertionError("unexpected character: " + c + " hex: " + Integer.toHexString(c)); + throw new IOException("Invalid string; unexpected character: " + c + " hex: " + Integer.toHexString(c)); } } return spare.toString(); @@ -808,7 +810,7 @@ public T readException() throws IOException { case 17: return (T) readStackTrace(new IOException(readOptionalString(), readException()), this); default: - assert false : "no such exception for id: " + key; + throw new IOException("no such exception for id: " + key); } } return null; From 91b61ce569e943edb4fcdb539c53c5b7562d440f Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Wed, 26 Apr 2017 16:08:16 +0200 Subject: [PATCH 083/619] [TEST] Do a reroute with retry_failed after a bridge partition on testAckedIndexing In case of a bridge partition, shard allocation can fail "index.allocation.max_retries" times if the master is the super-connected node and recovery source and target are on opposite sides of the bridge. This commit adds a reroute with retry_failed after healing the network partition so that the ensureGreen check succeeds. --- .../discovery/DiscoveryWithServiceDisruptionsIT.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java b/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java index 595d9a6d3f1b0..021e2be85edbc 100644 --- a/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java +++ b/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java @@ -585,6 +585,12 @@ public void testAckedIndexing() throws Exception { ensureStableCluster(nodes.size(), TimeValue.timeValueMillis(disruptionScheme.expectedTimeToHeal().millis() + DISRUPTION_HEALING_OVERHEAD.millis()), true, node); } + // in case of a bridge partition, shard allocation can fail "index.allocation.max_retries" times if the master + // is the super-connected node and recovery source and target are on opposite sides of the bridge + if (disruptionScheme instanceof NetworkDisruption && + ((NetworkDisruption) disruptionScheme).getDisruptedLinks() instanceof Bridge) { + assertAcked(client().admin().cluster().prepareReroute().setRetryFailed(true)); + } ensureGreen("test"); logger.info("validating successful docs"); From 0c12d0ce37932b174dc2b2a5a91824e05caac9de Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 26 Apr 2017 10:40:35 -0400 Subject: [PATCH 084/619] Make bats tests refuse to start on non-VMs (#24315) The bats tests are descructive and must be run as root. This is a horrible combination on any sane system but perfectly fine to do in a VM. This change modifies the tests so they revuse to start unless they are in an environment with an `/etc/is_vagrant_vm` file. The Vagrantfile creates it on startup. Closes #24137 --- Vagrantfile | 8 +++++++- .../resources/packaging/tests/20_tar_package.bats | 11 ++++++++--- .../resources/packaging/tests/30_deb_package.bats | 11 ++++++++--- .../resources/packaging/tests/40_rpm_package.bats | 11 ++++++++--- .../resources/packaging/tests/60_systemd.bats | 11 ++++++++--- .../resources/packaging/tests/70_sysv_initd.bats | 15 ++++++++++----- .../packaging/tests/75_bad_data_paths.bats | 11 ++++++++--- .../resources/packaging/tests/80_upgrade.bats | 11 ++++++++--- .../resources/packaging/tests/90_reinstall.bats | 11 ++++++++--- .../tests/module_and_plugin_test_cases.bash | 11 ++++++++--- .../test/resources/packaging/utils/modules.bash | 13 +++++++++---- .../test/resources/packaging/utils/packages.bash | 11 ++++++++--- .../test/resources/packaging/utils/plugins.bash | 15 ++++++++++----- .../src/test/resources/packaging/utils/tar.bash | 15 ++++++++++----- .../src/test/resources/packaging/utils/utils.bash | 11 ++++++++--- 15 files changed, 126 insertions(+), 50 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index f008b339c3fa4..a818d666655c6 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -104,6 +104,12 @@ SOURCE_PROMPT source /etc/profile.d/elasticsearch_prompt.sh SOURCE_PROMPT SHELL + # Creates a file to mark the machine as created by vagrant. Tests check + # for this file and refuse to run if it is not present so that they can't + # be run unexpectedly. + config.vm.provision "markerfile", type: "shell", inline: <<-SHELL + touch /etc/is_vagrant_vm + SHELL end config.config_procs.push ['2', set_prompt] end @@ -263,7 +269,7 @@ def provision(config, echo "==> Installing Gradle" curl -sS -o /tmp/gradle.zip -L https://services.gradle.org/distributions/gradle-3.3-bin.zip unzip /tmp/gradle.zip -d /opt - rm -rf /tmp/gradle.zip + rm -rf /tmp/gradle.zip ln -s /opt/gradle-3.3/bin/gradle /usr/bin/gradle # make nfs mounted gradle home dir writeable chown vagrant:vagrant /home/vagrant/.gradle diff --git a/qa/vagrant/src/test/resources/packaging/tests/20_tar_package.bats b/qa/vagrant/src/test/resources/packaging/tests/20_tar_package.bats index 2d502084a4ac6..9c5ca24d0a0bb 100644 --- a/qa/vagrant/src/test/resources/packaging/tests/20_tar_package.bats +++ b/qa/vagrant/src/test/resources/packaging/tests/20_tar_package.bats @@ -3,9 +3,14 @@ # This file is used to test the tar gz package. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # The test case can be executed with the Bash Automated # Testing System tool available at https://github.com/sstephenson/bats diff --git a/qa/vagrant/src/test/resources/packaging/tests/30_deb_package.bats b/qa/vagrant/src/test/resources/packaging/tests/30_deb_package.bats index b7e925f2899e4..b07825d7f2fde 100644 --- a/qa/vagrant/src/test/resources/packaging/tests/30_deb_package.bats +++ b/qa/vagrant/src/test/resources/packaging/tests/30_deb_package.bats @@ -4,9 +4,14 @@ # of a Debian package. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # The test case can be executed with the Bash Automated # Testing System tool available at https://github.com/sstephenson/bats diff --git a/qa/vagrant/src/test/resources/packaging/tests/40_rpm_package.bats b/qa/vagrant/src/test/resources/packaging/tests/40_rpm_package.bats index effd7e27ae9a6..b92b692401e56 100644 --- a/qa/vagrant/src/test/resources/packaging/tests/40_rpm_package.bats +++ b/qa/vagrant/src/test/resources/packaging/tests/40_rpm_package.bats @@ -3,9 +3,14 @@ # This file is used to test the installation of a RPM package. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # The test case can be executed with the Bash Automated # Testing System tool available at https://github.com/sstephenson/bats diff --git a/qa/vagrant/src/test/resources/packaging/tests/60_systemd.bats b/qa/vagrant/src/test/resources/packaging/tests/60_systemd.bats index 10897753ab3d5..b46fd6786bbcb 100644 --- a/qa/vagrant/src/test/resources/packaging/tests/60_systemd.bats +++ b/qa/vagrant/src/test/resources/packaging/tests/60_systemd.bats @@ -3,9 +3,14 @@ # This file is used to test the elasticsearch Systemd setup. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # The test case can be executed with the Bash Automated # Testing System tool available at https://github.com/sstephenson/bats diff --git a/qa/vagrant/src/test/resources/packaging/tests/70_sysv_initd.bats b/qa/vagrant/src/test/resources/packaging/tests/70_sysv_initd.bats index 70db874445620..fa6f91498ca02 100644 --- a/qa/vagrant/src/test/resources/packaging/tests/70_sysv_initd.bats +++ b/qa/vagrant/src/test/resources/packaging/tests/70_sysv_initd.bats @@ -3,9 +3,14 @@ # This file is used to test the elasticsearch init.d scripts. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # The test case can be executed with the Bash Automated # Testing System tool available at https://github.com/sstephenson/bats @@ -60,11 +65,11 @@ setup() { @test "[INIT.D] elasticsearch fails if startup script is not executable" { local INIT="/etc/init.d/elasticsearch" local DAEMON="$ESHOME/bin/elasticsearch" - + sudo chmod -x "$DAEMON" run "$INIT" sudo chmod +x "$DAEMON" - + [ "$status" -eq 1 ] [[ "$output" == *"The elasticsearch startup script does not exists or it is not executable, tried: $DAEMON"* ]] } diff --git a/qa/vagrant/src/test/resources/packaging/tests/75_bad_data_paths.bats b/qa/vagrant/src/test/resources/packaging/tests/75_bad_data_paths.bats index 0f802a439b702..59747bd68375f 100644 --- a/qa/vagrant/src/test/resources/packaging/tests/75_bad_data_paths.bats +++ b/qa/vagrant/src/test/resources/packaging/tests/75_bad_data_paths.bats @@ -4,9 +4,14 @@ # default.data.path setting into the data.path even when it doesn't belong. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # The test case can be executed with the Bash Automated # Testing System tool available at https://github.com/sstephenson/bats diff --git a/qa/vagrant/src/test/resources/packaging/tests/80_upgrade.bats b/qa/vagrant/src/test/resources/packaging/tests/80_upgrade.bats index 9f5bf9c502456..f402305156b8e 100644 --- a/qa/vagrant/src/test/resources/packaging/tests/80_upgrade.bats +++ b/qa/vagrant/src/test/resources/packaging/tests/80_upgrade.bats @@ -5,9 +5,14 @@ # fancy rolling restarts. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # The test case can be executed with the Bash Automated # Testing System tool available at https://github.com/sstephenson/bats diff --git a/qa/vagrant/src/test/resources/packaging/tests/90_reinstall.bats b/qa/vagrant/src/test/resources/packaging/tests/90_reinstall.bats index 4dd682efbdd6a..816247f95f93b 100644 --- a/qa/vagrant/src/test/resources/packaging/tests/90_reinstall.bats +++ b/qa/vagrant/src/test/resources/packaging/tests/90_reinstall.bats @@ -5,9 +5,14 @@ # fancy rolling restarts. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # The test case can be executed with the Bash Automated # Testing System tool available at https://github.com/sstephenson/bats diff --git a/qa/vagrant/src/test/resources/packaging/tests/module_and_plugin_test_cases.bash b/qa/vagrant/src/test/resources/packaging/tests/module_and_plugin_test_cases.bash index 64797a33f57cf..31b9ca4f3fd34 100644 --- a/qa/vagrant/src/test/resources/packaging/tests/module_and_plugin_test_cases.bash +++ b/qa/vagrant/src/test/resources/packaging/tests/module_and_plugin_test_cases.bash @@ -5,9 +5,14 @@ # rpm, and deb. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # The test case can be executed with the Bash Automated # Testing System tool available at https://github.com/sstephenson/bats diff --git a/qa/vagrant/src/test/resources/packaging/utils/modules.bash b/qa/vagrant/src/test/resources/packaging/utils/modules.bash index 2e80fd648f3be..9691fd7470199 100644 --- a/qa/vagrant/src/test/resources/packaging/utils/modules.bash +++ b/qa/vagrant/src/test/resources/packaging/utils/modules.bash @@ -4,9 +4,14 @@ # the .deb/.rpm packages and the SysV/Systemd scripts. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # Licensed to Elasticsearch under one or more contributor # license agreements. See the NOTICE file distributed with @@ -41,4 +46,4 @@ check_module() { check_secure_module() { check_module "$@" plugin-security.policy -} \ No newline at end of file +} diff --git a/qa/vagrant/src/test/resources/packaging/utils/packages.bash b/qa/vagrant/src/test/resources/packaging/utils/packages.bash index 700c1c661859e..dc1842c76f7ee 100644 --- a/qa/vagrant/src/test/resources/packaging/utils/packages.bash +++ b/qa/vagrant/src/test/resources/packaging/utils/packages.bash @@ -4,9 +4,14 @@ # the .deb/.rpm packages. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # Licensed to Elasticsearch under one or more contributor # license agreements. See the NOTICE file distributed with diff --git a/qa/vagrant/src/test/resources/packaging/utils/plugins.bash b/qa/vagrant/src/test/resources/packaging/utils/plugins.bash index 55e7fdfc48410..d6a4f389fd009 100644 --- a/qa/vagrant/src/test/resources/packaging/utils/plugins.bash +++ b/qa/vagrant/src/test/resources/packaging/utils/plugins.bash @@ -1,12 +1,17 @@ #!/bin/bash -# This file contains some utilities to test the elasticsearch scripts, -# the .deb/.rpm packages and the SysV/Systemd scripts. +# This file contains some utilities to test the elasticsearch +# plugin installation and uninstallation process. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # Licensed to Elasticsearch under one or more contributor # license agreements. See the NOTICE file distributed with diff --git a/qa/vagrant/src/test/resources/packaging/utils/tar.bash b/qa/vagrant/src/test/resources/packaging/utils/tar.bash index b5edebaf41ce9..5a34cb5b9fd8d 100644 --- a/qa/vagrant/src/test/resources/packaging/utils/tar.bash +++ b/qa/vagrant/src/test/resources/packaging/utils/tar.bash @@ -1,12 +1,17 @@ #!/bin/bash -# This file contains some utilities to test the elasticsearch scripts, -# the .deb/.rpm packages and the SysV/Systemd scripts. +# This file contains some utilities to test the elasticsearch +# tar distribution. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # Licensed to Elasticsearch under one or more contributor # license agreements. See the NOTICE file distributed with diff --git a/qa/vagrant/src/test/resources/packaging/utils/utils.bash b/qa/vagrant/src/test/resources/packaging/utils/utils.bash index 877f49b576d5d..bb0ebbf1814eb 100644 --- a/qa/vagrant/src/test/resources/packaging/utils/utils.bash +++ b/qa/vagrant/src/test/resources/packaging/utils/utils.bash @@ -4,9 +4,14 @@ # the .deb/.rpm packages and the SysV/Systemd scripts. # WARNING: This testing file must be executed as root and can -# dramatically change your system. It removes the 'elasticsearch' -# user/group and also many directories. Do not execute this file -# unless you know exactly what you are doing. +# dramatically change your system. It should only be executed +# in a throw-away VM like those made by the Vagrantfile at +# the root of the Elasticsearch source code. This should +# cause the script to fail if it is executed any other way: +[ -f /etc/is_vagrant_vm ] || { + >&2 echo "must be run on a vagrant VM" + exit 1 +} # Licensed to Elasticsearch under one or more contributor # license agreements. See the NOTICE file distributed with From db1b243343422b63f1ab01f2a4a6f7f0e95496ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 26 Apr 2017 19:15:48 +0200 Subject: [PATCH 085/619] InternalPercentilesBucket should not rely on ordered percents array (#24336) Currently InternalPercentilesBucket#percentile() relies on the percent array passed in to be in sorted order. This changes the aggregation to store an internal lookup table that is constructed from the percent/percentiles arrays passed in that can be used to look up the percentile values. Closes #24331 --- .../percentile/InternalPercentilesBucket.java | 32 ++++++- .../InternalPercentilesTestCase.java | 2 +- .../InternalPercentilesBucketTests.java | 92 +++++++++++++++++++ 3 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucketTests.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucket.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucket.java index 375011c4e8e18..a250769f685c3 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucket.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucket.java @@ -31,21 +31,35 @@ import java.io.IOException; import java.util.Arrays; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; public class InternalPercentilesBucket extends InternalNumericMetricsAggregation.MultiValue implements PercentilesBucket { private double[] percentiles; private double[] percents; + private final transient Map percentileLookups = new HashMap<>(); public InternalPercentilesBucket(String name, double[] percents, double[] percentiles, DocValueFormat formatter, List pipelineAggregators, Map metaData) { super(name, pipelineAggregators, metaData); + if ((percentiles.length == percents.length) == false) { + throw new IllegalArgumentException("The number of provided percents and percentiles didn't match. percents: " + + Arrays.toString(percents) + ", percentiles: " + Arrays.toString(percentiles)); + } this.format = formatter; this.percentiles = percentiles; this.percents = percents; + computeLookup(); + } + + private void computeLookup() { + for (int i = 0; i < percents.length; i++) { + percentileLookups.put(percents[i], percentiles[i]); + } } /** @@ -56,6 +70,7 @@ public InternalPercentilesBucket(StreamInput in) throws IOException { format = in.readNamedWriteable(DocValueFormat.class); percentiles = in.readDoubleArray(); percents = in.readDoubleArray(); + computeLookup(); } @Override @@ -72,12 +87,12 @@ public String getWriteableName() { @Override public double percentile(double percent) throws IllegalArgumentException { - int index = Arrays.binarySearch(percents, percent); - if (index < 0) { + Double percentile = percentileLookups.get(percent); + if (percentile == null) { throw new IllegalArgumentException("Percent requested [" + String.valueOf(percent) + "] was not" + " one of the computed percentiles. Available keys are: " + Arrays.toString(percents)); } - return percentiles[index]; + return percentile; } @Override @@ -116,6 +131,17 @@ public XContentBuilder doXContentBody(XContentBuilder builder, Params params) th return builder; } + @Override + protected boolean doEquals(Object obj) { + InternalPercentilesBucket that = (InternalPercentilesBucket) obj; + return Arrays.equals(percents, that.percents) && Arrays.equals(percentiles, that.percentiles); + } + + @Override + protected int doHashCode() { + return Objects.hash(Arrays.hashCode(percents), Arrays.hashCode(percentiles)); + } + public static class Iter implements Iterator { private final double[] percents; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesTestCase.java index eb26c792eb883..e94cf753205b0 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesTestCase.java @@ -50,7 +50,7 @@ protected T createTestInstance(String name, List pipelineAgg protected abstract T createTestInstance(String name, List pipelineAggregators, Map metaData, boolean keyed, DocValueFormat format, double[] percents, double[] values); - protected static double[] randomPercents() { + public static double[] randomPercents() { List randomCdfValues = randomSubsetOf(randomIntBetween(1, 7), 0.01d, 0.05d, 0.25d, 0.50d, 0.75d, 0.95d, 0.99d); double[] percents = new double[randomCdfValues.size()]; for (int i = 0; i < randomCdfValues.size(); i++) { diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucketTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucketTests.java new file mode 100644 index 0000000000000..0ea28f9838474 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucketTests.java @@ -0,0 +1,92 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.pipeline.bucketmetrics.percentile; + +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.search.aggregations.metrics.percentiles.InternalPercentilesTestCase.randomPercents; + +public class InternalPercentilesBucketTests extends InternalAggregationTestCase { + + @Override + protected InternalPercentilesBucket createTestInstance(String name, List pipelineAggregators, + Map metaData) { + return createTestInstance(name, pipelineAggregators, metaData, randomPercents()); + } + + private static InternalPercentilesBucket createTestInstance(String name, List pipelineAggregators, + Map metaData, double[] percents) { + DocValueFormat format = randomNumericDocValueFormat(); + final double[] percentiles = new double[percents.length]; + for (int i = 0; i < percents.length; ++i) { + percentiles[i] = frequently() ? randomDouble() : Double.NaN; + } + return new InternalPercentilesBucket(name, percents, percentiles, format, pipelineAggregators, metaData); + } + + @Override + public void testReduceRandom() { + expectThrows(UnsupportedOperationException.class, + () -> createTestInstance("name", Collections.emptyList(), null).reduce(null, null)); + } + + @Override + protected void assertReduced(InternalPercentilesBucket reduced, List inputs) { + // no test since reduce operation is unsupported + } + + @Override + protected Writeable.Reader instanceReader() { + return InternalPercentilesBucket::new; + } + + /** + * check that we don't rely on the percent array order and that the iterator returns the values in the original order + */ + public void testPercentOrder() { + final double[] percents = new double[]{ 0.50, 0.25, 0.01, 0.99, 0.60 }; + InternalPercentilesBucket aggregation = createTestInstance("test", Collections.emptyList(), Collections.emptyMap(), percents); + Iterator iterator = aggregation.iterator(); + for (double percent : percents) { + assertTrue(iterator.hasNext()); + Percentile percentile = iterator.next(); + assertEquals(percent, percentile.getPercent(), 0.0d); + assertEquals(aggregation.percentile(percent), percentile.getValue(), 0.0d); + } + } + + public void testErrorOnDifferentArgumentSize() { + final double[] percents = new double[]{ 0.1, 0.2, 0.3}; + final double[] percentiles = new double[]{ 0.10, 0.2}; + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new InternalPercentilesBucket("test", percents, + percentiles, DocValueFormat.RAW, Collections.emptyList(), Collections.emptyMap())); + assertEquals("The number of provided percents and percentiles didn't match. percents: [0.1, 0.2, 0.3], percentiles: [0.1, 0.2]", + e.getMessage()); + } +} From 7c3efb829bf73625a8a63b3a122db0b067360ed1 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 26 Apr 2017 13:25:34 -0400 Subject: [PATCH 086/619] Move char filters into analysis-common (#24261) Another step down the road to dropping the lucene-analyzers-common dependency from core. Note that this removes some tests that no longer compile from core. I played around with adding them to the analysis-common module where they would compile but we already test these in the tests generated from the example usage in the documentation. I'm not super happy with the way that `requriesAnalysisSettings` works with regards to plugins. I think it'd be fairly bug-prone for plugin authors to use. But I'm making it visible as is for now and I'll rethink later. A part of #23658 --- .../index/analysis/CharFilterFactory.java | 2 +- .../indices/analysis/AnalysisModule.java | 21 +- .../elasticsearch/plugins/AnalysisPlugin.java | 33 +++- .../index/analysis/AnalysisRegistryTests.java | 2 - .../index/analysis/CharFilterTests.java | 86 -------- .../index/analysis/CustomNormalizerTests.java | 52 ++++- .../indices/analysis/AnalysisModuleTests.java | 29 +-- .../indices/analyze/AnalyzeActionIT.java | 183 ------------------ .../elasticsearch/index/analysis/test1.json | 28 --- .../elasticsearch/index/analysis/test1.yml | 21 -- .../analysis/common/CommonAnalysisPlugin.java | 14 ++ .../common}/MappingCharFilterFactory.java | 5 +- .../PatternReplaceCharFilterFactory.java | 4 +- .../analysis/HtmlStripCharFilterFactory.java | 0 .../common/CommonAnalysisFactoryTests.java | 11 +- .../test/analysis-common/20_analyzers.yaml | 18 ++ .../analysis-common/40_token_filters.yaml | 54 ++++++ .../test/analysis-common/50_char_filters.yaml | 48 ++++- .../AnalysisFactoryTestCase.java | 9 +- 19 files changed, 241 insertions(+), 379 deletions(-) delete mode 100644 core/src/test/java/org/elasticsearch/index/analysis/CharFilterTests.java rename {core/src/main/java/org/elasticsearch/index/analysis => modules/analysis-common/src/main/java/org/elasticsearch/analysis/common}/MappingCharFilterFactory.java (95%) rename {core/src/main/java/org/elasticsearch/index/analysis => modules/analysis-common/src/main/java/org/elasticsearch/analysis/common}/PatternReplaceCharFilterFactory.java (92%) rename {core => modules/analysis-common}/src/main/java/org/elasticsearch/index/analysis/HtmlStripCharFilterFactory.java (100%) diff --git a/core/src/main/java/org/elasticsearch/index/analysis/CharFilterFactory.java b/core/src/main/java/org/elasticsearch/index/analysis/CharFilterFactory.java index 68692c89469bd..6f85615c95e1b 100644 --- a/core/src/main/java/org/elasticsearch/index/analysis/CharFilterFactory.java +++ b/core/src/main/java/org/elasticsearch/index/analysis/CharFilterFactory.java @@ -25,5 +25,5 @@ public interface CharFilterFactory { String name(); - Reader create(Reader tokenStream); + Reader create(Reader reader); } diff --git a/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java b/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java index c494c4cae9c35..26a4e4c1c5c3a 100644 --- a/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java +++ b/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java @@ -69,7 +69,6 @@ import org.elasticsearch.index.analysis.GreekAnalyzerProvider; import org.elasticsearch.index.analysis.HindiAnalyzerProvider; import org.elasticsearch.index.analysis.HindiNormalizationFilterFactory; -import org.elasticsearch.index.analysis.HtmlStripCharFilterFactory; import org.elasticsearch.index.analysis.HungarianAnalyzerProvider; import org.elasticsearch.index.analysis.HunspellTokenFilterFactory; import org.elasticsearch.index.analysis.IndicNormalizationFilterFactory; @@ -89,7 +88,6 @@ import org.elasticsearch.index.analysis.LithuanianAnalyzerProvider; import org.elasticsearch.index.analysis.LowerCaseTokenFilterFactory; import org.elasticsearch.index.analysis.LowerCaseTokenizerFactory; -import org.elasticsearch.index.analysis.MappingCharFilterFactory; import org.elasticsearch.index.analysis.MinHashTokenFilterFactory; import org.elasticsearch.index.analysis.NGramTokenFilterFactory; import org.elasticsearch.index.analysis.NGramTokenizerFactory; @@ -97,7 +95,6 @@ import org.elasticsearch.index.analysis.PathHierarchyTokenizerFactory; import org.elasticsearch.index.analysis.PatternAnalyzerProvider; import org.elasticsearch.index.analysis.PatternCaptureGroupTokenFilterFactory; -import org.elasticsearch.index.analysis.PatternReplaceCharFilterFactory; import org.elasticsearch.index.analysis.PatternReplaceTokenFilterFactory; import org.elasticsearch.index.analysis.PatternTokenizerFactory; import org.elasticsearch.index.analysis.PersianAnalyzerProvider; @@ -146,6 +143,8 @@ import java.io.IOException; import java.util.List; +import static org.elasticsearch.plugins.AnalysisPlugin.requriesAnalysisSettings; + /** * Sets up {@link AnalysisRegistry}. */ @@ -184,9 +183,6 @@ public AnalysisRegistry getAnalysisRegistry() { private NamedRegistry> setupCharFilters(List plugins) { NamedRegistry> charFilters = new NamedRegistry<>("char_filter"); - charFilters.register("html_strip", HtmlStripCharFilterFactory::new); - charFilters.register("pattern_replace", requriesAnalysisSettings(PatternReplaceCharFilterFactory::new)); - charFilters.register("mapping", requriesAnalysisSettings(MappingCharFilterFactory::new)); charFilters.extractAndRegister(plugins, AnalysisPlugin::getCharFilters); return charFilters; } @@ -340,19 +336,6 @@ private NamedRegistry>> setupNormalizers(Li return normalizers; } - private static AnalysisModule.AnalysisProvider requriesAnalysisSettings(AnalysisModule.AnalysisProvider provider) { - return new AnalysisModule.AnalysisProvider() { - @Override - public T get(IndexSettings indexSettings, Environment environment, String name, Settings settings) throws IOException { - return provider.get(indexSettings, environment, name, settings); - } - - @Override - public boolean requiresAnalysisSettings() { - return true; - } - }; - } /** * The basic factory interface for analysis components. diff --git a/core/src/main/java/org/elasticsearch/plugins/AnalysisPlugin.java b/core/src/main/java/org/elasticsearch/plugins/AnalysisPlugin.java index 8c23e530e4998..5e7e1053add58 100644 --- a/core/src/main/java/org/elasticsearch/plugins/AnalysisPlugin.java +++ b/core/src/main/java/org/elasticsearch/plugins/AnalysisPlugin.java @@ -23,12 +23,16 @@ import org.apache.lucene.analysis.CharFilter; import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.Tokenizer; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.analysis.AnalyzerProvider; import org.elasticsearch.index.analysis.CharFilterFactory; import org.elasticsearch.index.analysis.TokenFilterFactory; import org.elasticsearch.index.analysis.TokenizerFactory; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; +import java.io.IOException; import java.util.Map; import static java.util.Collections.emptyMap; @@ -52,28 +56,32 @@ */ public interface AnalysisPlugin { /** - * Override to add additional {@link CharFilter}s. + * Override to add additional {@link CharFilter}s. See {@link #requriesAnalysisSettings(AnalysisProvider)} + * how to on get the configuration from the index. */ default Map> getCharFilters() { return emptyMap(); } /** - * Override to add additional {@link TokenFilter}s. + * Override to add additional {@link TokenFilter}s. See {@link #requriesAnalysisSettings(AnalysisProvider)} + * how to on get the configuration from the index. */ default Map> getTokenFilters() { return emptyMap(); } /** - * Override to add additional {@link Tokenizer}s. + * Override to add additional {@link Tokenizer}s. See {@link #requriesAnalysisSettings(AnalysisProvider)} + * how to on get the configuration from the index. */ default Map> getTokenizers() { return emptyMap(); } /** - * Override to add additional {@link Analyzer}s. + * Override to add additional {@link Analyzer}s. See {@link #requriesAnalysisSettings(AnalysisProvider)} + * how to on get the configuration from the index. */ default Map>> getAnalyzers() { return emptyMap(); @@ -85,4 +93,21 @@ default Map>> getA default Map getHunspellDictionaries() { return emptyMap(); } + + /** + * Mark an {@link AnalysisProvider} as requiring the index's settings. + */ + static AnalysisProvider requriesAnalysisSettings(AnalysisProvider provider) { + return new AnalysisProvider() { + @Override + public T get(IndexSettings indexSettings, Environment environment, String name, Settings settings) throws IOException { + return provider.get(indexSettings, environment, name, settings); + } + + @Override + public boolean requiresAnalysisSettings() { + return true; + } + }; + } } diff --git a/core/src/test/java/org/elasticsearch/index/analysis/AnalysisRegistryTests.java b/core/src/test/java/org/elasticsearch/index/analysis/AnalysisRegistryTests.java index 0edd2fbe2c08f..6033186c81289 100644 --- a/core/src/test/java/org/elasticsearch/index/analysis/AnalysisRegistryTests.java +++ b/core/src/test/java/org/elasticsearch/index/analysis/AnalysisRegistryTests.java @@ -40,8 +40,6 @@ import org.elasticsearch.test.VersionUtils; import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; import java.util.Map; import static java.util.Collections.emptyMap; diff --git a/core/src/test/java/org/elasticsearch/index/analysis/CharFilterTests.java b/core/src/test/java/org/elasticsearch/index/analysis/CharFilterTests.java deleted file mode 100644 index f993cc1490c3b..0000000000000 --- a/core/src/test/java/org/elasticsearch/index/analysis/CharFilterTests.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -package org.elasticsearch.index.analysis; - -import org.elasticsearch.Version; -import org.elasticsearch.cluster.metadata.IndexMetaData; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.index.IndexSettings; -import org.elasticsearch.test.ESTokenStreamTestCase; -import org.elasticsearch.test.IndexSettingsModule; - -import static org.elasticsearch.test.ESTestCase.createTestAnalysis; - -public class CharFilterTests extends ESTokenStreamTestCase { - public void testMappingCharFilter() throws Exception { - Settings settings = Settings.builder() - .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) - .put("index.analysis.char_filter.my_mapping.type", "mapping") - .putArray("index.analysis.char_filter.my_mapping.mappings", "ph=>f", "qu=>q") - .put("index.analysis.analyzer.custom_with_char_filter.tokenizer", "standard") - .putArray("index.analysis.analyzer.custom_with_char_filter.char_filter", "my_mapping") - .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) - .build(); - IndexSettings idxSettings = IndexSettingsModule.newIndexSettings("test", settings); - IndexAnalyzers indexAnalyzers = createTestAnalysis(idxSettings, settings).indexAnalyzers; - NamedAnalyzer analyzer1 = indexAnalyzers.get("custom_with_char_filter"); - - assertTokenStreamContents(analyzer1.tokenStream("test", "jeff quit phish"), new String[]{"jeff", "qit", "fish"}); - - // Repeat one more time to make sure that char filter is reinitialized correctly - assertTokenStreamContents(analyzer1.tokenStream("test", "jeff quit phish"), new String[]{"jeff", "qit", "fish"}); - } - - public void testHtmlStripCharFilter() throws Exception { - Settings settings = Settings.builder() - .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) - .put("index.analysis.analyzer.custom_with_char_filter.tokenizer", "standard") - .putArray("index.analysis.analyzer.custom_with_char_filter.char_filter", "html_strip") - .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) - .build(); - IndexSettings idxSettings = IndexSettingsModule.newIndexSettings("test", settings); - - IndexAnalyzers indexAnalyzers = createTestAnalysis(idxSettings, settings).indexAnalyzers; - NamedAnalyzer analyzer1 = indexAnalyzers.get("custom_with_char_filter"); - - assertTokenStreamContents(analyzer1.tokenStream("test", "hello!"), new String[]{"hello"}); - - // Repeat one more time to make sure that char filter is reinitialized correctly - assertTokenStreamContents(analyzer1.tokenStream("test", "hello!"), new String[]{"hello"}); - } - - public void testPatternReplaceCharFilter() throws Exception { - Settings settings = Settings.builder() - .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) - .put("index.analysis.char_filter.my_mapping.type", "pattern_replace") - .put("index.analysis.char_filter.my_mapping.pattern", "ab*") - .put("index.analysis.char_filter.my_mapping.replacement", "oo") - .put("index.analysis.char_filter.my_mapping.flags", "CASE_INSENSITIVE") - .put("index.analysis.analyzer.custom_with_char_filter.tokenizer", "standard") - .putArray("index.analysis.analyzer.custom_with_char_filter.char_filter", "my_mapping") - .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) - .build(); - IndexSettings idxSettings = IndexSettingsModule.newIndexSettings("test", settings); - IndexAnalyzers indexAnalyzers = createTestAnalysis(idxSettings, settings).indexAnalyzers; - NamedAnalyzer analyzer1 = indexAnalyzers.get("custom_with_char_filter"); - - assertTokenStreamContents(analyzer1.tokenStream("test", "faBBbBB aBbbbBf"), new String[]{"foo", "oof"}); - } -} diff --git a/core/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java b/core/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java index 3e71a60973724..c6b5806099699 100644 --- a/core/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java +++ b/core/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java @@ -22,13 +22,18 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; +import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; +import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTokenStreamTestCase; import java.io.IOException; +import java.io.Reader; +import java.util.Map; -public class CustomNormalizerTests extends ESTokenStreamTestCase { +import static java.util.Collections.singletonMap; +public class CustomNormalizerTests extends ESTokenStreamTestCase { public void testBasics() throws IOException { Settings settings = Settings.builder() .putArray("index.analysis.normalizer.my_normalizer.filter", "lowercase", "asciifolding") @@ -66,12 +71,11 @@ public void testTokenizer() throws IOException { public void testCharFilters() throws IOException { Settings settings = Settings.builder() - .put("index.analysis.char_filter.my_mapping.type", "mapping") - .putArray("index.analysis.char_filter.my_mapping.mappings", "a => z") + .put("index.analysis.char_filter.my_mapping.type", "mock_char_filter") .putArray("index.analysis.normalizer.my_normalizer.char_filter", "my_mapping") .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) .build(); - ESTestCase.TestAnalysis analysis = AnalysisTestsHelper.createTestAnalysisFromSettings(settings); + ESTestCase.TestAnalysis analysis = AnalysisTestsHelper.createTestAnalysisFromSettings(settings, new MockCharFilterPlugin()); assertNull(analysis.indexAnalyzers.get("my_normalizer")); NamedAnalyzer normalizer = analysis.indexAnalyzers.getNormalizer("my_normalizer"); assertNotNull(normalizer); @@ -99,4 +103,44 @@ public void testIllegalCharFilters() throws IOException { () -> AnalysisTestsHelper.createTestAnalysisFromSettings(settings)); assertEquals("Custom normalizer [my_normalizer] may not use char filter [html_strip]", e.getMessage()); } + + private class MockCharFilterPlugin implements AnalysisPlugin { + @Override + public Map> getCharFilters() { + return singletonMap("mock_char_filter", (indexSettings, env, name, settings) -> { + class Factory implements CharFilterFactory, MultiTermAwareComponent { + @Override + public String name() { + return name; + } + @Override + public Reader create(Reader reader) { + return new Reader() { + + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + int result = reader.read(cbuf, off, len); + for (int i = off; i < result; i++) { + if (cbuf[i] == 'a') { + cbuf[i] = 'z'; + } + } + return result; + } + + @Override + public void close() throws IOException { + reader.close(); + } + }; + } + @Override + public Object getMultiTermComponent() { + return this; + } + } + return new Factory(); + }); + } + } } diff --git a/core/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java b/core/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java index 960d135371c07..3d479ca2da283 100644 --- a/core/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java +++ b/core/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java @@ -32,7 +32,6 @@ import org.apache.lucene.store.SimpleFSDirectory; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetaData; -import org.elasticsearch.common.inject.ModuleTestCase; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.env.Environment; @@ -40,17 +39,17 @@ import org.elasticsearch.index.analysis.Analysis; import org.elasticsearch.index.analysis.AnalysisRegistry; import org.elasticsearch.index.analysis.AnalysisTestsHelper; +import org.elasticsearch.index.analysis.CharFilterFactory; import org.elasticsearch.index.analysis.CustomAnalyzer; import org.elasticsearch.index.analysis.IndexAnalyzers; -import org.elasticsearch.index.analysis.MappingCharFilterFactory; import org.elasticsearch.index.analysis.NamedAnalyzer; -import org.elasticsearch.index.analysis.PatternReplaceCharFilterFactory; import org.elasticsearch.index.analysis.StandardTokenizerFactory; import org.elasticsearch.index.analysis.StopTokenFilterFactory; import org.elasticsearch.index.analysis.TokenFilterFactory; import org.elasticsearch.index.analysis.filter1.MyFilterTokenFilterFactory; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; import org.elasticsearch.plugins.AnalysisPlugin; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.IndexSettingsModule; import org.elasticsearch.test.VersionUtils; import org.hamcrest.MatcherAssert; @@ -72,7 +71,7 @@ import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; -public class AnalysisModuleTests extends ModuleTestCase { +public class AnalysisModuleTests extends ESTestCase { public IndexAnalyzers getIndexAnalyzers(Settings settings) throws IOException { return getIndexAnalyzers(getNewRegistry(settings), settings); @@ -90,6 +89,11 @@ public AnalysisRegistry getNewRegistry(Settings settings) { public Map> getTokenFilters() { return singletonMap("myfilter", MyFilterTokenFilterFactory::new); } + + @Override + public Map> getCharFilters() { + return AnalysisPlugin.super.getCharFilters(); + } })).getAnalysisRegistry(); } catch (IOException e) { throw new RuntimeException(e); @@ -184,29 +188,12 @@ private void testSimpleConfiguration(Settings settings) throws IOException { StopTokenFilterFactory stop1 = (StopTokenFilterFactory) custom1.tokenFilters()[0]; assertThat(stop1.stopWords().size(), equalTo(1)); - analyzer = indexAnalyzers.get("custom2").analyzer(); - assertThat(analyzer, instanceOf(CustomAnalyzer.class)); - // verify position increment gap analyzer = indexAnalyzers.get("custom6").analyzer(); assertThat(analyzer, instanceOf(CustomAnalyzer.class)); CustomAnalyzer custom6 = (CustomAnalyzer) analyzer; assertThat(custom6.getPositionIncrementGap("any_string"), equalTo(256)); - // verify characters mapping - analyzer = indexAnalyzers.get("custom5").analyzer(); - assertThat(analyzer, instanceOf(CustomAnalyzer.class)); - CustomAnalyzer custom5 = (CustomAnalyzer) analyzer; - assertThat(custom5.charFilters()[0], instanceOf(MappingCharFilterFactory.class)); - - // check custom pattern replace filter - analyzer = indexAnalyzers.get("custom3").analyzer(); - assertThat(analyzer, instanceOf(CustomAnalyzer.class)); - CustomAnalyzer custom3 = (CustomAnalyzer) analyzer; - PatternReplaceCharFilterFactory patternReplaceCharFilterFactory = (PatternReplaceCharFilterFactory) custom3.charFilters()[0]; - assertThat(patternReplaceCharFilterFactory.getPattern().pattern(), equalTo("sample(.*)")); - assertThat(patternReplaceCharFilterFactory.getReplacement(), equalTo("replacedSample $1")); - // check custom class name (my) analyzer = indexAnalyzers.get("custom4").analyzer(); assertThat(analyzer, instanceOf(CustomAnalyzer.class)); diff --git a/core/src/test/java/org/elasticsearch/indices/analyze/AnalyzeActionIT.java b/core/src/test/java/org/elasticsearch/indices/analyze/AnalyzeActionIT.java index 4f9b13d91cee6..ebfeb5f92d147 100644 --- a/core/src/test/java/org/elasticsearch/indices/analyze/AnalyzeActionIT.java +++ b/core/src/test/java/org/elasticsearch/indices/analyze/AnalyzeActionIT.java @@ -111,36 +111,6 @@ public void testAnalyzeWithNoIndex() throws Exception { assertThat(analyzeResponse.getTokens().get(0).getPositionLength(), equalTo(1)); } - public void testAnalyzeWithCharFilters() throws Exception { - assertAcked(prepareCreate("test").addAlias(new Alias("alias")) - .setSettings(Settings.builder().put(indexSettings()) - .put("index.analysis.char_filter.custom_mapping.type", "mapping") - .putArray("index.analysis.char_filter.custom_mapping.mappings", "ph=>f", "qu=>q") - .put("index.analysis.analyzer.custom_with_char_filter.tokenizer", "standard") - .putArray("index.analysis.analyzer.custom_with_char_filter.char_filter", "custom_mapping"))); - ensureGreen(); - - AnalyzeResponse analyzeResponse = client().admin().indices().prepareAnalyze("

THIS IS A

TEST").setTokenizer("standard").addCharFilter("html_strip").get(); - assertThat(analyzeResponse.getTokens().size(), equalTo(4)); - - analyzeResponse = client().admin().indices().prepareAnalyze("THIS IS A TEST").setTokenizer("keyword").addTokenFilter("lowercase").addCharFilter("html_strip").get(); - assertThat(analyzeResponse.getTokens().size(), equalTo(1)); - assertThat(analyzeResponse.getTokens().get(0).getTerm(), equalTo("this is a test")); - - analyzeResponse = client().admin().indices().prepareAnalyze(indexOrAlias(), "jeff quit phish").setTokenizer("keyword").addTokenFilter("lowercase").addCharFilter("custom_mapping").get(); - assertThat(analyzeResponse.getTokens().size(), equalTo(1)); - assertThat(analyzeResponse.getTokens().get(0).getTerm(), equalTo("jeff qit fish")); - - analyzeResponse = client().admin().indices().prepareAnalyze(indexOrAlias(), "jeff quit fish").setTokenizer("standard").addCharFilter("html_strip").addCharFilter("custom_mapping").get(); - assertThat(analyzeResponse.getTokens().size(), equalTo(3)); - AnalyzeResponse.AnalyzeToken token = analyzeResponse.getTokens().get(0); - assertThat(token.getTerm(), equalTo("jeff")); - token = analyzeResponse.getTokens().get(1); - assertThat(token.getTerm(), equalTo("qit")); - token = analyzeResponse.getTokens().get(2); - assertThat(token.getTerm(), equalTo("fish")); - } - public void testAnalyzeWithNonDefaultPostionLength() throws Exception { assertAcked(prepareCreate("test").addAlias(new Alias("alias")) .setSettings(Settings.builder().put(indexSettings()) @@ -263,46 +233,6 @@ public void testAnalyzerWithMultiValues() throws Exception { assertThat(token.getPositionLength(), equalTo(1)); } - public void testDetailAnalyze() throws Exception { - assertAcked(prepareCreate("test").addAlias(new Alias("alias")) - .setSettings( - Settings.builder() - .put("index.analysis.char_filter.my_mapping.type", "mapping") - .putArray("index.analysis.char_filter.my_mapping.mappings", "PH=>F") - .put("index.analysis.analyzer.test_analyzer.type", "custom") - .put("index.analysis.analyzer.test_analyzer.position_increment_gap", "100") - .put("index.analysis.analyzer.test_analyzer.tokenizer", "standard") - .putArray("index.analysis.analyzer.test_analyzer.char_filter", "my_mapping") - .putArray("index.analysis.analyzer.test_analyzer.filter", "snowball"))); - ensureGreen(); - - for (int i = 0; i < 10; i++) { - AnalyzeResponse analyzeResponse = admin().indices().prepareAnalyze().setIndex(indexOrAlias()).setText("THIS IS A PHISH") - .setExplain(true).addCharFilter("my_mapping").setTokenizer("keyword").addTokenFilter("lowercase").get(); - - assertThat(analyzeResponse.detail().analyzer(), IsNull.nullValue()); - //charfilters - assertThat(analyzeResponse.detail().charfilters().length, equalTo(1)); - assertThat(analyzeResponse.detail().charfilters()[0].getName(), equalTo("my_mapping")); - assertThat(analyzeResponse.detail().charfilters()[0].getTexts().length, equalTo(1)); - assertThat(analyzeResponse.detail().charfilters()[0].getTexts()[0], equalTo("THIS IS A FISH")); - //tokenizer - assertThat(analyzeResponse.detail().tokenizer().getName(), equalTo("keyword")); - assertThat(analyzeResponse.detail().tokenizer().getTokens().length, equalTo(1)); - assertThat(analyzeResponse.detail().tokenizer().getTokens()[0].getTerm(), equalTo("THIS IS A FISH")); - assertThat(analyzeResponse.detail().tokenizer().getTokens()[0].getStartOffset(), equalTo(0)); - assertThat(analyzeResponse.detail().tokenizer().getTokens()[0].getEndOffset(), equalTo(15)); - //tokenfilters - assertThat(analyzeResponse.detail().tokenfilters().length, equalTo(1)); - assertThat(analyzeResponse.detail().tokenfilters()[0].getName(), equalTo("lowercase")); - assertThat(analyzeResponse.detail().tokenfilters()[0].getTokens().length, equalTo(1)); - assertThat(analyzeResponse.detail().tokenfilters()[0].getTokens()[0].getTerm(), equalTo("this is a fish")); - assertThat(analyzeResponse.detail().tokenfilters()[0].getTokens()[0].getPosition(), equalTo(0)); - assertThat(analyzeResponse.detail().tokenfilters()[0].getTokens()[0].getStartOffset(), equalTo(0)); - assertThat(analyzeResponse.detail().tokenfilters()[0].getTokens()[0].getEndOffset(), equalTo(15)); - } - } - public void testDetailAnalyzeWithNoIndex() throws Exception { //analyzer only AnalyzeResponse analyzeResponse = client().admin().indices().prepareAnalyze("THIS IS A TEST") @@ -414,90 +344,6 @@ public void testDetailAnalyzeWithMultiValues() throws Exception { assertThat(token.getPositionLength(), equalTo(1)); } - public void testDetailAnalyzeWithMultiValuesWithCustomAnalyzer() throws Exception { - assertAcked(prepareCreate("test").addAlias(new Alias("alias")) - .setSettings( - Settings.builder() - .put("index.analysis.char_filter.my_mapping.type", "mapping") - .putArray("index.analysis.char_filter.my_mapping.mappings", "PH=>F") - .put("index.analysis.analyzer.test_analyzer.type", "custom") - .put("index.analysis.analyzer.test_analyzer.position_increment_gap", "100") - .put("index.analysis.analyzer.test_analyzer.tokenizer", "standard") - .putArray("index.analysis.analyzer.test_analyzer.char_filter", "my_mapping") - .putArray("index.analysis.analyzer.test_analyzer.filter", "snowball", "lowercase"))); - ensureGreen(); - - client().admin().indices().preparePutMapping("test") - .setType("document").setSource("simple", "type=text,analyzer=simple,position_increment_gap=100").get(); - - //only analyzer = - String[] texts = new String[]{"this is a PHISH", "the troubled text"}; - AnalyzeResponse analyzeResponse = client().admin().indices().prepareAnalyze().setIndex(indexOrAlias()).setText(texts) - .setExplain(true).setAnalyzer("test_analyzer").setText(texts).execute().get(); - - // charfilter - assertThat(analyzeResponse.detail().charfilters().length, equalTo(1)); - assertThat(analyzeResponse.detail().charfilters()[0].getName(), equalTo("my_mapping")); - assertThat(analyzeResponse.detail().charfilters()[0].getTexts().length, equalTo(2)); - assertThat(analyzeResponse.detail().charfilters()[0].getTexts()[0], equalTo("this is a FISH")); - assertThat(analyzeResponse.detail().charfilters()[0].getTexts()[1], equalTo("the troubled text")); - - // tokenizer - assertThat(analyzeResponse.detail().tokenizer().getName(), equalTo("standard")); - assertThat(analyzeResponse.detail().tokenizer().getTokens().length, equalTo(7)); - AnalyzeResponse.AnalyzeToken token = analyzeResponse.detail().tokenizer().getTokens()[3]; - - assertThat(token.getTerm(), equalTo("FISH")); - assertThat(token.getPosition(), equalTo(3)); - assertThat(token.getStartOffset(), equalTo(10)); - assertThat(token.getEndOffset(), equalTo(15)); - assertThat(token.getPositionLength(), equalTo(1)); - - token = analyzeResponse.detail().tokenizer().getTokens()[5]; - assertThat(token.getTerm(), equalTo("troubled")); - assertThat(token.getPosition(), equalTo(105)); - assertThat(token.getStartOffset(), equalTo(20)); - assertThat(token.getEndOffset(), equalTo(28)); - assertThat(token.getPositionLength(), equalTo(1)); - - // tokenfilter(snowball) - assertThat(analyzeResponse.detail().tokenfilters().length, equalTo(2)); - assertThat(analyzeResponse.detail().tokenfilters()[0].getName(), equalTo("snowball")); - assertThat(analyzeResponse.detail().tokenfilters()[0].getTokens().length, equalTo(7)); - token = analyzeResponse.detail().tokenfilters()[0].getTokens()[3]; - - assertThat(token.getTerm(), equalTo("FISH")); - assertThat(token.getPosition(), equalTo(3)); - assertThat(token.getStartOffset(), equalTo(10)); - assertThat(token.getEndOffset(), equalTo(15)); - assertThat(token.getPositionLength(), equalTo(1)); - - token = analyzeResponse.detail().tokenfilters()[0].getTokens()[5]; - assertThat(token.getTerm(), equalTo("troubl")); - assertThat(token.getPosition(), equalTo(105)); - assertThat(token.getStartOffset(), equalTo(20)); - assertThat(token.getEndOffset(), equalTo(28)); - assertThat(token.getPositionLength(), equalTo(1)); - - // tokenfilter(lowercase) - assertThat(analyzeResponse.detail().tokenfilters()[1].getName(), equalTo("lowercase")); - assertThat(analyzeResponse.detail().tokenfilters()[1].getTokens().length, equalTo(7)); - token = analyzeResponse.detail().tokenfilters()[1].getTokens()[3]; - - assertThat(token.getTerm(), equalTo("fish")); - assertThat(token.getPosition(), equalTo(3)); - assertThat(token.getStartOffset(), equalTo(10)); - assertThat(token.getEndOffset(), equalTo(15)); - assertThat(token.getPositionLength(), equalTo(1)); - - token = analyzeResponse.detail().tokenfilters()[0].getTokens()[5]; - assertThat(token.getTerm(), equalTo("troubl")); - assertThat(token.getPosition(), equalTo(105)); - assertThat(token.getStartOffset(), equalTo(20)); - assertThat(token.getEndOffset(), equalTo(28)); - assertThat(token.getPositionLength(), equalTo(1)); - } - public void testNonExistTokenizer() { IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> client().admin().indices() @@ -575,35 +421,6 @@ public void testCustomTokenFilterInRequest() throws Exception { assertThat(analyzeResponse.detail().tokenfilters()[1].getTokens()[0].getPositionLength(), equalTo(1)); } - - public void testCustomCharFilterInRequest() throws Exception { - Map charFilterSettings = new HashMap<>(); - charFilterSettings.put("type", "mapping"); - charFilterSettings.put("mappings", new String[]{"ph => f", "qu => q"}); - AnalyzeResponse analyzeResponse = client().admin().indices() - .prepareAnalyze() - .setText("jeff quit phish") - .setTokenizer("keyword") - .addCharFilter(charFilterSettings) - .setExplain(true) - .get(); - - assertThat(analyzeResponse.detail().analyzer(), IsNull.nullValue()); - //charfilters - assertThat(analyzeResponse.detail().charfilters().length, equalTo(1)); - assertThat(analyzeResponse.detail().charfilters()[0].getName(), equalTo("_anonymous_charfilter_[0]")); - assertThat(analyzeResponse.detail().charfilters()[0].getTexts().length, equalTo(1)); - assertThat(analyzeResponse.detail().charfilters()[0].getTexts()[0], equalTo("jeff qit fish")); - //tokenizer - assertThat(analyzeResponse.detail().tokenizer().getName(), equalTo("keyword")); - assertThat(analyzeResponse.detail().tokenizer().getTokens().length, equalTo(1)); - assertThat(analyzeResponse.detail().tokenizer().getTokens()[0].getTerm(), equalTo("jeff qit fish")); - assertThat(analyzeResponse.detail().tokenizer().getTokens()[0].getStartOffset(), equalTo(0)); - assertThat(analyzeResponse.detail().tokenizer().getTokens()[0].getEndOffset(), equalTo(15)); - assertThat(analyzeResponse.detail().tokenizer().getTokens()[0].getPositionLength(), equalTo(1)); - } - - public void testCustomTokenizerInRequest() throws Exception { Map tokenizerSettings = new HashMap<>(); tokenizerSettings.put("type", "nGram"); diff --git a/core/src/test/resources/org/elasticsearch/index/analysis/test1.json b/core/src/test/resources/org/elasticsearch/index/analysis/test1.json index 2244ea4361c03..38937a9b5af93 100644 --- a/core/src/test/resources/org/elasticsearch/index/analysis/test1.json +++ b/core/src/test/resources/org/elasticsearch/index/analysis/test1.json @@ -6,22 +6,6 @@ "type":"standard" } }, - "char_filter":{ - "my_html":{ - "type":"html_strip", - "escaped_tags":["xxx", "yyy"], - "read_ahead":1024 - }, - "my_pattern":{ - "type":"pattern_replace", - "pattern":"sample(.*)", - "replacement":"replacedSample $1" - }, - "my_mapping":{ - "type":"mapping", - "mappings":["ph=>f", "qu=>q"] - } - }, "filter":{ "stop":{ "type":"stop", @@ -48,22 +32,10 @@ "tokenizer":"standard", "filter":["stop", "stop2"] }, - "custom2":{ - "tokenizer":"standard", - "char_filter":["html_strip", "my_html"] - }, - "custom3":{ - "tokenizer":"standard", - "char_filter":["my_pattern"] - }, "custom4":{ "tokenizer":"standard", "filter":["my"] }, - "custom5":{ - "tokenizer":"standard", - "char_filter":["my_mapping"] - }, "custom6":{ "tokenizer":"standard", "position_increment_gap": 256 diff --git a/core/src/test/resources/org/elasticsearch/index/analysis/test1.yml b/core/src/test/resources/org/elasticsearch/index/analysis/test1.yml index afcdb9a88ef7f..f7a57d14dbe3d 100644 --- a/core/src/test/resources/org/elasticsearch/index/analysis/test1.yml +++ b/core/src/test/resources/org/elasticsearch/index/analysis/test1.yml @@ -3,18 +3,6 @@ index : tokenizer : standard : type : standard - char_filter : - my_html : - type : html_strip - escaped_tags : [xxx, yyy] - read_ahead : 1024 - my_pattern : - type: pattern_replace - pattern: sample(.*) - replacement: replacedSample $1 - my_mapping : - type : mapping - mappings : [ph=>f, qu=>q] filter : stop : type : stop @@ -34,18 +22,9 @@ index : custom1 : tokenizer : standard filter : [stop, stop2] - custom2 : - tokenizer : standard - char_filter : [html_strip, my_html] - custom3 : - tokenizer : standard - char_filter : [my_pattern] custom4 : tokenizer : standard filter : [my] - custom5 : - tokenizer : standard - char_filter : [my_mapping] custom6 : tokenizer : standard position_increment_gap: 256 diff --git a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java index bfd1bbdcc97b8..e17df4b44631e 100644 --- a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java +++ b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java @@ -19,6 +19,8 @@ package org.elasticsearch.analysis.common; +import org.elasticsearch.index.analysis.CharFilterFactory; +import org.elasticsearch.index.analysis.HtmlStripCharFilterFactory; import org.elasticsearch.index.analysis.TokenFilterFactory; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; import org.elasticsearch.plugins.AnalysisPlugin; @@ -26,6 +28,9 @@ import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; + +import static org.elasticsearch.plugins.AnalysisPlugin.requriesAnalysisSettings; public class CommonAnalysisPlugin extends Plugin implements AnalysisPlugin { @Override @@ -36,4 +41,13 @@ public Map> getTokenFilters() { filters.put("word_delimiter_graph", WordDelimiterGraphTokenFilterFactory::new); return filters; } + + @Override + public Map> getCharFilters() { + Map> filters = new TreeMap<>(); + filters.put("html_strip", HtmlStripCharFilterFactory::new); + filters.put("pattern_replace", requriesAnalysisSettings(PatternReplaceCharFilterFactory::new)); + filters.put("mapping", requriesAnalysisSettings(MappingCharFilterFactory::new)); + return filters; + } } diff --git a/core/src/main/java/org/elasticsearch/index/analysis/MappingCharFilterFactory.java b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/MappingCharFilterFactory.java similarity index 95% rename from core/src/main/java/org/elasticsearch/index/analysis/MappingCharFilterFactory.java rename to modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/MappingCharFilterFactory.java index c5a4e4bbdccdf..a4ba9ece547d8 100644 --- a/core/src/main/java/org/elasticsearch/index/analysis/MappingCharFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/MappingCharFilterFactory.java @@ -17,13 +17,16 @@ * under the License. */ -package org.elasticsearch.index.analysis; +package org.elasticsearch.analysis.common; import org.apache.lucene.analysis.charfilter.MappingCharFilter; import org.apache.lucene.analysis.charfilter.NormalizeCharMap; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexSettings; +import org.elasticsearch.index.analysis.AbstractCharFilterFactory; +import org.elasticsearch.index.analysis.Analysis; +import org.elasticsearch.index.analysis.MultiTermAwareComponent; import java.io.Reader; import java.util.List; diff --git a/core/src/main/java/org/elasticsearch/index/analysis/PatternReplaceCharFilterFactory.java b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/PatternReplaceCharFilterFactory.java similarity index 92% rename from core/src/main/java/org/elasticsearch/index/analysis/PatternReplaceCharFilterFactory.java rename to modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/PatternReplaceCharFilterFactory.java index 2562f20373bef..b243618b53faa 100644 --- a/core/src/main/java/org/elasticsearch/index/analysis/PatternReplaceCharFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/PatternReplaceCharFilterFactory.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.index.analysis; +package org.elasticsearch.analysis.common; import java.io.Reader; import java.util.regex.Pattern; @@ -27,6 +27,8 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexSettings; +import org.elasticsearch.index.analysis.AbstractCharFilterFactory; +import org.elasticsearch.index.analysis.MultiTermAwareComponent; public class PatternReplaceCharFilterFactory extends AbstractCharFilterFactory implements MultiTermAwareComponent { diff --git a/core/src/main/java/org/elasticsearch/index/analysis/HtmlStripCharFilterFactory.java b/modules/analysis-common/src/main/java/org/elasticsearch/index/analysis/HtmlStripCharFilterFactory.java similarity index 100% rename from core/src/main/java/org/elasticsearch/index/analysis/HtmlStripCharFilterFactory.java rename to modules/analysis-common/src/main/java/org/elasticsearch/index/analysis/HtmlStripCharFilterFactory.java diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java index 886dad37b56f1..78522f3b6f3fd 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java @@ -20,10 +20,12 @@ package org.elasticsearch.analysis.common; import org.elasticsearch.AnalysisFactoryTestCase; +import org.elasticsearch.index.analysis.HtmlStripCharFilterFactory; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; import static java.util.Collections.emptyList; import static java.util.stream.Collectors.toList; @@ -46,7 +48,14 @@ protected Map> getTokenFilters() { @Override protected Map> getCharFilters() { - Map> filters = new HashMap<>(super.getCharFilters()); + Map> filters = new TreeMap<>(super.getCharFilters()); + filters.put("htmlstrip", HtmlStripCharFilterFactory.class); + filters.put("mapping", MappingCharFilterFactory.class); + filters.put("patternreplace", PatternReplaceCharFilterFactory.class); + + // TODO: these charfilters are not yet exposed: useful? + // handling of zwnj for persian + filters.put("persian", Void.class); return filters; } diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/20_analyzers.yaml b/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/20_analyzers.yaml index 9fb34e7a821c6..abd9a3f5ae0d9 100644 --- a/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/20_analyzers.yaml +++ b/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/20_analyzers.yaml @@ -9,3 +9,21 @@ - length: { tokens: 2 } - match: { tokens.0.token: Foo } - match: { tokens.1.token: Bar! } + + - do: + indices.analyze: + body: + text: Foo Bar! + explain: true + analyzer: whitespace + - match: { detail.custom_analyzer: false } + - match: { detail.analyzer.name: whitespace } + - length: { detail.analyzer.tokens: 2 } + - match: { detail.analyzer.tokens.0.token: Foo } + - match: { detail.analyzer.tokens.0.start_offset: 0 } + - match: { detail.analyzer.tokens.0.end_offset: 3 } + - match: { detail.analyzer.tokens.0.position: 0 } + - match: { detail.analyzer.tokens.1.token: Bar! } + - match: { detail.analyzer.tokens.1.start_offset: 4 } + - match: { detail.analyzer.tokens.1.end_offset: 8 } + - match: { detail.analyzer.tokens.1.position: 1 } diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/40_token_filters.yaml b/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/40_token_filters.yaml index ac5bcb82e5783..0666a31623b10 100644 --- a/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/40_token_filters.yaml +++ b/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/40_token_filters.yaml @@ -80,3 +80,57 @@ - match: { tokens.1.token: qu1ck } - match: { tokens.2.token: brown } - match: { tokens.3.token: fox } + + - do: + indices.analyze: + body: + text: the qu1ck brown fox + explain: true + tokenizer: standard + filter: [word_delimiter_graph] + - match: { detail.custom_analyzer: true } + - match: { detail.tokenizer.name: standard } + - length: { detail.tokenizer.tokens: 4 } + - match: { detail.tokenizer.tokens.0.token: the } + - match: { detail.tokenizer.tokens.0.start_offset: 0 } + - match: { detail.tokenizer.tokens.0.end_offset: 3 } + - match: { detail.tokenizer.tokens.0.position: 0 } + - match: { detail.tokenizer.tokens.1.token: qu1ck } + - match: { detail.tokenizer.tokens.1.start_offset: 4 } + - match: { detail.tokenizer.tokens.1.end_offset: 9 } + - match: { detail.tokenizer.tokens.1.position: 1 } + - match: { detail.tokenizer.tokens.2.token: brown } + - match: { detail.tokenizer.tokens.2.start_offset: 10 } + - match: { detail.tokenizer.tokens.2.end_offset: 15 } + - match: { detail.tokenizer.tokens.2.position: 2 } + - match: { detail.tokenizer.tokens.3.token: fox } + - match: { detail.tokenizer.tokens.3.start_offset: 16 } + - match: { detail.tokenizer.tokens.3.end_offset: 19 } + - match: { detail.tokenizer.tokens.3.position: 3 } + - length: { detail.tokenfilters: 1 } + - match: { detail.tokenfilters.0.name: word_delimiter_graph } + - length: { detail.tokenfilters.0.tokens: 6 } + - match: { detail.tokenfilters.0.tokens.0.token: the } + - match: { detail.tokenfilters.0.tokens.0.start_offset: 0 } + - match: { detail.tokenfilters.0.tokens.0.end_offset: 3 } + - match: { detail.tokenfilters.0.tokens.0.position: 0 } + - match: { detail.tokenfilters.0.tokens.1.token: qu } + - match: { detail.tokenfilters.0.tokens.1.start_offset: 4 } + - match: { detail.tokenfilters.0.tokens.1.end_offset: 6 } + - match: { detail.tokenfilters.0.tokens.1.position: 1 } + - match: { detail.tokenfilters.0.tokens.2.token: "1" } + - match: { detail.tokenfilters.0.tokens.2.start_offset: 6 } + - match: { detail.tokenfilters.0.tokens.2.end_offset: 7 } + - match: { detail.tokenfilters.0.tokens.2.position: 2 } + - match: { detail.tokenfilters.0.tokens.3.token: ck } + - match: { detail.tokenfilters.0.tokens.3.start_offset: 7 } + - match: { detail.tokenfilters.0.tokens.3.end_offset: 9 } + - match: { detail.tokenfilters.0.tokens.3.position: 3 } + - match: { detail.tokenfilters.0.tokens.4.token: brown } + - match: { detail.tokenfilters.0.tokens.4.start_offset: 10 } + - match: { detail.tokenfilters.0.tokens.4.end_offset: 15 } + - match: { detail.tokenfilters.0.tokens.4.position: 4 } + - match: { detail.tokenfilters.0.tokens.5.token: fox } + - match: { detail.tokenfilters.0.tokens.5.start_offset: 16 } + - match: { detail.tokenfilters.0.tokens.5.end_offset: 19 } + - match: { detail.tokenfilters.0.tokens.5.position: 5 } diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/50_char_filters.yaml b/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/50_char_filters.yaml index 06775a2a72290..67e68428c07c7 100644 --- a/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/50_char_filters.yaml +++ b/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/50_char_filters.yaml @@ -1,5 +1,33 @@ -## Smoke tests for analyzers included in the analysis-common module +## Smoke tests for char filters included in the analysis-common module +"html_strip": + - do: + indices.analyze: + body: + text: testfoo + tokenizer: keyword + char_filter: + - type: html_strip + escaped_tags: ["xxx", "yyy"] + read_ahead: 1024 + - length: { tokens: 1 } + - match: { tokens.0.token: "\ntestfoo\n" } + +--- +"pattern_replace": + - do: + indices.analyze: + body: + text: sample6 sample1 + tokenizer: keyword + char_filter: + - type: pattern_replace + pattern: sample(.*) + replacement: replacedSample $1 + - length: { tokens: 1 } + - match: { tokens.0.token: "replacedSample 6 sample1" } + +--- "mapping": - do: indices.analyze: @@ -11,3 +39,21 @@ mappings: ["ph => f", "qu => q"] - length: { tokens: 1 } - match: { tokens.0.token: "jeff qit fish" } + + - do: + indices.analyze: + body: + text: jeff quit phish + explain: true + tokenizer: keyword + char_filter: + - type: mapping + mappings: ["ph => f", "qu => q"] + - match: { detail.custom_analyzer: true } + - length: { detail.charfilters.0.filtered_text: 1 } + - match: { detail.charfilters.0.filtered_text.0: "jeff qit fish" } + - length: { detail.tokenizer.tokens: 1 } + - match: { detail.tokenizer.tokens.0.token: "jeff qit fish" } + - match: { detail.tokenizer.tokens.0.start_offset: 0 } + - match: { detail.tokenizer.tokens.0.end_offset: 15 } + - match: { detail.tokenizer.tokens.0.position: 0 } diff --git a/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java b/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java index cbabdeef4af3c..d14f81c61dfeb 100644 --- a/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java @@ -45,7 +45,6 @@ import org.elasticsearch.index.analysis.GermanNormalizationFilterFactory; import org.elasticsearch.index.analysis.GermanStemTokenFilterFactory; import org.elasticsearch.index.analysis.HindiNormalizationFilterFactory; -import org.elasticsearch.index.analysis.HtmlStripCharFilterFactory; import org.elasticsearch.index.analysis.HunspellTokenFilterFactory; import org.elasticsearch.index.analysis.IndicNormalizationFilterFactory; import org.elasticsearch.index.analysis.KStemTokenFilterFactory; @@ -58,14 +57,12 @@ import org.elasticsearch.index.analysis.LimitTokenCountFilterFactory; import org.elasticsearch.index.analysis.LowerCaseTokenFilterFactory; import org.elasticsearch.index.analysis.LowerCaseTokenizerFactory; -import org.elasticsearch.index.analysis.MappingCharFilterFactory; import org.elasticsearch.index.analysis.MinHashTokenFilterFactory; import org.elasticsearch.index.analysis.MultiTermAwareComponent; import org.elasticsearch.index.analysis.NGramTokenFilterFactory; import org.elasticsearch.index.analysis.NGramTokenizerFactory; import org.elasticsearch.index.analysis.PathHierarchyTokenizerFactory; import org.elasticsearch.index.analysis.PatternCaptureGroupTokenFilterFactory; -import org.elasticsearch.index.analysis.PatternReplaceCharFilterFactory; import org.elasticsearch.index.analysis.PatternReplaceTokenFilterFactory; import org.elasticsearch.index.analysis.PatternTokenizerFactory; import org.elasticsearch.index.analysis.PersianNormalizationFilterFactory; @@ -325,9 +322,9 @@ private static String toCamelCase(String s) { static final Map> KNOWN_CHARFILTERS = new MapBuilder>() // exposed in ES - .put("htmlstrip", HtmlStripCharFilterFactory.class) - .put("mapping", MappingCharFilterFactory.class) - .put("patternreplace", PatternReplaceCharFilterFactory.class) + .put("htmlstrip", MovedToAnalysisCommon.class) + .put("mapping", MovedToAnalysisCommon.class) + .put("patternreplace", MovedToAnalysisCommon.class) // TODO: these charfilters are not yet exposed: useful? // handling of zwnj for persian From 416feeb7f925abd09b5a7059ffaf90f70068c9cd Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 26 Apr 2017 14:09:26 -0400 Subject: [PATCH 087/619] Rewrite description of `bool`'s `should` (#24342) Docs: rewrite description of `bool`'s `should` Rewrites the description of the `bool` query's `should` clauses so it is (hopefully) more clear what the defaults for `minimum_should_match` are. There is still an `[IMPORTANT]` section about `minimum_should_match` in a filter context. I think it is worth keeping because it is, well, important. Closes #23831 --- docs/reference/query-dsl/bool-query.asciidoc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/reference/query-dsl/bool-query.asciidoc b/docs/reference/query-dsl/bool-query.asciidoc index 4d66e5e7f64cd..a7092aaaab113 100644 --- a/docs/reference/query-dsl/bool-query.asciidoc +++ b/docs/reference/query-dsl/bool-query.asciidoc @@ -17,12 +17,15 @@ contribute to the score. in <>, meaning that scoring is ignored and clauses are considered for caching. -|`should` |The clause (query) should appear in the matching document. In -a boolean query with no `must` or `filter` clauses, one or more `should` clauses -must match a document. The minimum number of should clauses to match can -be set using the -<> -parameter. +|`should` |The clause (query) should appear in the matching document. If the +`bool` query is in a <> and has a `must` or +`filter` clause then a document will match the `bool` query even if none of the +`should` queries match. In this case these clauses are only used to influence +the score. If the `bool` query is a <> +or has neither `must` or `filter` then at least one of the `should` queries +must match a document for it to match the `bool` query. This behavior may be +explicitly controlled by settings the +<> parameter. |`must_not` |The clause (query) must not appear in the matching documents. Clauses are executed in <> meaning From 4c0eb35c22306fa5518c6bd432df59e58ef5c3a2 Mon Sep 17 00:00:00 2001 From: Koen De Groote Date: Wed, 26 Apr 2017 20:32:35 +0200 Subject: [PATCH 088/619] Removal of dead code from SnapshotsService (#24347) This code removes a few lines of dead code from SnapshotsService. Looks like a forgotten remnant of a past implementation. --- .../main/java/org/elasticsearch/snapshots/SnapshotsService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java index 1b829d700d4cc..16f865e2fb9e6 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java @@ -913,13 +913,11 @@ public void run() { try { final Repository repository = repositoriesService.repository(snapshot.getRepository()); logger.trace("[{}] finalizing snapshot in repository, state: [{}], failure[{}]", snapshot, entry.state(), failure); - ArrayList failures = new ArrayList<>(); ArrayList shardFailures = new ArrayList<>(); for (ObjectObjectCursor shardStatus : entry.shards()) { ShardId shardId = shardStatus.key; ShardSnapshotStatus status = shardStatus.value; if (status.state().failed()) { - failures.add(new ShardSearchFailure(status.reason(), new SearchShardTarget(status.nodeId(), shardId))); shardFailures.add(new SnapshotShardFailure(status.nodeId(), shardId, status.reason())); } } From 3187ed73fc4fd8527c695e18b7186abbd6d29453 Mon Sep 17 00:00:00 2001 From: Koen De Groote Date: Wed, 26 Apr 2017 20:44:03 +0200 Subject: [PATCH 089/619] Removal of dead code in ScriptedMetricAggregationBuilder (#24346) This code removes a few lines of dead code from ScriptedMetricAggregationBuilder. Just completely dead code, it adds things to a Set that is then not used in any way. --- .../metrics/scripted/ScriptedMetricAggregationBuilder.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregationBuilder.java index ea246feda4dee..2d6398ee31e15 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregationBuilder.java @@ -37,10 +37,8 @@ import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; -import java.util.HashSet; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.function.Function; public class ScriptedMetricAggregationBuilder extends AbstractAggregationBuilder { @@ -239,11 +237,6 @@ public static ScriptedMetricAggregationBuilder parse(String aggregationName, Que Map params = null; XContentParser.Token token; String currentFieldName = null; - Set scriptParameters = new HashSet<>(); - scriptParameters.add(INIT_SCRIPT_FIELD.getPreferredName()); - scriptParameters.add(MAP_SCRIPT_FIELD.getPreferredName()); - scriptParameters.add(COMBINE_SCRIPT_FIELD.getPreferredName()); - scriptParameters.add(REDUCE_SCRIPT_FIELD.getPreferredName()); XContentParser parser = context.parser(); while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { From 0e52e3420e7222e46dfb15b79f0c4615a14ca0d3 Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Wed, 26 Apr 2017 14:48:10 -0400 Subject: [PATCH 090/619] Fixes restore of a shrunken index when initial recovery node is gone (#24322) When an index is shrunk using the shrink APIs, the shrink operation adds some internal index settings to the shrink index, for example `index.shrink.source.name|uuid` to denote the source index, as well as `index.routing.allocation.initial_recovery._id` to denote the node on which all shards for the source index resided when the shrunken index was created. However, this presents a problem when taking a snapshot of the shrunken index and restoring it to a cluster where the initial recovery node is not present, or restoring to the same cluster where the initial recovery node is offline or decomissioned. The restore operation fails to allocate the shard in the shrunken index to a node when the initial recovery node is not present, and a restore type of recovery will *not* go through the PrimaryShardAllocator, meaning that it will not have the chance to force allocate the primary to a node in the cluster. Rather, restore initiated shard allocation goes through the BalancedShardAllocator which does not attempt to force allocate a primary. This commit fixes the aforementioned problem by not requiring allocation to occur on the initial recovery node when the recovery type is a restore of a snapshot. This commit also ensures that the internal shrink index settings are recognized and not archived (which can trip an assertion in the restore scenario). Closes #24257 --- .../cluster/metadata/IndexMetaData.java | 11 ++- .../metadata/MetaDataCreateIndexService.java | 2 +- .../cluster/routing/IndexRoutingTable.java | 4 +- .../cluster/routing/RecoverySource.java | 12 --- .../decider/FilterAllocationDecider.java | 15 +++- .../common/settings/IndexScopedSettings.java | 4 +- .../cluster/routing/RoutingTableTests.java | 3 +- .../FilterAllocationDeciderTests.java | 24 ++--- .../DedicatedClusterSnapshotRestoreIT.java | 88 +++++++++++++------ 9 files changed, 93 insertions(+), 70 deletions(-) rename core/src/test/java/org/elasticsearch/cluster/routing/allocation/{ => decider}/FilterAllocationDeciderTests.java (89%) diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java b/core/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java index 67f4d71bd4e1d..f1f6f8aee2232 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java @@ -41,8 +41,6 @@ import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.logging.DeprecationLogger; -import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; @@ -62,7 +60,6 @@ import org.joda.time.DateTimeZone; import java.io.IOException; -import java.text.ParseException; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; @@ -440,12 +437,14 @@ public MappingMetaData mapping(String mappingType) { return mappings.get(mappingType); } - public static final Setting INDEX_SHRINK_SOURCE_UUID = Setting.simpleString("index.shrink.source.uuid"); - public static final Setting INDEX_SHRINK_SOURCE_NAME = Setting.simpleString("index.shrink.source.name"); + public static final String INDEX_SHRINK_SOURCE_UUID_KEY = "index.shrink.source.uuid"; + public static final String INDEX_SHRINK_SOURCE_NAME_KEY = "index.shrink.source.name"; + public static final Setting INDEX_SHRINK_SOURCE_UUID = Setting.simpleString(INDEX_SHRINK_SOURCE_UUID_KEY); + public static final Setting INDEX_SHRINK_SOURCE_NAME = Setting.simpleString(INDEX_SHRINK_SOURCE_NAME_KEY); public Index getMergeSourceIndex() { - return INDEX_SHRINK_SOURCE_UUID.exists(settings) ? new Index(INDEX_SHRINK_SOURCE_NAME.get(settings), INDEX_SHRINK_SOURCE_UUID.get(settings)) : null; + return INDEX_SHRINK_SOURCE_UUID.exists(settings) ? new Index(INDEX_SHRINK_SOURCE_NAME.get(settings), INDEX_SHRINK_SOURCE_UUID.get(settings)) : null; } /** diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java index a3292e2cfd445..fc9ee533090a4 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java @@ -598,7 +598,7 @@ static void prepareShrinkIndexSettings(ClusterState currentState, Set ma indexSettingsBuilder // we use "i.r.a.initial_recovery" rather than "i.r.a.require|include" since we want the replica to allocate right away // once we are allocated. - .put("index.routing.allocation.initial_recovery._id", + .put(IndexMetaData.INDEX_ROUTING_INITIAL_RECOVERY_GROUP_SETTING.getKey() + "_id", Strings.arrayToCommaDelimitedString(nodesToAllocateOn.toArray())) // we only try once and then give up with a shrink index .put("index.allocation.max_retries", 1) diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/IndexRoutingTable.java b/core/src/main/java/org/elasticsearch/cluster/routing/IndexRoutingTable.java index d80a1c326cff6..c587629ef0c08 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/IndexRoutingTable.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/IndexRoutingTable.java @@ -139,8 +139,8 @@ boolean validate(MetaData metaData) { "allocation set " + inSyncAllocationIds); } - if (shardRouting.primary() && shardRouting.initializing() && shardRouting.relocating() == false && - RecoverySource.isInitialRecovery(shardRouting.recoverySource().getType()) == false && + if (shardRouting.primary() && shardRouting.initializing() && + shardRouting.recoverySource().getType() == RecoverySource.Type.EXISTING_STORE && inSyncAllocationIds.contains(shardRouting.allocationId().getId()) == false) throw new IllegalStateException("a primary shard routing " + shardRouting + " is a primary that is recovering from " + "a known allocation id but has no corresponding entry in the in-sync " + diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/RecoverySource.java b/core/src/main/java/org/elasticsearch/cluster/routing/RecoverySource.java index f613cdbbada15..32afad99f2764 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/RecoverySource.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/RecoverySource.java @@ -20,7 +20,6 @@ package org.elasticsearch.cluster.routing; import org.elasticsearch.Version; -import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; @@ -29,7 +28,6 @@ import org.elasticsearch.snapshots.Snapshot; import java.io.IOException; -import java.util.EnumSet; import java.util.Objects; /** @@ -249,14 +247,4 @@ public String toString() { return "peer recovery"; } } - - private static EnumSet INITIAL_RECOVERY_TYPES = EnumSet.of(Type.EMPTY_STORE, Type.LOCAL_SHARDS, Type.SNAPSHOT); - - /** - * returns true for recovery types that indicate that a primary is being allocated for the very first time. - * This recoveries can be controlled by {@link IndexMetaData#INDEX_ROUTING_INITIAL_RECOVERY_GROUP_SETTING} - */ - public static boolean isInitialRecovery(RecoverySource.Type type) { - return INITIAL_RECOVERY_TYPES.contains(type); - } } diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/FilterAllocationDecider.java b/core/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/FilterAllocationDecider.java index 85069392eb683..933b0a829d569 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/FilterAllocationDecider.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/FilterAllocationDecider.java @@ -30,6 +30,8 @@ import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; +import java.util.EnumSet; + import static org.elasticsearch.cluster.node.DiscoveryNodeFilters.IP_VALIDATOR; import static org.elasticsearch.cluster.node.DiscoveryNodeFilters.OpType.AND; import static org.elasticsearch.cluster.node.DiscoveryNodeFilters.OpType.OR; @@ -75,6 +77,17 @@ public class FilterAllocationDecider extends AllocationDecider { public static final Setting CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING = Setting.groupSetting(CLUSTER_ROUTING_EXCLUDE_GROUP_PREFIX + ".", IP_VALIDATOR, Property.Dynamic, Property.NodeScope); + /** + * The set of {@link RecoverySource.Type} values for which the + * {@link IndexMetaData#INDEX_ROUTING_INITIAL_RECOVERY_GROUP_SETTING} should apply. + * Note that we do not include the {@link RecoverySource.Type#SNAPSHOT} type here + * because if the snapshot is restored to a different cluster that does not contain + * the initial recovery node id, or to the same cluster where the initial recovery node + * id has been decommissioned, then the primary shards will never be allocated. + */ + static EnumSet INITIAL_RECOVERY_TYPES = + EnumSet.of(RecoverySource.Type.EMPTY_STORE, RecoverySource.Type.LOCAL_SHARDS); + private volatile DiscoveryNodeFilters clusterRequireFilters; private volatile DiscoveryNodeFilters clusterIncludeFilters; private volatile DiscoveryNodeFilters clusterExcludeFilters; @@ -98,7 +111,7 @@ public Decision canAllocate(ShardRouting shardRouting, RoutingNode node, Routing IndexMetaData indexMd = allocation.metaData().getIndexSafe(shardRouting.index()); DiscoveryNodeFilters initialRecoveryFilters = indexMd.getInitialRecoveryFilters(); if (initialRecoveryFilters != null && - RecoverySource.isInitialRecovery(shardRouting.recoverySource().getType()) && + INITIAL_RECOVERY_TYPES.contains(shardRouting.recoverySource().getType()) && initialRecoveryFilters.match(node.node()) == false) { String explanation = (shardRouting.recoverySource().getType() == RecoverySource.Type.LOCAL_SHARDS) ? "initial allocation of the shrunken index is only allowed on nodes [%s] that hold a copy of every shard in the index" : diff --git a/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java b/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java index 4094d69eddeb8..1568ac288d9b4 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java +++ b/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java @@ -191,9 +191,11 @@ protected boolean isPrivateSetting(String key) { case IndexMetaData.SETTING_VERSION_UPGRADED: case IndexMetaData.SETTING_INDEX_PROVIDED_NAME: case MergePolicyConfig.INDEX_MERGE_ENABLED: + case IndexMetaData.INDEX_SHRINK_SOURCE_UUID_KEY: + case IndexMetaData.INDEX_SHRINK_SOURCE_NAME_KEY: return true; default: - return false; + return IndexMetaData.INDEX_ROUTING_INITIAL_RECOVERY_GROUP_SETTING.getRawKey().match(key); } } } diff --git a/core/src/test/java/org/elasticsearch/cluster/routing/RoutingTableTests.java b/core/src/test/java/org/elasticsearch/cluster/routing/RoutingTableTests.java index e26fece7c6db1..a76ad4657c64a 100644 --- a/core/src/test/java/org/elasticsearch/cluster/routing/RoutingTableTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/routing/RoutingTableTests.java @@ -352,8 +352,7 @@ public static IndexMetaData updateActiveAllocations(IndexRoutingTable indexRouti Set insyncAids = shardTable.activeShards().stream().map( shr -> shr.allocationId().getId()).collect(Collectors.toSet()); final ShardRouting primaryShard = shardTable.primaryShard(); - if (primaryShard.initializing() && primaryShard.relocating() == false && - RecoverySource.isInitialRecovery(primaryShard.recoverySource().getType()) == false ) { + if (primaryShard.initializing() && shardRouting.recoverySource().getType() == RecoverySource.Type.EXISTING_STORE) { // simulate a primary was initialized based on aid insyncAids.add(primaryShard.allocationId().getId()); } diff --git a/core/src/test/java/org/elasticsearch/cluster/routing/allocation/FilterAllocationDeciderTests.java b/core/src/test/java/org/elasticsearch/cluster/routing/allocation/decider/FilterAllocationDeciderTests.java similarity index 89% rename from core/src/test/java/org/elasticsearch/cluster/routing/allocation/FilterAllocationDeciderTests.java rename to core/src/test/java/org/elasticsearch/cluster/routing/allocation/decider/FilterAllocationDeciderTests.java index c201736c51cef..c857a3f30ed76 100644 --- a/core/src/test/java/org/elasticsearch/cluster/routing/allocation/FilterAllocationDeciderTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/routing/allocation/decider/FilterAllocationDeciderTests.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.cluster.routing.allocation; +package org.elasticsearch.cluster.routing.allocation.decider; import org.elasticsearch.Version; import org.elasticsearch.cluster.ClusterState; @@ -29,19 +29,14 @@ import org.elasticsearch.cluster.routing.RecoverySource; import org.elasticsearch.cluster.routing.RoutingTable; import org.elasticsearch.cluster.routing.ShardRouting; +import org.elasticsearch.cluster.routing.allocation.AllocationService; +import org.elasticsearch.cluster.routing.allocation.RoutingAllocation; import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator; -import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders; -import org.elasticsearch.cluster.routing.allocation.decider.Decision; import org.elasticsearch.cluster.routing.allocation.decider.Decision.Type; -import org.elasticsearch.cluster.routing.allocation.decider.FilterAllocationDecider; -import org.elasticsearch.cluster.routing.allocation.decider.ReplicaAfterPrimaryActiveAllocationDecider; -import org.elasticsearch.cluster.routing.allocation.decider.SameShardAllocationDecider; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.snapshots.Snapshot; -import org.elasticsearch.snapshots.SnapshotId; import org.elasticsearch.test.gateway.TestGatewayAllocator; import java.util.Arrays; @@ -144,8 +139,7 @@ public void testFilterInitialRecovery() { } private ClusterState createInitialClusterState(AllocationService service, Settings settings) { - RecoverySource.Type recoveryType = randomFrom(RecoverySource.Type.EMPTY_STORE, - RecoverySource.Type.LOCAL_SHARDS, RecoverySource.Type.SNAPSHOT); + RecoverySource.Type recoveryType = randomFrom(FilterAllocationDecider.INITIAL_RECOVERY_TYPES); MetaData.Builder metaData = MetaData.builder(); final Settings.Builder indexSettings = settings(Version.CURRENT).put(settings); final IndexMetaData sourceIndex; @@ -164,9 +158,6 @@ private ClusterState createInitialClusterState(AllocationService service, Settin } final IndexMetaData.Builder indexMetaDataBuilder = IndexMetaData.builder("idx").settings(indexSettings) .numberOfShards(1).numberOfReplicas(1); - if (recoveryType == RecoverySource.Type.SNAPSHOT) { - indexMetaDataBuilder.putInSyncAllocationIds(0, Collections.singleton("_snapshot_restore")); - } final IndexMetaData indexMetaData = indexMetaDataBuilder.build(); metaData.put(indexMetaData, false); RoutingTable.Builder routingTableBuilder = RoutingTable.builder(); @@ -174,11 +165,6 @@ private ClusterState createInitialClusterState(AllocationService service, Settin case EMPTY_STORE: routingTableBuilder.addAsNew(indexMetaData); break; - case SNAPSHOT: - routingTableBuilder.addAsRestore(indexMetaData, new RecoverySource.SnapshotRecoverySource( - new Snapshot("repository", new SnapshotId("snapshot_name", "snapshot_uuid")), - Version.CURRENT, indexMetaData.getIndex().getName())); - break; case LOCAL_SHARDS: routingTableBuilder.addAsFromCloseToOpen(sourceIndex); routingTableBuilder.addAsNew(indexMetaData); @@ -192,7 +178,7 @@ private ClusterState createInitialClusterState(AllocationService service, Settin .getDefault(Settings.EMPTY)).metaData(metaData).routingTable(routingTable).build(); clusterState = ClusterState.builder(clusterState).nodes(DiscoveryNodes.builder().add(newNode("node1")).add(newNode("node2"))) .build(); - return service.reroute(clusterState, "reroute", false); + return service.reroute(clusterState, "reroute"); } public void testInvalidIPFilter() { diff --git a/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java b/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java index 08a3308172bd3..10c49b431a512 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java +++ b/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java @@ -54,6 +54,7 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.discovery.zen.ElectMasterService; +import org.elasticsearch.env.Environment; import org.elasticsearch.indices.recovery.RecoveryState; import org.elasticsearch.node.Node; import org.elasticsearch.plugins.Plugin; @@ -127,7 +128,6 @@ private void registerBuiltinWritables() { NonSnapshottableGatewayMetadata::readDiffFrom, NonSnapshottableGatewayMetadata::fromXContent); registerMetaDataCustom(SnapshotableGatewayNoApiMetadata.TYPE, SnapshotableGatewayNoApiMetadata::readFrom, NonSnapshottableGatewayMetadata::readDiffFrom, SnapshotableGatewayNoApiMetadata::fromXContent); - } @Override @@ -154,8 +154,6 @@ public void testRestorePersistentSettings() throws Exception { logger.info("--> wait for the second node to join the cluster"); assertThat(client.admin().cluster().prepareHealth().setWaitForNodes("2").get().isTimedOut(), equalTo(false)); - int random = randomIntBetween(10, 42); - logger.info("--> set test persistent setting"); client.admin().cluster().prepareUpdateSettings().setPersistentSettings( Settings.builder() @@ -723,7 +721,6 @@ public void sendResponse(RestResponse response) { if (clusterStateError.get() != null) { throw clusterStateError.get(); } - } public void testMasterShutdownDuringSnapshot() throws Exception { @@ -801,33 +798,72 @@ public void run() { assertEquals(0, snapshotInfo.failedShards()); } + /** + * Tests that a shrunken index (created via the shrink APIs) and subsequently snapshotted + * can be restored when the node the shrunken index was created on is no longer part of + * the cluster. + */ + public void testRestoreShrinkIndex() throws Exception { + logger.info("--> starting a master node and a data node"); + internalCluster().startMasterOnlyNode(); + internalCluster().startDataOnlyNode(); - private boolean snapshotIsDone(String repository, String snapshot) { - try { - SnapshotsStatusResponse snapshotsStatusResponse = client().admin().cluster().prepareSnapshotStatus(repository).setSnapshots(snapshot).get(); - if (snapshotsStatusResponse.getSnapshots().isEmpty()) { - return false; - } - for (SnapshotStatus snapshotStatus : snapshotsStatusResponse.getSnapshots()) { - if (snapshotStatus.getState().completed()) { - return true; - } - } - return false; - } catch (SnapshotMissingException ex) { - return false; - } - } + final Client client = client(); + final String repo = "test-repo"; + final String snapshot = "test-snap"; + final String sourceIdx = "test-idx"; + final String shrunkIdx = "test-idx-shrunk"; - private void createTestIndex(String name) { - assertAcked(prepareCreate(name, 0, Settings.builder().put("number_of_shards", between(1, 6)) - .put("number_of_replicas", between(1, 6)))); + logger.info("--> creating repository"); + assertAcked(client.admin().cluster().preparePutRepository(repo).setType("fs") + .setSettings(Settings.builder().put("location", randomRepoPath()) + .put("compress", randomBoolean()))); + + assertAcked(prepareCreate(sourceIdx, 0, Settings.builder() + .put("number_of_shards", between(1, 20)).put("number_of_replicas", 0))); + ensureGreen(); - logger.info("--> indexing some data into {}", name); - for (int i = 0; i < between(10, 500); i++) { - index(name, "doc", Integer.toString(i), "foo", "bar" + i); + logger.info("--> indexing some data"); + IndexRequestBuilder[] builders = new IndexRequestBuilder[randomIntBetween(10, 100)]; + for (int i = 0; i < builders.length; i++) { + builders[i] = client().prepareIndex(sourceIdx, "type1", + Integer.toString(i)).setSource("field1", "bar " + i); } + indexRandom(true, builders); + flushAndRefresh(); + + logger.info("--> shrink the index"); + assertAcked(client.admin().indices().prepareUpdateSettings(sourceIdx) + .setSettings(Settings.builder().put("index.blocks.write", true)).get()); + assertAcked(client.admin().indices().prepareShrinkIndex(sourceIdx, shrunkIdx).get()); + + logger.info("--> snapshot the shrunk index"); + CreateSnapshotResponse createResponse = client.admin().cluster() + .prepareCreateSnapshot(repo, snapshot) + .setWaitForCompletion(true).setIndices(shrunkIdx).get(); + assertEquals(SnapshotState.SUCCESS, createResponse.getSnapshotInfo().state()); + logger.info("--> delete index and stop the data node"); + assertAcked(client.admin().indices().prepareDelete(sourceIdx).get()); + assertAcked(client.admin().indices().prepareDelete(shrunkIdx).get()); + internalCluster().stopRandomDataNode(); + client().admin().cluster().prepareHealth().setTimeout("30s").setWaitForNodes("1"); + + logger.info("--> start a new data node"); + final Settings dataSettings = Settings.builder() + .put(Node.NODE_NAME_SETTING.getKey(), randomAlphaOfLength(5)) + .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) // to get a new node id + .build(); + internalCluster().startDataOnlyNode(dataSettings); + client().admin().cluster().prepareHealth().setTimeout("30s").setWaitForNodes("2"); + + logger.info("--> restore the shrunk index and ensure all shards are allocated"); + RestoreSnapshotResponse restoreResponse = client().admin().cluster() + .prepareRestoreSnapshot(repo, snapshot).setWaitForCompletion(true) + .setIndices(shrunkIdx).get(); + assertEquals(restoreResponse.getRestoreInfo().totalShards(), + restoreResponse.getRestoreInfo().successfulShards()); + ensureYellow(); } public static class SnapshottableMetadata extends TestCustomMetaData { From ebe98f9d621291717a68b1719aef48cfd4a4e469 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Wed, 26 Apr 2017 21:07:33 +0200 Subject: [PATCH 091/619] test: don't randomly wrap index reader --- .../bucket/nested/NestedAggregatorTests.java | 10 +++++----- .../bucket/nested/ReverseNestedAggregatorTests.java | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregatorTests.java index 304b7f03c59ef..7000924001f20 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregatorTests.java @@ -75,7 +75,7 @@ public void testNoDocs() throws IOException { NumberFieldMapper.NumberType.LONG); fieldType.setName(VALUE_FIELD_NAME); - Nested nested = search(newSearcher(indexReader, true, true), + Nested nested = search(newSearcher(indexReader, false, true), new MatchAllDocsQuery(), nestedBuilder, fieldType); assertEquals(NESTED_AGG, nested.getName()); @@ -122,7 +122,7 @@ public void testSingleNestingMax() throws IOException { NumberFieldMapper.NumberType.LONG); fieldType.setName(VALUE_FIELD_NAME); - Nested nested = search(newSearcher(indexReader, true, true), + Nested nested = search(newSearcher(indexReader, false, true), new MatchAllDocsQuery(), nestedBuilder, fieldType); assertEquals(expectedNestedDocs, nested.getDocCount()); @@ -171,7 +171,7 @@ public void testDoubleNestingMax() throws IOException { NumberFieldMapper.NumberType.LONG); fieldType.setName(VALUE_FIELD_NAME); - Nested nested = search(newSearcher(indexReader, true, true), + Nested nested = search(newSearcher(indexReader, false, true), new MatchAllDocsQuery(), nestedBuilder, fieldType); assertEquals(expectedNestedDocs, nested.getDocCount()); @@ -223,7 +223,7 @@ public void testOrphanedDocs() throws IOException { NumberFieldMapper.NumberType.LONG); fieldType.setName(VALUE_FIELD_NAME); - Nested nested = search(newSearcher(indexReader, true, true), + Nested nested = search(newSearcher(indexReader, false, true), new MatchAllDocsQuery(), nestedBuilder, fieldType); assertEquals(expectedNestedDocs, nested.getDocCount()); @@ -304,7 +304,7 @@ public void testResetRootDocId() throws Exception { bq.add(Queries.newNonNestedFilter(), BooleanClause.Occur.MUST); bq.add(new TermQuery(new Term(UidFieldMapper.NAME, "type#2")), BooleanClause.Occur.MUST_NOT); - Nested nested = search(newSearcher(indexReader, true, true), + Nested nested = search(newSearcher(indexReader, false, true), new ConstantScoreQuery(bq.build()), nestedBuilder, fieldType); assertEquals(NESTED_AGG, nested.getName()); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregatorTests.java index 74fb7ca9ca4d2..70e6c3559409a 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregatorTests.java @@ -67,7 +67,7 @@ public void testNoDocs() throws IOException { NumberFieldMapper.NumberType.LONG); fieldType.setName(VALUE_FIELD_NAME); - Nested nested = search(newSearcher(indexReader, true, true), + Nested nested = search(newSearcher(indexReader, false, true), new MatchAllDocsQuery(), nestedBuilder, fieldType); ReverseNested reverseNested = (ReverseNested) ((InternalAggregation)nested).getProperty(REVERSE_AGG_NAME); @@ -130,7 +130,7 @@ public void testMaxFromParentDocs() throws IOException { NumberFieldMapper.NumberType.LONG); fieldType.setName(VALUE_FIELD_NAME); - Nested nested = search(newSearcher(indexReader, true, true), + Nested nested = search(newSearcher(indexReader, false, true), new MatchAllDocsQuery(), nestedBuilder, fieldType); assertEquals(expectedNestedDocs, nested.getDocCount()); From b7bf651738c11fb7a35e07319e8eeffaea1d96ff Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Wed, 26 Apr 2017 21:06:20 +0200 Subject: [PATCH 092/619] [TEST] Fix cluster forming in testDynamicUpdateMinimumMasterNodes This test can run into a split-brain situation as minimum_master_nodes is not properly set. To prevent this, make sure that at least one of the two master nodes that are initially started has minimum_master_nodes correctly set. --- .../cluster/MinimumMasterNodesIT.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/cluster/MinimumMasterNodesIT.java b/core/src/test/java/org/elasticsearch/cluster/MinimumMasterNodesIT.java index c9057f4373f68..3fc67f3eb0ed0 100644 --- a/core/src/test/java/org/elasticsearch/cluster/MinimumMasterNodesIT.java +++ b/core/src/test/java/org/elasticsearch/cluster/MinimumMasterNodesIT.java @@ -263,18 +263,17 @@ public void testMultipleNodesShutdownNonMasterNodes() throws Exception { } public void testDynamicUpdateMinimumMasterNodes() throws Exception { - Settings settings = Settings.builder() - .put(ZenDiscovery.PING_TIMEOUT_SETTING.getKey(), "400ms") - .put(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING.getKey(), "1") - .build(); + Settings settingsWithMinMaster1 = Settings.builder() + .put(ZenDiscovery.PING_TIMEOUT_SETTING.getKey(), "400ms") + .put(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING.getKey(), 1) + .build(); - logger.info("--> start first node and wait for it to be a master"); - internalCluster().startNode(settings); - ensureClusterSizeConsistency(); + Settings settingsWithMinMaster2 = Settings.builder() + .put(settingsWithMinMaster1).put(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING.getKey(), 2) + .build(); - // wait until second node join the cluster - logger.info("--> start second node and wait for it to join"); - internalCluster().startNode(settings); + logger.info("--> start two nodes and wait for them to form a cluster"); + internalCluster().startNodes(settingsWithMinMaster1, settingsWithMinMaster2); ensureClusterSizeConsistency(); logger.info("--> setting minimum master node to 2"); @@ -292,7 +291,7 @@ public void testDynamicUpdateMinimumMasterNodes() throws Exception { assertNoMasterBlockOnAllNodes(); logger.info("--> bringing another node up"); - internalCluster().startNode(Settings.builder().put(settings).put(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING.getKey(), 2).build()); + internalCluster().startNode(settingsWithMinMaster2); ensureClusterSizeConsistency(); } From 2ed1f7a3390420835f9b270d20c5d512ba197827 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Wed, 26 Apr 2017 15:26:36 -0400 Subject: [PATCH 093/619] Avoid leaks in Long GC disruption tests We can leak disrupted threads here since we never wait for them to complete after freeing them from their loops. This commit addresses this by joining on disrupted threads, and addresses fallout from trying to join here. Relates #24338 --- .../test/disruption/LongGCDisruption.java | 55 ++++++++++++++----- .../disruption/LongGCDisruptionTests.java | 20 ++++++- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/test/framework/src/main/java/org/elasticsearch/test/disruption/LongGCDisruption.java b/test/framework/src/main/java/org/elasticsearch/test/disruption/LongGCDisruption.java index 98349086df5fa..45acde0932533 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/disruption/LongGCDisruption.java +++ b/test/framework/src/main/java/org/elasticsearch/test/disruption/LongGCDisruption.java @@ -106,6 +106,15 @@ protected void doRun() throws Exception { logger.warn("failed to stop node [{}]'s threads within [{}] millis. Stopping thread stack trace:\n {}" , disruptedNode, getStoppingTimeoutInMillis(), stackTrace(stoppingThread.getStackTrace())); stoppingThread.interrupt(); // best effort; + try { + /* + * We need to join on the stopping thread in case it has stopped a thread that is in a critical section and needs to + * be resumed. + */ + stoppingThread.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } throw new RuntimeException("stopping node threads took too long"); } // block detection checks if other threads are blocked waiting on an object that is held by one @@ -228,23 +237,39 @@ protected boolean stopNodeThreads(Set nodeThreads) { if (thread.isAlive() && nodeThreads.add(thread)) { liveThreadsFound = true; logger.trace("stopping thread [{}]", threadName); - thread.suspend(); - // double check the thread is not in a shared resource like logging. If so, let it go and come back.. - boolean safe = true; - safe: - for (StackTraceElement stackElement : thread.getStackTrace()) { - String className = stackElement.getClassName(); - for (Pattern unsafePattern : getUnsafeClasses()) { - if (unsafePattern.matcher(className).find()) { - safe = false; - break safe; + // we assume it is not safe to suspend the thread + boolean safe = false; + try { + /* + * At the bottom of this try-block we will know whether or not it is safe to suspend this thread; we start by + * assuming that it is safe. + */ + boolean definitelySafe = true; + thread.suspend(); + // double check the thread is not in a shared resource like logging; if so, let it go and come back + safe: + for (StackTraceElement stackElement : thread.getStackTrace()) { + String className = stackElement.getClassName(); + for (Pattern unsafePattern : getUnsafeClasses()) { + if (unsafePattern.matcher(className).find()) { + // it is definitely not safe to suspend the thread + definitelySafe = false; + break safe; + } } } - } - if (!safe) { - logger.trace("resuming thread [{}] as it is in a critical section", threadName); - thread.resume(); - nodeThreads.remove(thread); + safe = definitelySafe; + } finally { + if (!safe) { + /* + * Do not log before resuming as we might be interrupted while logging in which case we will throw an + * interrupted exception and never resume the stopped thread that is in a critical section. Also, logging before + * resuming makes for confusing log messages if we never hit the resume. + */ + thread.resume(); + logger.trace("resumed thread [{}] as it is in a critical section", threadName); + nodeThreads.remove(thread); + } } } } diff --git a/test/framework/src/test/java/org/elasticsearch/test/disruption/LongGCDisruptionTests.java b/test/framework/src/test/java/org/elasticsearch/test/disruption/LongGCDisruptionTests.java index 48bd18986c284..147a6df608a03 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/disruption/LongGCDisruptionTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/disruption/LongGCDisruptionTests.java @@ -22,6 +22,8 @@ import org.elasticsearch.test.ESTestCase; import java.lang.management.ThreadInfo; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -69,8 +71,8 @@ protected long getStoppingTimeoutInMillis() { final CountDownLatch pauseUnderLock = new CountDownLatch(1); final LockedExecutor lockedExecutor = new LockedExecutor(); final AtomicLong ops = new AtomicLong(); + final Thread[] threads = new Thread[10]; try { - Thread[] threads = new Thread[10]; for (int i = 0; i < 10; i++) { // at least one locked and one none lock thread final boolean lockedExec = (i < 9 && randomBoolean()) || i == 0; @@ -101,6 +103,9 @@ protected long getStoppingTimeoutInMillis() { } finally { stop.set(true); pauseUnderLock.countDown(); + for (final Thread thread : threads) { + thread.join(); + } } } @@ -121,8 +126,8 @@ protected Pattern[] getUnsafeClasses() { final AtomicBoolean stop = new AtomicBoolean(); final LockedExecutor lockedExecutor = new LockedExecutor(); final AtomicLong ops = new AtomicLong(); + final Thread[] threads = new Thread[10]; try { - Thread[] threads = new Thread[10]; for (int i = 0; i < 10; i++) { threads[i] = new Thread(() -> { for (int iter = 0; stop.get() == false; iter++) { @@ -150,6 +155,9 @@ protected Pattern[] getUnsafeClasses() { assertBusy(() -> assertThat(ops.get(), greaterThan(first))); } finally { stop.set(true); + for (final Thread thread : threads) { + thread.join(); + } } } @@ -183,6 +191,7 @@ protected long getBlockDetectionIntervalInMillis() { final CountDownLatch pauseUnderLock = new CountDownLatch(1); final LockedExecutor lockedExecutor = new LockedExecutor(); final AtomicLong ops = new AtomicLong(); + final List threads = new ArrayList<>(); try { for (int i = 0; i < 5; i++) { // at least one locked and one none lock thread @@ -206,6 +215,7 @@ protected long getBlockDetectionIntervalInMillis() { }); thread.setName("[" + disruptedNodeName + "][" + i + "]"); + threads.add(thread); thread.start(); } @@ -224,12 +234,13 @@ protected long getBlockDetectionIntervalInMillis() { } }); thread.setName("[" + blockedNodeName + "][" + i + "]"); + threads.add(thread); thread.start(); } // make sure some threads of test_node are under lock underLock.await(); disruption.startDisrupting(); - waitForBlockDetectionResult.await(30, TimeUnit.SECONDS); + assertTrue(waitForBlockDetectionResult.await(30, TimeUnit.SECONDS)); disruption.stopDisrupting(); ThreadInfo threadInfo = blockDetectionResult.get(); @@ -240,6 +251,9 @@ protected long getBlockDetectionIntervalInMillis() { } finally { stop.set(true); pauseUnderLock.countDown(); + for (final Thread thread : threads) { + thread.join(); + } } } } From 149629fec657e9948488195087f30583cd561f11 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 26 Apr 2017 21:45:49 +0200 Subject: [PATCH 094/619] Cross Cluster Search: propagate original indices per cluster (#24328) In case of a Cross Cluster Search, the coordinating node should split the original indices per cluster, and send over to each cluster only its own set of original indices, rather than the set taken from the original search request which contains all the indices. In fact, each remote cluster should not be aware of the indices belonging to other remote clusters. --- .../elasticsearch/action/OriginalIndices.java | 8 +- .../TransportClusterSearchShardsAction.java | 2 +- .../search/AbstractSearchAsyncAction.java | 15 ++- .../action/search/DfsQueryPhase.java | 5 +- .../action/search/FetchSearchPhase.java | 20 +-- .../action/search/InitialSearchPhase.java | 17 +-- .../search/RemoteClusterConnection.java | 6 +- .../action/search/RemoteClusterService.java | 27 ++-- .../action/search/SearchActionListener.java | 1 - .../SearchDfsQueryThenFetchAsyncAction.java | 5 +- .../action/search/SearchPhaseContext.java | 8 +- .../SearchQueryThenFetchAsyncAction.java | 8 +- .../action/search/SearchShardIterator.java | 55 ++++++++ .../action/search/SearchTransportService.java | 8 +- .../action/search/ShardSearchFailure.java | 4 +- .../action/search/TransportSearchAction.java | 60 +++++---- .../broadcast/TransportBroadcastAction.java | 4 +- .../node/TransportBroadcastByNodeAction.java | 2 +- .../TransportTermVectorsAction.java | 2 +- .../cluster/routing/GroupShardsIterator.java | 10 +- .../cluster/routing/IndexRoutingTable.java | 32 ----- .../cluster/routing/OperationRouting.java | 4 +- .../cluster/routing/PlainShardIterator.java | 1 - .../cluster/routing/PlainShardsIterator.java | 11 +- .../cluster/routing/RoutingTable.java | 17 ++- .../cluster/routing/ShardsIterator.java | 9 +- .../org/elasticsearch/search/SearchHit.java | 3 +- .../elasticsearch/search/SearchService.java | 6 +- .../search/SearchShardTarget.java | 15 ++- .../search/fetch/ShardFetchSearchRequest.java | 5 +- .../internal/ShardSearchTransportRequest.java | 5 +- .../search/query/QuerySearchRequest.java | 5 +- .../snapshots/SnapshotsService.java | 1 + .../ElasticsearchExceptionTests.java | 5 +- .../AbstractSearchAsyncActionTookTests.java | 50 +------ .../action/search/MockSearchPhaseContext.java | 9 +- .../search/RemoteClusterConnectionTests.java | 2 +- .../search/RemoteClusterServiceTests.java | 41 +++++- .../action/search/SearchAsyncActionTests.java | 24 ++-- .../search/ShardSearchFailureTests.java | 5 +- .../search/TransportSearchActionTests.java | 122 ++++++++++++++++++ .../TransportBroadcastByNodeActionTests.java | 10 +- ...rdFailedClusterStateTaskExecutorTests.java | 5 +- .../routing/GroupShardsIteratorTests.java | 4 +- .../structure/RoutingIteratorTests.java | 4 +- .../index/store/CorruptedFileIT.java | 7 +- .../index/suggest/stats/SuggestStatsIT.java | 4 +- .../elasticsearch/search/SearchHitTests.java | 4 +- .../search/SearchServiceTests.java | 42 +++--- .../ShardSearchTransportRequestTests.java | 3 +- .../search/stats/SearchStatsIT.java | 4 +- 51 files changed, 443 insertions(+), 283 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/action/search/SearchShardIterator.java create mode 100644 core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java diff --git a/core/src/main/java/org/elasticsearch/action/OriginalIndices.java b/core/src/main/java/org/elasticsearch/action/OriginalIndices.java index cc299f544b38f..39cf5c63242da 100644 --- a/core/src/main/java/org/elasticsearch/action/OriginalIndices.java +++ b/core/src/main/java/org/elasticsearch/action/OriginalIndices.java @@ -28,7 +28,10 @@ /** * Used to keep track of original indices within internal (e.g. shard level) requests */ -public class OriginalIndices implements IndicesRequest { +public final class OriginalIndices implements IndicesRequest { + + //constant to use when original indices are not applicable and will not be serialized across the wire + public static final OriginalIndices NONE = new OriginalIndices(null, null); private final String[] indices; private final IndicesOptions indicesOptions; @@ -39,7 +42,6 @@ public OriginalIndices(IndicesRequest indicesRequest) { public OriginalIndices(String[] indices, IndicesOptions indicesOptions) { this.indices = indices; - assert indicesOptions != null; this.indicesOptions = indicesOptions; } @@ -57,8 +59,8 @@ public static OriginalIndices readOriginalIndices(StreamInput in) throws IOExcep return new OriginalIndices(in.readStringArray(), IndicesOptions.readIndicesOptions(in)); } - public static void writeOriginalIndices(OriginalIndices originalIndices, StreamOutput out) throws IOException { + assert originalIndices != NONE; out.writeStringArrayNullable(originalIndices.indices); originalIndices.indicesOptions.writeIndicesOptions(out); } diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java index 01aafc0b0a940..8825a42676812 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java @@ -88,7 +88,7 @@ protected void masterOperation(final ClusterSearchShardsRequest request, final C } Set nodeIds = new HashSet<>(); - GroupShardsIterator groupShardsIterator = clusterService.operationRouting().searchShards(clusterState, concreteIndices, + GroupShardsIterator groupShardsIterator = clusterService.operationRouting().searchShards(clusterState, concreteIndices, routingMap, request.preference()); ShardRouting shard; ClusterSearchShardsGroup[] groupResponses = new ClusterSearchShardsGroup[groupShardsIterator.size()]; diff --git a/core/src/main/java/org/elasticsearch/action/search/AbstractSearchAsyncAction.java b/core/src/main/java/org/elasticsearch/action/search/AbstractSearchAsyncAction.java index c2137803411f3..0abebebdb1833 100644 --- a/core/src/main/java/org/elasticsearch/action/search/AbstractSearchAsyncAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/AbstractSearchAsyncAction.java @@ -29,7 +29,6 @@ import org.elasticsearch.action.ShardOperationFailedException; import org.elasticsearch.action.support.TransportActions; import org.elasticsearch.cluster.routing.GroupShardsIterator; -import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.util.concurrent.AtomicArray; @@ -75,8 +74,9 @@ protected AbstractSearchAsyncAction(String name, Logger logger, SearchTransportS Function nodeIdToConnection, Map aliasFilter, Map concreteIndexBoosts, Executor executor, SearchRequest request, - ActionListener listener, GroupShardsIterator shardsIts, TransportSearchAction.SearchTimeProvider timeProvider, - long clusterStateVersion, SearchTask task, SearchPhaseResults resultConsumer) { + ActionListener listener, GroupShardsIterator shardsIts, + TransportSearchAction.SearchTimeProvider timeProvider, long clusterStateVersion, + SearchTask task, SearchPhaseResults resultConsumer) { super(name, request, shardsIts, logger); this.timeProvider = timeProvider; this.logger = logger; @@ -209,8 +209,9 @@ public final void onShardFailure(final int shardIndex, @Nullable SearchShardTarg private void raisePhaseFailure(SearchPhaseExecutionException exception) { results.getSuccessfulResults().forEach((entry) -> { try { - Transport.Connection connection = nodeIdToConnection.apply(entry.getSearchShardTarget().getNodeId()); - sendReleaseSearchContext(entry.getRequestId(), connection); + SearchShardTarget searchShardTarget = entry.getSearchShardTarget(); + Transport.Connection connection = nodeIdToConnection.apply(searchShardTarget.getNodeId()); + sendReleaseSearchContext(entry.getRequestId(), connection, searchShardTarget.getOriginalIndices()); } catch (Exception inner) { inner.addSuppressed(exception); logger.trace("failed to release context", inner); @@ -296,11 +297,11 @@ public final void onFailure(Exception e) { listener.onFailure(e); } - public final ShardSearchTransportRequest buildShardSearchRequest(ShardIterator shardIt, ShardRouting shard) { + public final ShardSearchTransportRequest buildShardSearchRequest(SearchShardIterator shardIt, ShardRouting shard) { AliasFilter filter = aliasFilter.get(shard.index().getUUID()); assert filter != null; float indexBoost = concreteIndexBoosts.getOrDefault(shard.index().getUUID(), DEFAULT_INDEX_BOOST); - return new ShardSearchTransportRequest(request, shardIt.shardId(), getNumShards(), + return new ShardSearchTransportRequest(shardIt.getOriginalIndices(), request, shardIt.shardId(), getNumShards(), filter, indexBoost, timeProvider.getAbsoluteStartMillis()); } diff --git a/core/src/main/java/org/elasticsearch/action/search/DfsQueryPhase.java b/core/src/main/java/org/elasticsearch/action/search/DfsQueryPhase.java index 353baf117502a..66a88ce2fee65 100644 --- a/core/src/main/java/org/elasticsearch/action/search/DfsQueryPhase.java +++ b/core/src/main/java/org/elasticsearch/action/search/DfsQueryPhase.java @@ -73,7 +73,8 @@ public void run() throws IOException { for (final DfsSearchResult dfsResult : resultList) { final SearchShardTarget searchShardTarget = dfsResult.getSearchShardTarget(); Transport.Connection connection = context.getConnection(searchShardTarget.getNodeId()); - QuerySearchRequest querySearchRequest = new QuerySearchRequest(context.getRequest(), dfsResult.getRequestId(), dfs); + QuerySearchRequest querySearchRequest = new QuerySearchRequest(searchShardTarget.getOriginalIndices(), + dfsResult.getRequestId(), dfs); final int shardIndex = dfsResult.getShardIndex(); searchTransportService.sendExecuteQuery(connection, querySearchRequest, context.getTask(), new SearchActionListener(searchShardTarget, shardIndex) { @@ -95,7 +96,7 @@ public void onFailure(Exception exception) { // the query might not have been executed at all (for example because thread pool rejected // execution) and the search context that was created in dfs phase might not be released. // release it again to be in the safe side - context.sendReleaseSearchContext(querySearchRequest.id(), connection); + context.sendReleaseSearchContext(querySearchRequest.id(), connection, searchShardTarget.getOriginalIndices()); } } }); diff --git a/core/src/main/java/org/elasticsearch/action/search/FetchSearchPhase.java b/core/src/main/java/org/elasticsearch/action/search/FetchSearchPhase.java index a0e313f1d73f7..25231efe49b98 100644 --- a/core/src/main/java/org/elasticsearch/action/search/FetchSearchPhase.java +++ b/core/src/main/java/org/elasticsearch/action/search/FetchSearchPhase.java @@ -24,6 +24,7 @@ import org.apache.logging.log4j.util.Supplier; import org.apache.lucene.search.ScoreDoc; import org.elasticsearch.action.ActionRunnable; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.common.util.concurrent.AtomicArray; import org.elasticsearch.search.SearchPhaseResult; import org.elasticsearch.search.SearchShardTarget; @@ -73,7 +74,6 @@ final class FetchSearchPhase extends SearchPhase { this.context = context; this.logger = context.getLogger(); this.resultConsumer = resultConsumer; - } @Override @@ -112,7 +112,7 @@ private void innerRun() throws IOException { final IntArrayList[] docIdsToLoad = searchPhaseController.fillDocIdsToLoad(numShards, reducedQueryPhase.scoreDocs); if (reducedQueryPhase.scoreDocs.length == 0) { // no docs to fetch -- sidestep everything and return phaseResults.stream() - .map(e -> e.queryResult()) + .map(SearchPhaseResult::queryResult) .forEach(this::releaseIrrelevantSearchContext); // we have to release contexts here to free up resources finishPhase.run(); } else { @@ -135,10 +135,11 @@ private void innerRun() throws IOException { // in any case we count down this result since we don't talk to this shard anymore counter.countDown(); } else { - Transport.Connection connection = context.getConnection(queryResult.getSearchShardTarget().getNodeId()); + SearchShardTarget searchShardTarget = queryResult.getSearchShardTarget(); + Transport.Connection connection = context.getConnection(searchShardTarget.getNodeId()); ShardFetchSearchRequest fetchSearchRequest = createFetchRequest(queryResult.queryResult().getRequestId(), i, entry, - lastEmittedDocPerShard); - executeFetch(i, queryResult.getSearchShardTarget(), counter, fetchSearchRequest, queryResult.queryResult(), + lastEmittedDocPerShard, searchShardTarget.getOriginalIndices()); + executeFetch(i, searchShardTarget, counter, fetchSearchRequest, queryResult.queryResult(), connection); } } @@ -147,9 +148,9 @@ private void innerRun() throws IOException { } protected ShardFetchSearchRequest createFetchRequest(long queryId, int index, IntArrayList entry, - ScoreDoc[] lastEmittedDocPerShard) { + ScoreDoc[] lastEmittedDocPerShard, OriginalIndices originalIndices) { final ScoreDoc lastEmittedDoc = (lastEmittedDocPerShard != null) ? lastEmittedDocPerShard[index] : null; - return new ShardFetchSearchRequest(context.getRequest(), queryId, entry, lastEmittedDoc); + return new ShardFetchSearchRequest(originalIndices, queryId, entry, lastEmittedDoc); } private void executeFetch(final int shardIndex, final SearchShardTarget shardTarget, @@ -189,8 +190,9 @@ private void releaseIrrelevantSearchContext(QuerySearchResult queryResult) { // and if it has at lease one hit that didn't make it to the global topDocs if (context.getRequest().scroll() == null && queryResult.hasSearchContext()) { try { - Transport.Connection connection = context.getConnection(queryResult.getSearchShardTarget().getNodeId()); - context.sendReleaseSearchContext(queryResult.getRequestId(), connection); + SearchShardTarget searchShardTarget = queryResult.getSearchShardTarget(); + Transport.Connection connection = context.getConnection(searchShardTarget.getNodeId()); + context.sendReleaseSearchContext(queryResult.getRequestId(), connection, searchShardTarget.getOriginalIndices()); } catch (Exception e) { context.getLogger().trace("failed to release context", e); } diff --git a/core/src/main/java/org/elasticsearch/action/search/InitialSearchPhase.java b/core/src/main/java/org/elasticsearch/action/search/InitialSearchPhase.java index be91cebe50117..2453e2b80b5a1 100644 --- a/core/src/main/java/org/elasticsearch/action/search/InitialSearchPhase.java +++ b/core/src/main/java/org/elasticsearch/action/search/InitialSearchPhase.java @@ -46,12 +46,12 @@ */ abstract class InitialSearchPhase extends SearchPhase { private final SearchRequest request; - private final GroupShardsIterator shardsIts; + private final GroupShardsIterator shardsIts; private final Logger logger; private final int expectedTotalOps; private final AtomicInteger totalOps = new AtomicInteger(); - InitialSearchPhase(String name, SearchRequest request, GroupShardsIterator shardsIts, Logger logger) { + InitialSearchPhase(String name, SearchRequest request, GroupShardsIterator shardsIts, Logger logger) { super(name); this.request = request; this.shardsIts = shardsIts; @@ -64,10 +64,10 @@ abstract class InitialSearchPhase extends } private void onShardFailure(final int shardIndex, @Nullable ShardRouting shard, @Nullable String nodeId, - final ShardIterator shardIt, Exception e) { + final SearchShardIterator shardIt, Exception e) { // we always add the shard failure for a specific shard instance // we do make sure to clean it on a successful response from a shard - SearchShardTarget shardTarget = new SearchShardTarget(nodeId, shardIt.shardId()); + SearchShardTarget shardTarget = new SearchShardTarget(nodeId, shardIt.shardId(), shardIt.getOriginalIndices()); onShardFailure(shardIndex, shardTarget, e); if (totalOps.incrementAndGet() == expectedTotalOps) { @@ -124,7 +124,7 @@ private void onShardFailure(final int shardIndex, @Nullable ShardRouting shard, @Override public final void run() throws IOException { int shardIndex = -1; - for (final ShardIterator shardIt : shardsIts) { + for (final SearchShardIterator shardIt : shardsIts) { shardIndex++; final ShardRouting shard = shardIt.nextOrNull(); if (shard != null) { @@ -136,7 +136,7 @@ public final void run() throws IOException { } } - private void performPhaseOnShard(final int shardIndex, final ShardIterator shardIt, final ShardRouting shard) { + private void performPhaseOnShard(final int shardIndex, final SearchShardIterator shardIt, final ShardRouting shard) { if (shard == null) { // TODO upgrade this to an assert... // no more active shards... (we should not really get here, but just for safety) @@ -144,7 +144,7 @@ private void performPhaseOnShard(final int shardIndex, final ShardIterator shard } else { try { executePhaseOnShard(shardIt, shard, new SearchActionListener(new SearchShardTarget(shard.currentNodeId(), - shardIt.shardId()), shardIndex) { + shardIt.shardId(), shardIt.getOriginalIndices()), shardIndex) { @Override public void innerOnResponse(FirstResult result) { onShardResult(result, shardIt); @@ -213,7 +213,8 @@ private void onShardResult(FirstResult result, ShardIterator shardIt) { * @param shard the shard routing to send the request for * @param listener the listener to notify on response */ - protected abstract void executePhaseOnShard(ShardIterator shardIt, ShardRouting shard, SearchActionListener listener); + protected abstract void executePhaseOnShard(SearchShardIterator shardIt, ShardRouting shard, + SearchActionListener listener); /** * This class acts as a basic result collection that can be extended to do on-the-fly reduction or result processing diff --git a/core/src/main/java/org/elasticsearch/action/search/RemoteClusterConnection.java b/core/src/main/java/org/elasticsearch/action/search/RemoteClusterConnection.java index a9739cfe21ac8..a3f3f3a9612b5 100644 --- a/core/src/main/java/org/elasticsearch/action/search/RemoteClusterConnection.java +++ b/core/src/main/java/org/elasticsearch/action/search/RemoteClusterConnection.java @@ -162,7 +162,7 @@ public void onNodeDisconnected(DiscoveryNode node) { /** * Fetches all shards for the search request from this remote connection. This is used to later run the search on the remote end. */ - public void fetchSearchShards(SearchRequest searchRequest, final List indices, + public void fetchSearchShards(SearchRequest searchRequest, final String[] indices, ActionListener listener) { if (connectedNodes.isEmpty()) { // just in case if we are not connected for some reason we try to connect and if we fail we have to notify the listener @@ -176,10 +176,10 @@ public void fetchSearchShards(SearchRequest searchRequest, final List in } } - private void fetchShardsInternal(SearchRequest searchRequest, List indices, + private void fetchShardsInternal(SearchRequest searchRequest, String[] indices, final ActionListener listener) { final DiscoveryNode node = nodeSupplier.get(); - ClusterSearchShardsRequest searchShardsRequest = new ClusterSearchShardsRequest(indices.toArray(new String[indices.size()])) + ClusterSearchShardsRequest searchShardsRequest = new ClusterSearchShardsRequest(indices) .indicesOptions(searchRequest.indicesOptions()).local(true).preference(searchRequest.preference()) .routing(searchRequest.routing()); transportService.sendRequest(node, ClusterSearchShardsAction.NAME, searchShardsRequest, diff --git a/core/src/main/java/org/elasticsearch/action/search/RemoteClusterService.java b/core/src/main/java/org/elasticsearch/action/search/RemoteClusterService.java index 34cb5a84da755..40fed0299b3f9 100644 --- a/core/src/main/java/org/elasticsearch/action/search/RemoteClusterService.java +++ b/core/src/main/java/org/elasticsearch/action/search/RemoteClusterService.java @@ -22,14 +22,13 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsGroup; import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsResponse; import org.elasticsearch.action.support.GroupedActionListener; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.cluster.metadata.ClusterNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.routing.PlainShardIterator; -import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.common.Booleans; import org.elasticsearch.common.Strings; import org.elasticsearch.common.component.AbstractComponent; @@ -243,18 +242,18 @@ boolean isRemoteClusterRegistered(String clusterName) { return remoteClusters.containsKey(clusterName); } - void collectSearchShards(SearchRequest searchRequest, Map> remoteIndicesByCluster, + void collectSearchShards(SearchRequest searchRequest, Map remoteIndicesByCluster, ActionListener> listener) { final CountDown responsesCountDown = new CountDown(remoteIndicesByCluster.size()); final Map searchShardsResponses = new ConcurrentHashMap<>(); final AtomicReference transportException = new AtomicReference<>(); - for (Map.Entry> entry : remoteIndicesByCluster.entrySet()) { + for (Map.Entry entry : remoteIndicesByCluster.entrySet()) { final String clusterName = entry.getKey(); RemoteClusterConnection remoteClusterConnection = remoteClusters.get(clusterName); if (remoteClusterConnection == null) { throw new IllegalArgumentException("no such remote cluster: " + clusterName); } - final List indices = entry.getValue(); + final String[] indices = entry.getValue().indices(); remoteClusterConnection.fetchSearchShards(searchRequest, indices, new ActionListener() { @Override @@ -288,16 +287,16 @@ public void onFailure(Exception e) { } } - Function processRemoteShards(Map searchShardsResponses, - List remoteShardIterators, - Map aliasFilterMap) { + Map remoteIndicesByCluster, + List remoteShardIterators, + Map aliasFilterMap) { Map> nodeToCluster = new HashMap<>(); for (Map.Entry entry : searchShardsResponses.entrySet()) { - String clusterName = entry.getKey(); + String clusterAlias = entry.getKey(); ClusterSearchShardsResponse searchShardsResponse = entry.getValue(); for (DiscoveryNode remoteNode : searchShardsResponse.getNodes()) { - nodeToCluster.put(remoteNode.getId(), () -> getConnection(remoteNode, clusterName)); + nodeToCluster.put(remoteNode.getId(), () -> getConnection(remoteNode, clusterAlias)); } Map indicesAndFilters = searchShardsResponse.getIndicesAndFilters(); for (ClusterSearchShardsGroup clusterSearchShardsGroup : searchShardsResponse.getGroups()) { @@ -305,9 +304,11 @@ Function processRemoteShards(Map listener, - final GroupShardsIterator shardsIts, + final GroupShardsIterator shardsIts, final TransportSearchAction.SearchTimeProvider timeProvider, final long clusterStateVersion, final SearchTask task) { @@ -70,7 +69,7 @@ final class SearchDfsQueryThenFetchAsyncAction extends AbstractSearchAsyncAction @Override protected void executePhaseOnShard( - final ShardIterator shardIt, + final SearchShardIterator shardIt, final ShardRouting shard, final SearchActionListener listener) { getSearchTransport().sendExecuteDfs(getConnection(shard.currentNodeId()), diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchPhaseContext.java b/core/src/main/java/org/elasticsearch/action/search/SearchPhaseContext.java index 26c5403f4abba..a109ab9639716 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchPhaseContext.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchPhaseContext.java @@ -20,7 +20,7 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.cluster.routing.ShardIterator; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.common.Nullable; import org.elasticsearch.search.SearchShardTarget; @@ -97,16 +97,16 @@ interface SearchPhaseContext extends ActionListener, Executor { * @see org.elasticsearch.search.fetch.FetchSearchResult#getRequestId() * */ - default void sendReleaseSearchContext(long contextId, Transport.Connection connection) { + default void sendReleaseSearchContext(long contextId, Transport.Connection connection, OriginalIndices originalIndices) { if (connection != null) { - getSearchTransport().sendFreeContext(connection, contextId, getRequest()); + getSearchTransport().sendFreeContext(connection, contextId, originalIndices); } } /** * Builds an request for the initial search phase. */ - ShardSearchTransportRequest buildShardSearchRequest(ShardIterator shardIt, ShardRouting shard); + ShardSearchTransportRequest buildShardSearchRequest(SearchShardIterator shardIt, ShardRouting shard); /** * Processes the phase transition from on phase to another. This method handles all errors that happen during the initial run execution diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchQueryThenFetchAsyncAction.java b/core/src/main/java/org/elasticsearch/action/search/SearchQueryThenFetchAsyncAction.java index fd1d19770295a..855e02162849c 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchQueryThenFetchAsyncAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchQueryThenFetchAsyncAction.java @@ -22,7 +22,6 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; import org.elasticsearch.cluster.routing.GroupShardsIterator; -import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.search.SearchPhaseResult; import org.elasticsearch.search.internal.AliasFilter; @@ -32,8 +31,7 @@ import java.util.concurrent.Executor; import java.util.function.Function; -final class SearchQueryThenFetchAsyncAction - extends AbstractSearchAsyncAction { +final class SearchQueryThenFetchAsyncAction extends AbstractSearchAsyncAction { private final SearchPhaseController searchPhaseController; @@ -47,7 +45,7 @@ final class SearchQueryThenFetchAsyncAction final Executor executor, final SearchRequest request, final ActionListener listener, - final GroupShardsIterator shardsIts, + final GroupShardsIterator shardsIts, final TransportSearchAction.SearchTimeProvider timeProvider, long clusterStateVersion, SearchTask task) { @@ -70,7 +68,7 @@ final class SearchQueryThenFetchAsyncAction } protected void executePhaseOnShard( - final ShardIterator shardIt, + final SearchShardIterator shardIt, final ShardRouting shard, final SearchActionListener listener) { getSearchTransport().sendExecuteQuery( diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchShardIterator.java b/core/src/main/java/org/elasticsearch/action/search/SearchShardIterator.java new file mode 100644 index 0000000000000..ca78945a29929 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/action/search/SearchShardIterator.java @@ -0,0 +1,55 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.action.search; + +import org.elasticsearch.action.OriginalIndices; +import org.elasticsearch.cluster.routing.PlainShardIterator; +import org.elasticsearch.cluster.routing.ShardRouting; +import org.elasticsearch.index.shard.ShardId; + +import java.util.List; + +/** + * Extension of {@link PlainShardIterator} used in the search api, which also holds the {@link OriginalIndices} + * of the search request. Useful especially with cross cluster search, as each cluster has its own set of original indices. + */ +public final class SearchShardIterator extends PlainShardIterator { + + private final OriginalIndices originalIndices; + + /** + * Creates a {@link PlainShardIterator} instance that iterates over a subset of the given shards + * this the a given shardId. + * + * @param shardId shard id of the group + * @param shards shards to iterate + */ + public SearchShardIterator(ShardId shardId, List shards, OriginalIndices originalIndices) { + super(shardId, shards); + this.originalIndices = originalIndices; + } + + /** + * Returns the original indices associated with this shard iterator, specifically with the cluster that this shard belongs to. + */ + public OriginalIndices getOriginalIndices() { + return originalIndices; + } +} diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java index 80583e24c9c20..436d8da95eba1 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java @@ -92,8 +92,8 @@ public SearchTransportService(Settings settings, ClusterSettings clusterSettings } } - public void sendFreeContext(Transport.Connection connection, final long contextId, SearchRequest request) { - transportService.sendRequest(connection, FREE_CONTEXT_ACTION_NAME, new SearchFreeContextRequest(request, contextId), + public void sendFreeContext(Transport.Connection connection, final long contextId, OriginalIndices originalIndices) { + transportService.sendRequest(connection, FREE_CONTEXT_ACTION_NAME, new SearchFreeContextRequest(originalIndices, contextId), TransportRequestOptions.EMPTY, new ActionListenerResponseHandler<>(new ActionListener() { @Override public void onResponse(SearchFreeContextResponse response) { @@ -219,9 +219,9 @@ static class SearchFreeContextRequest extends ScrollFreeContextRequest implement SearchFreeContextRequest() { } - SearchFreeContextRequest(SearchRequest request, long id) { + SearchFreeContextRequest(OriginalIndices originalIndices, long id) { super(id); - this.originalIndices = new OriginalIndices(request); + this.originalIndices = originalIndices; } @Override diff --git a/core/src/main/java/org/elasticsearch/action/search/ShardSearchFailure.java b/core/src/main/java/org/elasticsearch/action/search/ShardSearchFailure.java index 2aa0ad3c7be57..6d5b30fd9bd6a 100644 --- a/core/src/main/java/org/elasticsearch/action/search/ShardSearchFailure.java +++ b/core/src/main/java/org/elasticsearch/action/search/ShardSearchFailure.java @@ -21,6 +21,7 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ExceptionsHelper; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.ShardOperationFailedException; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.Nullable; @@ -212,7 +213,8 @@ public static ShardSearchFailure fromXContent(XContentParser parser) throws IOEx } } return new ShardSearchFailure(exception, - new SearchShardTarget(nodeId, new ShardId(new Index(indexName, IndexMetaData.INDEX_UUID_NA_VALUE), shardId))); + new SearchShardTarget(nodeId, + new ShardId(new Index(indexName, IndexMetaData.INDEX_UUID_NA_VALUE), shardId), OriginalIndices.NONE)); } @Override diff --git a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 63a3ad0b62d63..6f7cc26e59e58 100644 --- a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -20,6 +20,7 @@ package org.elasticsearch.action.search; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.cluster.ClusterState; @@ -162,10 +163,8 @@ long getRelativeStartNanos() { long getRelativeCurrentNanos() { return relativeCurrentNanosProvider.getAsLong(); } - } - @Override protected void doExecute(Task task, SearchRequest searchRequest, ActionListener listener) { final long absoluteStartMillis = System.currentTimeMillis(); @@ -173,17 +172,27 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< final SearchTimeProvider timeProvider = new SearchTimeProvider(absoluteStartMillis, relativeStartNanos, System::nanoTime); - final String[] localIndices; - final Map> remoteClusterIndices; + final OriginalIndices localIndices; + final Map remoteClusterIndices; final ClusterState clusterState = clusterService.state(); if (remoteClusterService.isCrossClusterSearchEnabled()) { - remoteClusterIndices = remoteClusterService.groupClusterIndices( searchRequest.indices(), // empty string is not allowed + final Map> groupedIndices = remoteClusterService.groupClusterIndices(searchRequest.indices(), + // empty string is not allowed idx -> indexNameExpressionResolver.hasIndexOrAlias(idx, clusterState)); - List remove = remoteClusterIndices.remove(RemoteClusterService.LOCAL_CLUSTER_GROUP_KEY); - localIndices = remove == null ? Strings.EMPTY_ARRAY : remove.toArray(new String[remove.size()]); + List remove = groupedIndices.remove(RemoteClusterService.LOCAL_CLUSTER_GROUP_KEY); + String[] indices = remove == null ? Strings.EMPTY_ARRAY : remove.toArray(new String[remove.size()]); + localIndices = new OriginalIndices(indices, searchRequest.indicesOptions()); + Map originalIndicesMap = new HashMap<>(); + for (Map.Entry> entry : groupedIndices.entrySet()) { + String clusterAlias = entry.getKey(); + List originalIndices = entry.getValue(); + originalIndicesMap.put(clusterAlias, + new OriginalIndices(originalIndices.toArray(new String[originalIndices.size()]), searchRequest.indicesOptions())); + } + remoteClusterIndices = Collections.unmodifiableMap(originalIndicesMap); } else { remoteClusterIndices = Collections.emptyMap(); - localIndices = searchRequest.indices(); + localIndices = new OriginalIndices(searchRequest); } if (remoteClusterIndices.isEmpty()) { @@ -192,18 +201,18 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< } else { remoteClusterService.collectSearchShards(searchRequest, remoteClusterIndices, ActionListener.wrap((searchShardsResponses) -> { - List remoteShardIterators = new ArrayList<>(); + List remoteShardIterators = new ArrayList<>(); Map remoteAliasFilters = new HashMap<>(); Function connectionFunction = remoteClusterService.processRemoteShards( - searchShardsResponses, remoteShardIterators, remoteAliasFilters); + searchShardsResponses, remoteClusterIndices, remoteShardIterators, remoteAliasFilters); executeSearch((SearchTask)task, timeProvider, searchRequest, localIndices, remoteShardIterators, connectionFunction, clusterState, remoteAliasFilters, listener); }, listener::onFailure)); } } - private void executeSearch(SearchTask task, SearchTimeProvider timeProvider, SearchRequest searchRequest, String[] localIndices, - List remoteShardIterators, Function remoteConnections, + private void executeSearch(SearchTask task, SearchTimeProvider timeProvider, SearchRequest searchRequest, OriginalIndices localIndices, + List remoteShardIterators, Function remoteConnections, ClusterState clusterState, Map remoteAliasMap, ActionListener listener) { @@ -212,11 +221,11 @@ private void executeSearch(SearchTask task, SearchTimeProvider timeProvider, Sea // date math expressions and $now in scripts. This way all apis will deal with now in the same way instead // of just for the _search api final Index[] indices; - if (localIndices.length == 0 && remoteShardIterators.size() > 0) { + if (localIndices.indices().length == 0 && remoteShardIterators.size() > 0) { indices = Index.EMPTY_ARRAY; // don't search on _all if only remote indices were specified } else { indices = indexNameExpressionResolver.concreteIndices(clusterState, searchRequest.indicesOptions(), - timeProvider.getAbsoluteStartMillis(), localIndices); + timeProvider.getAbsoluteStartMillis(), localIndices.indices()); } Map aliasFilter = buildPerIndexAliasFilter(searchRequest, clusterState, indices, remoteAliasMap); Map> routingMap = indexNameExpressionResolver.resolveSearchRouting(clusterState, searchRequest.routing(), @@ -225,9 +234,9 @@ private void executeSearch(SearchTask task, SearchTimeProvider timeProvider, Sea for (int i = 0; i < indices.length; i++) { concreteIndices[i] = indices[i].getName(); } - GroupShardsIterator localShardsIterator = clusterService.operationRouting().searchShards(clusterState, concreteIndices, routingMap, + GroupShardsIterator localShardsIterator = clusterService.operationRouting().searchShards(clusterState, concreteIndices, routingMap, searchRequest.preference()); - GroupShardsIterator shardIterators = mergeShardsIterators(localShardsIterator, remoteShardIterators); + GroupShardsIterator shardIterators = mergeShardsIterators(localShardsIterator, localIndices, remoteShardIterators); failIfOverShardCountLimit(clusterService, shardIterators.size()); @@ -268,19 +277,17 @@ private void executeSearch(SearchTask task, SearchTimeProvider timeProvider, Sea Collections.unmodifiableMap(aliasFilter), concreteIndexBoosts, listener).start(); } - private static GroupShardsIterator mergeShardsIterators(GroupShardsIterator localShardsIterator, - List remoteShardIterators) { - if (remoteShardIterators.isEmpty()) { - return localShardsIterator; - } - List shards = new ArrayList<>(); - for (ShardIterator shardIterator : remoteShardIterators) { + static GroupShardsIterator mergeShardsIterators(GroupShardsIterator localShardsIterator, + OriginalIndices localIndices, + List remoteShardIterators) { + List shards = new ArrayList<>(); + for (SearchShardIterator shardIterator : remoteShardIterators) { shards.add(shardIterator); } for (ShardIterator shardIterator : localShardsIterator) { - shards.add(shardIterator); + shards.add(new SearchShardIterator(shardIterator.shardId(), shardIterator.getShardRoutings(), localIndices)); } - return new GroupShardsIterator(shards); + return new GroupShardsIterator<>(shards); } @Override @@ -288,7 +295,8 @@ protected final void doExecute(SearchRequest searchRequest, ActionListener shardIterators, SearchTimeProvider timeProvider, Function connectionLookup, long clusterStateVersion, Map aliasFilter, Map concreteIndexBoosts, diff --git a/core/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java b/core/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java index 0408b04cc837b..53764f4ee88d6 100644 --- a/core/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java +++ b/core/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java @@ -94,7 +94,7 @@ protected ShardResponse shardOperation(ShardRequest request, Task task) throws I * Determines the shards this operation will be executed on. The operation is executed once per shard iterator, typically * on the first shard in it. If the operation fails, it will be retried on the next shard in the iterator. */ - protected abstract GroupShardsIterator shards(ClusterState clusterState, Request request, String[] concreteIndices); + protected abstract GroupShardsIterator shards(ClusterState clusterState, Request request, String[] concreteIndices); protected abstract ClusterBlockException checkGlobalBlock(ClusterState state, Request request); @@ -107,7 +107,7 @@ protected class AsyncBroadcastAction { private final ActionListener listener; private final ClusterState clusterState; private final DiscoveryNodes nodes; - private final GroupShardsIterator shardsIts; + private final GroupShardsIterator shardsIts; private final int expectedOps; private final AtomicInteger counterOps = new AtomicInteger(); private final AtomicReferenceArray shardsResponses; diff --git a/core/src/main/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java b/core/src/main/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java index 412f54f435487..3ef967472a597 100644 --- a/core/src/main/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java +++ b/core/src/main/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java @@ -270,7 +270,7 @@ protected AsyncAction(Task task, Request request, ActionListener liste ShardsIterator shardIt = shards(clusterState, request, concreteIndices); nodeIds = new HashMap<>(); - for (ShardRouting shard : shardIt.asUnordered()) { + for (ShardRouting shard : shardIt) { // send a request to the shard only if it is assigned to a node that is in the local node's cluster state // a scenario in which a shard can be assigned but to a node that is not in the local node's cluster state // is when the shard is assigned to the master node, the local node has detected the master as failed diff --git a/core/src/main/java/org/elasticsearch/action/termvectors/TransportTermVectorsAction.java b/core/src/main/java/org/elasticsearch/action/termvectors/TransportTermVectorsAction.java index bb1e776f2e92b..5ff55a6fa552a 100644 --- a/core/src/main/java/org/elasticsearch/action/termvectors/TransportTermVectorsAction.java +++ b/core/src/main/java/org/elasticsearch/action/termvectors/TransportTermVectorsAction.java @@ -58,7 +58,7 @@ public TransportTermVectorsAction(Settings settings, ClusterService clusterServi protected ShardIterator shards(ClusterState state, InternalRequest request) { if (request.request().doc() != null && request.request().routing() == null) { // artificial document without routing specified, ignore its "id" and use either random shard or according to preference - GroupShardsIterator groupShardsIter = clusterService.operationRouting().searchShards(state, + GroupShardsIterator groupShardsIter = clusterService.operationRouting().searchShards(state, new String[] { request.concreteIndex() }, null, request.request().preference()); return groupShardsIter.iterator().next(); } diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/GroupShardsIterator.java b/core/src/main/java/org/elasticsearch/cluster/routing/GroupShardsIterator.java index e8e752fda1286..7b33c24d15ffd 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/GroupShardsIterator.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/GroupShardsIterator.java @@ -30,14 +30,14 @@ * ShardsIterators are always returned in ascending order independently of their order at construction * time. The incoming iterators are sorted to ensure consistent iteration behavior across Nodes / JVMs. */ -public final class GroupShardsIterator implements Iterable { +public final class GroupShardsIterator implements Iterable { - private final List iterators; + private final List iterators; /** * Constructs a enw GroupShardsIterator from the given list. */ - public GroupShardsIterator(List iterators) { + public GroupShardsIterator(List iterators) { CollectionUtil.timSort(iterators); this.iterators = iterators; } @@ -60,7 +60,7 @@ public int totalSize() { */ public int totalSizeWith1ForEmpty() { int size = 0; - for (ShardIterator shard : iterators) { + for (ShardIt shard : iterators) { size += Math.max(1, shard.size()); } return size; @@ -75,7 +75,7 @@ public int size() { } @Override - public Iterator iterator() { + public Iterator iterator() { return iterators.iterator(); } } diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/IndexRoutingTable.java b/core/src/main/java/org/elasticsearch/cluster/routing/IndexRoutingTable.java index c587629ef0c08..5a0bd0d426313 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/IndexRoutingTable.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/IndexRoutingTable.java @@ -23,7 +23,6 @@ import com.carrotsearch.hppc.cursors.IntCursor; import com.carrotsearch.hppc.cursors.IntObjectCursor; import org.apache.lucene.util.CollectionUtil; -import org.elasticsearch.Version; import org.elasticsearch.cluster.AbstractDiffable; import org.elasticsearch.cluster.Diff; import org.elasticsearch.cluster.metadata.IndexMetaData; @@ -265,37 +264,6 @@ public ShardsIterator randomAllActiveShardsIt() { return new PlainShardsIterator(shuffler.shuffle(allActiveShards)); } - /** - * A group shards iterator where each group ({@link ShardIterator} - * is an iterator across shard replication group. - */ - public GroupShardsIterator groupByShardsIt() { - // use list here since we need to maintain identity across shards - ArrayList set = new ArrayList<>(shards.size()); - for (IndexShardRoutingTable indexShard : this) { - set.add(indexShard.shardsIt()); - } - return new GroupShardsIterator(set); - } - - /** - * A groups shards iterator where each groups is a single {@link ShardRouting} and a group - * is created for each shard routing. - *

- * This basically means that components that use the {@link GroupShardsIterator} will iterate - * over *all* the shards (all the replicas) within the index.

- */ - public GroupShardsIterator groupByAllIt() { - // use list here since we need to maintain identity across shards - ArrayList set = new ArrayList<>(); - for (IndexShardRoutingTable indexShard : this) { - for (ShardRouting shardRouting : indexShard) { - set.add(shardRouting.shardsIt()); - } - } - return new GroupShardsIterator(set); - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/OperationRouting.java b/core/src/main/java/org/elasticsearch/cluster/routing/OperationRouting.java index 6881cc7565764..5280725169980 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/OperationRouting.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/OperationRouting.java @@ -68,7 +68,7 @@ public ShardIterator getShards(ClusterState clusterState, String index, int shar return preferenceActiveShardIterator(indexShard, clusterState.nodes().getLocalNodeId(), clusterState.nodes(), preference); } - public GroupShardsIterator searchShards(ClusterState clusterState, String[] concreteIndices, @Nullable Map> routing, @Nullable String preference) { + public GroupShardsIterator searchShards(ClusterState clusterState, String[] concreteIndices, @Nullable Map> routing, @Nullable String preference) { final Set shards = computeTargetedShards(clusterState, concreteIndices, routing); final Set set = new HashSet<>(shards.size()); for (IndexShardRoutingTable shard : shards) { @@ -77,7 +77,7 @@ public GroupShardsIterator searchShards(ClusterState clusterState, String[] conc set.add(iterator); } } - return new GroupShardsIterator(new ArrayList<>(set)); + return new GroupShardsIterator<>(new ArrayList<>(set)); } private static final Map> EMPTY_ROUTING = Collections.emptyMap(); diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/PlainShardIterator.java b/core/src/main/java/org/elasticsearch/cluster/routing/PlainShardIterator.java index 5950bd35d37f0..bb45ca66956f8 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/PlainShardIterator.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/PlainShardIterator.java @@ -43,7 +43,6 @@ public PlainShardIterator(ShardId shardId, List shards) { this.shardId = shardId; } - @Override public ShardId shardId() { return this.shardId; diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/PlainShardsIterator.java b/core/src/main/java/org/elasticsearch/cluster/routing/PlainShardsIterator.java index c2ac941607932..6cb1989a8dd02 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/PlainShardsIterator.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/PlainShardsIterator.java @@ -18,6 +18,8 @@ */ package org.elasticsearch.cluster.routing; +import java.util.Collections; +import java.util.Iterator; import java.util.List; /** @@ -74,7 +76,12 @@ public int sizeActive() { } @Override - public Iterable asUnordered() { - return shards; + public List getShardRoutings() { + return Collections.unmodifiableList(shards); + } + + @Override + public Iterator iterator() { + return shards.iterator(); } } diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/RoutingTable.java b/core/src/main/java/org/elasticsearch/cluster/routing/RoutingTable.java index 0b1a0044567d6..a248d6a939a0c 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/RoutingTable.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/RoutingTable.java @@ -238,7 +238,7 @@ public GroupShardsIterator allActiveShardsGrouped(String[] indices, boolean incl return allSatisfyingPredicateShardsGrouped(indices, includeEmpty, includeRelocationTargets, ACTIVE_PREDICATE); } - public GroupShardsIterator allAssignedShardsGrouped(String[] indices, boolean includeEmpty) { + public GroupShardsIterator allAssignedShardsGrouped(String[] indices, boolean includeEmpty) { return allAssignedShardsGrouped(indices, includeEmpty, false); } @@ -249,14 +249,14 @@ public GroupShardsIterator allAssignedShardsGrouped(String[] indices, boolean in * @param includeRelocationTargets if true, an extra shard iterator will be added for relocating shards. The extra * iterator contains a single ShardRouting pointing at the relocating target */ - public GroupShardsIterator allAssignedShardsGrouped(String[] indices, boolean includeEmpty, boolean includeRelocationTargets) { + public GroupShardsIterator allAssignedShardsGrouped(String[] indices, boolean includeEmpty, boolean includeRelocationTargets) { return allSatisfyingPredicateShardsGrouped(indices, includeEmpty, includeRelocationTargets, ASSIGNED_PREDICATE); } - private static Predicate ACTIVE_PREDICATE = shardRouting -> shardRouting.active(); - private static Predicate ASSIGNED_PREDICATE = shardRouting -> shardRouting.assignedToNode(); + private static Predicate ACTIVE_PREDICATE = ShardRouting::active; + private static Predicate ASSIGNED_PREDICATE = ShardRouting::assignedToNode; - private GroupShardsIterator allSatisfyingPredicateShardsGrouped(String[] indices, boolean includeEmpty, boolean includeRelocationTargets, Predicate predicate) { + private GroupShardsIterator allSatisfyingPredicateShardsGrouped(String[] indices, boolean includeEmpty, boolean includeRelocationTargets, Predicate predicate) { // use list here since we need to maintain identity across shards ArrayList set = new ArrayList<>(); for (String index : indices) { @@ -278,7 +278,7 @@ private GroupShardsIterator allSatisfyingPredicateShardsGrouped(String[] indices } } } - return new GroupShardsIterator(set); + return new GroupShardsIterator<>(set); } public ShardsIterator allShards(String[] indices) { @@ -320,9 +320,8 @@ private ShardsIterator allShardsSatisfyingPredicate(String[] indices, Predicate< * @param indices The indices to return all the shards (replicas) * @return All the primary shards grouped into a single shard element group each * @throws IndexNotFoundException If an index passed does not exists - * @see IndexRoutingTable#groupByAllIt() */ - public GroupShardsIterator activePrimaryShardsGrouped(String[] indices, boolean includeEmpty) { + public GroupShardsIterator activePrimaryShardsGrouped(String[] indices, boolean includeEmpty) { // use list here since we need to maintain identity across shards ArrayList set = new ArrayList<>(); for (String index : indices) { @@ -339,7 +338,7 @@ public GroupShardsIterator activePrimaryShardsGrouped(String[] indices, boolean } } } - return new GroupShardsIterator(set); + return new GroupShardsIterator<>(set); } @Override diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/ShardsIterator.java b/core/src/main/java/org/elasticsearch/cluster/routing/ShardsIterator.java index 024138e4db66e..638875ea07138 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/ShardsIterator.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/ShardsIterator.java @@ -18,10 +18,12 @@ */ package org.elasticsearch.cluster.routing; +import java.util.List; + /** * Allows to iterate over unrelated shards. */ -public interface ShardsIterator { +public interface ShardsIterator extends Iterable { /** * Resets the iterator to its initial state. @@ -60,6 +62,9 @@ public interface ShardsIterator { @Override boolean equals(Object other); - Iterable asUnordered(); + /** + * Returns the {@link ShardRouting}s that this shards iterator holds. + */ + List getShardRoutings(); } diff --git a/core/src/main/java/org/elasticsearch/search/SearchHit.java b/core/src/main/java/org/elasticsearch/search/SearchHit.java index 71b0b9127b246..d0d5047863fec 100644 --- a/core/src/main/java/org/elasticsearch/search/SearchHit.java +++ b/core/src/main/java/org/elasticsearch/search/SearchHit.java @@ -21,6 +21,7 @@ import org.apache.lucene.search.Explanation; import org.elasticsearch.ElasticsearchParseException; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; @@ -544,7 +545,7 @@ public static SearchHit createFromMap(Map values) { ShardId shardId = get(Fields._SHARD, values, null); String nodeId = get(Fields._NODE, values, null); if (shardId != null && nodeId != null) { - searchHit.shard(new SearchShardTarget(nodeId, shardId)); + searchHit.shard(new SearchShardTarget(nodeId, shardId, OriginalIndices.NONE)); } searchHit.fields(fields); return searchHit; diff --git a/core/src/main/java/org/elasticsearch/search/SearchService.java b/core/src/main/java/org/elasticsearch/search/SearchService.java index e601cec0fea55..b1192c59e4cc7 100644 --- a/core/src/main/java/org/elasticsearch/search/SearchService.java +++ b/core/src/main/java/org/elasticsearch/search/SearchService.java @@ -24,6 +24,7 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ExceptionsHelper; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.search.SearchTask; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.service.ClusterService; @@ -40,7 +41,6 @@ import org.elasticsearch.index.IndexService; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.engine.Engine; -import org.elasticsearch.search.collapse.CollapseContext; import org.elasticsearch.index.query.InnerHitBuilder; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.shard.IndexEventListener; @@ -55,6 +55,7 @@ import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.SearchContextAggregations; import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.collapse.CollapseContext; import org.elasticsearch.search.dfs.DfsPhase; import org.elasticsearch.search.dfs.DfsSearchResult; import org.elasticsearch.search.fetch.FetchPhase; @@ -498,7 +499,8 @@ public DefaultSearchContext createSearchContext(ShardSearchRequest request, Time throws IOException { IndexService indexService = indicesService.indexServiceSafe(request.shardId().getIndex()); IndexShard indexShard = indexService.getShard(request.shardId().getId()); - SearchShardTarget shardTarget = new SearchShardTarget(clusterService.localNode().getId(), indexShard.shardId()); + SearchShardTarget shardTarget = new SearchShardTarget(clusterService.localNode().getId(), + indexShard.shardId(), OriginalIndices.NONE); Engine.Searcher engineSearcher = searcher == null ? indexShard.acquireSearcher("search") : searcher; final DefaultSearchContext searchContext = new DefaultSearchContext(idGenerator.incrementAndGet(), request, shardTarget, diff --git a/core/src/main/java/org/elasticsearch/search/SearchShardTarget.java b/core/src/main/java/org/elasticsearch/search/SearchShardTarget.java index 5fd20555f81a0..88045cd361825 100644 --- a/core/src/main/java/org/elasticsearch/search/SearchShardTarget.java +++ b/core/src/main/java/org/elasticsearch/search/SearchShardTarget.java @@ -19,6 +19,7 @@ package org.elasticsearch.search; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -36,6 +37,9 @@ public class SearchShardTarget implements Writeable, Comparable createAction( System::nanoTime); } - final ShardIterator it = new ShardIterator() { - @Override - public ShardId shardId() { - return null; - } - - @Override - public void reset() { - - } - - @Override - public int compareTo(ShardIterator o) { - return 0; - } - - @Override - public int size() { - return 0; - } - - @Override - public int sizeActive() { - return 0; - } - - @Override - public ShardRouting nextOrNull() { - return null; - } - - @Override - public int remaining() { - return 0; - } - - @Override - public Iterable asUnordered() { - return null; - } - }; - return new AbstractSearchAsyncAction( "test", null, @@ -108,7 +63,7 @@ public Iterable asUnordered() { null, null, null, - new GroupShardsIterator(Collections.singletonList(it)), + new GroupShardsIterator<>(Collections.singletonList(new SearchShardIterator(null, Collections.emptyList(), null))), timeProvider, 0, null, @@ -123,7 +78,7 @@ protected SearchPhase getNextPhase( @Override protected void executePhaseOnShard( - final ShardIterator shardIt, + final SearchShardIterator shardIt, final ShardRouting shard, final SearchActionListener listener) { @@ -157,5 +112,4 @@ private void runTestTook(final boolean controlled) { assertThat(actual, greaterThanOrEqualTo(TimeUnit.NANOSECONDS.toMillis(expected.get()))); } } - } diff --git a/core/src/test/java/org/elasticsearch/action/search/MockSearchPhaseContext.java b/core/src/test/java/org/elasticsearch/action/search/MockSearchPhaseContext.java index 4a5b65c0a0a64..98b6d2e7527bd 100644 --- a/core/src/test/java/org/elasticsearch/action/search/MockSearchPhaseContext.java +++ b/core/src/test/java/org/elasticsearch/action/search/MockSearchPhaseContext.java @@ -19,7 +19,7 @@ package org.elasticsearch.action.search; import org.apache.logging.log4j.Logger; -import org.elasticsearch.cluster.routing.ShardIterator; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.logging.Loggers; @@ -29,14 +29,11 @@ import org.elasticsearch.transport.Transport; import org.junit.Assert; -import java.io.IOException; -import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -114,7 +111,7 @@ public SearchTransportService getSearchTransport() { } @Override - public ShardSearchTransportRequest buildShardSearchRequest(ShardIterator shardIt, ShardRouting shard) { + public ShardSearchTransportRequest buildShardSearchRequest(SearchShardIterator shardIt, ShardRouting shard) { Assert.fail("should not be called"); return null; } @@ -145,7 +142,7 @@ public void onFailure(Exception e) { } @Override - public void sendReleaseSearchContext(long contextId, Transport.Connection connection) { + public void sendReleaseSearchContext(long contextId, Transport.Connection connection, OriginalIndices originalIndices) { releasedSearchContexts.add(contextId); } } diff --git a/core/src/test/java/org/elasticsearch/action/search/RemoteClusterConnectionTests.java b/core/src/test/java/org/elasticsearch/action/search/RemoteClusterConnectionTests.java index d73b6709121da..8cf6d7d48c77e 100644 --- a/core/src/test/java/org/elasticsearch/action/search/RemoteClusterConnectionTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/RemoteClusterConnectionTests.java @@ -382,7 +382,7 @@ public void testFetchShards() throws Exception { failReference.set(x); responseLatch.countDown(); }); - connection.fetchSearchShards(request, Arrays.asList("test-index"), shardsListener); + connection.fetchSearchShards(request, new String[]{"test-index"}, shardsListener); responseLatch.await(); assertNull(failReference.get()); assertNotNull(reference.get()); diff --git a/core/src/test/java/org/elasticsearch/action/search/RemoteClusterServiceTests.java b/core/src/test/java/org/elasticsearch/action/search/RemoteClusterServiceTests.java index 81ee9141e2b59..63f6e8aa5a63e 100644 --- a/core/src/test/java/org/elasticsearch/action/search/RemoteClusterServiceTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/RemoteClusterServiceTests.java @@ -20,10 +20,11 @@ import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsGroup; import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsResponse; +import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRoutingState; import org.elasticsearch.cluster.routing.TestShardRouting; @@ -204,7 +205,7 @@ public void testIncrementallyAddClusters() throws IOException { public void testProcessRemoteShards() throws IOException { try (RemoteClusterService service = new RemoteClusterService(Settings.EMPTY, null)) { assertFalse(service.isCrossClusterSearchEnabled()); - List iteratorList = new ArrayList<>(); + List iteratorList = new ArrayList<>(); Map searchShardsResponseMap = new HashMap<>(); DiscoveryNode[] nodes = new DiscoveryNode[] { new DiscoveryNode("node1", buildNewFakeTransportAddress(), Version.CURRENT), @@ -225,11 +226,26 @@ public void testProcessRemoteShards() throws IOException { TestShardRouting.newShardRouting("bar", 0, "node1", false, ShardRoutingState.STARTED)}) }; searchShardsResponseMap.put("test_cluster_1", new ClusterSearchShardsResponse(groups, nodes, indicesAndAliases)); + DiscoveryNode[] nodes2 = new DiscoveryNode[] { + new DiscoveryNode("node3", buildNewFakeTransportAddress(), Version.CURRENT) + }; + ClusterSearchShardsGroup[] groups2 = new ClusterSearchShardsGroup[] { + new ClusterSearchShardsGroup(new ShardId("xyz", "xyz_id", 0), + new ShardRouting[] {TestShardRouting.newShardRouting("xyz", 0, "node3", true, ShardRoutingState.STARTED)}) + }; + searchShardsResponseMap.put("test_cluster_2", new ClusterSearchShardsResponse(groups2, nodes2, null)); + + Map remoteIndicesByCluster = new HashMap<>(); + remoteIndicesByCluster.put("test_cluster_1", + new OriginalIndices(new String[]{"fo*", "ba*"}, IndicesOptions.strictExpandOpenAndForbidClosed())); + remoteIndicesByCluster.put("test_cluster_2", + new OriginalIndices(new String[]{"x*"}, IndicesOptions.strictExpandOpenAndForbidClosed())); Map remoteAliases = new HashMap<>(); - service.processRemoteShards(searchShardsResponseMap, iteratorList, remoteAliases); - assertEquals(3, iteratorList.size()); - for (ShardIterator iterator : iteratorList) { + service.processRemoteShards(searchShardsResponseMap, remoteIndicesByCluster, iteratorList, remoteAliases); + assertEquals(4, iteratorList.size()); + for (SearchShardIterator iterator : iteratorList) { if (iterator.shardId().getIndexName().endsWith("foo")) { + assertArrayEquals(new String[]{"fo*", "ba*"}, iterator.getOriginalIndices().indices()); assertTrue(iterator.shardId().getId() == 0 || iterator.shardId().getId() == 1); assertEquals("test_cluster_1:foo", iterator.shardId().getIndexName()); ShardRouting shardRouting = iterator.nextOrNull(); @@ -239,7 +255,8 @@ public void testProcessRemoteShards() throws IOException { assertNotNull(shardRouting); assertEquals(shardRouting.getIndexName(), "foo"); assertNull(iterator.nextOrNull()); - } else { + } else if (iterator.shardId().getIndexName().endsWith("bar")) { + assertArrayEquals(new String[]{"fo*", "ba*"}, iterator.getOriginalIndices().indices()); assertEquals(0, iterator.shardId().getId()); assertEquals("test_cluster_1:bar", iterator.shardId().getIndexName()); ShardRouting shardRouting = iterator.nextOrNull(); @@ -249,13 +266,23 @@ public void testProcessRemoteShards() throws IOException { assertNotNull(shardRouting); assertEquals(shardRouting.getIndexName(), "bar"); assertNull(iterator.nextOrNull()); + } else if (iterator.shardId().getIndexName().endsWith("xyz")) { + assertArrayEquals(new String[]{"x*"}, iterator.getOriginalIndices().indices()); + assertEquals(0, iterator.shardId().getId()); + assertEquals("test_cluster_2:xyz", iterator.shardId().getIndexName()); + ShardRouting shardRouting = iterator.nextOrNull(); + assertNotNull(shardRouting); + assertEquals(shardRouting.getIndexName(), "xyz"); + assertNull(iterator.nextOrNull()); } } - assertEquals(2, remoteAliases.size()); + assertEquals(3, remoteAliases.size()); assertTrue(remoteAliases.toString(), remoteAliases.containsKey("foo_id")); assertTrue(remoteAliases.toString(), remoteAliases.containsKey("bar_id")); + assertTrue(remoteAliases.toString(), remoteAliases.containsKey("xyz_id")); assertEquals(new TermsQueryBuilder("foo", "bar"), remoteAliases.get("foo_id").getQueryBuilder()); assertEquals(new MatchAllQueryBuilder(), remoteAliases.get("bar_id").getQueryBuilder()); + assertNull(remoteAliases.get("xyz_id").getQueryBuilder()); } } diff --git a/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java b/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java index 4813dc8ae7d17..2d94fe2edd0db 100644 --- a/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java @@ -20,11 +20,11 @@ import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.OriginalIndices; +import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.GroupShardsIterator; -import org.elasticsearch.cluster.routing.PlainShardIterator; import org.elasticsearch.cluster.routing.RecoverySource; -import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.UnassignedInfo; import org.elasticsearch.common.Strings; @@ -76,12 +76,14 @@ public void onFailure(Exception e) { Map> nodeToContextMap = new HashMap<>(); AtomicInteger contextIdGenerator = new AtomicInteger(0); - GroupShardsIterator shardsIter = getShardsIter("idx", randomIntBetween(1, 10), randomBoolean(), primaryNode, replicaNode); + GroupShardsIterator shardsIter = getShardsIter("idx", + new OriginalIndices(new String[]{"idx"}, IndicesOptions.strictExpandOpenAndForbidClosed()), + randomIntBetween(1, 10), randomBoolean(), primaryNode, replicaNode); AtomicInteger numFreedContext = new AtomicInteger(); SearchTransportService transportService = new SearchTransportService(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, Collections.singleton(RemoteClusterService.REMOTE_CLUSTERS_SEEDS)), null) { @Override - public void sendFreeContext(Transport.Connection connection, long contextId, SearchRequest request) { + public void sendFreeContext(Transport.Connection connection, long contextId, OriginalIndices originalIndices) { numFreedContext.incrementAndGet(); assertTrue(nodeToContextMap.containsKey(connection.getNode())); assertTrue(nodeToContextMap.get(connection.getNode()).remove(contextId)); @@ -110,7 +112,7 @@ public void sendFreeContext(Transport.Connection connection, long contextId, Sea TestSearchResponse response = new TestSearchResponse(); @Override - protected void executePhaseOnShard(ShardIterator shardIt, ShardRouting shard, SearchActionListener + protected void executePhaseOnShard(SearchShardIterator shardIt, ShardRouting shard, SearchActionListener listener) { assertTrue("shard: " + shard.shardId() + " has been queried twice", response.queried.add(shard.shardId())); Transport.Connection connection = getConnection(shard.currentNodeId()); @@ -133,7 +135,7 @@ public void run() throws IOException { for (int i = 0; i < results.getNumShards(); i++) { TestSearchPhaseResult result = results.results.get(i); assertEquals(result.node.getId(), result.getSearchShardTarget().getNodeId()); - sendReleaseSearchContext(result.getRequestId(), new MockConnection(result.node)); + sendReleaseSearchContext(result.getRequestId(), new MockConnection(result.node), OriginalIndices.NONE); } responseListener.onResponse(response); latch.countDown(); @@ -154,9 +156,9 @@ public void run() throws IOException { } } - private GroupShardsIterator getShardsIter(String index, int numShards, boolean doReplicas, DiscoveryNode primaryNode, - DiscoveryNode replicaNode) { - ArrayList list = new ArrayList<>(); + private static GroupShardsIterator getShardsIter(String index, OriginalIndices originalIndices, int numShards, + boolean doReplicas, DiscoveryNode primaryNode, DiscoveryNode replicaNode) { + ArrayList list = new ArrayList<>(); for (int i = 0; i < numShards; i++) { ArrayList started = new ArrayList<>(); ArrayList initializing = new ArrayList<>(); @@ -184,9 +186,9 @@ private GroupShardsIterator getShardsIter(String index, int numShards, boolean d } Collections.shuffle(started, random()); started.addAll(initializing); - list.add(new PlainShardIterator(new ShardId(new Index(index, "_na_"), i), started)); + list.add(new SearchShardIterator(new ShardId(new Index(index, "_na_"), i), started, originalIndices)); } - return new GroupShardsIterator(list); + return new GroupShardsIterator<>(list); } public static class TestSearchResponse extends SearchResponse { diff --git a/core/src/test/java/org/elasticsearch/action/search/ShardSearchFailureTests.java b/core/src/test/java/org/elasticsearch/action/search/ShardSearchFailureTests.java index 96afbb276d5b9..eac949c7753a2 100644 --- a/core/src/test/java/org/elasticsearch/action/search/ShardSearchFailureTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/ShardSearchFailureTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.action.search; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.XContentParser; @@ -42,7 +43,7 @@ public static ShardSearchFailure createTestItem() { String indexUuid = randomAlphaOfLengthBetween(5, 10); int shardId = randomInt(); return new ShardSearchFailure(ex, - new SearchShardTarget(nodeId, new ShardId(new Index(indexName, indexUuid), shardId))); + new SearchShardTarget(nodeId, new ShardId(new Index(indexName, indexUuid), shardId), null)); } public void testFromXContent() throws IOException { @@ -73,7 +74,7 @@ public void testFromXContent() throws IOException { public void testToXContent() throws IOException { ShardSearchFailure failure = new ShardSearchFailure(new ParsingException(0, 0, "some message", null), - new SearchShardTarget("nodeId", new ShardId(new Index("indexName", "indexUuid"), 123))); + new SearchShardTarget("nodeId", new ShardId(new Index("indexName", "indexUuid"), 123), OriginalIndices.NONE)); BytesReference xContent = toXContent(failure, XContentType.JSON, randomBoolean()); assertEquals( "{\"shard\":123," diff --git a/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java b/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java new file mode 100644 index 0000000000000..696e25de75e9b --- /dev/null +++ b/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java @@ -0,0 +1,122 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.action.search; + +import org.elasticsearch.action.OriginalIndices; +import org.elasticsearch.action.support.IndicesOptions; +import org.elasticsearch.cluster.routing.GroupShardsIterator; +import org.elasticsearch.cluster.routing.PlainShardIterator; +import org.elasticsearch.cluster.routing.ShardIterator; +import org.elasticsearch.cluster.routing.ShardRouting; +import org.elasticsearch.cluster.routing.TestShardRouting; +import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.elasticsearch.cluster.routing.ShardRoutingState.STARTED; + +public class TransportSearchActionTests extends ESTestCase { + + public void testMergeShardsIterators() throws IOException { + List localShardIterators = new ArrayList<>(); + { + ShardId shardId = new ShardId("local_index", "local_index_uuid", 0); + ShardRouting shardRouting = TestShardRouting.newShardRouting(shardId, "local_node", true, STARTED); + ShardIterator shardIterator = new PlainShardIterator(shardId, Collections.singletonList(shardRouting)); + localShardIterators.add(shardIterator); + } + { + ShardId shardId2 = new ShardId("local_index_2", "local_index_2_uuid", 1); + ShardRouting shardRouting2 = TestShardRouting.newShardRouting(shardId2, "local_node", true, STARTED); + ShardIterator shardIterator2 = new PlainShardIterator(shardId2, Collections.singletonList(shardRouting2)); + localShardIterators.add(shardIterator2); + } + GroupShardsIterator localShardsIterator = new GroupShardsIterator<>(localShardIterators); + + OriginalIndices localIndices = new OriginalIndices(new String[]{"local_alias", "local_index_2"}, + IndicesOptions.strictExpandOpenAndForbidClosed()); + + OriginalIndices remoteIndices = new OriginalIndices(new String[]{"remote_alias", "remote_index_2"}, + IndicesOptions.strictExpandOpen()); + List remoteShardIterators = new ArrayList<>(); + { + ShardId remoteShardId = new ShardId("remote_index", "remote_index_uuid", 2); + ShardRouting remoteShardRouting = TestShardRouting.newShardRouting(remoteShardId, "remote_node", true, STARTED); + SearchShardIterator remoteShardIterator = new SearchShardIterator(remoteShardId, + Collections.singletonList(remoteShardRouting), remoteIndices); + remoteShardIterators.add(remoteShardIterator); + } + { + ShardId remoteShardId2 = new ShardId("remote_index_2", "remote_index_2_uuid", 3); + ShardRouting remoteShardRouting2 = TestShardRouting.newShardRouting(remoteShardId2, "remote_node", true, STARTED); + SearchShardIterator remoteShardIterator2 = new SearchShardIterator(remoteShardId2, + Collections.singletonList(remoteShardRouting2), remoteIndices); + remoteShardIterators.add(remoteShardIterator2); + } + OriginalIndices remoteIndices2 = new OriginalIndices(new String[]{"remote_index_3"}, IndicesOptions.strictExpand()); + + { + ShardId remoteShardId3 = new ShardId("remote_index_3", "remote_index_3_uuid", 4); + ShardRouting remoteShardRouting3 = TestShardRouting.newShardRouting(remoteShardId3, "remote_node", true, STARTED); + SearchShardIterator remoteShardIterator3 = new SearchShardIterator(remoteShardId3, + Collections.singletonList(remoteShardRouting3), remoteIndices2); + remoteShardIterators.add(remoteShardIterator3); + } + + GroupShardsIterator searchShardIterators = TransportSearchAction.mergeShardsIterators(localShardsIterator, + localIndices, remoteShardIterators); + + assertEquals(searchShardIterators.size(), 5); + int i = 0; + for (SearchShardIterator searchShardIterator : searchShardIterators) { + switch(i++) { + case 0: + assertEquals("local_index", searchShardIterator.shardId().getIndexName()); + assertEquals(0, searchShardIterator.shardId().getId()); + assertSame(localIndices, searchShardIterator.getOriginalIndices()); + break; + case 1: + assertEquals("local_index_2", searchShardIterator.shardId().getIndexName()); + assertEquals(1, searchShardIterator.shardId().getId()); + assertSame(localIndices, searchShardIterator.getOriginalIndices()); + break; + case 2: + assertEquals("remote_index", searchShardIterator.shardId().getIndexName()); + assertEquals(2, searchShardIterator.shardId().getId()); + assertSame(remoteIndices, searchShardIterator.getOriginalIndices()); + break; + case 3: + assertEquals("remote_index_2", searchShardIterator.shardId().getIndexName()); + assertEquals(3, searchShardIterator.shardId().getId()); + assertSame(remoteIndices, searchShardIterator.getOriginalIndices()); + break; + case 4: + assertEquals("remote_index_3", searchShardIterator.shardId().getIndexName()); + assertEquals(4, searchShardIterator.shardId().getId()); + assertSame(remoteIndices2, searchShardIterator.getOriginalIndices()); + break; + } + } + } +} diff --git a/core/src/test/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeActionTests.java b/core/src/test/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeActionTests.java index ef025e3c37b01..93d8be990de8c 100644 --- a/core/src/test/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeActionTests.java @@ -296,7 +296,7 @@ public void testOneRequestIsSentToEachNodeHoldingAShard() { ShardsIterator shardIt = clusterService.state().routingTable().allShards(new String[]{TEST_INDEX}); Set set = new HashSet<>(); - for (ShardRouting shard : shardIt.asUnordered()) { + for (ShardRouting shard : shardIt) { set.add(shard.currentNodeId()); } @@ -332,7 +332,7 @@ public void testRequestsAreNotSentToFailedMaster() { // the master should not be in the list of nodes that requests were sent to ShardsIterator shardIt = clusterService.state().routingTable().allShards(new String[]{TEST_INDEX}); Set set = new HashSet<>(); - for (ShardRouting shard : shardIt.asUnordered()) { + for (ShardRouting shard : shardIt) { if (!shard.currentNodeId().equals(masterNode.getId())) { set.add(shard.currentNodeId()); } @@ -352,8 +352,8 @@ public void testRequestsAreNotSentToFailedMaster() { public void testOperationExecution() throws Exception { ShardsIterator shardIt = clusterService.state().routingTable().allShards(new String[]{TEST_INDEX}); Set shards = new HashSet<>(); - String nodeId = shardIt.asUnordered().iterator().next().currentNodeId(); - for (ShardRouting shard : shardIt.asUnordered()) { + String nodeId = shardIt.iterator().next().currentNodeId(); + for (ShardRouting shard : shardIt) { if (nodeId.equals(shard.currentNodeId())) { shards.add(shard); } @@ -417,7 +417,7 @@ public void testResultAggregation() throws ExecutionException, InterruptedExcept ShardsIterator shardIt = clusterService.state().getRoutingTable().allShards(new String[]{TEST_INDEX}); Map> map = new HashMap<>(); - for (ShardRouting shard : shardIt.asUnordered()) { + for (ShardRouting shard : shardIt) { if (!map.containsKey(shard.currentNodeId())) { map.put(shard.currentNodeId(), new ArrayList<>()); } diff --git a/core/src/test/java/org/elasticsearch/cluster/action/shard/ShardFailedClusterStateTaskExecutorTests.java b/core/src/test/java/org/elasticsearch/cluster/action/shard/ShardFailedClusterStateTaskExecutorTests.java index 91420fa227aab..fe11cc9cd5fd5 100644 --- a/core/src/test/java/org/elasticsearch/cluster/action/shard/ShardFailedClusterStateTaskExecutorTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/action/shard/ShardFailedClusterStateTaskExecutorTests.java @@ -165,10 +165,9 @@ private ClusterState createClusterStateWithStartedShards(String reason) { private List createExistingShards(ClusterState currentState, String reason) { List shards = new ArrayList<>(); - GroupShardsIterator shardGroups = - currentState.routingTable().allAssignedShardsGrouped(new String[] { INDEX }, true); + GroupShardsIterator shardGroups = currentState.routingTable().allAssignedShardsGrouped(new String[] { INDEX }, true); for (ShardIterator shardIt : shardGroups) { - for (ShardRouting shard : shardIt.asUnordered()) { + for (ShardRouting shard : shardIt) { shards.add(shard); } } diff --git a/core/src/test/java/org/elasticsearch/cluster/routing/GroupShardsIteratorTests.java b/core/src/test/java/org/elasticsearch/cluster/routing/GroupShardsIteratorTests.java index 72cf0391fd451..f2571fce3391d 100644 --- a/core/src/test/java/org/elasticsearch/cluster/routing/GroupShardsIteratorTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/routing/GroupShardsIteratorTests.java @@ -43,7 +43,7 @@ public void testSize() { list.add(new PlainShardIterator(new ShardId(index, 0), Arrays.asList(newRouting(index, 0, true)))); list.add(new PlainShardIterator(new ShardId(index, 1), Arrays.asList(newRouting(index, 1, true)))); - GroupShardsIterator iter = new GroupShardsIterator(list); + GroupShardsIterator iter = new GroupShardsIterator<>(list); assertEquals(7, iter.totalSizeWith1ForEmpty()); assertEquals(5, iter.size()); assertEquals(6, iter.totalSize()); @@ -67,7 +67,7 @@ public void testIterate() { Collections.shuffle(list, random()); ArrayList actualIterators = new ArrayList<>(); - GroupShardsIterator iter = new GroupShardsIterator(list); + GroupShardsIterator iter = new GroupShardsIterator<>(list); for (ShardIterator shardsIterator : iter) { actualIterators.add(shardsIterator); } diff --git a/core/src/test/java/org/elasticsearch/cluster/structure/RoutingIteratorTests.java b/core/src/test/java/org/elasticsearch/cluster/structure/RoutingIteratorTests.java index 86fa25872e042..172bcd6bd558b 100644 --- a/core/src/test/java/org/elasticsearch/cluster/structure/RoutingIteratorTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/structure/RoutingIteratorTests.java @@ -376,7 +376,7 @@ public void testShardsAndPreferNodeRouting() { OperationRouting operationRouting = new OperationRouting(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)); - GroupShardsIterator shardIterators = operationRouting.searchShards(clusterState, new String[]{"test"}, null, "_shards:0"); + GroupShardsIterator shardIterators = operationRouting.searchShards(clusterState, new String[]{"test"}, null, "_shards:0"); assertThat(shardIterators.size(), equalTo(1)); assertThat(shardIterators.iterator().next().shardId().id(), equalTo(0)); @@ -443,7 +443,7 @@ public void testReplicaShardPreferenceIters() throws Exception { clusterState = strategy.applyStartedShards(clusterState, clusterState.getRoutingNodes().shardsWithState(INITIALIZING)); // When replicas haven't initialized, it comes back with the primary first, then initializing replicas - GroupShardsIterator shardIterators = operationRouting.searchShards(clusterState, new String[]{"test"}, null, "_replica_first"); + GroupShardsIterator shardIterators = operationRouting.searchShards(clusterState, new String[]{"test"}, null, "_replica_first"); assertThat(shardIterators.size(), equalTo(2)); // two potential shards ShardIterator iter = shardIterators.iterator().next(); assertThat(iter.size(), equalTo(3)); // three potential candidates for the shard diff --git a/core/src/test/java/org/elasticsearch/index/store/CorruptedFileIT.java b/core/src/test/java/org/elasticsearch/index/store/CorruptedFileIT.java index 91cfba0c70db5..9bb180c9818ac 100644 --- a/core/src/test/java/org/elasticsearch/index/store/CorruptedFileIT.java +++ b/core/src/test/java/org/elasticsearch/index/store/CorruptedFileIT.java @@ -35,7 +35,6 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.cluster.metadata.IndexMetaData; -import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.GroupShardsIterator; import org.elasticsearch.cluster.routing.IndexShardRoutingTable; import org.elasticsearch.cluster.routing.ShardIterator; @@ -52,7 +51,6 @@ import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.gateway.PrimaryShardAllocator; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.MergePolicyConfig; @@ -73,9 +71,6 @@ import org.elasticsearch.test.junit.annotations.TestLogging; import org.elasticsearch.test.store.MockFSIndexStore; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.ConnectionProfile; -import org.elasticsearch.transport.Transport; -import org.elasticsearch.transport.TransportException; import org.elasticsearch.transport.TransportRequest; import org.elasticsearch.transport.TransportRequestOptions; import org.elasticsearch.transport.TransportService; @@ -292,7 +287,7 @@ public void testCorruptPrimaryNoReplica() throws ExecutionException, Interrupted } assertThat(response.getStatus(), is(ClusterHealthStatus.RED)); ClusterState state = client().admin().cluster().prepareState().get().getState(); - GroupShardsIterator shardIterators = state.getRoutingTable().activePrimaryShardsGrouped(new String[]{"test"}, false); + GroupShardsIterator shardIterators = state.getRoutingTable().activePrimaryShardsGrouped(new String[]{"test"}, false); for (ShardIterator iterator : shardIterators) { ShardRouting routing; while ((routing = iterator.nextOrNull()) != null) { diff --git a/core/src/test/java/org/elasticsearch/index/suggest/stats/SuggestStatsIT.java b/core/src/test/java/org/elasticsearch/index/suggest/stats/SuggestStatsIT.java index 925bf56fe700a..25eb6df4c278f 100644 --- a/core/src/test/java/org/elasticsearch/index/suggest/stats/SuggestStatsIT.java +++ b/core/src/test/java/org/elasticsearch/index/suggest/stats/SuggestStatsIT.java @@ -150,10 +150,10 @@ private SearchRequestBuilder addSuggestions(SearchRequestBuilder request, int i) private Set nodeIdsWithIndex(String... indices) { ClusterState state = client().admin().cluster().prepareState().execute().actionGet().getState(); - GroupShardsIterator allAssignedShardsGrouped = state.routingTable().allAssignedShardsGrouped(indices, true); + GroupShardsIterator allAssignedShardsGrouped = state.routingTable().allAssignedShardsGrouped(indices, true); Set nodes = new HashSet<>(); for (ShardIterator shardIterator : allAssignedShardsGrouped) { - for (ShardRouting routing : shardIterator.asUnordered()) { + for (ShardRouting routing : shardIterator) { if (routing.active()) { nodes.add(routing.currentNodeId()); } diff --git a/core/src/test/java/org/elasticsearch/search/SearchHitTests.java b/core/src/test/java/org/elasticsearch/search/SearchHitTests.java index 51fffc3e95f32..a2c11e8a64157 100644 --- a/core/src/test/java/org/elasticsearch/search/SearchHitTests.java +++ b/core/src/test/java/org/elasticsearch/search/SearchHitTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.search; import org.apache.lucene.search.Explanation; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.Tuple; @@ -128,7 +129,8 @@ public static SearchHit createTestItem(boolean withOptionalInnerHits) { } if (randomBoolean()) { hit.shard(new SearchShardTarget(randomAlphaOfLengthBetween(5, 10), - new ShardId(new Index(randomAlphaOfLengthBetween(5, 10), randomAlphaOfLengthBetween(5, 10)), randomInt()))); + new ShardId(new Index(randomAlphaOfLengthBetween(5, 10), randomAlphaOfLengthBetween(5, 10)), randomInt()), + OriginalIndices.NONE)); } return hit; } diff --git a/core/src/test/java/org/elasticsearch/search/SearchServiceTests.java b/core/src/test/java/org/elasticsearch/search/SearchServiceTests.java index 6fc795a88255f..31edc3ac808ea 100644 --- a/core/src/test/java/org/elasticsearch/search/SearchServiceTests.java +++ b/core/src/test/java/org/elasticsearch/search/SearchServiceTests.java @@ -184,8 +184,8 @@ public void onFailure(Exception e) { for (int i = 0; i < rounds; i++) { try { SearchPhaseResult searchPhaseResult = service.executeQueryPhase( - new ShardSearchLocalRequest(indexShard.shardId(), 1, SearchType.DEFAULT, - new SearchSourceBuilder(), new String[0], false, new AliasFilter(null, Strings.EMPTY_ARRAY), 1.0f), + new ShardSearchLocalRequest(indexShard.shardId(), 1, SearchType.DEFAULT, + new SearchSourceBuilder(), new String[0], false, new AliasFilter(null, Strings.EMPTY_ARRAY), 1.0f), new SearchTask(123L, "", "", "", null)); IntArrayList intCursors = new IntArrayList(1); intCursors.add(0); @@ -213,16 +213,16 @@ public void testTimeout() throws IOException { final IndexService indexService = indicesService.indexServiceSafe(resolveIndex("index")); final IndexShard indexShard = indexService.getShard(0); final SearchContext contextWithDefaultTimeout = service.createContext( - new ShardSearchLocalRequest( - indexShard.shardId(), - 1, - SearchType.DEFAULT, - new SearchSourceBuilder(), - new String[0], - false, - new AliasFilter(null, Strings.EMPTY_ARRAY), - 1.0f), - null); + new ShardSearchLocalRequest( + indexShard.shardId(), + 1, + SearchType.DEFAULT, + new SearchSourceBuilder(), + new String[0], + false, + new AliasFilter(null, Strings.EMPTY_ARRAY), + 1.0f), + null); try { // the search context should inherit the default timeout assertThat(contextWithDefaultTimeout.timeout(), equalTo(TimeValue.timeValueSeconds(5))); @@ -233,15 +233,15 @@ public void testTimeout() throws IOException { final long seconds = randomIntBetween(6, 10); final SearchContext context = service.createContext( - new ShardSearchLocalRequest( - indexShard.shardId(), - 1, - SearchType.DEFAULT, - new SearchSourceBuilder().timeout(TimeValue.timeValueSeconds(seconds)), - new String[0], - false, - new AliasFilter(null, Strings.EMPTY_ARRAY), - 1.0f), + new ShardSearchLocalRequest( + indexShard.shardId(), + 1, + SearchType.DEFAULT, + new SearchSourceBuilder().timeout(TimeValue.timeValueSeconds(seconds)), + new String[0], + false, + new AliasFilter(null, Strings.EMPTY_ARRAY), + 1.0f), null); try { // the search context should inherit the query timeout diff --git a/core/src/test/java/org/elasticsearch/search/internal/ShardSearchTransportRequestTests.java b/core/src/test/java/org/elasticsearch/search/internal/ShardSearchTransportRequestTests.java index b93b3795cb55d..7a0e10af99c5a 100644 --- a/core/src/test/java/org/elasticsearch/search/internal/ShardSearchTransportRequestTests.java +++ b/core/src/test/java/org/elasticsearch/search/internal/ShardSearchTransportRequestTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.search.internal; import org.elasticsearch.Version; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.cluster.metadata.AliasMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData; @@ -95,7 +96,7 @@ private ShardSearchTransportRequest createShardSearchTransportRequest() throws I } else { filteringAliases = new AliasFilter(null, Strings.EMPTY_ARRAY); } - return new ShardSearchTransportRequest(searchRequest, shardId, + return new ShardSearchTransportRequest(new OriginalIndices(searchRequest), searchRequest, shardId, randomIntBetween(1, 100), filteringAliases, randomBoolean() ? 1.0f : randomFloat(), Math.abs(randomLong())); } diff --git a/core/src/test/java/org/elasticsearch/search/stats/SearchStatsIT.java b/core/src/test/java/org/elasticsearch/search/stats/SearchStatsIT.java index 83fb38f18a287..11806a1cea9f6 100644 --- a/core/src/test/java/org/elasticsearch/search/stats/SearchStatsIT.java +++ b/core/src/test/java/org/elasticsearch/search/stats/SearchStatsIT.java @@ -165,10 +165,10 @@ public void testSimpleStats() throws Exception { private Set nodeIdsWithIndex(String... indices) { ClusterState state = client().admin().cluster().prepareState().execute().actionGet().getState(); - GroupShardsIterator allAssignedShardsGrouped = state.routingTable().allAssignedShardsGrouped(indices, true); + GroupShardsIterator allAssignedShardsGrouped = state.routingTable().allAssignedShardsGrouped(indices, true); Set nodes = new HashSet<>(); for (ShardIterator shardIterator : allAssignedShardsGrouped) { - for (ShardRouting routing : shardIterator.asUnordered()) { + for (ShardRouting routing : shardIterator) { if (routing.active()) { nodes.add(routing.currentNodeId()); } From bc45d10e82e169e93d331866c7e2a7f3cae51db7 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 26 Apr 2017 16:04:38 -0400 Subject: [PATCH 095/619] Remove most usages of 1-arg Script ctor (#24325) The one argument ctor for `Script` creates a script with the default language but most usages of are for testing and either don't care about the language or are for use with `MockScriptEngine`. This replaces most usages of the one argument ctor on `Script` with calls to `ESTestCase#mockScript` to make it clear that the tests don't need the default scripting language. I've also factored out some copy and pasted script generation code into a single place. I would have had to change that code to use `mockScript` anyway, so it was easier to perform the refactor. Relates to #16314 --- .../elasticsearch/client/RequestTests.java | 5 ++--- .../functionscore/ScoreFunctionBuilders.java | 5 ++++- .../function/ScriptScoreFunctionTests.java | 5 ++--- .../aggregations/BaseAggregationTestCase.java | 19 +++++++++++++++++ .../bucket/SignificantTermsTests.java | 21 ++++--------------- .../aggregations/bucket/TermsTests.java | 18 ++-------------- .../DiversifiedAggregationBuilderTests.java | 15 +------------ .../AbstractNumericMetricTestCase.java | 15 +------------ .../metrics/GeoCentroidTests.java | 15 +------------ .../aggregations/metrics/MissingTests.java | 15 +------------ .../metrics/PercentileRanksTests.java | 14 +------------ .../metrics/PercentilesTests.java | 15 +------------ .../metrics/ScriptedMetricTests.java | 2 +- .../aggregations/metrics/TopHitsTests.java | 6 +++--- .../aggregations/metrics/ValueCountTests.java | 15 +------------ .../metrics/cardinality/CardinalityTests.java | 15 +------------ .../pipeline/BucketScriptTests.java | 2 +- .../pipeline/BucketSelectorTests.java | 2 +- .../search/sort/ScriptSortBuilderTests.java | 6 +++--- .../ingest/common/ScriptProcessorTests.java | 2 +- ...AsyncBulkByScrollActionScriptTestCase.java | 5 +---- .../index/reindex/ReindexRequestTests.java | 3 +-- .../reindex/UpdateByQueryRequestTests.java | 3 +-- .../search/RandomSearchRequestGenerator.java | 14 +++++++++---- .../org/elasticsearch/test/ESTestCase.java | 12 +++++++++++ 25 files changed, 76 insertions(+), 173 deletions(-) diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java index 58b7df922727c..c527125e10b60 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java @@ -39,7 +39,6 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.VersionType; -import org.elasticsearch.script.Script; import org.elasticsearch.search.fetch.subphase.FetchSourceContext; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.RandomObjects; @@ -287,7 +286,7 @@ public void testUpdate() throws IOException { expectedParams.put("doc_as_upsert", "true"); } } else { - updateRequest.script(new Script("_value + 1")); + updateRequest.script(mockScript("_value + 1")); updateRequest.scriptedUpsert(randomBoolean()); } if (randomBoolean()) { @@ -520,7 +519,7 @@ public void testBulkWithDifferentContentTypes() throws IOException { { BulkRequest bulkRequest = new BulkRequest(); bulkRequest.add(new DeleteRequest("index", "type", "0")); - bulkRequest.add(new UpdateRequest("index", "type", "1").script(new Script("test"))); + bulkRequest.add(new UpdateRequest("index", "type", "1").script(mockScript("test"))); bulkRequest.add(new DeleteRequest("index", "type", "2")); Request request = Request.bulk(bulkRequest); diff --git a/core/src/main/java/org/elasticsearch/index/query/functionscore/ScoreFunctionBuilders.java b/core/src/main/java/org/elasticsearch/index/query/functionscore/ScoreFunctionBuilders.java index e6fb632f5aa2a..100ff29dfeb69 100644 --- a/core/src/main/java/org/elasticsearch/index/query/functionscore/ScoreFunctionBuilders.java +++ b/core/src/main/java/org/elasticsearch/index/query/functionscore/ScoreFunctionBuilders.java @@ -20,6 +20,9 @@ package org.elasticsearch.index.query.functionscore; import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptType; + +import static java.util.Collections.emptyMap; /** * Static method aliases for constructors of known {@link ScoreFunctionBuilder}s. @@ -69,7 +72,7 @@ public static ScriptScoreFunctionBuilder scriptFunction(Script script) { } public static ScriptScoreFunctionBuilder scriptFunction(String script) { - return (new ScriptScoreFunctionBuilder(new Script(script))); + return (new ScriptScoreFunctionBuilder(new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, script, emptyMap()))); } public static RandomScoreFunctionBuilder randomFunction(int seed) { diff --git a/core/src/test/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunctionTests.java b/core/src/test/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunctionTests.java index 21fbf43a133a8..d7ee7629c92d9 100644 --- a/core/src/test/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunctionTests.java +++ b/core/src/test/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunctionTests.java @@ -21,9 +21,8 @@ import org.apache.lucene.index.LeafReaderContext; import org.elasticsearch.script.AbstractDoubleSearchScript; -import org.elasticsearch.script.LeafSearchScript; -import org.elasticsearch.script.Script; import org.elasticsearch.script.GeneralScriptException; +import org.elasticsearch.script.LeafSearchScript; import org.elasticsearch.script.SearchScript; import org.elasticsearch.test.ESTestCase; @@ -35,7 +34,7 @@ public class ScriptScoreFunctionTests extends ESTestCase { */ public void testScriptScoresReturnsNaN() throws IOException { // script that always returns NaN - ScoreFunction scoreFunction = new ScriptScoreFunction(new Script("Double.NaN"), new SearchScript() { + ScoreFunction scoreFunction = new ScriptScoreFunction(mockScript("Double.NaN"), new SearchScript() { @Override public LeafSearchScript getLeafSearchScript(LeafReaderContext context) throws IOException { return new AbstractDoubleSearchScript() { diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java index 651c261aa819a..c76d1a5f0ddc5 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java @@ -34,6 +34,7 @@ import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.indices.IndicesModule; import org.elasticsearch.search.SearchModule; +import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder; import org.elasticsearch.test.AbstractQueryTestCase; import org.elasticsearch.test.ESTestCase; @@ -176,4 +177,22 @@ public String randomNumericField() { return INT_FIELD_NAME; } } + + protected void randomFieldOrScript(ValuesSourceAggregationBuilder factory, String field) { + int choice = randomInt(2); + switch (choice) { + case 0: + factory.field(field); + break; + case 1: + factory.field(field); + factory.script(mockScript("_value + 1")); + break; + case 2: + factory.script(mockScript("doc[" + field + "] + 1")); + break; + default: + throw new AssertionError("Unknow random operation [" + choice + "]"); + } + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsTests.java index f545ae500a2f1..9fe1c0ea479e0 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsTests.java @@ -22,7 +22,6 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.automaton.RegExp; import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.script.Script; import org.elasticsearch.search.aggregations.BaseAggregationTestCase; import org.elasticsearch.search.aggregations.bucket.significant.SignificantTermsAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.significant.heuristics.ChiSquare; @@ -34,6 +33,7 @@ import org.elasticsearch.search.aggregations.bucket.significant.heuristics.SignificanceHeuristic; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregatorFactory.ExecutionMode; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; + import java.util.SortedSet; import java.util.TreeSet; @@ -54,21 +54,8 @@ protected SignificantTermsAggregationBuilder createTestAggregatorBuilder() { String name = randomAlphaOfLengthBetween(3, 20); SignificantTermsAggregationBuilder factory = new SignificantTermsAggregationBuilder(name, null); String field = randomAlphaOfLengthBetween(3, 20); - int randomFieldBranch = randomInt(2); - switch (randomFieldBranch) { - case 0: - factory.field(field); - break; - case 1: - factory.field(field); - factory.script(new Script("_value + 1")); - break; - case 2: - factory.script(new Script("doc[" + field + "] + 1")); - break; - default: - fail(); - } + randomFieldOrScript(factory, field); + if (randomBoolean()) { factory.missing("MISSING"); } @@ -179,7 +166,7 @@ protected SignificantTermsAggregationBuilder createTestAggregatorBuilder() { significanceHeuristic = new MutualInformation(randomBoolean(), randomBoolean()); break; case 4: - significanceHeuristic = new ScriptHeuristic(new Script("foo")); + significanceHeuristic = new ScriptHeuristic(mockScript("foo")); break; case 5: significanceHeuristic = new JLHScore(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsTests.java index d0a38d0890ab9..42f6ef78f4b17 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsTests.java @@ -21,13 +21,13 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.automaton.RegExp; -import org.elasticsearch.script.Script; import org.elasticsearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.elasticsearch.search.aggregations.BaseAggregationTestCase; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregatorFactory.ExecutionMode; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; + import java.util.ArrayList; import java.util.List; import java.util.SortedSet; @@ -50,21 +50,7 @@ protected TermsAggregationBuilder createTestAggregatorBuilder() { String name = randomAlphaOfLengthBetween(3, 20); TermsAggregationBuilder factory = new TermsAggregationBuilder(name, null); String field = randomAlphaOfLengthBetween(3, 20); - int randomFieldBranch = randomInt(2); - switch (randomFieldBranch) { - case 0: - factory.field(field); - break; - case 1: - factory.field(field); - factory.script(new Script("_value + 1")); - break; - case 2: - factory.script(new Script("doc[" + field + "] + 1")); - break; - default: - fail(); - } + randomFieldOrScript(factory, field); if (randomBoolean()) { factory.missing("MISSING"); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/DiversifiedAggregationBuilderTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/DiversifiedAggregationBuilderTests.java index eed258bb7881d..635cedcb09798 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/DiversifiedAggregationBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/DiversifiedAggregationBuilderTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.search.aggregations.bucket.sampler; -import org.elasticsearch.script.Script; import org.elasticsearch.search.aggregations.BaseAggregationTestCase; import org.elasticsearch.search.aggregations.bucket.sampler.SamplerAggregator.ExecutionMode; @@ -29,19 +28,7 @@ public class DiversifiedAggregationBuilderTests extends BaseAggregationTestCase< protected final DiversifiedAggregationBuilder createTestAggregatorBuilder() { DiversifiedAggregationBuilder factory = new DiversifiedAggregationBuilder("foo"); String field = randomNumericField(); - int randomFieldBranch = randomInt(3); - switch (randomFieldBranch) { - case 0: - factory.field(field); - break; - case 1: - factory.field(field); - factory.script(new Script("_value + 1")); - break; - case 2: - factory.script(new Script("doc[" + field + "] + 1")); - break; - } + randomFieldOrScript(factory, field); if (randomBoolean()) { factory.missing("MISSING"); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AbstractNumericMetricTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AbstractNumericMetricTestCase.java index f1ccf344a7c44..975a1610a61d9 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AbstractNumericMetricTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AbstractNumericMetricTestCase.java @@ -19,7 +19,6 @@ package org.elasticsearch.search.aggregations.metrics; -import org.elasticsearch.script.Script; import org.elasticsearch.search.aggregations.BaseAggregationTestCase; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder; @@ -31,19 +30,7 @@ public abstract class AbstractNumericMetricTestCase { @@ -28,19 +27,7 @@ public class CardinalityTests extends BaseAggregationTestCase params = new HashMap<>(); if (randomBoolean()) { diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/BucketSelectorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/BucketSelectorTests.java index c3e477a3a5051..8dd63942d866f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/BucketSelectorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/BucketSelectorTests.java @@ -40,7 +40,7 @@ protected BucketSelectorPipelineAggregationBuilder createTestAggregatorFactory() } Script script; if (randomBoolean()) { - script = new Script("script"); + script = mockScript("script"); } else { Map params = new HashMap<>(); if (randomBoolean()) { diff --git a/core/src/test/java/org/elasticsearch/search/sort/ScriptSortBuilderTests.java b/core/src/test/java/org/elasticsearch/search/sort/ScriptSortBuilderTests.java index 526be491f3dd2..0b9d250832b3c 100644 --- a/core/src/test/java/org/elasticsearch/search/sort/ScriptSortBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/search/sort/ScriptSortBuilderTests.java @@ -43,7 +43,7 @@ protected ScriptSortBuilder createTestItem() { public static ScriptSortBuilder randomScriptSortBuilder() { ScriptSortType type = randomBoolean() ? ScriptSortType.NUMBER : ScriptSortType.STRING; - ScriptSortBuilder builder = new ScriptSortBuilder(new Script(randomAlphaOfLengthBetween(5, 10)), + ScriptSortBuilder builder = new ScriptSortBuilder(mockScript(randomAlphaOfLengthBetween(5, 10)), type); if (randomBoolean()) { builder.order(randomFrom(SortOrder.values())); @@ -76,7 +76,7 @@ protected ScriptSortBuilder mutate(ScriptSortBuilder original) throws IOExceptio Script script = original.script(); ScriptSortType type = original.type(); if (randomBoolean()) { - result = new ScriptSortBuilder(new Script(script.getIdOrCode() + "_suffix"), type); + result = new ScriptSortBuilder(mockScript(script.getIdOrCode() + "_suffix"), type); } else { result = new ScriptSortBuilder(script, type.equals(ScriptSortType.NUMBER) ? ScriptSortType.STRING : ScriptSortType.NUMBER); } @@ -251,7 +251,7 @@ public void testParseUnexpectedToken() throws IOException { * script sort of type {@link ScriptSortType} does not work with {@link SortMode#AVG}, {@link SortMode#MEDIAN} or {@link SortMode#SUM} */ public void testBadSortMode() throws IOException { - ScriptSortBuilder builder = new ScriptSortBuilder(new Script("something"), ScriptSortType.STRING); + ScriptSortBuilder builder = new ScriptSortBuilder(mockScript("something"), ScriptSortType.STRING); String sortMode = randomFrom(new String[] { "avg", "median", "sum" }); Exception e = expectThrows(IllegalArgumentException.class, () -> builder.sortMode(SortMode.fromString(sortMode))); assertEquals("script sort of type [string] doesn't support mode [" + sortMode + "]", e.getMessage()); diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ScriptProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ScriptProcessorTests.java index e76f3016ddadc..5356d9c9e0b72 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ScriptProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ScriptProcessorTests.java @@ -45,7 +45,7 @@ public void testScripting() throws Exception { int randomBytesTotal = randomBytesIn + randomBytesOut; ScriptService scriptService = mock(ScriptService.class); - Script script = new Script("_script"); + Script script = mockScript("_script"); ExecutableScript executableScript = mock(ExecutableScript.class); when(scriptService.executable(any(CompiledScript.class), any())).thenReturn(executableScript); diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java index b565f0749f1f3..fd41a6d25f384 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java @@ -30,7 +30,6 @@ import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; -import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptService; import org.junit.Before; import org.mockito.Matchers; @@ -49,8 +48,6 @@ public abstract class AbstractAsyncBulkByScrollActionScriptTestCase< Response extends BulkByScrollResponse> extends AbstractAsyncBulkByScrollActionTestCase { - private static final Script EMPTY_SCRIPT = new Script(""); - protected ScriptService scriptService; @Before @@ -66,7 +63,7 @@ protected T applyScript(Consumer> when(scriptService.executable(any(CompiledScript.class), Matchers.>any())) .thenReturn(executableScript); - AbstractAsyncBulkByScrollAction action = action(scriptService, request().setScript(EMPTY_SCRIPT)); + AbstractAsyncBulkByScrollAction action = action(scriptService, request().setScript(mockScript(""))); RequestWrapper result = action.buildScriptApplier().apply(AbstractAsyncBulkByScrollAction.wrap(index), doc); return (result != null) ? (T) result.self() : null; } diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java index c2b072396716f..d1bb6f6096c39 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java @@ -25,7 +25,6 @@ import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.index.reindex.remote.RemoteInfo; -import org.elasticsearch.script.Script; import org.elasticsearch.search.slice.SliceBuilder; import static java.util.Collections.emptyMap; @@ -71,7 +70,7 @@ public void testNoSliceWithWorkers() { @Override protected void extraRandomizationForSlice(ReindexRequest original) { if (randomBoolean()) { - original.setScript(new Script(randomAlphaOfLength(5))); + original.setScript(mockScript(randomAlphaOfLength(5))); } if (randomBoolean()) { original.setRemoteInfo(new RemoteInfo(randomAlphaOfLength(5), randomAlphaOfLength(5), between(1, 10000), diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java index c932e83ce1c9f..700f45b42c515 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java @@ -22,7 +22,6 @@ import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestTestCase; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.IndicesOptions; -import org.elasticsearch.script.Script; import static org.apache.lucene.util.TestUtil.randomSimpleString; @@ -68,7 +67,7 @@ protected UpdateByQueryRequest newRequest() { @Override protected void extraRandomizationForSlice(UpdateByQueryRequest original) { if (randomBoolean()) { - original.setScript(new Script(randomAlphaOfLength(5))); + original.setScript(mockScript(randomAlphaOfLength(5))); } if (randomBoolean()) { original.setPipeline(randomAlphaOfLength(5)); diff --git a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java index 2a072a1d3be7b..18448b5829c13 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java +++ b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java @@ -31,6 +31,7 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptType; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.collapse.CollapseBuilder; @@ -50,8 +51,10 @@ import java.util.List; import java.util.function.Supplier; +import static java.util.Collections.emptyMap; import static org.elasticsearch.test.ESTestCase.between; import static org.elasticsearch.test.ESTestCase.generateRandomStringArray; +import static org.elasticsearch.test.ESTestCase.mockScript; import static org.elasticsearch.test.ESTestCase.randomAlphaOfLengthBetween; import static org.elasticsearch.test.ESTestCase.randomBoolean; import static org.elasticsearch.test.ESTestCase.randomByte; @@ -164,9 +167,9 @@ public static SearchSourceBuilder randomSearchSourceBuilder( int scriptFieldsSize = randomInt(25); for (int i = 0; i < scriptFieldsSize; i++) { if (randomBoolean()) { - builder.scriptField(randomAlphaOfLengthBetween(5, 50), new Script("foo"), randomBoolean()); + builder.scriptField(randomAlphaOfLengthBetween(5, 50), mockScript("foo"), randomBoolean()); } else { - builder.scriptField(randomAlphaOfLengthBetween(5, 50), new Script("foo")); + builder.scriptField(randomAlphaOfLengthBetween(5, 50), mockScript("foo")); } } } @@ -242,8 +245,11 @@ public static SearchSourceBuilder randomSearchSourceBuilder( builder.sort(SortBuilders.scoreSort().order(randomFrom(SortOrder.values()))); break; case 3: - builder.sort(SortBuilders.scriptSort(new Script("foo"), - ScriptSortBuilder.ScriptSortType.NUMBER).order(randomFrom(SortOrder.values()))); + builder.sort(SortBuilders + .scriptSort( + new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, "foo", emptyMap()), + ScriptSortBuilder.ScriptSortType.NUMBER) + .order(randomFrom(SortOrder.values()))); break; case 4: builder.sort(randomAlphaOfLengthBetween(5, 20)); diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index 29241b4b19d50..58d67ea3b98ad 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -29,6 +29,7 @@ import com.carrotsearch.randomizedtesting.generators.RandomPicks; import com.carrotsearch.randomizedtesting.generators.RandomStrings; import com.carrotsearch.randomizedtesting.rules.TestRuleAdapter; + import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -90,8 +91,10 @@ import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.plugins.MapperPlugin; import org.elasticsearch.script.MockScriptEngine; +import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptModule; import org.elasticsearch.script.ScriptService; +import org.elasticsearch.script.ScriptType; import org.elasticsearch.search.MockSearchService; import org.elasticsearch.test.junit.listeners.LoggingListener; import org.elasticsearch.test.junit.listeners.ReproduceInfoPrinter; @@ -133,6 +136,7 @@ import java.util.stream.Stream; import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static org.elasticsearch.common.util.CollectionUtils.arrayAsArrayList; import static org.hamcrest.Matchers.empty; @@ -1083,6 +1087,14 @@ protected NamedXContentRegistry xContentRegistry() { return new NamedXContentRegistry(ClusterModule.getNamedXWriteables()); } + /** + * Create a "mock" script for use either with {@link MockScriptEngine} or anywhere where you need a script but don't really care about + * its contents. + */ + public static final Script mockScript(String id) { + return new Script(ScriptType.INLINE, MockScriptEngine.NAME, id, emptyMap()); + } + /** Returns the suite failure marker: internal use only! */ public static TestRuleMarkFailure getSuiteFailureMarker() { return suiteFailureMarker; From 774368d2acdb522fab7c0d887ef5a80942c225d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 26 Apr 2017 22:32:30 +0200 Subject: [PATCH 096/619] Add parsing for InternalStatsBucket and InternalExtendedStatsBucket (#24312) --- .../stats/extended/ParsedExtendedStats.java | 27 ++++---- .../stats/ParsedStatsBucket.java | 46 ++++++++++++++ .../extended/ParsedExtendedStatsBucket.java | 46 ++++++++++++++ .../InternalAggregationTestCase.java | 6 ++ .../metrics/InternalExtendedStatsTests.java | 10 ++- .../metrics/InternalStatsBucketTests.java | 63 +++++++++++++++++++ .../metrics/InternalStatsTests.java | 7 ++- .../InternalExtendedStatsBucketTests.java | 63 +++++++++++++++++++ 8 files changed, 253 insertions(+), 15 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/ParsedStatsBucket.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/extended/ParsedExtendedStatsBucket.java create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsBucketTests.java create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/extended/InternalExtendedStatsBucketTests.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java index 2c17376e88725..299b9ce65a42f 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java @@ -142,6 +142,11 @@ protected XContentBuilder otherStatsToXContent(XContentBuilder builder, Params p private static final ObjectParser PARSER = new ObjectParser<>(ParsedExtendedStats.class.getSimpleName(), true, ParsedExtendedStats::new); + static { + declareExtendedStatsFields(PARSER); + } + + private static final ConstructingObjectParser, Void> STD_BOUNDS_PARSER = new ConstructingObjectParser<>( ParsedExtendedStats.class.getSimpleName() + "_STD_BOUNDS", true, args -> new Tuple<>((Double) args[0], (Double) args[1])); static { @@ -159,23 +164,23 @@ protected XContentBuilder otherStatsToXContent(XContentBuilder builder, Params p STD_BOUNDS_AS_STRING_PARSER.declareString(constructorArg(), new ParseField(Fields.UPPER)); } - static { - declareAggregationFields(PARSER); - declareStatsFields(PARSER); - PARSER.declareField((agg, value) -> agg.sumOfSquares = value, (parser, context) -> parseDouble(parser, 0), + protected static void declareExtendedStatsFields(ObjectParser objectParser) { + declareAggregationFields(objectParser); + declareStatsFields(objectParser); + objectParser.declareField((agg, value) -> agg.sumOfSquares = value, (parser, context) -> parseDouble(parser, 0), new ParseField(Fields.SUM_OF_SQRS), ValueType.DOUBLE_OR_NULL); - PARSER.declareField((agg, value) -> agg.variance = value, (parser, context) -> parseDouble(parser, 0), + objectParser.declareField((agg, value) -> agg.variance = value, (parser, context) -> parseDouble(parser, 0), new ParseField(Fields.VARIANCE), ValueType.DOUBLE_OR_NULL); - PARSER.declareField((agg, value) -> agg.stdDeviation = value, (parser, context) -> parseDouble(parser, 0), + objectParser.declareField((agg, value) -> agg.stdDeviation = value, (parser, context) -> parseDouble(parser, 0), new ParseField(Fields.STD_DEVIATION), ValueType.DOUBLE_OR_NULL); - PARSER.declareObject(ParsedExtendedStats::setStdDeviationBounds, STD_BOUNDS_PARSER, new ParseField(Fields.STD_DEVIATION_BOUNDS)); - PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.SUM_OF_SQRS_AS_STRING, value), + objectParser.declareObject(ParsedExtendedStats::setStdDeviationBounds, STD_BOUNDS_PARSER, new ParseField(Fields.STD_DEVIATION_BOUNDS)); + objectParser.declareString((agg, value) -> agg.valueAsString.put(Fields.SUM_OF_SQRS_AS_STRING, value), new ParseField(Fields.SUM_OF_SQRS_AS_STRING)); - PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.VARIANCE_AS_STRING, value), + objectParser.declareString((agg, value) -> agg.valueAsString.put(Fields.VARIANCE_AS_STRING, value), new ParseField(Fields.VARIANCE_AS_STRING)); - PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.STD_DEVIATION_AS_STRING, value), + objectParser.declareString((agg, value) -> agg.valueAsString.put(Fields.STD_DEVIATION_AS_STRING, value), new ParseField(Fields.STD_DEVIATION_AS_STRING)); - PARSER.declareObject(ParsedExtendedStats::setStdDeviationBoundsAsString, STD_BOUNDS_AS_STRING_PARSER, + objectParser.declareObject(ParsedExtendedStats::setStdDeviationBoundsAsString, STD_BOUNDS_AS_STRING_PARSER, new ParseField(Fields.STD_DEVIATION_BOUNDS_AS_STRING)); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/ParsedStatsBucket.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/ParsedStatsBucket.java new file mode 100644 index 0000000000000..4738ad7d9daa6 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/ParsedStatsBucket.java @@ -0,0 +1,46 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.stats.ParsedStats; + + +public class ParsedStatsBucket extends ParsedStats implements StatsBucket { + + @Override + protected String getType() { + return StatsBucketPipelineAggregationBuilder.NAME; + } + + private static final ObjectParser PARSER = new ObjectParser<>( + ParsedStatsBucket.class.getSimpleName(), true, ParsedStatsBucket::new); + + static { + declareStatsFields(PARSER); + } + + public static ParsedStatsBucket fromXContent(XContentParser parser, final String name) { + ParsedStatsBucket parsedStatsBucket = PARSER.apply(parser, null); + parsedStatsBucket.setName(name); + return parsedStatsBucket; + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/extended/ParsedExtendedStatsBucket.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/extended/ParsedExtendedStatsBucket.java new file mode 100644 index 0000000000000..08bfbfe587b36 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/extended/ParsedExtendedStatsBucket.java @@ -0,0 +1,46 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.extended; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.stats.extended.ParsedExtendedStats; + + +public class ParsedExtendedStatsBucket extends ParsedExtendedStats implements ExtendedStatsBucket { + + @Override + protected String getType() { + return ExtendedStatsBucketPipelineAggregationBuilder.NAME; + } + + private static final ObjectParser PARSER = new ObjectParser<>( + ParsedExtendedStatsBucket.class.getSimpleName(), true, ParsedExtendedStatsBucket::new); + + static { + declareExtendedStatsFields(PARSER); + } + + public static ParsedExtendedStatsBucket fromXContent(XContentParser parser, final String name) { + ParsedExtendedStatsBucket parsedStatsBucket = PARSER.apply(parser, null); + parsedStatsBucket.setName(name); + return parsedStatsBucket; + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index 1a8a674a531d8..05a4f0eef8221 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -63,6 +63,10 @@ import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.InternalBucketMetricValue; import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.ParsedBucketMetricValue; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.ParsedStatsBucket; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.StatsBucketPipelineAggregationBuilder; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.extended.ExtendedStatsBucketPipelineAggregationBuilder; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.extended.ParsedExtendedStatsBucket; import org.elasticsearch.search.aggregations.pipeline.derivative.DerivativePipelineAggregationBuilder; import org.elasticsearch.search.aggregations.pipeline.derivative.ParsedDerivative; import org.elasticsearch.test.AbstractWireSerializingTestCase; @@ -104,7 +108,9 @@ static List getNamedXContents() { namedXContents.put(DerivativePipelineAggregationBuilder.NAME, (p, c) -> ParsedDerivative.fromXContent(p, (String) c)); namedXContents.put(InternalBucketMetricValue.NAME, (p, c) -> ParsedBucketMetricValue.fromXContent(p, (String) c)); namedXContents.put(StatsAggregationBuilder.NAME, (p, c) -> ParsedStats.fromXContent(p, (String) c)); + namedXContents.put(StatsBucketPipelineAggregationBuilder.NAME, (p, c) -> ParsedStatsBucket.fromXContent(p, (String) c)); namedXContents.put(ExtendedStatsAggregationBuilder.NAME, (p, c) -> ParsedExtendedStats.fromXContent(p, (String) c)); + namedXContents.put(ExtendedStatsBucketPipelineAggregationBuilder.NAME, (p, c) -> ParsedExtendedStatsBucket.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalExtendedStatsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalExtendedStatsTests.java index b6d9fd3dd7bd9..d9ce145fd39dc 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalExtendedStatsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalExtendedStatsTests.java @@ -33,7 +33,7 @@ import java.util.Map; public class InternalExtendedStatsTests extends InternalAggregationTestCase { - private double sigma; + protected double sigma; @Before public void randomSigma() { @@ -48,8 +48,12 @@ protected InternalExtendedStats createTestInstance(String name, List pipelineAggregators, Map metaData) { + return new InternalExtendedStats(name, count, sum, min, max, sumOfSqrs, sigma, formatter, pipelineAggregators, metaData); } @Override diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsBucketTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsBucketTests.java new file mode 100644 index 0000000000000..cbb097a72820e --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsBucketTests.java @@ -0,0 +1,63 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics; + +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.ParsedAggregation; +import org.elasticsearch.search.aggregations.metrics.stats.InternalStats; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.InternalStatsBucket; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.ParsedStatsBucket; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class InternalStatsBucketTests extends InternalStatsTests { + + @Override + protected InternalStatsBucket createInstance(String name, long count, double sum, double min, double max, + DocValueFormat formatter, List pipelineAggregators, Map metaData) { + return new InternalStatsBucket(name, count, sum, min, max, formatter, pipelineAggregators, metaData); + } + + @Override + public void testReduceRandom() { + expectThrows(UnsupportedOperationException.class, + () -> createTestInstance("name", Collections.emptyList(), null).reduce(null, null)); + } + + @Override + protected void assertReduced(InternalStats reduced, List inputs) { + // no test since reduce operation is unsupported + } + + @Override + protected Writeable.Reader instanceReader() { + return InternalStatsBucket::new; + } + + @Override + protected void assertFromXContent(InternalStats aggregation, ParsedAggregation parsedAggregation) { + super.assertFromXContent(aggregation, parsedAggregation); + assertTrue(parsedAggregation instanceof ParsedStatsBucket); + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java index 354470114a962..6bfcfdd1ee5d7 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java @@ -38,7 +38,12 @@ protected InternalStats createTestInstance(String name, List double max = randomDoubleBetween(-1000000, 1000000, true); double sum = randomDoubleBetween(-1000000, 1000000, true); DocValueFormat format = randomNumericDocValueFormat(); - return new InternalStats(name, count, sum, min, max, format, pipelineAggregators, metaData); + return createInstance(name, count, sum, min, max, format, pipelineAggregators, metaData); + } + + protected InternalStats createInstance(String name, long count, double sum, double min, double max, DocValueFormat formatter, + List pipelineAggregators, Map metaData) { + return new InternalStats(name, count, sum, min, max, formatter, pipelineAggregators, metaData); } @Override diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/extended/InternalExtendedStatsBucketTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/extended/InternalExtendedStatsBucketTests.java new file mode 100644 index 0000000000000..5261c686174a5 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/extended/InternalExtendedStatsBucketTests.java @@ -0,0 +1,63 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.extended; + +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.ParsedAggregation; +import org.elasticsearch.search.aggregations.metrics.InternalExtendedStatsTests; +import org.elasticsearch.search.aggregations.metrics.stats.extended.InternalExtendedStats; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class InternalExtendedStatsBucketTests extends InternalExtendedStatsTests { + + @Override + protected InternalExtendedStatsBucket createInstance(String name, long count, double sum, double min, double max, double sumOfSqrs, + double sigma, DocValueFormat formatter, List pipelineAggregators, Map metaData) { + return new InternalExtendedStatsBucket(name, count, sum, min, max, sumOfSqrs, sigma, formatter, pipelineAggregators, + Collections.emptyMap()); + } + + @Override + public void testReduceRandom() { + expectThrows(UnsupportedOperationException.class, + () -> createTestInstance("name", Collections.emptyList(), null).reduce(null, null)); + } + + @Override + protected void assertReduced(InternalExtendedStats reduced, List inputs) { + // no test since reduce operation is unsupported + } + + @Override + protected Writeable.Reader instanceReader() { + return InternalExtendedStatsBucket::new; + } + + @Override + protected void assertFromXContent(InternalExtendedStats aggregation, ParsedAggregation parsedAggregation) { + super.assertFromXContent(aggregation, parsedAggregation); + assertTrue(parsedAggregation instanceof ParsedExtendedStatsBucket); + } +} From 0e74f5ddb16dbd5377cea304b4075c048ff1eb22 Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Wed, 26 Apr 2017 16:34:53 -0400 Subject: [PATCH 097/619] [TEST] fixes shard count of source shard index in a restore shrink index test --- .../snapshots/DedicatedClusterSnapshotRestoreIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java b/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java index 10c49b431a512..2c1dfc899b690 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java +++ b/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java @@ -820,7 +820,7 @@ public void testRestoreShrinkIndex() throws Exception { .put("compress", randomBoolean()))); assertAcked(prepareCreate(sourceIdx, 0, Settings.builder() - .put("number_of_shards", between(1, 20)).put("number_of_replicas", 0))); + .put("number_of_shards", between(2, 10)).put("number_of_replicas", 0))); ensureGreen(); logger.info("--> indexing some data"); From e2b95f9758d1cd15688e2c466b4840006715e2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 26 Apr 2017 23:07:49 +0200 Subject: [PATCH 098/619] Fix order of static field declarations for parser --- .../stats/extended/ParsedExtendedStats.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java index 299b9ce65a42f..f8a219fa6b7d0 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java @@ -142,26 +142,21 @@ protected XContentBuilder otherStatsToXContent(XContentBuilder builder, Params p private static final ObjectParser PARSER = new ObjectParser<>(ParsedExtendedStats.class.getSimpleName(), true, ParsedExtendedStats::new); - static { - declareExtendedStatsFields(PARSER); - } - - private static final ConstructingObjectParser, Void> STD_BOUNDS_PARSER = new ConstructingObjectParser<>( ParsedExtendedStats.class.getSimpleName() + "_STD_BOUNDS", true, args -> new Tuple<>((Double) args[0], (Double) args[1])); - static { - STD_BOUNDS_PARSER.declareField(constructorArg(), (parser, context) -> parseDouble(parser, 0), - new ParseField(Fields.LOWER), ValueType.DOUBLE_OR_NULL); - STD_BOUNDS_PARSER.declareField(constructorArg(), (parser, context) -> parseDouble(parser, 0), - new ParseField(Fields.UPPER), ValueType.DOUBLE_OR_NULL); - } private static final ConstructingObjectParser, Void> STD_BOUNDS_AS_STRING_PARSER = new ConstructingObjectParser<>( ParsedExtendedStats.class.getSimpleName() + "_STD_BOUNDS_AS_STRING", true, args -> new Tuple<>((String) args[0], (String) args[1])); + static { + STD_BOUNDS_PARSER.declareField(constructorArg(), (parser, context) -> parseDouble(parser, 0), + new ParseField(Fields.LOWER), ValueType.DOUBLE_OR_NULL); + STD_BOUNDS_PARSER.declareField(constructorArg(), (parser, context) -> parseDouble(parser, 0), + new ParseField(Fields.UPPER), ValueType.DOUBLE_OR_NULL); STD_BOUNDS_AS_STRING_PARSER.declareString(constructorArg(), new ParseField(Fields.LOWER)); STD_BOUNDS_AS_STRING_PARSER.declareString(constructorArg(), new ParseField(Fields.UPPER)); + declareExtendedStatsFields(PARSER); } protected static void declareExtendedStatsFields(ObjectParser objectParser) { From b46c39ea2e82bf917a4605c3105623a63532fcbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Thu, 27 Apr 2017 00:02:08 +0200 Subject: [PATCH 099/619] Fixed another sneaky LineLength violation --- .../metrics/stats/extended/ParsedExtendedStats.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java index f8a219fa6b7d0..0eb0bdb215de9 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java @@ -168,7 +168,8 @@ protected static void declareExtendedStatsFields(ObjectParser agg.stdDeviation = value, (parser, context) -> parseDouble(parser, 0), new ParseField(Fields.STD_DEVIATION), ValueType.DOUBLE_OR_NULL); - objectParser.declareObject(ParsedExtendedStats::setStdDeviationBounds, STD_BOUNDS_PARSER, new ParseField(Fields.STD_DEVIATION_BOUNDS)); + objectParser.declareObject(ParsedExtendedStats::setStdDeviationBounds, STD_BOUNDS_PARSER, + new ParseField(Fields.STD_DEVIATION_BOUNDS)); objectParser.declareString((agg, value) -> agg.valueAsString.put(Fields.SUM_OF_SQRS_AS_STRING, value), new ParseField(Fields.SUM_OF_SQRS_AS_STRING)); objectParser.declareString((agg, value) -> agg.valueAsString.put(Fields.VARIANCE_AS_STRING, value), From d387dcfd6cd16e9c2f8e476d80fefd6f3b1c6049 Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Wed, 26 Apr 2017 18:39:26 -0400 Subject: [PATCH 100/619] [TEST] fixes NPE in RoutingTableTests --- .../org/elasticsearch/cluster/routing/RoutingTableTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/org/elasticsearch/cluster/routing/RoutingTableTests.java b/core/src/test/java/org/elasticsearch/cluster/routing/RoutingTableTests.java index a76ad4657c64a..055adbaebbce5 100644 --- a/core/src/test/java/org/elasticsearch/cluster/routing/RoutingTableTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/routing/RoutingTableTests.java @@ -352,7 +352,7 @@ public static IndexMetaData updateActiveAllocations(IndexRoutingTable indexRouti Set insyncAids = shardTable.activeShards().stream().map( shr -> shr.allocationId().getId()).collect(Collectors.toSet()); final ShardRouting primaryShard = shardTable.primaryShard(); - if (primaryShard.initializing() && shardRouting.recoverySource().getType() == RecoverySource.Type.EXISTING_STORE) { + if (primaryShard.initializing() && primaryShard.recoverySource().getType() == RecoverySource.Type.EXISTING_STORE) { // simulate a primary was initialized based on aid insyncAids.add(primaryShard.allocationId().getId()); } From 7f9d84cb1a31dc1fa0050298688352362675e8e6 Mon Sep 17 00:00:00 2001 From: Koen De Groote Date: Thu, 27 Apr 2017 00:54:23 +0200 Subject: [PATCH 101/619] The parseObject method in DocumentParse can be void. There is no point in the code that actually expects the return, plus the variable created for it was never actually used. (#24350) --- .../java/org/elasticsearch/index/mapper/DocumentParser.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java b/core/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java index 5ec1eab21156d..489f4702bc36c 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java @@ -455,10 +455,9 @@ private static void parseObjectOrField(ParseContext context, Mapper mapper) thro } } - private static ObjectMapper parseObject(final ParseContext context, ObjectMapper mapper, String currentFieldName) throws IOException { + private static void parseObject(final ParseContext context, ObjectMapper mapper, String currentFieldName) throws IOException { assert currentFieldName != null; - ObjectMapper update = null; Mapper objectMapper = getMapper(mapper, currentFieldName); if (objectMapper != null) { context.path().add(currentFieldName); @@ -492,8 +491,6 @@ private static ObjectMapper parseObject(final ParseContext context, ObjectMapper context.path().remove(); } } - - return update; } private static void parseArray(ParseContext context, ObjectMapper parentMapper, String lastFieldName) throws IOException { From 74acc594a9b40d29833accc4596bc92523ff6e3b Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Wed, 26 Apr 2017 21:22:15 -0400 Subject: [PATCH 102/619] Fix inconsistencies in long GC disruption This commit fixes some inconsistencies in long GC disruption where we mixed stopping and suspending when the action we are performing on threads is suspending which is distinct from stopping a thread. --- .../IntermittentLongGCDisruption.java | 2 +- .../test/disruption/LongGCDisruption.java | 64 +++++++++---------- .../disruption/LongGCDisruptionTests.java | 4 +- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/test/framework/src/main/java/org/elasticsearch/test/disruption/IntermittentLongGCDisruption.java b/test/framework/src/main/java/org/elasticsearch/test/disruption/IntermittentLongGCDisruption.java index c49ba55e2e638..9dd7883aab4c6 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/disruption/IntermittentLongGCDisruption.java +++ b/test/framework/src/main/java/org/elasticsearch/test/disruption/IntermittentLongGCDisruption.java @@ -78,7 +78,7 @@ private void simulateLongGC(final TimeValue duration) throws InterruptedExceptio logger.info("node [{}] goes into GC for for [{}]", disruptedNode, duration); final Set nodeThreads = new HashSet<>(); try { - while (stopNodeThreads(nodeThreads)) ; + while (suspendThreads(nodeThreads)) ; if (!nodeThreads.isEmpty()) { Thread.sleep(duration.millis()); } diff --git a/test/framework/src/main/java/org/elasticsearch/test/disruption/LongGCDisruption.java b/test/framework/src/main/java/org/elasticsearch/test/disruption/LongGCDisruption.java index 45acde0932533..deebb4f53c124 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/disruption/LongGCDisruption.java +++ b/test/framework/src/main/java/org/elasticsearch/test/disruption/LongGCDisruption.java @@ -42,9 +42,9 @@ public class LongGCDisruption extends SingleNodeDisruption { private static final Pattern[] unsafeClasses = new Pattern[]{ - // logging has shared JVM locks - we may suspend a thread and block other nodes from doing their thing + // logging has shared JVM locks; we may suspend a thread and block other nodes from doing their thing Pattern.compile("logging\\.log4j"), - // security manager is shared across all nodes AND it uses synced hashmaps interanlly + // security manager is shared across all nodes and it uses synchronized maps internally Pattern.compile("java\\.lang\\.SecurityManager"), // SecureRandom instance from SecureRandomHolder class is shared by all nodes Pattern.compile("java\\.security\\.SecureRandom") @@ -74,48 +74,48 @@ assert isDisruptedNodeThread(currentThreadName) == false : // we spawn a background thread to protect against deadlock which can happen // if there are shared resources between caller thread and and suspended threads // see unsafeClasses to how to avoid that - final AtomicReference stoppingError = new AtomicReference<>(); - final Thread stoppingThread = new Thread(new AbstractRunnable() { + final AtomicReference suspendingError = new AtomicReference<>(); + final Thread suspendingThread = new Thread(new AbstractRunnable() { @Override public void onFailure(Exception e) { - stoppingError.set(e); + suspendingError.set(e); } @Override protected void doRun() throws Exception { - // keep trying to stop threads, until no new threads are discovered. - while (stopNodeThreads(suspendedThreads)) { + // keep trying to suspend threads, until no new threads are discovered. + while (suspendThreads(suspendedThreads)) { if (Thread.interrupted()) { return; } } } }); - stoppingThread.setName(currentThreadName + "[LongGCDisruption][threadStopper]"); - stoppingThread.start(); + suspendingThread.setName(currentThreadName + "[LongGCDisruption][threadSuspender]"); + suspendingThread.start(); try { - stoppingThread.join(getStoppingTimeoutInMillis()); + suspendingThread.join(getSuspendingTimeoutInMillis()); } catch (InterruptedException e) { - stoppingThread.interrupt(); // best effort to signal stopping + suspendingThread.interrupt(); // best effort to signal suspending throw new RuntimeException(e); } - if (stoppingError.get() != null) { - throw new RuntimeException("unknown error while stopping threads", stoppingError.get()); + if (suspendingError.get() != null) { + throw new RuntimeException("unknown error while suspending threads", suspendingError.get()); } - if (stoppingThread.isAlive()) { - logger.warn("failed to stop node [{}]'s threads within [{}] millis. Stopping thread stack trace:\n {}" - , disruptedNode, getStoppingTimeoutInMillis(), stackTrace(stoppingThread.getStackTrace())); - stoppingThread.interrupt(); // best effort; + if (suspendingThread.isAlive()) { + logger.warn("failed to suspend node [{}]'s threads within [{}] millis. Suspending thread stack trace:\n {}" + , disruptedNode, getSuspendingTimeoutInMillis(), stackTrace(suspendingThread.getStackTrace())); + suspendingThread.interrupt(); // best effort; try { /* - * We need to join on the stopping thread in case it has stopped a thread that is in a critical section and needs to - * be resumed. + * We need to join on the suspending thread in case it has suspended a thread that is in a critical section and + * needs to be resumed. */ - stoppingThread.join(); + suspendingThread.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } - throw new RuntimeException("stopping node threads took too long"); + throw new RuntimeException("suspending node threads took too long"); } // block detection checks if other threads are blocked waiting on an object that is held by one // of the threads that was suspended @@ -190,7 +190,7 @@ private void stopBlockDetection() { if (blockDetectionThread != null) { try { blockDetectionThread.interrupt(); // best effort - blockDetectionThread.join(getStoppingTimeoutInMillis()); + blockDetectionThread.join(getSuspendingTimeoutInMillis()); } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -216,9 +216,9 @@ public TimeValue expectedTimeToHeal() { * returns true if some live threads were found. The caller is expected to call this method * until no more "live" are found. */ - @SuppressWarnings("deprecation") // stops/resumes threads intentionally - @SuppressForbidden(reason = "stops/resumes threads intentionally") - protected boolean stopNodeThreads(Set nodeThreads) { + @SuppressWarnings("deprecation") // suspends/resumes threads intentionally + @SuppressForbidden(reason = "suspends/resumes threads intentionally") + protected boolean suspendThreads(Set nodeThreads) { Thread[] allThreads = null; while (allThreads == null) { allThreads = new Thread[Thread.activeCount()]; @@ -236,12 +236,12 @@ protected boolean stopNodeThreads(Set nodeThreads) { if (isDisruptedNodeThread(threadName)) { if (thread.isAlive() && nodeThreads.add(thread)) { liveThreadsFound = true; - logger.trace("stopping thread [{}]", threadName); + logger.trace("suspending thread [{}]", threadName); // we assume it is not safe to suspend the thread boolean safe = false; try { /* - * At the bottom of this try-block we will know whether or not it is safe to suspend this thread; we start by + * At the bottom of this try-block we will know whether or not it is safe to suspend the thread; we start by * assuming that it is safe. */ boolean definitelySafe = true; @@ -263,8 +263,8 @@ protected boolean stopNodeThreads(Set nodeThreads) { if (!safe) { /* * Do not log before resuming as we might be interrupted while logging in which case we will throw an - * interrupted exception and never resume the stopped thread that is in a critical section. Also, logging before - * resuming makes for confusing log messages if we never hit the resume. + * interrupted exception and never resume the suspended thread that is in a critical section. Also, logging + * before resuming makes for confusing log messages if we never hit the resume. */ thread.resume(); logger.trace("resumed thread [{}] as it is in a critical section", threadName); @@ -283,7 +283,7 @@ protected Pattern[] getUnsafeClasses() { } // for testing - protected long getStoppingTimeoutInMillis() { + protected long getSuspendingTimeoutInMillis() { return TimeValue.timeValueSeconds(30).getMillis(); } @@ -309,8 +309,8 @@ protected void onBlockDetected(ThreadInfo blockedThread, @Nullable ThreadInfo bl "Stack trace of blocking thread: " + blockingThreadStackTrace); } - @SuppressWarnings("deprecation") // stops/resumes threads intentionally - @SuppressForbidden(reason = "stops/resumes threads intentionally") + @SuppressWarnings("deprecation") // suspends/resumes threads intentionally + @SuppressForbidden(reason = "suspends/resumes threads intentionally") protected void resumeThreads(Set threads) { for (Thread thread : threads) { thread.resume(); diff --git a/test/framework/src/test/java/org/elasticsearch/test/disruption/LongGCDisruptionTests.java b/test/framework/src/test/java/org/elasticsearch/test/disruption/LongGCDisruptionTests.java index 147a6df608a03..e872755cfd052 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/disruption/LongGCDisruptionTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/disruption/LongGCDisruptionTests.java @@ -62,7 +62,7 @@ protected Pattern[] getUnsafeClasses() { } @Override - protected long getStoppingTimeoutInMillis() { + protected long getSuspendingTimeoutInMillis() { return 100; } }; @@ -99,7 +99,7 @@ protected long getStoppingTimeoutInMillis() { // make sure some threads are under lock underLock.await(); RuntimeException e = expectThrows(RuntimeException.class, disruption::startDisrupting); - assertThat(e.getMessage(), containsString("stopping node threads took too long")); + assertThat(e.getMessage(), containsString("suspending node threads took too long")); } finally { stop.set(true); pauseUnderLock.countDown(); From 1be2800120661bb98f039c99f7f9681ea433a18f Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Thu, 27 Apr 2017 08:43:20 +0200 Subject: [PATCH 103/619] Only allow one type on 7.0 indices (#24317) This adds the `index.mapping.single_type` setting, which enforces that indices have at most one type when it is true. The default value is true for 6.0+ indices and false for old indices. Relates #15613 --- .../common/settings/IndexScopedSettings.java | 1 + .../index/mapper/MapperService.java | 24 ++++ .../admin/indices/create/ShrinkIndexIT.java | 12 +- .../action/bulk/BulkWithUpdatesIT.java | 8 +- .../elasticsearch/aliases/IndexAliasesIT.java | 1 + .../metadata/MetaDataMappingServiceTests.java | 1 + .../fieldstats/FieldStatsTests.java | 8 +- .../gateway/RecoveryFromGatewayIT.java | 1 + .../org/elasticsearch/get/GetActionIT.java | 11 +- .../fielddata/AbstractFieldDataTestCase.java | 5 +- .../index/mapper/DynamicMappingIT.java | 2 +- .../index/mapper/DynamicMappingTests.java | 3 +- .../index/mapper/MapperServiceTests.java | 18 ++- .../index/mapper/NestedObjectMapperTests.java | 5 +- .../index/mapper/ParentFieldMapperTests.java | 2 +- .../index/mapper/UpdateMappingTests.java | 11 +- .../indices/IndicesOptionsIntegrationIT.java | 4 +- .../indices/exists/types/TypesExistsIT.java | 1 + .../mapping/SimpleGetFieldMappingsIT.java | 3 + .../indices/mapping/SimpleGetMappingsIT.java | 3 + .../mapping/UpdateMappingIntegrationIT.java | 7 +- .../indices/stats/IndexStatsIT.java | 14 +- .../template/SimpleIndexTemplateIT.java | 11 +- .../aggregations/bucket/ChildrenIT.java | 5 + .../aggregations/bucket/DoubleTermsIT.java | 49 +++---- .../aggregations/bucket/LongTermsIT.java | 67 +++++----- .../aggregations/bucket/ReverseNestedIT.java | 55 ++++---- .../aggregations/bucket/StringTermsIT.java | 51 ++++---- .../aggregations/metrics/TopHitsIT.java | 54 ++++---- .../basic/TransportSearchFailuresIT.java | 2 +- .../basic/TransportTwoNodesSearchIT.java | 2 +- .../search/child/ChildQuerySearchIT.java | 55 ++++++-- .../search/child/ParentFieldLoadingIT.java | 1 + .../search/fetch/subphase/InnerHitsIT.java | 24 +++- .../highlight/HighlighterSearchIT.java | 1 + .../search/fields/SearchFieldsIT.java | 4 +- .../search/morelikethis/MoreLikeThisIT.java | 7 +- .../search/nested/SimpleNestedIT.java | 1 + .../search/query/SearchQueryIT.java | 7 +- .../search/slice/SearchSliceIT.java | 2 +- .../search/sort/FieldSortIT.java | 4 +- .../search/suggest/SuggestSearchIT.java | 4 +- .../SharedClusterSnapshotRestoreIT.java | 4 +- .../java/org/elasticsearch/tribe/TribeIT.java | 4 +- .../org/elasticsearch/update/UpdateIT.java | 4 +- docs/build.gradle | 2 +- .../bucket/children-aggregation.asciidoc | 3 + .../pattern-replace-charfilter.asciidoc | 4 +- docs/reference/docs/index_.asciidoc | 3 + docs/reference/indices/put-mapping.asciidoc | 6 + docs/reference/mapping.asciidoc | 3 + .../mapping/dynamic/default-mapping.asciidoc | 3 + .../mapping/fields/parent-field.asciidoc | 6 + .../mapping/fields/type-field.asciidoc | 7 + .../query-dsl/parent-id-query.asciidoc | 3 + .../query-dsl/percolate-query.asciidoc | 61 ++++----- .../expression/MoreExpressionTests.java | 4 +- .../script/mustache/SearchTemplateIT.java | 4 +- .../percolator/CandidateQueryTests.java | 7 +- .../PercolatorFieldMapperTests.java | 3 +- .../percolator/PercolatorQuerySearchIT.java | 122 +++++++++--------- .../rest-api-spec/test/10_basic.yaml | 10 +- .../index/reindex/ReindexBasicTests.java | 33 ++--- .../reindex/ReindexParentChildTests.java | 1 + .../test/delete_by_query/30_by_type.yaml | 7 + .../rest-api-spec/test/reindex/90_remote.yaml | 4 + .../rest-api-spec/test/reindex/10_script.yaml | 4 + .../test/indices.exists_type/10_basic.yaml | 6 + .../test/indices.get_mapping/10_basic.yaml | 52 ++++++++ .../test/indices.sort/10_basic.yaml | 2 +- .../rest-api-spec/test/mget/15_ids.yaml | 11 ++ .../test/search.aggregation/50_filter.yaml | 4 +- .../70_adjacency_matrix.yaml | 2 +- .../test/search.inner_hits/10_basic.yaml | 10 ++ .../test/AbstractQueryTestCase.java | 3 +- 75 files changed, 580 insertions(+), 373 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java b/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java index 1568ac288d9b4..ff99fb80d5298 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java +++ b/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java @@ -135,6 +135,7 @@ public final class IndexScopedSettings extends AbstractScopedSettings { MapperService.INDEX_MAPPING_NESTED_FIELDS_LIMIT_SETTING, MapperService.INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING, MapperService.INDEX_MAPPING_DEPTH_LIMIT_SETTING, + MapperService.INDEX_MAPPING_SINGLE_TYPE_SETTING, BitsetFilterCache.INDEX_LOAD_RANDOM_ACCESS_FILTERS_EAGERLY_SETTING, IndexModule.INDEX_STORE_TYPE_SETTING, IndexModule.INDEX_STORE_PRE_LOAD_SETTING, diff --git a/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java b/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java index 55cfebe41c1db..b0761d0df1127 100755 --- a/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java @@ -25,6 +25,7 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.DelegatingAnalyzerWrapper; import org.elasticsearch.ElasticsearchGenerationException; +import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.common.Nullable; @@ -32,6 +33,7 @@ import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; @@ -94,6 +96,19 @@ public enum MergeReason { public static final boolean INDEX_MAPPER_DYNAMIC_DEFAULT = true; public static final Setting INDEX_MAPPER_DYNAMIC_SETTING = Setting.boolSetting("index.mapper.dynamic", INDEX_MAPPER_DYNAMIC_DEFAULT, Property.Dynamic, Property.IndexScope); + public static final Setting INDEX_MAPPING_SINGLE_TYPE_SETTING; + static { + Function defValue = settings -> { + // TODO: uncomment this + /*boolean singleType = true; + if (settings.getAsVersion(IndexMetaData.SETTING_VERSION_CREATED, null) != null) { + singleType = Version.indexCreated(settings).onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED); + }*/ + boolean singleType = false; + return Boolean.valueOf(singleType).toString(); + }; + INDEX_MAPPING_SINGLE_TYPE_SETTING = Setting.boolSetting("index.mapping.single_type", defValue, Property.IndexScope, Property.Final); + } private static ObjectHashSet META_FIELDS = ObjectHashSet.from( "_uid", "_id", "_type", "_all", "_parent", "_routing", "_index", "_size", "_timestamp", "_ttl" @@ -457,6 +472,15 @@ private synchronized Map internalMerge(@Nullable Documen } } + if (indexSettings.getValue(INDEX_MAPPING_SINGLE_TYPE_SETTING)) { + Set actualTypes = new HashSet<>(mappers.keySet()); + actualTypes.remove(DEFAULT_MAPPING); + if (actualTypes.size() > 1) { + throw new IllegalArgumentException( + "Rejecting mapping update to [" + index().getName() + "] as the final mapping would have more than 1 type: " + actualTypes); + } + } + // make structures immutable mappers = Collections.unmodifiableMap(mappers); results = Collections.unmodifiableMap(results); diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java b/core/src/test/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java index dea04d1710685..36c7da7894b50 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java @@ -143,7 +143,7 @@ public void testCreateShrinkIndex() { .put("index.version.created", version) ).get(); for (int i = 0; i < 20; i++) { - client().prepareIndex("source", randomFrom("t1", "t2", "t3")) + client().prepareIndex("source", "type") .setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); } ImmutableOpenMap dataNodes = client().admin().cluster().prepareState().get().getState().nodes() @@ -179,7 +179,7 @@ public void testCreateShrinkIndex() { } for (int i = 20; i < 40; i++) { - client().prepareIndex("target", randomFrom("t1", "t2", "t3")) + client().prepareIndex("target", "type") .setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); } flushAndRefresh(); @@ -197,7 +197,7 @@ public void testCreateShrinkIndexFails() throws Exception { .put("number_of_shards", randomIntBetween(2, 7)) .put("number_of_replicas", 0)).get(); for (int i = 0; i < 20; i++) { - client().prepareIndex("source", randomFrom("t1", "t2", "t3")) + client().prepareIndex("source", "type") .setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); } ImmutableOpenMap dataNodes = client().admin().cluster().prepareState().get().getState().nodes() @@ -275,10 +275,10 @@ public void testCreateShrinkWithIndexSort() throws Exception { .put("number_of_shards", 8) .put("number_of_replicas", 0) ) - .addMapping("t1", "id", "type=keyword,doc_values=true") + .addMapping("type", "id", "type=keyword,doc_values=true") .get(); for (int i = 0; i < 20; i++) { - client().prepareIndex("source", "t1", Integer.toString(i)) + client().prepareIndex("source", "type", Integer.toString(i)) .setSource("{\"foo\" : \"bar\", \"id\" : " + i + "}", XContentType.JSON).get(); } ImmutableOpenMap dataNodes = client().admin().cluster().prepareState().get().getState().nodes() @@ -326,7 +326,7 @@ public void testCreateShrinkWithIndexSort() throws Exception { // ... and that the index sort is also applied to updates for (int i = 20; i < 40; i++) { - client().prepareIndex("target", randomFrom("t1", "t2", "t3")) + client().prepareIndex("target", "type") .setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); } flushAndRefresh(); diff --git a/core/src/test/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java b/core/src/test/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java index da16b688340fa..21930b3763b6a 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java @@ -455,6 +455,7 @@ public void testBulkIndexingWhileInitializing() throws Exception { */ public void testBulkUpdateDocAsUpsertWithParent() throws Exception { client().admin().indices().prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent", "{\"parent\":{}}", XContentType.JSON) .addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}", XContentType.JSON) .execute().actionGet(); @@ -519,6 +520,7 @@ public void testBulkUpdateDocAsUpsertWithParent() throws Exception { */ public void testBulkUpdateUpsertWithParent() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent", "{\"parent\":{}}", XContentType.JSON) .addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}", XContentType.JSON)); ensureGreen(); @@ -603,8 +605,10 @@ public void testBulkUpdateUpsertWithParent() throws Exception { * Test for https://github.com/elastic/elasticsearch/issues/8365 */ public void testBulkUpdateChildMissingParentRouting() throws Exception { - assertAcked(prepareCreate("test").addMapping("parent", "{\"parent\":{}}", XContentType.JSON) - .addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}", XContentType.JSON)); + assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) + .addMapping("parent", "{\"parent\":{}}", XContentType.JSON) + .addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}", XContentType.JSON)); ensureGreen(); BulkRequestBuilder builder = client().prepareBulk(); diff --git a/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java b/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java index 6959758b1102e..fcec3d6b59445 100644 --- a/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java +++ b/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java @@ -826,6 +826,7 @@ public void testAliasFilterWithNowInRangeFilterAndQuery() throws Exception { public void testAliasesFilterWithHasChildQuery() throws Exception { assertAcked(prepareCreate("my-index") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent") ); diff --git a/core/src/test/java/org/elasticsearch/cluster/metadata/MetaDataMappingServiceTests.java b/core/src/test/java/org/elasticsearch/cluster/metadata/MetaDataMappingServiceTests.java index 9fd90e3c314e0..13f7549973dbc 100644 --- a/core/src/test/java/org/elasticsearch/cluster/metadata/MetaDataMappingServiceTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/metadata/MetaDataMappingServiceTests.java @@ -54,6 +54,7 @@ public void testAddChildTypePointingToAlreadyExistingType() throws Exception { // Tests _parent meta field logic, because part of the validation is in MetaDataMappingService public void testAddExtraChildTypePointingToAlreadyParentExistingType() throws Exception { IndexService indexService = createIndex("test", client().admin().indices().prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child1", "_parent", "type=parent") ); diff --git a/core/src/test/java/org/elasticsearch/fieldstats/FieldStatsTests.java b/core/src/test/java/org/elasticsearch/fieldstats/FieldStatsTests.java index a396f4bfa6b8f..379806a7166fd 100644 --- a/core/src/test/java/org/elasticsearch/fieldstats/FieldStatsTests.java +++ b/core/src/test/java/org/elasticsearch/fieldstats/FieldStatsTests.java @@ -319,9 +319,9 @@ public void testMerge_notAvailable() { public void testNumberFiltering() { createIndex("test1", Settings.EMPTY, "type", "value", "type=long"); - client().prepareIndex("test1", "test").setSource("value", 1L).get(); + client().prepareIndex("test1", "type").setSource("value", 1L).get(); createIndex("test2", Settings.EMPTY, "type", "value", "type=long"); - client().prepareIndex("test2", "test").setSource("value", 3L).get(); + client().prepareIndex("test2", "type").setSource("value", 3L).get(); client().admin().indices().prepareRefresh().get(); FieldStatsResponse response = client().prepareFieldStats() @@ -426,10 +426,10 @@ public void testDateFiltering() { String dateTime2Str = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parser().print(dateTime2); createIndex("test1", Settings.EMPTY, "type", "value", "type=date", "value2", "type=date,index=false"); - client().prepareIndex("test1", "test") + client().prepareIndex("test1", "type") .setSource("value", dateTime1Str, "value2", dateTime1Str).get(); createIndex("test2", Settings.EMPTY, "type", "value", "type=date"); - client().prepareIndex("test2", "test").setSource("value", dateTime2Str).get(); + client().prepareIndex("test2", "type").setSource("value", dateTime2Str).get(); client().admin().indices().prepareRefresh().get(); FieldStatsResponse response = client().prepareFieldStats() diff --git a/core/src/test/java/org/elasticsearch/gateway/RecoveryFromGatewayIT.java b/core/src/test/java/org/elasticsearch/gateway/RecoveryFromGatewayIT.java index f957a55bf1409..4210f9c32c17f 100644 --- a/core/src/test/java/org/elasticsearch/gateway/RecoveryFromGatewayIT.java +++ b/core/src/test/java/org/elasticsearch/gateway/RecoveryFromGatewayIT.java @@ -318,6 +318,7 @@ public void testLatestVersionLoaded() throws Exception { // clean two nodes internalCluster().startNodes(2, Settings.builder().put("gateway.recover_after_nodes", 2).build()); + assertAcked(client().admin().indices().prepareCreate("test").setSettings("index.mapping.single_type", false)); client().prepareIndex("test", "type1", "1").setSource(jsonBuilder().startObject().field("field", "value1").endObject()).execute().actionGet(); client().admin().indices().prepareFlush().execute().actionGet(); client().prepareIndex("test", "type1", "2").setSource(jsonBuilder().startObject().field("field", "value2").endObject()).execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/get/GetActionIT.java b/core/src/test/java/org/elasticsearch/get/GetActionIT.java index 2797ae7fcc844..e6439011cf6ee 100644 --- a/core/src/test/java/org/elasticsearch/get/GetActionIT.java +++ b/core/src/test/java/org/elasticsearch/get/GetActionIT.java @@ -35,7 +35,6 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.lucene.uid.Versions; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.engine.VersionConflictEngineException; @@ -255,7 +254,7 @@ public void testGetDocWithMultivaluedFields() throws Exception { assertAcked(prepareCreate("test") .addMapping("type1", mapping1, XContentType.JSON) .addMapping("type2", mapping2, XContentType.JSON) - .setSettings(Settings.builder().put("index.refresh_interval", -1))); + .setSettings("index.refresh_interval", -1, "index.mapping.single_type", false)); ensureGreen(); GetResponse response = client().prepareGet("test", "type1", "1").get(); @@ -530,7 +529,7 @@ public void testGetFieldsMetaData() throws Exception { .addMapping("parent") .addMapping("my-type1", "_parent", "type=parent", "field1", "type=keyword,store=true") .addAlias(new Alias("alias")) - .setSettings(Settings.builder().put("index.refresh_interval", -1))); + .setSettings("index.refresh_interval", -1, "index.mapping.single_type", false)); client().prepareIndex("test", "my-type1", "1") .setRouting("1") @@ -594,7 +593,7 @@ public void testGetFieldsNonLeafField() throws Exception { public void testGetFieldsComplexField() throws Exception { assertAcked(prepareCreate("my-index") - .setSettings(Settings.builder().put("index.refresh_interval", -1)) + .setSettings("index.refresh_interval", -1, "index.mapping.single_type", false) .addMapping("my-type2", jsonBuilder().startObject().startObject("my-type2").startObject("properties") .startObject("field1").field("type", "object").startObject("properties") .startObject("field2").field("type", "object").startObject("properties") @@ -726,6 +725,7 @@ public void testUngeneratedFieldsThatAreAlwaysStored() throws IOException { String createIndexSource = "{\n" + " \"settings\": {\n" + " \"index.translog.flush_threshold_size\": \"1pb\",\n" + + " \"index.mapping.single_type\": false," + " \"refresh_interval\": \"-1\"\n" + " },\n" + " \"mappings\": {\n" + @@ -738,7 +738,8 @@ public void testUngeneratedFieldsThatAreAlwaysStored() throws IOException { " }\n" + " }\n" + "}"; - assertAcked(prepareCreate("test").addAlias(new Alias("alias")).setSource(createIndexSource, XContentType.JSON)); + assertAcked(prepareCreate("test") + .addAlias(new Alias("alias")).setSource(createIndexSource, XContentType.JSON)); ensureGreen(); client().prepareIndex("test", "doc").setId("1").setSource("{}", XContentType.JSON).setParent("1").get(); diff --git a/core/src/test/java/org/elasticsearch/index/fielddata/AbstractFieldDataTestCase.java b/core/src/test/java/org/elasticsearch/index/fielddata/AbstractFieldDataTestCase.java index cef6806631f17..99aa08160820d 100644 --- a/core/src/test/java/org/elasticsearch/index/fielddata/AbstractFieldDataTestCase.java +++ b/core/src/test/java/org/elasticsearch/index/fielddata/AbstractFieldDataTestCase.java @@ -31,7 +31,9 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.store.RAMDirectory; +import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.lucene.index.ElasticsearchDirectoryReader; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.cache.bitset.BitsetFilterCache; import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource.Nested; @@ -41,6 +43,7 @@ import org.elasticsearch.index.mapper.KeywordFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.Mapper.BuilderContext; +import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.NumberFieldMapper; import org.elasticsearch.index.mapper.ParentFieldMapper; @@ -131,7 +134,7 @@ public > IFD getForField(String type, String field @Before public void setup() throws Exception { - indexService = createIndex("test"); + indexService = createIndex("test", Settings.builder().put("mapping.single_type", false).build()); mapperService = indexService.mapperService(); indicesFieldDataCache = getInstanceFromNode(IndicesService.class).getIndicesFieldDataCache(); ifdService = indexService.fieldData(); diff --git a/core/src/test/java/org/elasticsearch/index/mapper/DynamicMappingIT.java b/core/src/test/java/org/elasticsearch/index/mapper/DynamicMappingIT.java index e6d5769467b68..91a498541ed6f 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/DynamicMappingIT.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/DynamicMappingIT.java @@ -71,7 +71,7 @@ private static void assertMappingsHaveField(GetMappingsResponse mappings, String } public void testMappingsPropagatedToMasterNodeImmediately() throws IOException { - createIndex("index"); + assertAcked(prepareCreate("index").setSettings("index.mapping.single_type", false)); // works when the type has been dynamically created client().prepareIndex("index", "type", "1").setSource("foo", 3).get(); diff --git a/core/src/test/java/org/elasticsearch/index/mapper/DynamicMappingTests.java b/core/src/test/java/org/elasticsearch/index/mapper/DynamicMappingTests.java index 71ae77aa55e6e..daf3d99ad5b0e 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/DynamicMappingTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/DynamicMappingTests.java @@ -34,6 +34,7 @@ import org.elasticsearch.index.IndexService; import org.elasticsearch.index.mapper.BooleanFieldMapper.BooleanFieldType; import org.elasticsearch.index.mapper.DateFieldMapper.DateFieldType; +import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.index.mapper.NumberFieldMapper.NumberFieldType; import org.elasticsearch.test.ESSingleNodeTestCase; @@ -524,7 +525,7 @@ public void testReuseExistingMappings() throws IOException, Exception { } public void testMixTemplateMultiFieldAndMappingReuse() throws Exception { - IndexService indexService = createIndex("test"); + IndexService indexService = createIndex("test", Settings.builder().put("mapping.single_type", false).build()); XContentBuilder mappings1 = jsonBuilder().startObject() .startObject("type1") .startArray("dynamic_templates") diff --git a/core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java b/core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java index 7141550a44fcd..4d467d641d9fa 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.mapper; import org.elasticsearch.ExceptionsHelper; -import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentFactory; @@ -30,6 +29,7 @@ import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.index.mapper.NumberFieldMapper.NumberFieldType; import org.elasticsearch.test.ESSingleNodeTestCase; +import org.hamcrest.Matchers; import java.io.IOException; import java.io.UncheckedIOException; @@ -74,7 +74,7 @@ public void testTypeNameTooLong() { } public void testTypes() throws Exception { - IndexService indexService1 = createIndex("index1"); + IndexService indexService1 = createIndex("index1", Settings.builder().put("index.mapping.single_type", false).build()); MapperService mapperService = indexService1.mapperService(); assertEquals(Collections.emptySet(), mapperService.types()); @@ -207,7 +207,7 @@ public void testMergeParentTypesSame() { } public void testOtherDocumentMappersOnlyUpdatedWhenChangingFieldType() throws IOException { - IndexService indexService = createIndex("test"); + IndexService indexService = createIndex("test", Settings.builder().put("index.mapping.single_type", false).build()); CompressedXContent simpleMapping = new CompressedXContent(XContentFactory.jsonBuilder().startObject() .startObject("properties") @@ -309,4 +309,16 @@ public void testIndexSortWithNestedFields() throws IOException { assertThat(invalidNestedException.getMessage(), containsString("cannot have nested fields when index sort is activated")); } + + @AwaitsFix(bugUrl="https://github.com/elastic/elasticsearch/pull/24317#issuecomment-297624290") + public void testForbidMultipleTypes() throws IOException { + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").endObject().endObject().string(); + MapperService mapperService = createIndex("test").mapperService(); + mapperService.merge("type", new CompressedXContent(mapping), MergeReason.MAPPING_UPDATE, randomBoolean()); + + String mapping2 = XContentFactory.jsonBuilder().startObject().startObject("type2").endObject().endObject().string(); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> mapperService.merge("type2", new CompressedXContent(mapping2), MergeReason.MAPPING_UPDATE, randomBoolean())); + assertThat(e.getMessage(), Matchers.startsWith("Rejecting mapping update to [test] as the final mapping would have more than 1 type: ")); + } } diff --git a/core/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java index 91cf8fdde851b..f4a8ce11c56a8 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java @@ -382,8 +382,9 @@ public void testLimitOfNestedFieldsPerIndex() throws Exception { .mapperService().merge("type", new CompressedXContent(mapping.apply("type")), MergeReason.MAPPING_UPDATE, false)); assertThat(e.getMessage(), containsString("Limit of nested fields [1] in index [test3] has been exceeded")); - MapperService mapperService = createIndex("test4", Settings.builder().put(MapperService.INDEX_MAPPING_NESTED_FIELDS_LIMIT_SETTING.getKey(), 2) - .build()).mapperService(); + MapperService mapperService = createIndex("test4", Settings.builder() + .put("mapping.single_type", false) + .put(MapperService.INDEX_MAPPING_NESTED_FIELDS_LIMIT_SETTING.getKey(), 2).build()).mapperService(); mapperService.merge("type1", new CompressedXContent(mapping.apply("type1")), MergeReason.MAPPING_UPDATE, false); // merging same fields, but different type is ok mapperService.merge("type2", new CompressedXContent(mapping.apply("type2")), MergeReason.MAPPING_UPDATE, false); diff --git a/core/src/test/java/org/elasticsearch/index/mapper/ParentFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/ParentFieldMapperTests.java index af5061e62ae95..7ef1b751eeba3 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/ParentFieldMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/ParentFieldMapperTests.java @@ -67,7 +67,7 @@ public void testJoinFieldSet() throws Exception { String childMapping = XContentFactory.jsonBuilder().startObject().startObject("child_type") .startObject("_parent").field("type", "parent_type").endObject() .endObject().endObject().string(); - IndexService indexService = createIndex("test"); + IndexService indexService = createIndex("test", Settings.builder().put("mapping.single_type", false).build()); indexService.mapperService().merge("parent_type", new CompressedXContent(parentMapping), MergeReason.MAPPING_UPDATE, false); indexService.mapperService().merge("child_type", new CompressedXContent(childMapping), MergeReason.MAPPING_UPDATE, false); diff --git a/core/src/test/java/org/elasticsearch/index/mapper/UpdateMappingTests.java b/core/src/test/java/org/elasticsearch/index/mapper/UpdateMappingTests.java index 9321801d1f439..7c18d9cb1a6fd 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/UpdateMappingTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/UpdateMappingTests.java @@ -42,15 +42,6 @@ protected Collection> getPlugins() { return pluginList(InternalSettingsPlugin.class); } - private void testNoConflictWhileMergingAndMappingChanged(XContentBuilder mapping, XContentBuilder mappingUpdate, XContentBuilder expectedMapping) throws IOException { - IndexService indexService = createIndex("test", Settings.builder().build(), "type", mapping); - // simulate like in MetaDataMappingService#putMapping - indexService.mapperService().merge("type", new CompressedXContent(mappingUpdate.bytes()), MapperService.MergeReason.MAPPING_UPDATE, false); - // make sure mappings applied - CompressedXContent mappingAfterUpdate = indexService.mapperService().documentMapper("type").mappingSource(); - assertThat(mappingAfterUpdate.toString(), equalTo(expectedMapping.string())); - } - public void testConflictFieldsMapping(String fieldName) throws Exception { //test store, ... all the parameters that are not to be changed just like in other fields XContentBuilder mapping = XContentFactory.jsonBuilder() @@ -159,7 +150,7 @@ public void testConflictNewTypeUpdate() throws Exception { .startObject("properties").startObject("foo").field("type", "long").endObject() .endObject().endObject().endObject(); XContentBuilder mapping2 = XContentFactory.jsonBuilder().startObject().startObject("type2").endObject().endObject(); - MapperService mapperService = createIndex("test", Settings.builder().build()).mapperService(); + MapperService mapperService = createIndex("test", Settings.builder().put("mapping.single_type", false).build()).mapperService(); mapperService.merge("type1", new CompressedXContent(mapping1.string()), MapperService.MergeReason.MAPPING_UPDATE, false); mapperService.merge("type2", new CompressedXContent(mapping2.string()), MapperService.MergeReason.MAPPING_UPDATE, false); diff --git a/core/src/test/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java b/core/src/test/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java index 2734fd7828421..ce2b5088ac994 100644 --- a/core/src/test/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java +++ b/core/src/test/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java @@ -547,7 +547,9 @@ public void testPutMapping() throws Exception { verify(client().admin().indices().preparePutMapping("foo").setType("type1").setSource("field", "type=text"), true); verify(client().admin().indices().preparePutMapping("_all").setType("type1").setSource("field", "type=text"), true); - createIndex("foo", "foobar", "bar", "barbaz"); + for (String index : Arrays.asList("foo", "foobar", "bar", "barbaz")) { + assertAcked(prepareCreate(index).setSettings("index.mapping.single_type", false)); + } verify(client().admin().indices().preparePutMapping("foo").setType("type1").setSource("field", "type=text"), false); assertThat(client().admin().indices().prepareGetMappings("foo").get().mappings().get("foo").get("type1"), notNullValue()); diff --git a/core/src/test/java/org/elasticsearch/indices/exists/types/TypesExistsIT.java b/core/src/test/java/org/elasticsearch/indices/exists/types/TypesExistsIT.java index ffe44b3e1c136..31e3ca8232609 100644 --- a/core/src/test/java/org/elasticsearch/indices/exists/types/TypesExistsIT.java +++ b/core/src/test/java/org/elasticsearch/indices/exists/types/TypesExistsIT.java @@ -40,6 +40,7 @@ public class TypesExistsIT extends ESIntegTestCase { public void testSimple() throws Exception { Client client = client(); CreateIndexResponse response1 = client.admin().indices().prepareCreate("test1") + .setSettings("index.mapping.single_type", false) .addMapping("type1", jsonBuilder().startObject().startObject("type1").endObject().endObject()) .addMapping("type2", jsonBuilder().startObject().startObject("type2").endObject().endObject()) .execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/indices/mapping/SimpleGetFieldMappingsIT.java b/core/src/test/java/org/elasticsearch/indices/mapping/SimpleGetFieldMappingsIT.java index f654dfe19f64f..57f3ec29e3262 100644 --- a/core/src/test/java/org/elasticsearch/indices/mapping/SimpleGetFieldMappingsIT.java +++ b/core/src/test/java/org/elasticsearch/indices/mapping/SimpleGetFieldMappingsIT.java @@ -67,9 +67,11 @@ private XContentBuilder getMappingForType(String type) throws IOException { public void testSimpleGetFieldMappings() throws Exception { assertAcked(prepareCreate("indexa") + .setSettings("index.mapping.single_type", false) .addMapping("typeA", getMappingForType("typeA")) .addMapping("typeB", getMappingForType("typeB"))); assertAcked(client().admin().indices().prepareCreate("indexb") + .setSettings("index.mapping.single_type", false) .addMapping("typeA", getMappingForType("typeA")) .addMapping("typeB", getMappingForType("typeB"))); @@ -184,6 +186,7 @@ public void testSimpleGetFieldMappingsWithPretty() throws Exception { public void testGetFieldMappingsWithBlocks() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("typeA", getMappingForType("typeA")) .addMapping("typeB", getMappingForType("typeB"))); diff --git a/core/src/test/java/org/elasticsearch/indices/mapping/SimpleGetMappingsIT.java b/core/src/test/java/org/elasticsearch/indices/mapping/SimpleGetMappingsIT.java index 6b96e4e102da1..89b0d48ef22df 100644 --- a/core/src/test/java/org/elasticsearch/indices/mapping/SimpleGetMappingsIT.java +++ b/core/src/test/java/org/elasticsearch/indices/mapping/SimpleGetMappingsIT.java @@ -56,12 +56,14 @@ private XContentBuilder getMappingForType(String type) throws IOException { public void testSimpleGetMappings() throws Exception { client().admin().indices().prepareCreate("indexa") + .setSettings("index.mapping.single_type", false) .addMapping("typeA", getMappingForType("typeA")) .addMapping("typeB", getMappingForType("typeB")) .addMapping("Atype", getMappingForType("Atype")) .addMapping("Btype", getMappingForType("Btype")) .execute().actionGet(); client().admin().indices().prepareCreate("indexb") + .setSettings("index.mapping.single_type", false) .addMapping("typeA", getMappingForType("typeA")) .addMapping("typeB", getMappingForType("typeB")) .addMapping("Atype", getMappingForType("Atype")) @@ -143,6 +145,7 @@ public void testSimpleGetMappings() throws Exception { public void testGetMappingsWithBlocks() throws IOException { client().admin().indices().prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("typeA", getMappingForType("typeA")) .addMapping("typeB", getMappingForType("typeB")) .execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/indices/mapping/UpdateMappingIntegrationIT.java b/core/src/test/java/org/elasticsearch/indices/mapping/UpdateMappingIntegrationIT.java index 5b62c99d242f6..d41ef16d77191 100644 --- a/core/src/test/java/org/elasticsearch/indices/mapping/UpdateMappingIntegrationIT.java +++ b/core/src/test/java/org/elasticsearch/indices/mapping/UpdateMappingIntegrationIT.java @@ -69,6 +69,7 @@ public void testDynamicUpdates() throws Exception { .put("index.number_of_shards", 1) .put("index.number_of_replicas", 0) .put(MapperService.INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING.getKey(), Long.MAX_VALUE) + .put("index.mapping.single_type", false) ).execute().actionGet(); client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet(); @@ -281,7 +282,7 @@ public void run() { Client client1 = clientArray.get(i % clientArray.size()); Client client2 = clientArray.get((i + 1) % clientArray.size()); String indexName = i % 2 == 0 ? "test2" : "test1"; - String typeName = "type" + (i % 10); + String typeName = "type"; String fieldName = Thread.currentThread().getName() + "_" + i; PutMappingResponse response = client1.admin().indices().preparePutMapping(indexName).setType(typeName).setSource( @@ -341,7 +342,9 @@ public void testPutMappingsWithBlocks() throws Exception { } public void testUpdateMappingOnAllTypes() throws IOException { - assertAcked(prepareCreate("index").addMapping("type1", "f", "type=keyword").addMapping("type2", "f", "type=keyword")); + assertAcked(prepareCreate("index") + .setSettings("index.mapping.single_type", false) + .addMapping("type1", "f", "type=keyword").addMapping("type2", "f", "type=keyword")); assertAcked(client().admin().indices().preparePutMapping("index") .setType("type1") diff --git a/core/src/test/java/org/elasticsearch/indices/stats/IndexStatsIT.java b/core/src/test/java/org/elasticsearch/indices/stats/IndexStatsIT.java index 312ad78e6ca4e..f8cf9bd7a3c92 100644 --- a/core/src/test/java/org/elasticsearch/indices/stats/IndexStatsIT.java +++ b/core/src/test/java/org/elasticsearch/indices/stats/IndexStatsIT.java @@ -383,7 +383,8 @@ public void testThrottleStats() throws Exception { } public void testSimpleStats() throws Exception { - createIndex("test1", "test2"); + assertAcked(prepareCreate("test1").setSettings("index.mapping.single_type", false)); + createIndex("test2"); ensureGreen(); client().prepareIndex("test1", "type1", Integer.toString(1)).setSource("field", "value").execute().actionGet(); @@ -512,7 +513,7 @@ public void testSimpleStats() throws Exception { } public void testMergeStats() { - createIndex("test1"); + assertAcked(prepareCreate("test1").setSettings("index.mapping.single_type", false)); ensureGreen(); @@ -548,7 +549,8 @@ public void testMergeStats() { } public void testSegmentsStats() { - assertAcked(prepareCreate("test1", 2, settingsBuilder().put(SETTING_NUMBER_OF_REPLICAS, between(0, 1)))); + assertAcked(prepareCreate("test1") + .setSettings(SETTING_NUMBER_OF_REPLICAS, between(0, 1), "index.mapping.single_type", false)); ensureGreen(); NumShards test1 = getNumShards("test1"); @@ -573,7 +575,7 @@ public void testSegmentsStats() { public void testAllFlags() throws Exception { // rely on 1 replica for this tests - createIndex("test1"); + assertAcked(prepareCreate("test1").setSettings("index.mapping.single_type", false)); createIndex("test2"); ensureGreen(); @@ -695,7 +697,7 @@ public void testFlagOrdinalOrder() { } public void testMultiIndex() throws Exception { - createIndex("test1"); + assertAcked(prepareCreate("test1").setSettings("index.mapping.single_type", false)); createIndex("test2"); ensureGreen(); @@ -735,6 +737,7 @@ public void testMultiIndex() throws Exception { public void testFieldDataFieldsParam() throws Exception { assertAcked(client().admin().indices().prepareCreate("test1") + .setSettings("index.mapping.single_type", false) .addMapping("type", "bar", "type=text,fielddata=true", "baz", "type=text,fielddata=true").get()); @@ -782,6 +785,7 @@ public void testFieldDataFieldsParam() throws Exception { public void testCompletionFieldsParam() throws Exception { assertAcked(prepareCreate("test1") + .setSettings("index.mapping.single_type", false) .addMapping( "bar", "{ \"properties\": { \"bar\": { \"type\": \"text\", \"fields\": { \"completion\": { \"type\": \"completion\" }}},\"baz\": { \"type\": \"text\", \"fields\": { \"completion\": { \"type\": \"completion\" }}}}}", XContentType.JSON)); diff --git a/core/src/test/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java b/core/src/test/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java index 10ccc4f002588..017026844f11d 100644 --- a/core/src/test/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java +++ b/core/src/test/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java @@ -383,7 +383,8 @@ public void testIndexTemplateWithAliases() throws Exception { .get(); assertAcked(prepareCreate("test_index") - .addMapping("type1").addMapping("type2").addMapping("typeX").addMapping("typeY").addMapping("typeZ")); + .setSettings("index.mapping.single_type", false) + .addMapping("type1").addMapping("type2").addMapping("typeX").addMapping("typeY").addMapping("typeZ")); ensureGreen(); client().prepareIndex("test_index", "type1", "1").setSource("field", "A value").get(); @@ -439,7 +440,9 @@ public void testIndexTemplateWithAliasesInSource() { "}"), XContentType.JSON).get(); - assertAcked(prepareCreate("test_index").addMapping("type1").addMapping("type2")); + assertAcked(prepareCreate("test_index") + .setSettings("index.mapping.single_type", false) + .addMapping("type1").addMapping("type2")); ensureGreen(); GetAliasesResponse getAliasesResponse = client().admin().indices().prepareGetAliases().setIndices("test_index").get(); @@ -474,7 +477,9 @@ public void testIndexTemplateWithAliasesSource() { " \"alias3\" : { \"routing\" : \"1\" }" + " }\n").get(); - assertAcked(prepareCreate("test_index").addMapping("type1").addMapping("type2")); + assertAcked(prepareCreate("test_index") + .setSettings("index.mapping.single_type", false) + .addMapping("type1").addMapping("type2")); ensureGreen(); GetAliasesResponse getAliasesResponse = client().admin().indices().prepareGetAliases().setIndices("test_index").get(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenIT.java index 9075f7b87355e..bfe483ca89050 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenIT.java @@ -68,6 +68,7 @@ public class ChildrenIT extends ESIntegTestCase { public void setupSuiteScopeCluster() throws Exception { assertAcked( prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("article", "category", "type=keyword") .addMapping("comment", "_parent", "type=article", "commenter", "type=keyword") ); @@ -238,6 +239,7 @@ public void testWithDeletes() throws Exception { String indexName = "xyz"; assertAcked( prepareCreate(indexName) + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent", "count", "type=long") ); @@ -296,6 +298,7 @@ public void testPostCollection() throws Exception { String childType = "variantsku"; assertAcked( prepareCreate(indexName) + .setSettings("index.mapping.single_type", false) .addMapping(masterType, "brand", "type=text", "name", "type=keyword", "material", "type=text") .addMapping(childType, "_parent", "type=masterprod", "color", "type=keyword", "size", "type=keyword") ); @@ -359,6 +362,7 @@ public void testHierarchicalChildrenAggs() { .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0) ) + .setSettings("index.mapping.single_type", false) .addMapping(grandParentType, "name", "type=keyword") .addMapping(parentType, "_parent", "type=" + grandParentType) .addMapping(childType, "_parent", "type=" + parentType) @@ -402,6 +406,7 @@ public void testPostCollectAllLeafReaders() throws Exception { assertAcked( prepareCreate("index") + .setSettings("index.mapping.single_type", false) .addMapping("parentType", "name", "type=keyword", "town", "type=keyword") .addMapping("childType", "_parent", "type=parentType", "name", "type=keyword", "age", "type=integer") ); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java index 816ef980a3e33..cf28541121c12 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java @@ -139,7 +139,7 @@ public void setupSuiteScopeCluster() throws Exception { } for (int i = 0; i < 100; i++) { - builders.add(client().prepareIndex("idx", "high_card_type").setSource(jsonBuilder() + builders.add(client().prepareIndex("high_card_idx", "type").setSource(jsonBuilder() .startObject() .field(SINGLE_VALUED_FIELD_NAME, (double) i) .startArray(MULTI_VALUED_FIELD_NAME).value((double)i).value(i + 1d).endArray() @@ -279,14 +279,14 @@ private String key(Terms.Bucket bucket) { public void testSizeIsZero() { IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> client() - .prepareSearch("idx").setTypes("high_card_type").addAggregation(terms("terms").field(SINGLE_VALUED_FIELD_NAME) + .prepareSearch("high_card_idx").addAggregation(terms("terms").field(SINGLE_VALUED_FIELD_NAME) .minDocCount(randomInt(1)).size(0).collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet()); assertThat(exception.getMessage(), containsString("[size] must be greater than 0. Found [0] in [terms]")); } public void testSingleValueField() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values()))) @@ -310,7 +310,7 @@ public void testSingleValueField() throws Exception { } public void testSingleValueFieldWithMaxSize() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("high_card_type") + SearchResponse response = client().prepareSearch("high_card_idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .size(20) @@ -345,7 +345,7 @@ public void testSingleValueFieldWithFiltering() throws Exception { } private void testIncludeExcludeResults(double[] includes, double[] excludes, double[] expecteds) { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .includeExclude(new IncludeExclude(includes, excludes)) @@ -374,7 +374,7 @@ public void testMultiValueFieldWithPartitionedFiltering() throws Exception { private void runTestFieldWithPartitionedFiltering(String field) throws Exception { // Find total number of unique terms - SearchResponse allResponse = client().prepareSearch("idx").setTypes("type") + SearchResponse allResponse = client().prepareSearch("idx") .addAggregation(terms("terms").field(field).size(10000).collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); assertSearchResponse(allResponse); @@ -387,7 +387,7 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception final int numPartitions = randomIntBetween(2, 4); Set foundTerms = new HashSet<>(); for (int partition = 0; partition < numPartitions; partition++) { - SearchResponse response = client().prepareSearch("idx").setTypes("type").addAggregation(terms("terms").field(field) + SearchResponse response = client().prepareSearch("idx").addAggregation(terms("terms").field(field) .includeExclude(new IncludeExclude(partition, numPartitions)).collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); assertSearchResponse(response); @@ -402,7 +402,7 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception } public void testSingleValueFieldOrderedByTermAsc() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -428,7 +428,7 @@ public void testSingleValueFieldOrderedByTermAsc() throws Exception { } public void testSingleValueFieldOrderedByTermDesc() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -454,7 +454,7 @@ public void testSingleValueFieldOrderedByTermDesc() throws Exception { } public void testSingleValuedFieldWithSubAggregation() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -489,7 +489,7 @@ public void testSingleValuedFieldWithSubAggregation() throws Exception { } public void testSingleValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -514,7 +514,7 @@ public void testSingleValuedFieldWithValueScript() throws Exception { } public void testMultiValuedField() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(MULTI_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values()))) @@ -542,7 +542,7 @@ public void testMultiValuedField() throws Exception { } public void testMultiValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(MULTI_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -571,7 +571,7 @@ public void testMultiValuedFieldWithValueScript() throws Exception { } public void testMultiValuedFieldWithValueScriptNotUnique() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(MULTI_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -614,7 +614,6 @@ public void testMultiValuedFieldWithValueScriptNotUnique() throws Exception { public void testScriptSingleValue() throws Exception { SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .addAggregation( terms("terms") .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -641,7 +640,6 @@ public void testScriptSingleValue() throws Exception { public void testScriptMultiValued() throws Exception { SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .addAggregation( terms("terms") .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -670,7 +668,7 @@ public void testScriptMultiValued() throws Exception { } public void testUnmapped() throws Exception { - SearchResponse response = client().prepareSearch("idx_unmapped").setTypes("type") + SearchResponse response = client().prepareSearch("idx_unmapped") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .size(randomIntBetween(1, 5)) @@ -687,7 +685,7 @@ public void testUnmapped() throws Exception { } public void testPartiallyUnmapped() throws Exception { - SearchResponse response = client().prepareSearch("idx_unmapped", "idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx_unmapped", "idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values()))) @@ -711,7 +709,7 @@ public void testPartiallyUnmapped() throws Exception { } public void testPartiallyUnmappedWithFormat() throws Exception { - SearchResponse response = client().prepareSearch("idx_unmapped", "idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx_unmapped", "idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -759,7 +757,6 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAsc() throws boolean asc = true; SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) .order(Terms.Order.aggregation("avg_i", asc)).subAggregation(avg("avg_i").field(SINGLE_VALUED_FIELD_NAME))) @@ -788,7 +785,6 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscWithSubTer boolean asc = true; SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .addAggregation( terms("terms") .field(SINGLE_VALUED_FIELD_NAME) @@ -833,7 +829,6 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws boolean asc = randomBoolean(); SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .addAggregation( terms("num_tags").field("num_tag").collectMode(randomFrom(SubAggCollectionMode.values())) .order(Terms.Order.aggregation("filter", asc)) @@ -870,7 +865,6 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels( boolean asc = randomBoolean(); SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .addAggregation( terms("tags") .field("num_tag") @@ -927,7 +921,6 @@ public void testSingleValuedFieldOrderedByMissingSubAggregation() throws Excepti for (String index : Arrays.asList("idx", "idx_unmapped")) { try { client().prepareSearch(index) - .setTypes("type") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) .order(Terms.Order.aggregation("avg_i", true))).execute().actionGet(); @@ -944,7 +937,6 @@ public void testSingleValuedFieldOrderedByNonMetricsOrMultiBucketSubAggregation( for (String index : Arrays.asList("idx", "idx_unmapped")) { try { client().prepareSearch(index) - .setTypes("type") .addAggregation( terms("terms") .field(SINGLE_VALUED_FIELD_NAME) @@ -966,7 +958,6 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithUknownMet for (String index : Arrays.asList("idx", "idx_unmapped")) { try { client().prepareSearch(index) - .setTypes("type") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME + "2").collectMode(randomFrom(SubAggCollectionMode.values())) .order(Terms.Order.aggregation("stats.foo", true)) @@ -985,7 +976,6 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithoutMetric for (String index : Arrays.asList("idx", "idx_unmapped")) { try { client().prepareSearch(index) - .setTypes("type") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) .order(Terms.Order.aggregation("stats", true)) @@ -1004,7 +994,6 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationDesc() throws boolean asc = false; SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) .order(Terms.Order.aggregation("avg_i", asc)).subAggregation(avg("avg_i").field(SINGLE_VALUED_FIELD_NAME))) @@ -1035,7 +1024,6 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws E boolean asc = true; SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) .order(Terms.Order.aggregation("stats.avg", asc)) @@ -1064,7 +1052,6 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws boolean asc = false; SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) .order(Terms.Order.aggregation("stats.avg", asc)) @@ -1093,7 +1080,6 @@ public void testSingleValuedFieldOrderedByMultiValueExtendedStatsAsc() throws Ex boolean asc = true; SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) .order(Terms.Order.aggregation("stats.variance", asc)) @@ -1128,7 +1114,6 @@ public void testScriptScore() { SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .setQuery(functionScoreQuery(scriptFunction(scoringScript))) .addAggregation( terms("terms") diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java index f010d5058974f..6793768a91fe2 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java @@ -113,7 +113,7 @@ protected Map, Object>> pluginScripts() { @Override public void setupSuiteScopeCluster() throws Exception { - createIndex("idx"); + createIndex("idx", "high_card_idx"); IndexRequestBuilder[] lowCardBuilders = new IndexRequestBuilder[NUM_DOCS]; for (int i = 0; i < lowCardBuilders.length; i++) { lowCardBuilders[i] = client().prepareIndex("idx", "type").setSource(jsonBuilder() @@ -123,10 +123,10 @@ public void setupSuiteScopeCluster() throws Exception { .field("num_tag", i < lowCardBuilders.length / 2 + 1 ? 1 : 0) // used to test order by single-bucket sub agg .endObject()); } - indexRandom(randomBoolean(), lowCardBuilders); + indexRandom(true, lowCardBuilders); IndexRequestBuilder[] highCardBuilders = new IndexRequestBuilder[100]; // TODO randomize the size? for (int i = 0; i < highCardBuilders.length; i++) { - highCardBuilders[i] = client().prepareIndex("idx", "high_card_type").setSource(jsonBuilder() + highCardBuilders[i] = client().prepareIndex("high_card_idx", "type").setSource(jsonBuilder() .startObject() .field(SINGLE_VALUED_FIELD_NAME, i) .startArray(MULTI_VALUED_FIELD_NAME).value(i).value(i + 1).endArray() @@ -268,7 +268,7 @@ private String key(Terms.Bucket bucket) { // the main purpose of this test is to make sure we're not allocating 2GB of memory per shard public void testSizeIsZero() { IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, - () -> client().prepareSearch("idx").setTypes("high_card_type") + () -> client().prepareSearch("high_card_idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -279,7 +279,7 @@ public void testSizeIsZero() { } public void testSingleValueField() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values()))) @@ -317,7 +317,8 @@ public void testSingleValueFieldWithFiltering() throws Exception { private void testIncludeExcludeResults(int minDocCount, long[] includes, long[] excludes, long[] expectedWithCounts, long[] expectedZeroCounts) { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx", "high_card_idx") + .setQuery(QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery("_index", "high_card_idx"))) .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .includeExclude(new IncludeExclude(includes, excludes)) @@ -355,7 +356,7 @@ public void testMultiValueFieldWithPartitionedFiltering() throws Exception { private void runTestFieldWithPartitionedFiltering(String field) throws Exception { // Find total number of unique terms - SearchResponse allResponse = client().prepareSearch("idx").setTypes("type") + SearchResponse allResponse = client().prepareSearch("idx") .addAggregation(terms("terms").field(field).collectMode(randomFrom(SubAggCollectionMode.values()))).execute().actionGet(); assertSearchResponse(allResponse); Terms terms = allResponse.getAggregations().get("terms"); @@ -367,7 +368,7 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception final int numPartitions = randomIntBetween(2, 4); Set foundTerms = new HashSet<>(); for (int partition = 0; partition < numPartitions; partition++) { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation( terms("terms").field(field).includeExclude(new IncludeExclude(partition, numPartitions)) .collectMode(randomFrom(SubAggCollectionMode.values()))) @@ -386,7 +387,7 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception public void testSingleValueFieldWithMaxSize() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("high_card_type") + SearchResponse response = client().prepareSearch("high_card_idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .size(20) @@ -412,7 +413,7 @@ public void testSingleValueFieldWithMaxSize() throws Exception { } public void testSingleValueFieldOrderedByTermAsc() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -436,7 +437,7 @@ public void testSingleValueFieldOrderedByTermAsc() throws Exception { } public void testSingleValueFieldOrderedByTermDesc() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -462,7 +463,7 @@ public void testSingleValueFieldOrderedByTermDesc() throws Exception { } public void testSingleValuedFieldWithSubAggregation() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -496,7 +497,7 @@ public void testSingleValuedFieldWithSubAggregation() throws Exception { } public void testSingleValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -521,7 +522,7 @@ public void testSingleValuedFieldWithValueScript() throws Exception { } public void testMultiValuedField() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(MULTI_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values()))) @@ -549,7 +550,7 @@ public void testMultiValuedField() throws Exception { } public void testMultiValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(MULTI_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -578,7 +579,7 @@ public void testMultiValuedFieldWithValueScript() throws Exception { } public void testMultiValuedFieldWithValueScriptNotUnique() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(MULTI_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -622,7 +623,7 @@ public void testScriptSingleValue() throws Exception { Script script = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['" + SINGLE_VALUED_FIELD_NAME + "'].value", Collections.emptyMap()); - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .collectMode(randomFrom(SubAggCollectionMode.values())) .script(script)) @@ -649,7 +650,7 @@ public void testScriptMultiValued() throws Exception { Script script = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['" + MULTI_VALUED_FIELD_NAME + "']", Collections.emptyMap()); - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .collectMode(randomFrom(SubAggCollectionMode.values())) .script(script)) @@ -677,7 +678,7 @@ public void testScriptMultiValued() throws Exception { } public void testUnmapped() throws Exception { - SearchResponse response = client().prepareSearch("idx_unmapped").setTypes("type") + SearchResponse response = client().prepareSearch("idx_unmapped") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .size(randomIntBetween(1, 5)) @@ -694,7 +695,7 @@ public void testUnmapped() throws Exception { } public void testPartiallyUnmapped() throws Exception { - SearchResponse response = client().prepareSearch("idx_unmapped", "idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx_unmapped", "idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values()))) @@ -718,7 +719,7 @@ public void testPartiallyUnmapped() throws Exception { } public void testPartiallyUnmappedWithFormat() throws Exception { - SearchResponse response = client().prepareSearch("idx_unmapped", "idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx_unmapped", "idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -764,7 +765,7 @@ public void testEmptyAggregation() throws Exception { public void testSingleValuedFieldOrderedBySingleValueSubAggregationAsc() throws Exception { boolean asc = true; - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -793,7 +794,7 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAsc() throws public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscWithTermsSubAgg() throws Exception { boolean asc = true; - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -837,7 +838,7 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscWithTermsS public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws Exception { boolean asc = randomBoolean(); - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("num_tags") .field("num_tag") .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -874,7 +875,7 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels() throws Exception { boolean asc = randomBoolean(); - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("tags") .field("num_tag") .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -929,7 +930,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels( public void testSingleValuedFieldOrderedByMissingSubAggregation() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index).setTypes("type") + client().prepareSearch(index) .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -947,7 +948,7 @@ public void testSingleValuedFieldOrderedByMissingSubAggregation() throws Excepti public void testSingleValuedFieldOrderedByNonMetricsOrMultiBucketSubAggregation() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index).setTypes("type") + client().prepareSearch(index) .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -967,7 +968,7 @@ public void testSingleValuedFieldOrderedByNonMetricsOrMultiBucketSubAggregation( public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithUknownMetric() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index).setTypes("type") + client().prepareSearch(index) .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -987,7 +988,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithUknownMet public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithoutMetric() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index).setTypes("type") + client().prepareSearch(index) .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -1006,7 +1007,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithoutMetric public void testSingleValuedFieldOrderedBySingleValueSubAggregationDesc() throws Exception { boolean asc = false; - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -1038,7 +1039,7 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationDesc() throws public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws Exception { boolean asc = true; - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -1068,7 +1069,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws E public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws Exception { boolean asc = false; - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -1098,7 +1099,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws public void testSingleValuedFieldOrderedByMultiValueExtendedStatsAsc() throws Exception { boolean asc = true; - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java index 3fdd32d22bb3b..6118cb69ee747 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java @@ -60,18 +60,19 @@ public class ReverseNestedIT extends ESIntegTestCase { @Override public void setupSuiteScopeCluster() throws Exception { - assertAcked(prepareCreate("idx") + assertAcked(prepareCreate("idx1") .addMapping( - "type1", + "type", jsonBuilder().startObject().startObject("properties") .startObject("field1").field("type", "keyword").endObject() .startObject("nested1").field("type", "nested").startObject("properties") .startObject("field2").field("type", "keyword").endObject() .endObject().endObject() .endObject().endObject() - ) + )); + assertAcked(prepareCreate("idx2") .addMapping( - "type2", + "type", jsonBuilder().startObject().startObject("properties") .startObject("nested1").field("type", "nested").startObject("properties") .startObject("field1").field("type", "keyword").endObject() @@ -83,31 +84,31 @@ public void setupSuiteScopeCluster() throws Exception { ) ); - insertType1(Arrays.asList("a", "b", "c"), Arrays.asList("1", "2", "3", "4")); - insertType1(Arrays.asList("b", "c", "d"), Arrays.asList("4", "5", "6", "7")); - insertType1(Arrays.asList("c", "d", "e"), Arrays.asList("7", "8", "9", "1")); + insertIdx1(Arrays.asList("a", "b", "c"), Arrays.asList("1", "2", "3", "4")); + insertIdx1(Arrays.asList("b", "c", "d"), Arrays.asList("4", "5", "6", "7")); + insertIdx1(Arrays.asList("c", "d", "e"), Arrays.asList("7", "8", "9", "1")); refresh(); - insertType1(Arrays.asList("a", "e"), Arrays.asList("7", "4", "1", "1")); - insertType1(Arrays.asList("a", "c"), Arrays.asList("2", "1")); - insertType1(Arrays.asList("a"), Arrays.asList("3", "4")); + insertIdx1(Arrays.asList("a", "e"), Arrays.asList("7", "4", "1", "1")); + insertIdx1(Arrays.asList("a", "c"), Arrays.asList("2", "1")); + insertIdx1(Arrays.asList("a"), Arrays.asList("3", "4")); refresh(); - insertType1(Arrays.asList("x", "c"), Arrays.asList("1", "8")); - insertType1(Arrays.asList("y", "c"), Arrays.asList("6")); - insertType1(Arrays.asList("z"), Arrays.asList("5", "9")); + insertIdx1(Arrays.asList("x", "c"), Arrays.asList("1", "8")); + insertIdx1(Arrays.asList("y", "c"), Arrays.asList("6")); + insertIdx1(Arrays.asList("z"), Arrays.asList("5", "9")); refresh(); - insertType2(new String[][]{new String[]{"a", "0", "0", "1", "2"}, new String[]{"b", "0", "1", "1", "2"}, new String[]{"a", "0"}}); - insertType2(new String[][]{new String[]{"c", "1", "1", "2", "2"}, new String[]{"d", "3", "4"}}); + insertIdx2(new String[][]{new String[]{"a", "0", "0", "1", "2"}, new String[]{"b", "0", "1", "1", "2"}, new String[]{"a", "0"}}); + insertIdx2(new String[][]{new String[]{"c", "1", "1", "2", "2"}, new String[]{"d", "3", "4"}}); refresh(); - insertType2(new String[][]{new String[]{"a", "0", "0", "0", "0"}, new String[]{"b", "0", "0", "0", "0"}}); - insertType2(new String[][]{new String[]{"e", "1", "2"}, new String[]{"f", "3", "4"}}); + insertIdx2(new String[][]{new String[]{"a", "0", "0", "0", "0"}, new String[]{"b", "0", "0", "0", "0"}}); + insertIdx2(new String[][]{new String[]{"e", "1", "2"}, new String[]{"f", "3", "4"}}); refresh(); ensureSearchable(); } - private void insertType1(List values1, List values2) throws Exception { + private void insertIdx1(List values1, List values2) throws Exception { XContentBuilder source = jsonBuilder() .startObject() .array("field1", values1.toArray()) @@ -116,10 +117,10 @@ private void insertType1(List values1, List values2) throws Exce source.startObject().field("field2", value1).endObject(); } source.endArray().endObject(); - indexRandom(false, client().prepareIndex("idx", "type1").setRouting("1").setSource(source)); + indexRandom(false, client().prepareIndex("idx1", "type").setRouting("1").setSource(source)); } - private void insertType2(String[][] values) throws Exception { + private void insertIdx2(String[][] values) throws Exception { XContentBuilder source = jsonBuilder() .startObject() .startArray("nested1"); @@ -131,11 +132,11 @@ private void insertType2(String[][] values) throws Exception { source.endArray().endObject(); } source.endArray().endObject(); - indexRandom(false, client().prepareIndex("idx", "type2").setRouting("1").setSource(source)); + indexRandom(false, client().prepareIndex("idx2", "type").setRouting("1").setSource(source)); } public void testSimpleReverseNestedToRoot() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type1") + SearchResponse response = client().prepareSearch("idx1") .addAggregation(nested("nested1", "nested1") .subAggregation( terms("field2").field("nested1.field2") @@ -323,7 +324,7 @@ public void testSimpleReverseNestedToRoot() throws Exception { } public void testSimpleNested1ToRootToNested2() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type2") + SearchResponse response = client().prepareSearch("idx2") .addAggregation(nested("nested1", "nested1") .subAggregation( reverseNested("nested1_to_root") @@ -345,7 +346,7 @@ public void testSimpleNested1ToRootToNested2() throws Exception { } public void testSimpleReverseNestedToNested1() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type2") + SearchResponse response = client().prepareSearch("idx2") .addAggregation(nested("nested1", "nested1.nested2") .subAggregation( terms("field2").field("nested1.nested2.field2").order(Terms.Order.term(true)) @@ -448,7 +449,7 @@ public void testSimpleReverseNestedToNested1() throws Exception { public void testReverseNestedAggWithoutNestedAgg() { try { - client().prepareSearch("idx") + client().prepareSearch("idx2") .addAggregation(terms("field2").field("nested1.nested2.field2") .collectMode(randomFrom(SubAggCollectionMode.values())) .subAggregation( @@ -466,7 +467,7 @@ public void testReverseNestedAggWithoutNestedAgg() { } public void testNonExistingNestedField() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") + SearchResponse searchResponse = client().prepareSearch("idx2") .setQuery(matchAllQuery()) .addAggregation(nested("nested2", "nested1.nested2").subAggregation(reverseNested("incorrect").path("nested3"))) .get(); @@ -479,7 +480,7 @@ public void testNonExistingNestedField() throws Exception { assertThat(reverseNested.getDocCount(), is(0L)); // Test that parsing the reverse_nested agg doesn't fail, because the parent nested agg is unmapped: - searchResponse = client().prepareSearch("idx") + searchResponse = client().prepareSearch("idx1") .setQuery(matchAllQuery()) .addAggregation(nested("incorrect1", "incorrect1").subAggregation(reverseNested("incorrect2").path("incorrect2"))) .get(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/StringTermsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/StringTermsIT.java index deda92dc3532e..b5dbcd9085a86 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/StringTermsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/StringTermsIT.java @@ -136,8 +136,12 @@ public void setupSuiteScopeCluster() throws Exception { getMultiSortDocs(builders); + assertAcked(client().admin().indices().prepareCreate("high_card_idx") + .addMapping("type", SINGLE_VALUED_FIELD_NAME, "type=keyword", + MULTI_VALUED_FIELD_NAME, "type=keyword", + "tag", "type=keyword").get()); for (int i = 0; i < 100; i++) { - builders.add(client().prepareIndex("idx", "high_card_type").setSource( + builders.add(client().prepareIndex("high_card_idx", "type").setSource( jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val" + Strings.padStart(i + "", 3, '0')) .startArray(MULTI_VALUED_FIELD_NAME).value("val" + Strings.padStart(i + "", 3, '0')) .value("val" + Strings.padStart((i + 1) + "", 3, '0')).endArray().endObject())); @@ -203,26 +207,26 @@ private void getMultiSortDocs(List builders) throws IOExcep MULTI_VALUED_FIELD_NAME, "type=keyword", "tag", "type=keyword").get()); for (int i = 1; i <= 3; i++) { - builders.add(client().prepareIndex("sort_idx", "multi_sort_type").setSource( + builders.add(client().prepareIndex("sort_idx", "type").setSource( jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val1").field("l", 1).field("d", i).endObject())); - builders.add(client().prepareIndex("sort_idx", "multi_sort_type").setSource( + builders.add(client().prepareIndex("sort_idx", "type").setSource( jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val2").field("l", 2).field("d", i).endObject())); } - builders.add(client().prepareIndex("sort_idx", "multi_sort_type").setSource( + builders.add(client().prepareIndex("sort_idx", "type").setSource( jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val3").field("l", 3).field("d", 1).endObject())); - builders.add(client().prepareIndex("sort_idx", "multi_sort_type").setSource( + builders.add(client().prepareIndex("sort_idx", "type").setSource( jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val3").field("l", 3).field("d", 2).endObject())); - builders.add(client().prepareIndex("sort_idx", "multi_sort_type").setSource( + builders.add(client().prepareIndex("sort_idx", "type").setSource( jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val4").field("l", 3).field("d", 1).endObject())); - builders.add(client().prepareIndex("sort_idx", "multi_sort_type").setSource( + builders.add(client().prepareIndex("sort_idx", "type").setSource( jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val4").field("l", 3).field("d", 3).endObject())); - builders.add(client().prepareIndex("sort_idx", "multi_sort_type").setSource( + builders.add(client().prepareIndex("sort_idx", "type").setSource( jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val5").field("l", 5).field("d", 1).endObject())); - builders.add(client().prepareIndex("sort_idx", "multi_sort_type").setSource( + builders.add(client().prepareIndex("sort_idx", "type").setSource( jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val5").field("l", 5).field("d", 2).endObject())); - builders.add(client().prepareIndex("sort_idx", "multi_sort_type").setSource( + builders.add(client().prepareIndex("sort_idx", "type").setSource( jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val6").field("l", 5).field("d", 1).endObject())); - builders.add(client().prepareIndex("sort_idx", "multi_sort_type").setSource( + builders.add(client().prepareIndex("sort_idx", "type").setSource( jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val7").field("l", 5).field("d", 1).endObject())); } @@ -234,8 +238,7 @@ private String key(Terms.Bucket bucket) { public void testSizeIsZero() { final int minDocCount = randomInt(1); IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> client() - .prepareSearch("idx") - .setTypes("high_card_type") + .prepareSearch("high_card_idx") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())).minDocCount(minDocCount).size(0)).execute() @@ -302,8 +305,7 @@ public void testSingleValueFieldWithRegexFiltering() throws Exception { // we should be left with: val000, val001, val002, val003, val004, val005, val006, val007, val008, val009 SearchResponse response = client() - .prepareSearch("idx") - .setTypes("high_card_type") + .prepareSearch("high_card_idx") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())).includeExclude(new IncludeExclude("val00.+", null))) @@ -327,8 +329,7 @@ public void testSingleValueFieldWithRegexFiltering() throws Exception { // we should be left with: val002, val003, val004, val005, val006, val007, val008, val009 response = client() - .prepareSearch("idx") - .setTypes("high_card_type") + .prepareSearch("high_card_idx") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -353,8 +354,7 @@ public void testSingleValueFieldWithRegexFiltering() throws Exception { // we should be left with: val000, val001, val002, val003, val004, val005, val006, val007, val008, val009 response = client() - .prepareSearch("idx") - .setTypes("high_card_type") + .prepareSearch("high_card_idx") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -380,8 +380,7 @@ public void testSingleValueFieldWithExactTermFiltering() throws Exception { // include without exclude String incVals[] = { "val000", "val001", "val002", "val003", "val004", "val005", "val006", "val007", "val008", "val009" }; SearchResponse response = client() - .prepareSearch("idx") - .setTypes("high_card_type") + .prepareSearch("high_card_idx") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())).includeExclude(new IncludeExclude(incVals, null))) @@ -408,8 +407,7 @@ public void testSingleValueFieldWithExactTermFiltering() throws Exception { String excVals[] = { "val000", "val001" }; response = client() - .prepareSearch("idx") - .setTypes("high_card_type") + .prepareSearch("high_card_idx") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())).includeExclude(new IncludeExclude(incVals, excVals))) @@ -432,8 +430,7 @@ public void testSingleValueFieldWithExactTermFiltering() throws Exception { // Check case with only exact term exclude clauses response = client() - .prepareSearch("idx") - .setTypes("high_card_type") + .prepareSearch("high_card_idx") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())).includeExclude(new IncludeExclude(null, excVals))) @@ -500,8 +497,7 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception public void testSingleValueFieldWithMaxSize() throws Exception { SearchResponse response = client() - .prepareSearch("idx") - .setTypes("high_card_type") + .prepareSearch("high_card_idx") .addAggregation( terms("terms") .executionHint(randomExecutionHint()) @@ -1505,7 +1501,6 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAsCompound private void assertMultiSortResponse(String[] expectedKeys, Terms.Order... order) { SearchResponse response = client() .prepareSearch("sort_idx") - .setTypes("multi_sort_type") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.compound(order)) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java index a3cfac728ce4a..aeef44d62726d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java @@ -115,7 +115,8 @@ public static String randomExecutionHint() { @Override public void setupSuiteScopeCluster() throws Exception { - assertAcked(prepareCreate("idx").addMapping("type", TERMS_AGGS_FIELD, "type=keyword", "group", "type=keyword")); + assertAcked(prepareCreate("idx").addMapping("type", TERMS_AGGS_FIELD, "type=keyword")); + assertAcked(prepareCreate("field-collapsing").addMapping("type", "group", "type=keyword")); createIndex("empty"); assertAcked(prepareCreate("articles").addMapping("article", jsonBuilder().startObject().startObject("article").startObject("properties") .startObject(TERMS_AGGS_FIELD) @@ -160,49 +161,49 @@ public void setupSuiteScopeCluster() throws Exception { .endObject())); } - builders.add(client().prepareIndex("idx", "field-collapsing", "1").setSource(jsonBuilder() + builders.add(client().prepareIndex("field-collapsing", "type", "1").setSource(jsonBuilder() .startObject() .field("group", "a") .field("text", "term x y z b") .endObject())); - builders.add(client().prepareIndex("idx", "field-collapsing", "2").setSource(jsonBuilder() + builders.add(client().prepareIndex("field-collapsing", "type", "2").setSource(jsonBuilder() .startObject() .field("group", "a") .field("text", "term x y z n rare") .field("value", 1) .endObject())); - builders.add(client().prepareIndex("idx", "field-collapsing", "3").setSource(jsonBuilder() + builders.add(client().prepareIndex("field-collapsing", "type", "3").setSource(jsonBuilder() .startObject() .field("group", "b") .field("text", "x y z term") .endObject())); - builders.add(client().prepareIndex("idx", "field-collapsing", "4").setSource(jsonBuilder() + builders.add(client().prepareIndex("field-collapsing", "type", "4").setSource(jsonBuilder() .startObject() .field("group", "b") .field("text", "x y term") .endObject())); - builders.add(client().prepareIndex("idx", "field-collapsing", "5").setSource(jsonBuilder() + builders.add(client().prepareIndex("field-collapsing", "type", "5").setSource(jsonBuilder() .startObject() .field("group", "b") .field("text", "x term") .endObject())); - builders.add(client().prepareIndex("idx", "field-collapsing", "6").setSource(jsonBuilder() + builders.add(client().prepareIndex("field-collapsing", "type", "6").setSource(jsonBuilder() .startObject() .field("group", "b") .field("text", "term rare") .field("value", 3) .endObject())); - builders.add(client().prepareIndex("idx", "field-collapsing", "7").setSource(jsonBuilder() + builders.add(client().prepareIndex("field-collapsing", "type", "7").setSource(jsonBuilder() .startObject() .field("group", "c") .field("text", "x y z term") .endObject())); - builders.add(client().prepareIndex("idx", "field-collapsing", "8").setSource(jsonBuilder() + builders.add(client().prepareIndex("field-collapsing", "type", "8").setSource(jsonBuilder() .startObject() .field("group", "c") .field("text", "x y term b") .endObject())); - builders.add(client().prepareIndex("idx", "field-collapsing", "9").setSource(jsonBuilder() + builders.add(client().prepareIndex("field-collapsing", "type", "9").setSource(jsonBuilder() .startObject() .field("group", "c") .field("text", "rare x term") @@ -270,7 +271,6 @@ private String key(Terms.Bucket bucket) { public void testBasics() throws Exception { SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .addAggregation(terms("terms") .executionHint(randomExecutionHint()) .field(TERMS_AGGS_FIELD) @@ -309,8 +309,7 @@ public void testBasics() throws Exception { public void testIssue11119() throws Exception { // Test that top_hits aggregation is fed scores if query results size=0 SearchResponse response = client() - .prepareSearch("idx") - .setTypes("field-collapsing") + .prepareSearch("field-collapsing") .setSize(0) .setQuery(matchQuery("text", "x y z")) .addAggregation(terms("terms").executionHint(randomExecutionHint()).field("group").subAggregation(topHits("hits"))) @@ -344,8 +343,7 @@ public void testIssue11119() throws Exception { // tied up with the need to feed scores into the agg tree even when // users don't want ranked set of query results.) response = client() - .prepareSearch("idx") - .setTypes("field-collapsing") + .prepareSearch("field-collapsing") .setSize(0) .setMinScore(0.0001f) .setQuery(matchQuery("text", "x y z")) @@ -365,7 +363,7 @@ public void testIssue11119() throws Exception { public void testBreadthFirstWithScoreNeeded() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .executionHint(randomExecutionHint()) .collectMode(SubAggCollectionMode.BREADTH_FIRST) @@ -395,7 +393,7 @@ public void testBreadthFirstWithScoreNeeded() throws Exception { } public void testBreadthFirstWithAggOrderAndScoreNeeded() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .executionHint(randomExecutionHint()) .collectMode(SubAggCollectionMode.BREADTH_FIRST) @@ -448,7 +446,7 @@ public void testBasicsGetProperty() throws Exception { public void testPagination() throws Exception { int size = randomIntBetween(1, 10); int from = randomIntBetween(0, 10); - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .executionHint(randomExecutionHint()) .field(TERMS_AGGS_FIELD) @@ -463,7 +461,6 @@ public void testPagination() throws Exception { assertSearchResponse(response); SearchResponse control = client().prepareSearch("idx") - .setTypes("type") .setFrom(from) .setSize(size) .setPostFilter(QueryBuilders.termQuery(TERMS_AGGS_FIELD, "val0")) @@ -493,7 +490,7 @@ public void testPagination() throws Exception { } public void testSortByBucket() throws Exception { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .executionHint(randomExecutionHint()) .field(TERMS_AGGS_FIELD) @@ -533,8 +530,7 @@ public void testSortByBucket() throws Exception { public void testFieldCollapsing() throws Exception { SearchResponse response = client() - .prepareSearch("idx") - .setTypes("field-collapsing") + .prepareSearch("field-collapsing") .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .setQuery(matchQuery("text", "term rare")) .addAggregation( @@ -575,7 +571,7 @@ public void testFieldCollapsing() throws Exception { } public void testFetchFeatures() { - SearchResponse response = client().prepareSearch("idx").setTypes("type") + SearchResponse response = client().prepareSearch("idx") .setQuery(matchQuery("text", "text").queryName("test")) .addAggregation(terms("terms") .executionHint(randomExecutionHint()) @@ -633,7 +629,7 @@ public void testFetchFeatures() { public void testInvalidSortField() throws Exception { try { - client().prepareSearch("idx").setTypes("type") + client().prepareSearch("idx") .addAggregation(terms("terms") .executionHint(randomExecutionHint()) .field(TERMS_AGGS_FIELD) @@ -648,7 +644,7 @@ public void testInvalidSortField() throws Exception { } public void testEmptyIndex() throws Exception { - SearchResponse response = client().prepareSearch("empty").setTypes("type") + SearchResponse response = client().prepareSearch("empty") .addAggregation(topHits("hits")) .get(); assertSearchResponse(response); @@ -663,7 +659,7 @@ public void testTrackScores() throws Exception { boolean[] trackScores = new boolean[]{true, false}; for (boolean trackScore : trackScores) { logger.info("Track score={}", trackScore); - SearchResponse response = client().prepareSearch("idx").setTypes("field-collapsing") + SearchResponse response = client().prepareSearch("field-collapsing") .setQuery(matchQuery("text", "term rare")) .addAggregation(terms("terms") .field("group") @@ -949,7 +945,6 @@ public void testTopHitsInNested() throws Exception { public void testDontExplode() throws Exception { SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .addAggregation(terms("terms") .executionHint(randomExecutionHint()) .field(TERMS_AGGS_FIELD) @@ -964,7 +959,6 @@ public void testDontExplode() throws Exception { public void testNoStoredFields() throws Exception { SearchResponse response = client() .prepareSearch("idx") - .setTypes("type") .addAggregation(terms("terms") .executionHint(randomExecutionHint()) .field(TERMS_AGGS_FIELD) @@ -1062,7 +1056,6 @@ public void testWithRescore() { .addRescorer( RescoreBuilder.queryRescorer(new MatchAllQueryBuilder().boost(3.0f)) ) - .setTypes("type") .addAggregation(terms("terms") .field(TERMS_AGGS_FIELD) .subAggregation( @@ -1085,7 +1078,6 @@ public void testWithRescore() { .addRescorer( RescoreBuilder.queryRescorer(new MatchAllQueryBuilder().boost(3.0f)) ) - .setTypes("type") .addAggregation(terms("terms") .field(TERMS_AGGS_FIELD) .subAggregation( @@ -1109,7 +1101,6 @@ public void testWithRescore() { .addRescorer( RescoreBuilder.queryRescorer(new MatchAllQueryBuilder().boost(3.0f)) ) - .setTypes("type") .addAggregation(terms("terms") .field(TERMS_AGGS_FIELD) .subAggregation( @@ -1132,7 +1123,6 @@ public void testWithRescore() { .addRescorer( RescoreBuilder.queryRescorer(new MatchAllQueryBuilder().boost(3.0f)) ) - .setTypes("type") .addAggregation(terms("terms") .field(TERMS_AGGS_FIELD) .subAggregation( diff --git a/core/src/test/java/org/elasticsearch/search/basic/TransportSearchFailuresIT.java b/core/src/test/java/org/elasticsearch/search/basic/TransportSearchFailuresIT.java index 8e81a3a852ec5..9384847b2ac77 100644 --- a/core/src/test/java/org/elasticsearch/search/basic/TransportSearchFailuresIT.java +++ b/core/src/test/java/org/elasticsearch/search/basic/TransportSearchFailuresIT.java @@ -117,7 +117,7 @@ public void testFailedSearchWithWrongQuery() throws Exception { } private void index(Client client, String id, String nameValue, int age) throws IOException { - client.index(Requests.indexRequest("test").type("type1").id(id).source(source(id, nameValue, age))).actionGet(); + client.index(Requests.indexRequest("test").type("type").id(id).source(source(id, nameValue, age))).actionGet(); } private XContentBuilder source(String id, String nameValue, int age) throws IOException { diff --git a/core/src/test/java/org/elasticsearch/search/basic/TransportTwoNodesSearchIT.java b/core/src/test/java/org/elasticsearch/search/basic/TransportTwoNodesSearchIT.java index 3c16ff7188cc2..ed70aa119f6a2 100644 --- a/core/src/test/java/org/elasticsearch/search/basic/TransportTwoNodesSearchIT.java +++ b/core/src/test/java/org/elasticsearch/search/basic/TransportTwoNodesSearchIT.java @@ -97,7 +97,7 @@ private Set prepareData(int numShards) throws Exception { } private void index(String id, String nameValue, int age) throws IOException { - client().index(Requests.indexRequest("test").type("type1").id(id).source(source(id, nameValue, age))).actionGet(); + client().index(Requests.indexRequest("test").type("type").id(id).source(source(id, nameValue, age))).actionGet(); } private XContentBuilder source(String id, String nameValue, int age) throws IOException { diff --git a/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java b/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java index efeabc93a0e28..697352c5edce7 100644 --- a/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java +++ b/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java @@ -118,6 +118,7 @@ public void testSelfReferentialIsForbidden() { public void testMultiLevelChild() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent") .addMapping("grandchild", "_parent", "type=child")); @@ -173,6 +174,7 @@ public void testMultiLevelChild() throws Exception { // see #2744 public void test2744() throws IOException { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("foo") .addMapping("test", "_parent", "type=foo")); ensureGreen(); @@ -192,6 +194,7 @@ public void test2744() throws IOException { public void testSimpleChildQuery() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -265,6 +268,7 @@ public void testSimpleChildQuery() throws Exception { // Issue #3290 public void testCachingBugWithFqueryFilter() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -304,6 +308,7 @@ public void testCachingBugWithFqueryFilter() throws Exception { public void testHasParentFilter() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -353,6 +358,7 @@ public void testHasParentFilter() throws Exception { public void testSimpleChildQueryWithFlush() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -422,6 +428,7 @@ public void testSimpleChildQueryWithFlush() throws Exception { public void testScopedFacet() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent", "c_field", "type=keyword")); ensureGreen(); @@ -459,6 +466,7 @@ public void testScopedFacet() throws Exception { public void testDeletedParent() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -494,6 +502,7 @@ public void testDeletedParent() throws Exception { public void testDfsSearchType() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -521,6 +530,7 @@ public void testDfsSearchType() throws Exception { public void testHasChildAndHasParentFailWhenSomeSegmentsDontContainAnyParentOrChildDocs() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -545,6 +555,7 @@ public void testHasChildAndHasParentFailWhenSomeSegmentsDontContainAnyParentOrCh public void testCountApiUsage() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -575,6 +586,7 @@ public void testCountApiUsage() throws Exception { public void testExplainUsage() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -663,6 +675,7 @@ List createDocBuilders() { public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent") .addMapping("child1", "_parent", "type=parent")); @@ -750,6 +763,7 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { // Issue #2536 public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -784,6 +798,7 @@ public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Excep public void testHasChildAndHasParentFilter_withFilter() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -811,6 +826,7 @@ public void testHasChildAndHasParentFilter_withFilter() throws Exception { public void testHasChildInnerHitsHighlighting() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -836,6 +852,7 @@ public void testHasChildInnerHitsHighlighting() throws Exception { public void testHasChildAndHasParentWrappedInAQueryFilter() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -867,6 +884,7 @@ public void testHasChildAndHasParentWrappedInAQueryFilter() throws Exception { public void testSimpleQueryRewrite() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent", "p_field", "type=keyword") .addMapping("child", "_parent", "type=parent", "c_field", "type=keyword")); ensureGreen(); @@ -915,6 +933,7 @@ public void testSimpleQueryRewrite() throws Exception { // Issue #3144 public void testReIndexingParentAndChildDocuments() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -976,6 +995,7 @@ public void testReIndexingParentAndChildDocuments() throws Exception { // Issue #3203 public void testHasChildQueryWithMinimumScore() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1000,8 +1020,7 @@ public void testHasChildQueryWithMinimumScore() throws Exception { public void testParentFieldQuery() throws Exception { assertAcked(prepareCreate("test") - .setSettings(Settings.builder().put(indexSettings()) - .put("index.refresh_interval", -1)) + .setSettings("index.refresh_interval", -1, "index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1034,8 +1053,7 @@ public void testParentFieldQuery() throws Exception { public void testParentIdQuery() throws Exception { assertAcked(prepareCreate("test") - .setSettings(Settings.builder().put(indexSettings()) - .put("index.refresh_interval", -1)) + .setSettings("index.refresh_interval", -1, "index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1059,6 +1077,7 @@ public void testParentIdQuery() throws Exception { public void testHasChildNotBeingCached() throws IOException { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1121,6 +1140,7 @@ private QueryBuilder randomHasParent(String type, String field, String value) { // Issue #3818 public void testHasChildQueryOnlyReturnsSingleChildType() { assertAcked(prepareCreate("grandissue") + .setSettings("index.mapping.single_type", false) .addMapping("grandparent", "name", "type=text") .addMapping("parent", "_parent", "type=grandparent") .addMapping("child_type_one", "_parent", "type=parent") @@ -1173,6 +1193,7 @@ public void testHasChildQueryOnlyReturnsSingleChildType() { public void testIndexChildDocWithNoParentMapping() throws IOException { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child1")); ensureGreen(); @@ -1220,6 +1241,7 @@ public void testAddingParentToExistingMapping() throws IOException { public void testHasChildQueryWithNestedInnerObjects() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent", "objects", "type=nested") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1261,6 +1283,7 @@ public void testHasChildQueryWithNestedInnerObjects() throws Exception { public void testNamedFilters() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1354,11 +1377,7 @@ public void testParentChildQueriesNoParentType() throws Exception { public void testParentChildCaching() throws Exception { assertAcked(prepareCreate("test") - .setSettings( - Settings.builder() - .put(indexSettings()) - .put("index.refresh_interval", -1) - ) + .setSettings("index.refresh_interval", -1, "index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1402,6 +1421,7 @@ public void testParentChildCaching() throws Exception { public void testParentChildQueriesViaScrollApi() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1446,6 +1466,7 @@ public void testParentChildQueriesViaScrollApi() throws Exception { // Issue #5783 public void testQueryBeforeChildType() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("features") .addMapping("posts", "_parent", "type=features") .addMapping("specials")); @@ -1465,6 +1486,7 @@ public void testQueryBeforeChildType() throws Exception { // Issue #6256 public void testParentFieldInMultiMatchField() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("type1") .addMapping("type2", "_parent", "type=type1") ); @@ -1483,6 +1505,7 @@ public void testParentFieldInMultiMatchField() throws Exception { public void testTypeIsAppliedInHasParentInnerQuery() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1570,6 +1593,7 @@ private SearchResponse minMaxQuery(ScoreMode scoreMode, int minChildren, Integer public void testMinMaxChildren() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent", "id", "type=long") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1883,7 +1907,9 @@ public void testMinMaxChildren() throws Exception { } public void testParentFieldToNonExistingType() { - assertAcked(prepareCreate("test").addMapping("parent").addMapping("child", "_parent", "type=parent2")); + assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) + .addMapping("parent").addMapping("child", "_parent", "type=parent2")); client().prepareIndex("test", "parent", "1").setSource("{}", XContentType.JSON).get(); client().prepareIndex("test", "child", "1").setParent("1").setSource("{}", XContentType.JSON).get(); refresh(); @@ -1898,7 +1924,9 @@ public void testParentFieldToNonExistingType() { } public void testHasParentInnerQueryType() { - assertAcked(prepareCreate("test").addMapping("parent-type").addMapping("child-type", "_parent", "type=parent-type")); + assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) + .addMapping("parent-type").addMapping("child-type", "_parent", "type=parent-type")); client().prepareIndex("test", "child-type", "child-id").setParent("parent-id").setSource("{}", XContentType.JSON).get(); client().prepareIndex("test", "parent-type", "parent-id").setSource("{}", XContentType.JSON).get(); refresh(); @@ -1909,7 +1937,9 @@ public void testHasParentInnerQueryType() { } public void testHasChildInnerQueryType() { - assertAcked(prepareCreate("test").addMapping("parent-type").addMapping("child-type", "_parent", "type=parent-type")); + assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) + .addMapping("parent-type").addMapping("child-type", "_parent", "type=parent-type")); client().prepareIndex("test", "child-type", "child-id").setParent("parent-id").setSource("{}", XContentType.JSON).get(); client().prepareIndex("test", "parent-type", "parent-id").setSource("{}", XContentType.JSON).get(); refresh(); @@ -1921,6 +1951,7 @@ public void testHasChildInnerQueryType() { public void testHighlightersIgnoreParentChild() { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent-type", "searchText", "type=text,term_vector=with_positions_offsets,index_options=offsets") .addMapping("child-type", "_parent", "type=parent-type", "searchText", "type=text,term_vector=with_positions_offsets,index_options=offsets")); diff --git a/core/src/test/java/org/elasticsearch/search/child/ParentFieldLoadingIT.java b/core/src/test/java/org/elasticsearch/search/child/ParentFieldLoadingIT.java index 44b01b3a4b974..d3a8946571bfb 100644 --- a/core/src/test/java/org/elasticsearch/search/child/ParentFieldLoadingIT.java +++ b/core/src/test/java/org/elasticsearch/search/child/ParentFieldLoadingIT.java @@ -60,6 +60,7 @@ protected Collection> nodePlugins() { .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), -1) // We never want merges in this test to ensure we have two segments for the last validation .put(MergePolicyConfig.INDEX_MERGE_ENABLED, false) + .put("index.mapping.single_type", false) .build(); public void testEagerParentFieldLoading() throws Exception { diff --git a/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java b/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java index 7d5b57b8ca10d..f93fbabdf2674 100644 --- a/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java +++ b/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java @@ -245,6 +245,7 @@ public void testRandomNested() throws Exception { public void testSimpleParentChild() throws Exception { assertAcked(prepareCreate("articles") + .setSettings("index.mapping.single_type", false) .addMapping("article", "title", "type=text") .addMapping("comment", "_parent", "type=article", "message", "type=text,fielddata=true") ); @@ -319,6 +320,7 @@ public void testSimpleParentChild() throws Exception { public void testRandomParentChild() throws Exception { assertAcked(prepareCreate("idx") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("child1", "_parent", "type=parent") .addMapping("child2", "_parent", "type=parent") @@ -402,6 +404,7 @@ public void testRandomParentChild() throws Exception { public void testInnerHitsOnHasParent() throws Exception { assertAcked(prepareCreate("stack") + .setSettings("index.mapping.single_type", false) .addMapping("question", "body", "type=text") .addMapping("answer", "_parent", "type=question", "body", "type=text") ); @@ -444,9 +447,10 @@ public void testInnerHitsOnHasParent() throws Exception { public void testParentChildMultipleLayers() throws Exception { assertAcked(prepareCreate("articles") - .addMapping("article", "title", "type=text") - .addMapping("comment", "_parent", "type=article", "message", "type=text") - .addMapping("remark", "_parent", "type=comment", "message", "type=text") + .setSettings("index.mapping.single_type", false) + .addMapping("article", "title", "type=text") + .addMapping("comment", "_parent", "type=article", "message", "type=text") + .addMapping("remark", "_parent", "type=comment", "message", "type=text") ); List requests = new ArrayList<>(); @@ -720,6 +724,7 @@ public void testInnerHitsWithObjectFieldThatHasANestedField() throws Exception { public void testRoyals() throws Exception { assertAcked( prepareCreate("royals") + .setSettings("index.mapping.single_type", false) .addMapping("king") .addMapping("prince", "_parent", "type=king") .addMapping("duke", "_parent", "type=prince") @@ -907,7 +912,9 @@ public void testMatchesQueriesNestedInnerHits() throws Exception { } public void testMatchesQueriesParentChildInnerHits() throws Exception { - assertAcked(prepareCreate("index").addMapping("child", "_parent", "type=parent")); + assertAcked(prepareCreate("index") + .setSettings("index.mapping.single_type", false) + .addMapping("child", "_parent", "type=parent")); List requests = new ArrayList<>(); requests.add(client().prepareIndex("index", "parent", "1").setSource("{}", XContentType.JSON)); requests.add(client().prepareIndex("index", "child", "1").setParent("1").setSource("field", "value1")); @@ -946,7 +953,9 @@ public void testMatchesQueriesParentChildInnerHits() throws Exception { } public void testDontExplode() throws Exception { - assertAcked(prepareCreate("index1").addMapping("child", "_parent", "type=parent")); + assertAcked(prepareCreate("index1") + .setSettings("index.mapping.single_type", false) + .addMapping("child", "_parent", "type=parent")); List requests = new ArrayList<>(); requests.add(client().prepareIndex("index1", "parent", "1").setSource("{}", XContentType.JSON)); requests.add(client().prepareIndex("index1", "child", "1").setParent("1").setSource("field", "value1")); @@ -1012,7 +1021,9 @@ public void testNestedSourceFiltering() throws Exception { } public void testNestedInnerHitWrappedInParentChildInnerhit() throws Exception { - assertAcked(prepareCreate("test").addMapping("child_type", "_parent", "type=parent_type", "nested_type", "type=nested")); + assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) + .addMapping("child_type", "_parent", "type=parent_type", "nested_type", "type=nested")); client().prepareIndex("test", "parent_type", "1").setSource("key", "value").get(); client().prepareIndex("test", "child_type", "2").setParent("1").setSource("nested_type", Collections.singletonMap("key", "value")) .get(); @@ -1030,6 +1041,7 @@ public void testNestedInnerHitWrappedInParentChildInnerhit() throws Exception { public void testInnerHitsWithIgnoreUnmapped() throws Exception { assertAcked(prepareCreate("index1") + .setSettings("index.mapping.single_type", false) .addMapping("parent_type", "nested_type", "type=nested") .addMapping("child_type", "_parent", "type=parent_type") ); diff --git a/core/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java b/core/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java index 819b2c7d644da..01cc605393b70 100644 --- a/core/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java +++ b/core/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java @@ -1433,6 +1433,7 @@ public void testCommonTermsTermVector() throws IOException { public void testPhrasePrefix() throws IOException { Builder builder = Settings.builder() .put(indexSettings()) + .put("index.mapping.single_type", false) .put("index.analysis.analyzer.synonym.tokenizer", "whitespace") .putArray("index.analysis.analyzer.synonym.filter", "synonym", "lowercase") .put("index.analysis.filter.synonym.type", "synonym") diff --git a/core/src/test/java/org/elasticsearch/search/fields/SearchFieldsIT.java b/core/src/test/java/org/elasticsearch/search/fields/SearchFieldsIT.java index 428ac63eaefa7..b33196cf005c5 100644 --- a/core/src/test/java/org/elasticsearch/search/fields/SearchFieldsIT.java +++ b/core/src/test/java/org/elasticsearch/search/fields/SearchFieldsIT.java @@ -639,7 +639,8 @@ public void testSearchFieldsNonLeafField() throws Exception { public void testGetFieldsComplexField() throws Exception { client().admin().indices().prepareCreate("my-index") - .setSettings(Settings.builder().put("index.refresh_interval", -1)) + .setSettings("index.refresh_interval", -1) + .setSettings("index.mapping.single_type", false) .addMapping("my-type2", jsonBuilder() .startObject() .startObject("my-type2") @@ -871,6 +872,7 @@ public void testScriptFields() throws Exception { public void testLoadMetadata() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("parent") .addMapping("my-type1", "_parent", "type=parent")); diff --git a/core/src/test/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java b/core/src/test/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java index 81a3fc0d6b57b..0d2c5cf1bdc53 100644 --- a/core/src/test/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java +++ b/core/src/test/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java @@ -81,7 +81,9 @@ public void testSimpleMoreLikeThis() throws Exception { public void testSimpleMoreLikeOnLongField() throws Exception { logger.info("Creating index test"); - assertAcked(prepareCreate("test").addMapping("type1", "some_long", "type=long")); + assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) + .addMapping("type1", "some_long", "type=long")); logger.info("Running Cluster Health"); assertThat(ensureGreen(), equalTo(ClusterHealthStatus.GREEN)); @@ -357,7 +359,8 @@ public void testSimpleMoreLikeThisIds() throws Exception { public void testSimpleMoreLikeThisIdsMultipleTypes() throws Exception { logger.info("Creating index test"); int numOfTypes = randomIntBetween(2, 10); - CreateIndexRequestBuilder createRequestBuilder = prepareCreate("test"); + CreateIndexRequestBuilder createRequestBuilder = prepareCreate("test") + .setSettings("index.mapping.single_type", false); for (int i = 0; i < numOfTypes; i++) { createRequestBuilder.addMapping("type" + i, jsonBuilder().startObject().startObject("type" + i).startObject("properties") .startObject("text").field("type", "text").endObject() diff --git a/core/src/test/java/org/elasticsearch/search/nested/SimpleNestedIT.java b/core/src/test/java/org/elasticsearch/search/nested/SimpleNestedIT.java index 47164688ea50e..316e83ad1bbcf 100644 --- a/core/src/test/java/org/elasticsearch/search/nested/SimpleNestedIT.java +++ b/core/src/test/java/org/elasticsearch/search/nested/SimpleNestedIT.java @@ -57,6 +57,7 @@ public class SimpleNestedIT extends ESIntegTestCase { public void testSimpleNested() throws Exception { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("type1", "nested1", "type=nested") .addMapping("type2", "nested1", "type=nested")); ensureGreen(); diff --git a/core/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java b/core/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java index a30049c70d538..d920c6a67b45a 100644 --- a/core/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java +++ b/core/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java @@ -544,7 +544,7 @@ public void testDateRangeInQueryStringWithTimeZone_10477() { } public void testTypeFilter() throws Exception { - assertAcked(prepareCreate("test")); + assertAcked(prepareCreate("test").setSettings("index.mapping.single_type", false)); indexRandom(true, client().prepareIndex("test", "type1", "1").setSource("field1", "value1"), client().prepareIndex("test", "type2", "1").setSource("field1", "value1"), client().prepareIndex("test", "type1", "2").setSource("field1", "value1"), @@ -1180,7 +1180,7 @@ public void testTermsLookupFilter() throws Exception { } public void testBasicQueryById() throws Exception { - createIndex("test"); + assertAcked(prepareCreate("test").setSettings("index.mapping.single_type", false)); client().prepareIndex("test", "type1", "1").setSource("field1", "value1").get(); client().prepareIndex("test", "type2", "2").setSource("field1", "value2").get(); @@ -1447,6 +1447,7 @@ public void testSpanNot() throws IOException, ExecutionException, InterruptedExc public void testSimpleDFSQuery() throws IOException { assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("s", jsonBuilder() .startObject() .startObject("s") @@ -1642,7 +1643,7 @@ public void testMinScore() throws ExecutionException, InterruptedException { } public void testQueryStringWithSlopAndFields() { - createIndex("test"); + assertAcked(prepareCreate("test").setSettings("index.mapping.single_type", false)); client().prepareIndex("test", "customer", "1").setSource("desc", "one two three").get(); client().prepareIndex("test", "product", "2").setSource("desc", "one two three").get(); diff --git a/core/src/test/java/org/elasticsearch/search/slice/SearchSliceIT.java b/core/src/test/java/org/elasticsearch/search/slice/SearchSliceIT.java index aa166f6e1a231..2f89c5b2306b5 100644 --- a/core/src/test/java/org/elasticsearch/search/slice/SearchSliceIT.java +++ b/core/src/test/java/org/elasticsearch/search/slice/SearchSliceIT.java @@ -88,7 +88,7 @@ private int setupIndex(boolean withDocs) throws IOException, ExecutionException, builder.field("static_int", 0); builder.field("invalid_random_int", randomInt()); builder.endObject(); - requests.add(client().prepareIndex("test", "test").setSource(builder)); + requests.add(client().prepareIndex("test", "type").setSource(builder)); } indexRandom(true, requests); return numberOfShards; diff --git a/core/src/test/java/org/elasticsearch/search/sort/FieldSortIT.java b/core/src/test/java/org/elasticsearch/search/sort/FieldSortIT.java index a8911afe1fad2..4bddc1c556adc 100644 --- a/core/src/test/java/org/elasticsearch/search/sort/FieldSortIT.java +++ b/core/src/test/java/org/elasticsearch/search/sort/FieldSortIT.java @@ -176,7 +176,7 @@ public void testIssue6614() throws ExecutionException, InterruptedException { public void testTrackScores() throws Exception { assertAcked(client().admin().indices().prepareCreate("test") - .addMapping("type", "svalue", "type=keyword").get()); + .addMapping("type1", "svalue", "type=keyword").get()); ensureGreen(); index("test", "type1", jsonBuilder().startObject() .field("id", "1") @@ -421,7 +421,7 @@ public void testScoreSortDirectionWithFunctionScore() throws Exception { public void testIssue2986() { assertAcked(client().admin().indices().prepareCreate("test") - .addMapping("type", "field1", "type=keyword").get()); + .addMapping("post", "field1", "type=keyword").get()); client().prepareIndex("test", "post", "1").setSource("{\"field1\":\"value1\"}", XContentType.JSON).execute().actionGet(); client().prepareIndex("test", "post", "2").setSource("{\"field1\":\"value2\"}", XContentType.JSON).execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java b/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java index 82f06441ef6da..92fb2cd2777f6 100644 --- a/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java +++ b/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java @@ -881,8 +881,8 @@ public void testEmptyShards() throws IOException, InterruptedException { .put("index.analysis.filter.shingler.output_unigrams", true)).addMapping("type1", mappingBuilder)); ensureGreen(); - index("test", "type2", "1", "foo", "bar"); - index("test", "type2", "2", "foo", "bar"); + index("test", "type1", "11", "foo", "bar"); + index("test", "type1", "12", "foo", "bar"); index("test", "type1", "1", "name", "Just testing the suggestions api"); index("test", "type1", "2", "name", "An other title about equal length"); refresh(); diff --git a/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java b/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java index c3f801f77fbe3..d8a0036a45c7c 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java +++ b/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java @@ -505,9 +505,9 @@ public void testIncludeGlobalState() throws Exception { assertThat(client.admin().indices() .preparePutTemplate("test-template") .setPatterns(Collections.singletonList("te*")) - .addMapping("test-mapping", XContentFactory.jsonBuilder() + .addMapping("doc", XContentFactory.jsonBuilder() .startObject() - .startObject("test-mapping") + .startObject("doc") .startObject("properties") .startObject("field1") .field("type", "text") diff --git a/core/src/test/java/org/elasticsearch/tribe/TribeIT.java b/core/src/test/java/org/elasticsearch/tribe/TribeIT.java index 7c58622e5cced..f678d4528fe0f 100644 --- a/core/src/test/java/org/elasticsearch/tribe/TribeIT.java +++ b/core/src/test/java/org/elasticsearch/tribe/TribeIT.java @@ -386,10 +386,10 @@ public void testOnConflictPrefer() throws Exception { public void testTribeOnOneCluster() throws Exception { try (Releasable tribeNode = startTribeNode()) { // Creates 2 indices, test1 on cluster1 and test2 on cluster2 - assertAcked(cluster1.client().admin().indices().prepareCreate("test1")); + assertAcked(cluster1.client().admin().indices().prepareCreate("test1").setSettings("index.mapping.single_type", false)); ensureGreen(cluster1.client()); - assertAcked(cluster2.client().admin().indices().prepareCreate("test2")); + assertAcked(cluster2.client().admin().indices().prepareCreate("test2").setSettings("index.mapping.single_type", false)); ensureGreen(cluster2.client()); // Wait for the tribe node to retrieve the indices into its cluster state diff --git a/core/src/test/java/org/elasticsearch/update/UpdateIT.java b/core/src/test/java/org/elasticsearch/update/UpdateIT.java index 75c3feb038a6d..893bbbd6115a8 100644 --- a/core/src/test/java/org/elasticsearch/update/UpdateIT.java +++ b/core/src/test/java/org/elasticsearch/update/UpdateIT.java @@ -728,7 +728,9 @@ public void testUpdateRequestWithScriptAndShouldUpsertDoc() throws Exception { } public void testContextVariables() throws Exception { - assertAcked(prepareCreate("test").addAlias(new Alias("alias")) + assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) + .addAlias(new Alias("alias")) .addMapping("type1", XContentFactory.jsonBuilder() .startObject() .startObject("type1") diff --git a/docs/build.gradle b/docs/build.gradle index a587cec658f0c..2648fad3cc002 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -207,7 +207,7 @@ buildRestTests.setups['ledger'] = ''' - do: bulk: index: ledger - type: item + type: sale refresh: true body: | {"index":{}} diff --git a/docs/reference/aggregations/bucket/children-aggregation.asciidoc b/docs/reference/aggregations/bucket/children-aggregation.asciidoc index 9c3b2926dacb1..57ea9ce56cc98 100644 --- a/docs/reference/aggregations/bucket/children-aggregation.asciidoc +++ b/docs/reference/aggregations/bucket/children-aggregation.asciidoc @@ -12,6 +12,9 @@ For example, let's say we have an index of questions and answers. The answer typ -------------------------------------------------- PUT child_example { + "settings": { + "mapping.single_type": false + }, "mappings": { "answer" : { "_parent" : { diff --git a/docs/reference/analysis/charfilters/pattern-replace-charfilter.asciidoc b/docs/reference/analysis/charfilters/pattern-replace-charfilter.asciidoc index 6f21f4521d3b6..e674f8bf0e7b4 100644 --- a/docs/reference/analysis/charfilters/pattern-replace-charfilter.asciidoc +++ b/docs/reference/analysis/charfilters/pattern-replace-charfilter.asciidoc @@ -205,7 +205,7 @@ the length of the original text: [source,js] ---------------------------- -PUT my_index/my_doc/1?refresh +PUT my_index/my_type/1?refresh { "text": "The fooBarBaz method" } @@ -245,7 +245,7 @@ The output from the above is: "hits": [ { "_index": "my_index", - "_type": "my_doc", + "_type": "my_type", "_id": "1", "_score": 0.2824934, "_source": { diff --git a/docs/reference/docs/index_.asciidoc b/docs/reference/docs/index_.asciidoc index bcbcee2f907a8..2f042aec132ba 100644 --- a/docs/reference/docs/index_.asciidoc +++ b/docs/reference/docs/index_.asciidoc @@ -279,6 +279,9 @@ For example: -------------------------------------------------- PUT blogs { + "settings": { + "mapping.single_type": false + }, "mappings": { "tag_parent": {}, "blog_tag": { diff --git a/docs/reference/indices/put-mapping.asciidoc b/docs/reference/indices/put-mapping.asciidoc index 6bc623b95e251..0dd2ffc3d8a93 100644 --- a/docs/reference/indices/put-mapping.asciidoc +++ b/docs/reference/indices/put-mapping.asciidoc @@ -8,6 +8,9 @@ fields to an existing type: -------------------------------------------------- PUT twitter <1> { + "settings": { + "mapping.single_type": false + }, "mappings": { "tweet": { "properties": { @@ -147,6 +150,9 @@ For example, this fails: ----------------------------------- PUT my_index { + "settings": { + "mapping.single_type": false + }, "mappings": { "type_one": { "properties": { diff --git a/docs/reference/mapping.asciidoc b/docs/reference/mapping.asciidoc index be77e29d7de22..59e49c317af13 100644 --- a/docs/reference/mapping.asciidoc +++ b/docs/reference/mapping.asciidoc @@ -153,6 +153,9 @@ index, as follows: --------------------------------------- PUT my_index <1> { + "settings": { + "mapping.single_type": false + }, "mappings": { "user": { <2> "properties": { <3> diff --git a/docs/reference/mapping/dynamic/default-mapping.asciidoc b/docs/reference/mapping/dynamic/default-mapping.asciidoc index 926c66ced51c7..14836727440f0 100644 --- a/docs/reference/mapping/dynamic/default-mapping.asciidoc +++ b/docs/reference/mapping/dynamic/default-mapping.asciidoc @@ -12,6 +12,9 @@ mapping types, can be customised by adding a mapping type with the name -------------------------------------------------- PUT my_index { + "settings": { + "mapping.single_type": false + }, "mappings": { "_default_": { <1> "_source": { diff --git a/docs/reference/mapping/fields/parent-field.asciidoc b/docs/reference/mapping/fields/parent-field.asciidoc index 82343b1a908b6..7edaa3949d92c 100644 --- a/docs/reference/mapping/fields/parent-field.asciidoc +++ b/docs/reference/mapping/fields/parent-field.asciidoc @@ -8,6 +8,9 @@ index by making one mapping type the parent of another: -------------------------------------------------- PUT my_index { + "settings": { + "mapping.single_type": false + }, "mappings": { "my_parent": {}, "my_child": { @@ -137,6 +140,9 @@ make sense to disable eager loading: -------------------------------------------------- PUT my_index { + "settings": { + "mapping.single_type": false + }, "mappings": { "my_parent": {}, "my_child": { diff --git a/docs/reference/mapping/fields/type-field.asciidoc b/docs/reference/mapping/fields/type-field.asciidoc index 07f62e016e93c..fb577b5a03f35 100644 --- a/docs/reference/mapping/fields/type-field.asciidoc +++ b/docs/reference/mapping/fields/type-field.asciidoc @@ -11,6 +11,13 @@ scripts, and when sorting: [source,js] -------------------------- # Example documents +PUT my_index +{ + "settings": { + "mapping.single_type": false + } +} + PUT my_index/type_1/1 { "text": "Document with type 1" diff --git a/docs/reference/query-dsl/parent-id-query.asciidoc b/docs/reference/query-dsl/parent-id-query.asciidoc index c35f5111103b9..4fdfa70312eb7 100644 --- a/docs/reference/query-dsl/parent-id-query.asciidoc +++ b/docs/reference/query-dsl/parent-id-query.asciidoc @@ -10,6 +10,9 @@ Given the following mapping definition: -------------------------------------------- PUT /my_index { + "settings": { + "mapping.single_type": false + }, "mappings": { "blog_post": { "properties": { diff --git a/docs/reference/query-dsl/percolate-query.asciidoc b/docs/reference/query-dsl/percolate-query.asciidoc index 1d43bff06a136..296255d2ded67 100644 --- a/docs/reference/query-dsl/percolate-query.asciidoc +++ b/docs/reference/query-dsl/percolate-query.asciidoc @@ -9,22 +9,18 @@ to match with the stored queries. [float] === Sample Usage -Create an index with two mappings: +Create an index with two fields: [source,js] -------------------------------------------------- PUT /my-index { "mappings": { - "doctype": { + "doc": { "properties": { "message": { "type": "text" - } - } - }, - "queries": { - "properties": { + }, "query": { "type": "percolator" } @@ -35,23 +31,20 @@ PUT /my-index -------------------------------------------------- // CONSOLE -The `doctype` mapping is the mapping used to preprocess -the document defined in the `percolator` query before it -gets indexed into a temporary index. +The `message` field is the field used to preprocess the document defined in +the `percolator` query before it gets indexed into a temporary index. -The `queries` mapping is the mapping used for indexing -the query documents. The `query` field will hold a json -object that represents an actual Elasticsearch query. The -`query` field has been configured to use the -<>. This field type understands -the query dsl and stored the query in such a way that it -can be used later on to match documents defined on the `percolate` query. +The `query` field is used for indexing the query documents. It will hold a +json object that represents an actual Elasticsearch query. The `query` field +has been configured to use the <>. This field +type understands the query dsl and stored the query in such a way that it can be +used later on to match documents defined on the `percolate` query. Register a query in the percolator: [source,js] -------------------------------------------------- -PUT /my-index/queries/1?refresh +PUT /my-index/doc/1?refresh { "query" : { "match" : { @@ -72,7 +65,7 @@ GET /my-index/_search "query" : { "percolate" : { "field" : "query", - "document_type" : "doctype", + "document_type" : "doc", "document" : { "message" : "A new bonsai tree in the office" } @@ -101,7 +94,7 @@ The above request will yield the following response: "hits": [ { <1> "_index": "my-index", - "_type": "queries", + "_type": "doc", "_id": "1", "_score": 0.5716521, "_source": { @@ -159,7 +152,7 @@ Index the document we want to percolate: [source,js] -------------------------------------------------- -PUT /my-index/message/1 +PUT /my-index/doc/2 { "message" : "A new bonsai tree in the office" } @@ -172,8 +165,8 @@ Index response: -------------------------------------------------- { "_index": "my-index", - "_type": "message", - "_id": "1", + "_type": "doc", + "_id": "2", "_version": 1, "_shards": { "total": 2, @@ -182,7 +175,7 @@ Index response: }, "created": true, "result": "created", - "_seq_no" : 1, + "_seq_no" : 0, "_primary_term" : 1 } -------------------------------------------------- @@ -197,10 +190,10 @@ GET /my-index/_search "query" : { "percolate" : { "field": "query", - "document_type" : "doctype", + "document_type" : "doc", "index" : "my-index", - "type" : "message", - "id" : "1", + "type" : "doc", + "id" : "2", "version" : 1 <1> } } @@ -231,7 +224,7 @@ Save a query: [source,js] -------------------------------------------------- -PUT /my-index/queries/1?refresh +PUT /my-index/doc/3?refresh { "query" : { "match" : { @@ -247,7 +240,7 @@ Save another query: [source,js] -------------------------------------------------- -PUT /my-index/queries/2?refresh +PUT /my-index/doc/4?refresh { "query" : { "match" : { @@ -268,7 +261,7 @@ GET /my-index/_search "query" : { "percolate" : { "field": "query", - "document_type" : "doctype", + "document_type" : "doc", "document" : { "message" : "The quick brown fox jumps over the lazy dog" } @@ -302,8 +295,8 @@ This will yield the following response. "hits": [ { "_index": "my-index", - "_type": "queries", - "_id": "2", + "_type": "doc", + "_id": "4", "_score": 0.5446649, "_source": { "query": { @@ -320,8 +313,8 @@ This will yield the following response. }, { "_index": "my-index", - "_type": "queries", - "_id": "1", + "_type": "doc", + "_id": "3", "_score": 0.5446649, "_source": { "query": { diff --git a/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/MoreExpressionTests.java b/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/MoreExpressionTests.java index b0cbc70a6766d..190a7745add67 100644 --- a/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/MoreExpressionTests.java +++ b/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/MoreExpressionTests.java @@ -670,10 +670,10 @@ public void testGeo() throws Exception { } public void testBoolean() throws Exception { - XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().startObject().startObject("type1") + XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().startObject().startObject("doc") .startObject("properties").startObject("vip").field("type", "boolean"); xContentBuilder.endObject().endObject().endObject().endObject(); - assertAcked(prepareCreate("test").addMapping("type1", xContentBuilder)); + assertAcked(prepareCreate("test").addMapping("doc", xContentBuilder)); ensureGreen(); indexRandom(true, client().prepareIndex("test", "doc", "1").setSource("price", 1.0, "vip", true), diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java index d123b2f307ede..15065d441ad64 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java @@ -56,10 +56,10 @@ protected Collection> getPlugins() { @Before public void setup() throws IOException { createIndex("test"); - client().prepareIndex("test", "testtype", "1") + client().prepareIndex("test", "type", "1") .setSource(jsonBuilder().startObject().field("text", "value1").endObject()) .get(); - client().prepareIndex("test", "testtype", "2") + client().prepareIndex("test", "type", "2") .setSource(jsonBuilder().startObject().field("text", "value2").endObject()) .get(); client().admin().indices().prepareRefresh().get(); diff --git a/modules/percolator/src/test/java/org/elasticsearch/percolator/CandidateQueryTests.java b/modules/percolator/src/test/java/org/elasticsearch/percolator/CandidateQueryTests.java index 8c594a0545f00..05c8889f32387 100644 --- a/modules/percolator/src/test/java/org/elasticsearch/percolator/CandidateQueryTests.java +++ b/modules/percolator/src/test/java/org/elasticsearch/percolator/CandidateQueryTests.java @@ -127,12 +127,11 @@ public void init() throws Exception { documentMapper = mapperService.merge("type", new CompressedXContent(mapper), MapperService.MergeReason.MAPPING_UPDATE, true); String queryField = "query_field"; - String mappingType = "query"; - String percolatorMapper = XContentFactory.jsonBuilder().startObject().startObject(mappingType) + String percolatorMapper = XContentFactory.jsonBuilder().startObject().startObject("type") .startObject("properties").startObject(queryField).field("type", "percolator").endObject().endObject() .endObject().endObject().string(); - mapperService.merge(mappingType, new CompressedXContent(percolatorMapper), MapperService.MergeReason.MAPPING_UPDATE, true); - fieldMapper = (PercolatorFieldMapper) mapperService.documentMapper(mappingType).mappers().getMapper(queryField); + mapperService.merge("type", new CompressedXContent(percolatorMapper), MapperService.MergeReason.MAPPING_UPDATE, true); + fieldMapper = (PercolatorFieldMapper) mapperService.documentMapper("type").mappers().getMapper(queryField); fieldType = (PercolatorFieldMapper.FieldType) fieldMapper.fieldType(); queries = new ArrayList<>(); diff --git a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java index d8f246b74ef86..ae585dc9dc703 100644 --- a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java +++ b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java @@ -52,6 +52,7 @@ import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.SourceToParse; +import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.BoostingQueryBuilder; import org.elasticsearch.index.query.ConstantScoreQueryBuilder; @@ -113,7 +114,7 @@ protected Collection> getPlugins() { @Before public void init() throws Exception { - indexService = createIndex("test", Settings.EMPTY); + indexService = createIndex("test", Settings.builder().put("mapping.single_type", false).build()); mapperService = indexService.mapperService(); String mapper = XContentFactory.jsonBuilder().startObject().startObject("type") diff --git a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java index 5760b260b0116..382fbcb3cf7be 100644 --- a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java +++ b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java @@ -113,17 +113,16 @@ public void testPercolateScriptQuery() throws IOException { public void testPercolatorQuery() throws Exception { createIndex("test", client().admin().indices().prepareCreate("test") - .addMapping("type", "field1", "type=keyword", "field2", "type=keyword") - .addMapping("queries", "query", "type=percolator") + .addMapping("type", "field1", "type=keyword", "field2", "type=keyword", "query", "type=percolator") ); - client().prepareIndex("test", "queries", "1") + client().prepareIndex("test", "type", "1") .setSource(jsonBuilder().startObject().field("query", matchAllQuery()).endObject()) .get(); - client().prepareIndex("test", "queries", "2") + client().prepareIndex("test", "type", "2") .setSource(jsonBuilder().startObject().field("query", matchQuery("field1", "value")).endObject()) .get(); - client().prepareIndex("test", "queries", "3") + client().prepareIndex("test", "type", "3") .setSource(jsonBuilder().startObject().field("query", boolQuery() .must(matchQuery("field1", "value")) .must(matchQuery("field2", "value")) @@ -162,45 +161,44 @@ public void testPercolatorQuery() throws Exception { public void testPercolatorRangeQueries() throws Exception { createIndex("test", client().admin().indices().prepareCreate("test") - .addMapping("type", "field1", "type=long", "field2", "type=double", "field3", "type=ip") - .addMapping("queries", "query", "type=percolator") + .addMapping("type", "field1", "type=long", "field2", "type=double", "field3", "type=ip", "query", "type=percolator") ); - client().prepareIndex("test", "queries", "1") + client().prepareIndex("test", "type", "1") .setSource(jsonBuilder().startObject().field("query", rangeQuery("field1").from(10).to(12)).endObject()) .get(); - client().prepareIndex("test", "queries", "2") + client().prepareIndex("test", "type", "2") .setSource(jsonBuilder().startObject().field("query", rangeQuery("field1").from(20).to(22)).endObject()) .get(); - client().prepareIndex("test", "queries", "3") + client().prepareIndex("test", "type", "3") .setSource(jsonBuilder().startObject().field("query", boolQuery() .must(rangeQuery("field1").from(10).to(12)) .must(rangeQuery("field1").from(12).to(14)) ).endObject()).get(); client().admin().indices().prepareRefresh().get(); - client().prepareIndex("test", "queries", "4") + client().prepareIndex("test", "type", "4") .setSource(jsonBuilder().startObject().field("query", rangeQuery("field2").from(10).to(12)).endObject()) .get(); - client().prepareIndex("test", "queries", "5") + client().prepareIndex("test", "type", "5") .setSource(jsonBuilder().startObject().field("query", rangeQuery("field2").from(20).to(22)).endObject()) .get(); - client().prepareIndex("test", "queries", "6") + client().prepareIndex("test", "type", "6") .setSource(jsonBuilder().startObject().field("query", boolQuery() .must(rangeQuery("field2").from(10).to(12)) .must(rangeQuery("field2").from(12).to(14)) ).endObject()).get(); client().admin().indices().prepareRefresh().get(); - client().prepareIndex("test", "queries", "7") + client().prepareIndex("test", "type", "7") .setSource(jsonBuilder().startObject() .field("query", rangeQuery("field3").from("192.168.1.0").to("192.168.1.5")) .endObject()) .get(); - client().prepareIndex("test", "queries", "8") + client().prepareIndex("test", "type", "8") .setSource(jsonBuilder().startObject() .field("query", rangeQuery("field3").from("192.168.1.20").to("192.168.1.30")) .endObject()) .get(); - client().prepareIndex("test", "queries", "9") + client().prepareIndex("test", "type", "9") .setSource(jsonBuilder().startObject().field("query", boolQuery() .must(rangeQuery("field3").from("192.168.1.0").to("192.168.1.5")) .must(rangeQuery("field3").from("192.168.1.5").to("192.168.1.10")) @@ -258,25 +256,24 @@ public void testPercolatorRangeQueries() throws Exception { public void testPercolatorQueryExistingDocument() throws Exception { createIndex("test", client().admin().indices().prepareCreate("test") - .addMapping("type", "field1", "type=keyword", "field2", "type=keyword") - .addMapping("queries", "query", "type=percolator") + .addMapping("type", "field1", "type=keyword", "field2", "type=keyword", "query", "type=percolator") ); - client().prepareIndex("test", "queries", "1") + client().prepareIndex("test", "type", "1") .setSource(jsonBuilder().startObject().field("query", matchAllQuery()).endObject()) .get(); - client().prepareIndex("test", "queries", "2") + client().prepareIndex("test", "type", "2") .setSource(jsonBuilder().startObject().field("query", matchQuery("field1", "value")).endObject()) .get(); - client().prepareIndex("test", "queries", "3") + client().prepareIndex("test", "type", "3") .setSource(jsonBuilder().startObject().field("query", boolQuery() .must(matchQuery("field1", "value")) .must(matchQuery("field2", "value")) ).endObject()).get(); - client().prepareIndex("test", "type", "1").setSource("{}", XContentType.JSON).get(); - client().prepareIndex("test", "type", "2").setSource("field1", "value").get(); - client().prepareIndex("test", "type", "3").setSource("field1", "value", "field2", "value").get(); + client().prepareIndex("test", "type", "4").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test", "type", "5").setSource("field1", "value").get(); + client().prepareIndex("test", "type", "6").setSource("field1", "value", "field2", "value").get(); client().admin().indices().prepareRefresh().get(); logger.info("percolating empty doc"); @@ -288,7 +285,7 @@ public void testPercolatorQueryExistingDocument() throws Exception { logger.info("percolating doc with 1 field"); response = client().prepareSearch() - .setQuery(new PercolateQueryBuilder("query", "type", "test", "type", "2", null, null, null)) + .setQuery(new PercolateQueryBuilder("query", "type", "test", "type", "5", null, null, null)) .addSort("_uid", SortOrder.ASC) .get(); assertHitCount(response, 2); @@ -297,7 +294,7 @@ public void testPercolatorQueryExistingDocument() throws Exception { logger.info("percolating doc with 2 fields"); response = client().prepareSearch() - .setQuery(new PercolateQueryBuilder("query", "type", "test", "type", "3", null, null, null)) + .setQuery(new PercolateQueryBuilder("query", "type", "test", "type", "6", null, null, null)) .addSort("_uid", SortOrder.ASC) .get(); assertHitCount(response, 3); @@ -308,15 +305,14 @@ public void testPercolatorQueryExistingDocument() throws Exception { public void testPercolatorQueryExistingDocumentSourceDisabled() throws Exception { createIndex("test", client().admin().indices().prepareCreate("test") - .addMapping("type", "_source", "enabled=false", "field1", "type=keyword") - .addMapping("queries", "query", "type=percolator") + .addMapping("type", "_source", "enabled=false", "field1", "type=keyword", "query", "type=percolator") ); - client().prepareIndex("test", "queries", "1") + client().prepareIndex("test", "type", "1") .setSource(jsonBuilder().startObject().field("query", matchAllQuery()).endObject()) .get(); - client().prepareIndex("test", "type", "1").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test", "type", "2").setSource("{}", XContentType.JSON).get(); client().admin().indices().prepareRefresh().get(); logger.info("percolating empty doc with source disabled"); @@ -331,18 +327,17 @@ public void testPercolatorQueryExistingDocumentSourceDisabled() throws Exception public void testPercolatorSpecificQueries() throws Exception { createIndex("test", client().admin().indices().prepareCreate("test") - .addMapping("type", "field1", "type=text", "field2", "type=text") - .addMapping("queries", "query", "type=percolator") + .addMapping("type", "field1", "type=text", "field2", "type=text", "query", "type=percolator") ); - client().prepareIndex("test", "queries", "1") + client().prepareIndex("test", "type", "1") .setSource(jsonBuilder().startObject().field("query", commonTermsQuery("field1", "quick brown fox")).endObject()) .get(); - client().prepareIndex("test", "queries", "2") + client().prepareIndex("test", "type", "2") .setSource(jsonBuilder().startObject().field("query", multiMatchQuery("quick brown fox", "field1", "field2") .type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)).endObject()) .get(); - client().prepareIndex("test", "queries", "3") + client().prepareIndex("test", "type", "3") .setSource(jsonBuilder().startObject().field("query", spanNearQuery(spanTermQuery("field1", "quick"), 0) .addClause(spanTermQuery("field1", "brown")) @@ -352,7 +347,7 @@ public void testPercolatorSpecificQueries() throws Exception { .get(); client().admin().indices().prepareRefresh().get(); - client().prepareIndex("test", "queries", "4") + client().prepareIndex("test", "type", "4") .setSource(jsonBuilder().startObject().field("query", spanNotQuery( spanNearQuery(spanTermQuery("field1", "quick"), 0) @@ -367,7 +362,7 @@ public void testPercolatorSpecificQueries() throws Exception { .get(); // doesn't match - client().prepareIndex("test", "queries", "5") + client().prepareIndex("test", "type", "5") .setSource(jsonBuilder().startObject().field("query", spanNotQuery( spanNearQuery(spanTermQuery("field1", "quick"), 0) @@ -410,22 +405,21 @@ public void testPercolatorQueryWithHighlighting() throws Exception { fieldMapping.append(",index_options=offsets"); } createIndex("test", client().admin().indices().prepareCreate("test") - .addMapping("type", "field1", fieldMapping) - .addMapping("queries", "query", "type=percolator") + .addMapping("type", "field1", fieldMapping, "query", "type=percolator") ); - client().prepareIndex("test", "queries", "1") + client().prepareIndex("test", "type", "1") .setSource(jsonBuilder().startObject().field("query", matchQuery("field1", "brown fox")).endObject()) .execute().actionGet(); - client().prepareIndex("test", "queries", "2") + client().prepareIndex("test", "type", "2") .setSource(jsonBuilder().startObject().field("query", matchQuery("field1", "lazy dog")).endObject()) .execute().actionGet(); - client().prepareIndex("test", "queries", "3") + client().prepareIndex("test", "type", "3") .setSource(jsonBuilder().startObject().field("query", termQuery("field1", "jumps")).endObject()) .execute().actionGet(); - client().prepareIndex("test", "queries", "4") + client().prepareIndex("test", "type", "4") .setSource(jsonBuilder().startObject().field("query", termQuery("field1", "dog")).endObject()) .execute().actionGet(); - client().prepareIndex("test", "queries", "5") + client().prepareIndex("test", "type", "5") .setSource(jsonBuilder().startObject().field("query", termQuery("field1", "fox")).endObject()) .execute().actionGet(); client().admin().indices().prepareRefresh().get(); @@ -454,14 +448,13 @@ public void testPercolatorQueryWithHighlighting() throws Exception { public void testTakePositionOffsetGapIntoAccount() throws Exception { createIndex("test", client().admin().indices().prepareCreate("test") - .addMapping("type", "field", "type=text,position_increment_gap=5") - .addMapping("queries", "query", "type=percolator") + .addMapping("type", "field", "type=text,position_increment_gap=5", "query", "type=percolator") ); - client().prepareIndex("test", "queries", "1") + client().prepareIndex("test", "type", "1") .setSource(jsonBuilder().startObject().field("query", new MatchPhraseQueryBuilder("field", "brown fox").slop(4)).endObject()) .get(); - client().prepareIndex("test", "queries", "2") + client().prepareIndex("test", "type", "2") .setSource(jsonBuilder().startObject().field("query", new MatchPhraseQueryBuilder("field", "brown fox").slop(5)).endObject()) .get(); @@ -478,6 +471,7 @@ public void testTakePositionOffsetGapIntoAccount() throws Exception { public void testManyPercolatorFields() throws Exception { String queryFieldName = randomAlphaOfLength(8); createIndex("test", client().admin().indices().prepareCreate("test") + .setSettings("index.mapping.single_type", false) .addMapping("doc_type", "field", "type=keyword") .addMapping("query_type1", queryFieldName, "type=percolator") .addMapping("query_type2", queryFieldName, "type=percolator", "second_query_field", "type=percolator") @@ -498,9 +492,11 @@ public void testManyPercolatorFields() throws Exception { public void testWithMultiplePercolatorFields() throws Exception { String queryFieldName = randomAlphaOfLength(8); createIndex("test1", client().admin().indices().prepareCreate("test1") + .setSettings("index.mapping.single_type", false) .addMapping("doc_type", "field", "type=keyword") .addMapping("query_type", queryFieldName, "type=percolator")); createIndex("test2", client().admin().indices().prepareCreate("test2") + .setSettings("index.mapping.single_type", false) .addMapping("doc_type", "field", "type=keyword") .addMapping("query_type", jsonBuilder().startObject().startObject("query_type").startObject("properties") .startObject("object_field") @@ -560,21 +556,20 @@ public void testWithMultiplePercolatorFields() throws Exception { public void testPercolateQueryWithNestedDocuments() throws Exception { XContentBuilder mapping = XContentFactory.jsonBuilder(); - mapping.startObject().startObject("properties").startObject("companyname").field("type", "text").endObject() - .startObject("employee").field("type", "nested").startObject("properties") - .startObject("name").field("type", "text").endObject().endObject().endObject().endObject() + mapping.startObject().startObject("properties").startObject("query").field("type", "percolator").endObject() + .startObject("companyname").field("type", "text").endObject().startObject("employee").field("type", "nested") + .startObject("properties").startObject("name").field("type", "text").endObject().endObject().endObject().endObject() .endObject(); createIndex("test", client().admin().indices().prepareCreate("test") .addMapping("employee", mapping) - .addMapping("queries", "query", "type=percolator") ); - client().prepareIndex("test", "queries", "q1").setSource(jsonBuilder().startObject() + client().prepareIndex("test", "employee", "q1").setSource(jsonBuilder().startObject() .field("query", QueryBuilders.nestedQuery("employee", QueryBuilders.matchQuery("employee.name", "virginia potts").operator(Operator.AND), ScoreMode.Avg) ).endObject()) .get(); // this query should never match as it doesn't use nested query: - client().prepareIndex("test", "queries", "q2").setSource(jsonBuilder().startObject() + client().prepareIndex("test", "employee", "q2").setSource(jsonBuilder().startObject() .field("query", QueryBuilders.matchQuery("employee.name", "virginia")).endObject()) .get(); client().admin().indices().prepareRefresh().get(); @@ -730,26 +725,25 @@ public void testPercolateQueryWithNestedDocuments_doLeakFieldDataCacheEntries() public void testPercolatorQueryViaMultiSearch() throws Exception { createIndex("test", client().admin().indices().prepareCreate("test") - .addMapping("type", "field1", "type=text") - .addMapping("queries", "query", "type=percolator") + .addMapping("type", "field1", "type=text", "query", "type=percolator") ); - client().prepareIndex("test", "queries", "1") + client().prepareIndex("test", "type", "1") .setSource(jsonBuilder().startObject().field("query", matchQuery("field1", "b")).field("a", "b").endObject()) .execute().actionGet(); - client().prepareIndex("test", "queries", "2") + client().prepareIndex("test", "type", "2") .setSource(jsonBuilder().startObject().field("query", matchQuery("field1", "c")).endObject()) .execute().actionGet(); - client().prepareIndex("test", "queries", "3") + client().prepareIndex("test", "type", "3") .setSource(jsonBuilder().startObject().field("query", boolQuery() .must(matchQuery("field1", "b")) .must(matchQuery("field1", "c")) ).endObject()) .execute().actionGet(); - client().prepareIndex("test", "queries", "4") + client().prepareIndex("test", "type", "4") .setSource(jsonBuilder().startObject().field("query", matchAllQuery()).endObject()) .execute().actionGet(); - client().prepareIndex("test", "type", "1") + client().prepareIndex("test", "type", "5") .setSource(jsonBuilder().startObject().field("field1", "c").endObject()) .execute().actionGet(); client().admin().indices().prepareRefresh().get(); @@ -768,9 +762,9 @@ public void testPercolatorQueryViaMultiSearch() throws Exception { .setQuery(new PercolateQueryBuilder("query", "type", jsonBuilder().startObject().field("field1", "d").endObject().bytes(), XContentType.JSON))) .add(client().prepareSearch("test") - .setQuery(new PercolateQueryBuilder("query", "type", "test", "type", "1", null, null, null))) + .setQuery(new PercolateQueryBuilder("query", "type", "test", "type", "5", null, null, null))) .add(client().prepareSearch("test") // non existing doc, so error element - .setQuery(new PercolateQueryBuilder("query", "type", "test", "type", "2", null, null, null))) + .setQuery(new PercolateQueryBuilder("query", "type", "test", "type", "6", null, null, null))) .get(); MultiSearchResponse.Item item = response.getResponses()[0]; @@ -803,7 +797,7 @@ public void testPercolatorQueryViaMultiSearch() throws Exception { assertThat(item.getFailureMessage(), notNullValue()); assertThat(item.getFailureMessage(), equalTo("all shards failed")); assertThat(ExceptionsHelper.unwrapCause(item.getFailure().getCause()).getMessage(), - containsString("[test/type/2] couldn't be found")); + containsString("[test/type/6] couldn't be found")); } } diff --git a/modules/percolator/src/test/resources/rest-api-spec/test/10_basic.yaml b/modules/percolator/src/test/resources/rest-api-spec/test/10_basic.yaml index d8f2b3264e8db..2ef653f117d6d 100644 --- a/modules/percolator/src/test/resources/rest-api-spec/test/10_basic.yaml +++ b/modules/percolator/src/test/resources/rest-api-spec/test/10_basic.yaml @@ -5,19 +5,17 @@ index: queries_index body: mappings: - queries: + type: properties: query: type: percolator - test_type: - properties: foo: type: keyword - do: index: index: queries_index - type: queries + type: type id: test_percolator body: query: @@ -31,7 +29,7 @@ body: - query: percolate: - document_type: test_type + document_type: type field: query document: foo: bar @@ -43,7 +41,7 @@ - index: queries_index - query: percolate: - document_type: test_type + document_type: type field: query document: foo: bar diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java index e7f9994b90487..c26984e09b649 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java @@ -40,24 +40,25 @@ public void testFiltering() throws Exception { assertHitCount(client().prepareSearch("source").setSize(0).get(), 4); // Copy all the docs - ReindexRequestBuilder copy = reindex().source("source").destination("dest", "all").refresh(true); + ReindexRequestBuilder copy = reindex().source("source").destination("dest", "type").refresh(true); assertThat(copy.get(), matcher().created(4)); - assertHitCount(client().prepareSearch("dest").setTypes("all").setSize(0).get(), 4); + assertHitCount(client().prepareSearch("dest").setSize(0).get(), 4); // Now none of them - copy = reindex().source("source").destination("all", "none").filter(termQuery("foo", "no_match")).refresh(true); + createIndex("none"); + copy = reindex().source("source").destination("none", "type").filter(termQuery("foo", "no_match")).refresh(true); assertThat(copy.get(), matcher().created(0)); - assertHitCount(client().prepareSearch("dest").setTypes("none").setSize(0).get(), 0); + assertHitCount(client().prepareSearch("none").setSize(0).get(), 0); // Now half of them - copy = reindex().source("source").destination("dest", "half").filter(termQuery("foo", "a")).refresh(true); + copy = reindex().source("source").destination("dest_half", "type").filter(termQuery("foo", "a")).refresh(true); assertThat(copy.get(), matcher().created(2)); - assertHitCount(client().prepareSearch("dest").setTypes("half").setSize(0).get(), 2); + assertHitCount(client().prepareSearch("dest_half").setSize(0).get(), 2); // Limit with size - copy = reindex().source("source").destination("dest", "size_one").size(1).refresh(true); + copy = reindex().source("source").destination("dest_size_one", "type").size(1).refresh(true); assertThat(copy.get(), matcher().created(1)); - assertHitCount(client().prepareSearch("dest").setTypes("size_one").setSize(0).get(), 1); + assertHitCount(client().prepareSearch("dest_size_one").setSize(0).get(), 1); } public void testCopyMany() throws Exception { @@ -71,20 +72,20 @@ public void testCopyMany() throws Exception { assertHitCount(client().prepareSearch("source").setSize(0).get(), max); // Copy all the docs - ReindexRequestBuilder copy = reindex().source("source").destination("dest", "all").refresh(true); + ReindexRequestBuilder copy = reindex().source("source").destination("dest", "type").refresh(true); // Use a small batch size so we have to use more than one batch copy.source().setSize(5); assertThat(copy.get(), matcher().created(max).batches(max, 5)); - assertHitCount(client().prepareSearch("dest").setTypes("all").setSize(0).get(), max); + assertHitCount(client().prepareSearch("dest").setSize(0).get(), max); // Copy some of the docs int half = max / 2; - copy = reindex().source("source").destination("dest", "half").refresh(true); + copy = reindex().source("source").destination("dest_half", "type").refresh(true); // Use a small batch size so we have to use more than one batch copy.source().setSize(5); copy.size(half); // The real "size" of the request. assertThat(copy.get(), matcher().created(half).batches(half, 5)); - assertHitCount(client().prepareSearch("dest").setTypes("half").setSize(0).get(), half); + assertHitCount(client().prepareSearch("dest_half").setSize(0).get(), half); } public void testCopyManyWithSlices() throws Exception { @@ -100,20 +101,20 @@ public void testCopyManyWithSlices() throws Exception { assertHitCount(client().prepareSearch("source").setSize(0).get(), max); // Copy all the docs - ReindexRequestBuilder copy = reindex().source("source").destination("dest", "all").refresh(true).setSlices(workers); + ReindexRequestBuilder copy = reindex().source("source").destination("dest", "type").refresh(true).setSlices(workers); // Use a small batch size so we have to use more than one batch copy.source().setSize(5); assertThat(copy.get(), matcher().created(max).batches(greaterThanOrEqualTo(max / 5)).slices(hasSize(workers))); - assertHitCount(client().prepareSearch("dest").setTypes("all").setSize(0).get(), max); + assertHitCount(client().prepareSearch("dest").setTypes("type").setSize(0).get(), max); // Copy some of the docs int half = max / 2; - copy = reindex().source("source").destination("dest", "half").refresh(true).setSlices(workers); + copy = reindex().source("source").destination("dest_half", "type").refresh(true).setSlices(workers); // Use a small batch size so we have to use more than one batch copy.source().setSize(5); copy.size(half); // The real "size" of the request. BulkByScrollResponse response = copy.get(); assertThat(response, matcher().created(lessThanOrEqualTo((long) half)).slices(hasSize(workers))); - assertHitCount(client().prepareSearch("dest").setTypes("half").setSize(0).get(), response.getCreated()); + assertHitCount(client().prepareSearch("dest_half").setSize(0).get(), response.getCreated()); } } diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java index c528de7694bea..17769eeb4ea8b 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java @@ -93,6 +93,7 @@ public void testErrorMessageWhenBadParentChild() throws Exception { */ private void createParentChildIndex(String indexName) throws Exception { CreateIndexRequestBuilder create = client().admin().indices().prepareCreate(indexName); + create.setSettings("index.mapping.single_type", false); create.addMapping("city", "{\"_parent\": {\"type\": \"country\"}}", XContentType.JSON); create.addMapping("neighborhood", "{\"_parent\": {\"type\": \"city\"}}", XContentType.JSON); assertAcked(create); diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/30_by_type.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/30_by_type.yaml index 1ee249fc6bbe6..4ed279a01656d 100644 --- a/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/30_by_type.yaml +++ b/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/30_by_type.yaml @@ -1,5 +1,12 @@ --- "Delete by type": + - do: + indices.create: + index: test + body: + settings: + mapping.single_type: false + - do: index: index: test diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml index 8690d329d3877..b30f263e869eb 100644 --- a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml +++ b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml @@ -164,6 +164,8 @@ indices.create: index: source body: + settings: + mapping.single_type: false mappings: foo: {} bar: @@ -173,6 +175,8 @@ indices.create: index: dest body: + settings: + mapping.single_type: false mappings: foo: {} bar: diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/10_script.yaml b/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/10_script.yaml index a56eb036c6d0f..766e5ff3e7f4b 100644 --- a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/10_script.yaml +++ b/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/10_script.yaml @@ -87,6 +87,8 @@ indices.create: index: new_twitter body: + settings: + mapping.single_type: false mappings: tweet: _parent: { type: "user" } @@ -186,6 +188,8 @@ indices.create: index: new_twitter body: + settings: + mapping.single_type: false mappings: tweet: _parent: { type: "user" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_type/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_type/10_basic.yaml index fb0dad5679804..92fe27766694b 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_type/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_type/10_basic.yaml @@ -1,9 +1,15 @@ --- "Exists type": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: indices.create: index: test_1 body: + settings: + mapping.single_type: false mappings: type_1: {} type_2: {} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yaml index 0881b03f74400..37acc10a48221 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yaml @@ -4,6 +4,8 @@ setup: indices.create: index: test_1 body: + settings: + mapping.single_type: false mappings: type_1: {} type_2: {} @@ -11,6 +13,8 @@ setup: indices.create: index: test_2 body: + settings: + mapping.single_type: false mappings: type_2: {} type_3: {} @@ -18,6 +22,10 @@ setup: --- "Get /_mapping": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: indices.get_mapping: {} @@ -29,6 +37,10 @@ setup: --- "Get /{index}/_mapping": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: indices.get_mapping: index: test_1 @@ -41,6 +53,10 @@ setup: --- "Get /{index}/_mapping/_all": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: indices.get_mapping: index: test_1 @@ -53,6 +69,10 @@ setup: --- "Get /{index}/_mapping/*": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: indices.get_mapping: index: test_1 @@ -65,6 +85,10 @@ setup: --- "Get /{index}/_mapping/{type}": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: indices.get_mapping: index: test_1 @@ -77,6 +101,10 @@ setup: --- "Get /{index}/_mapping/{type,type}": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: indices.get_mapping: index: test_1 @@ -89,6 +117,10 @@ setup: --- "Get /{index}/_mapping/{type*}": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: indices.get_mapping: index: test_1 @@ -101,6 +133,10 @@ setup: --- "Get /_mapping/{type}": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: indices.get_mapping: type: type_2 @@ -113,6 +149,10 @@ setup: --- "Get /_all/_mapping/{type}": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: indices.get_mapping: index: _all @@ -126,6 +166,10 @@ setup: --- "Get /*/_mapping/{type}": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: indices.get_mapping: index: '*' @@ -139,6 +183,10 @@ setup: --- "Get /index,index/_mapping/{type}": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: indices.get_mapping: index: test_1,test_2 @@ -151,6 +199,10 @@ setup: --- "Get /index*/_mapping/{type}": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: indices.get_mapping: index: '*2' diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.sort/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.sort/10_basic.yaml index 705c2d6f2cbe3..679b4f4e535c7 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.sort/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.sort/10_basic.yaml @@ -14,7 +14,7 @@ number_of_replicas: 1 index.sort.field: rank mappings: - t: + test: properties: rank: type: integer diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/15_ids.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/15_ids.yaml index 87c08a3f61639..17e6d5b57fd5e 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/15_ids.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/15_ids.yaml @@ -1,5 +1,16 @@ --- "IDs": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + + - do: + indices.create: + index: test_1 + body: + settings: + mapping.single_type: false + - do: index: index: test_1 diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/50_filter.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/50_filter.yaml index 63cca00509cd7..2bef1b6aa2309 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/50_filter.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/50_filter.yaml @@ -7,12 +7,10 @@ setup: number_of_shards: 1 number_of_replicas: 0 mappings: - post: + test: properties: mentions: type: keyword - user: - properties: notifications: type: keyword diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/70_adjacency_matrix.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/70_adjacency_matrix.yaml index 143ff9e3d1300..dc018363bf4ff 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/70_adjacency_matrix.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/70_adjacency_matrix.yaml @@ -7,7 +7,7 @@ setup: number_of_shards: 1 number_of_replicas: 0 mappings: - post: + test: properties: num: type: integer diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml index 100b44dcb0479..d50c3dcb574fa 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml @@ -4,6 +4,8 @@ setup: indices.create: index: test body: + settings: + mapping.single_type: false mappings: type_1: properties: @@ -16,6 +18,10 @@ setup: --- "Nested inner hits": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: index: index: test @@ -43,6 +49,10 @@ setup: --- "Parent/child inner hits": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + - do: index: index: test diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java index cb271b25b3c68..4cd321b4fb8a8 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java @@ -181,7 +181,8 @@ public static void beforeClass() { .put(ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING.getKey(), false) .build(); indexSettings = Settings.builder() - .put(IndexMetaData.SETTING_VERSION_CREATED, indexVersionCreated).build(); + .put(IndexMetaData.SETTING_VERSION_CREATED, indexVersionCreated) + .put("index.mapping.single_type", false).build(); index = new Index(randomAlphaOfLengthBetween(1, 10), "_na_"); From 4a5c3c5a4a54ae0309461e1ae7c6750f8aabe0b0 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 27 Apr 2017 00:59:54 -0700 Subject: [PATCH 104/619] Test: Write node ports file before starting tribe service (#24351) The tribe service can take a while to initialize, depending on how many cluster it needs to connect to. This change moves writing the ports file used by tests to before the tribe service is started. --- .../common/settings/ClusterSettings.java | 3 +-- .../main/java/org/elasticsearch/node/Node.java | 16 ++++++++-------- .../AzureDiscoveryClusterFormationTests.java | 2 +- qa/smoke-test-tribe-node/build.gradle | 10 ++++++++++ 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java index 1d894481373c3..0e92c43844c55 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java +++ b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java @@ -61,7 +61,6 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.gateway.GatewayService; -import org.elasticsearch.gateway.PrimaryShardAllocator; import org.elasticsearch.http.HttpTransportSettings; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.indices.IndexingMemoryController; @@ -345,7 +344,7 @@ public void apply(Settings value, Settings current, Settings previous) { SearchService.DEFAULT_KEEPALIVE_SETTING, SearchService.KEEPALIVE_INTERVAL_SETTING, SearchService.LOW_LEVEL_CANCELLATION_SETTING, - Node.WRITE_PORTS_FIELD_SETTING, + Node.WRITE_PORTS_FILE_SETTING, Node.NODE_NAME_SETTING, Node.NODE_DATA_SETTING, Node.NODE_MASTER_SETTING, diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index 103add6280983..01ccf68dc36aa 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -168,7 +168,7 @@ public class Node implements Closeable { - public static final Setting WRITE_PORTS_FIELD_SETTING = + public static final Setting WRITE_PORTS_FILE_SETTING = Setting.boolSetting("node.portsfile", false, Property.NodeScope); public static final Setting NODE_DATA_SETTING = Setting.boolSetting("node.data", true, Property.NodeScope); public static final Setting NODE_MASTER_SETTING = @@ -727,13 +727,7 @@ public void onTimeout(TimeValue timeout) { injector.getInstance(HttpServerTransport.class).start(); } - // start nodes now, after the http server, because it may take some time - tribeService.startNodes(); - // starts connecting to remote clusters if any cluster is configured - SearchTransportService searchTransportService = injector.getInstance(SearchTransportService.class); - searchTransportService.start(); - - if (WRITE_PORTS_FIELD_SETTING.get(settings)) { + if (WRITE_PORTS_FILE_SETTING.get(settings)) { if (NetworkModule.HTTP_ENABLED.get(settings)) { HttpServerTransport http = injector.getInstance(HttpServerTransport.class); writePortsFile("http", http.boundAddress()); @@ -742,6 +736,12 @@ public void onTimeout(TimeValue timeout) { writePortsFile("transport", transport.boundAddress()); } + // start nodes now, after the http server, because it may take some time + tribeService.startNodes(); + // starts connecting to remote clusters if any cluster is configured + SearchTransportService searchTransportService = injector.getInstance(SearchTransportService.class); + searchTransportService.start(); + logger.info("started"); return this; diff --git a/plugins/discovery-azure-classic/src/test/java/org/elasticsearch/discovery/azure/classic/AzureDiscoveryClusterFormationTests.java b/plugins/discovery-azure-classic/src/test/java/org/elasticsearch/discovery/azure/classic/AzureDiscoveryClusterFormationTests.java index 9787dc136e3b7..09fa16b8ed05e 100644 --- a/plugins/discovery-azure-classic/src/test/java/org/elasticsearch/discovery/azure/classic/AzureDiscoveryClusterFormationTests.java +++ b/plugins/discovery-azure-classic/src/test/java/org/elasticsearch/discovery/azure/classic/AzureDiscoveryClusterFormationTests.java @@ -109,7 +109,7 @@ protected Settings nodeSettings(int nodeOrdinal) { .put(DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey(), AzureDiscoveryPlugin.AZURE) .put(Environment.PATH_LOGS_SETTING.getKey(), resolve) .put(TransportSettings.PORT.getKey(), 0) - .put(Node.WRITE_PORTS_FIELD_SETTING.getKey(), "true") + .put(Node.WRITE_PORTS_FILE_SETTING.getKey(), "true") .put(AzureComputeService.Management.ENDPOINT_SETTING.getKey(), "https://" + InetAddress.getLoopbackAddress().getHostAddress() + ":" + httpsServer.getAddress().getPort()) .put(Environment.PATH_CONF_SETTING.getKey(), keyStoreFile.getParent().toAbsolutePath()) diff --git a/qa/smoke-test-tribe-node/build.gradle b/qa/smoke-test-tribe-node/build.gradle index 4ddb178068d2f..009298adc6aa0 100644 --- a/qa/smoke-test-tribe-node/build.gradle +++ b/qa/smoke-test-tribe-node/build.gradle @@ -52,4 +52,14 @@ integTestCluster { setting 'tribe.two.http.enabled', 'true' setting 'tribe.two.http.port', '40250-40299' setting 'tribe.two.transport.tcp.port', '40250-40399' + + waitCondition = { node, ant -> + File tmpFile = new File(node.cwd, 'wait.success') + // 5 nodes: tribe + clusterOne (1 node + tribe internal node) + clusterTwo (1 node + tribe internal node) + ant.get(src: "http://${node.httpUri()}/_cluster/health?wait_for_nodes=>=5&wait_for_status=yellow", + dest: tmpFile.toString(), + ignoreerrors: true, + retries: 10) + return tmpFile.exists() + } } From 8a8410b5ceac748b50424b62a99e4c312719c33f Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Thu, 27 Apr 2017 10:27:54 +0200 Subject: [PATCH 105/619] Added bwc indices for v2.4.5 --- .../indices/bwc/unsupported-2.4.5.zip | Bin 0 -> 371842 bytes .../indices/bwc/unsupportedrepo-2.4.5.zip | Bin 0 -> 179875 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 core/src/test/resources/indices/bwc/unsupported-2.4.5.zip create mode 100644 core/src/test/resources/indices/bwc/unsupportedrepo-2.4.5.zip diff --git a/core/src/test/resources/indices/bwc/unsupported-2.4.5.zip b/core/src/test/resources/indices/bwc/unsupported-2.4.5.zip new file mode 100644 index 0000000000000000000000000000000000000000..253944a3aee093b12a075a269f2b684d9b157fff GIT binary patch literal 371842 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 z z6x83j6W*Wi>+^g6@%`chcxc<(lXKtqb6?lxkP2nSNYN8W@=`KGPme60xe>}eH}<{L zgGX=sPw1y^{XTZ`1sM!FXlo&?1B2>hFqnX75xj>-Im}AM(yi{vW(u8duho`J3X&EU z;AaP&#A}luUYj`g(uoyOV(6Kt9{%SnigB7C`eC?Tw2VBo9(dneEXXv4{i<9-*RAD6 z75sc6SUii?8j7!pwhPIm*KE--<#|UB-L>rl0-Pt*O^4eAo2b->M_}JKP>?nCwJ6Dn z$IqHz#_Nqmq*-ZN8up8I%35&qvHA4M(o4}7jv%j0K*+a@`vCU>N6FyReBS^yiS6~+ zI0BKu(?M0*K21p@@6r`JEookt!lJiZ{3bICNRI;0X*<00j%U8U?ZIzoe>{5Y2d00I zP$&pN>q$lovfI$l(h)~ z+#Y~VcQ5daJ+x%?mSa-$vBA{+r{^Kqd@_UwDD_Pk;AlG4Sd%yZodk|ENyAGyO<{os z4_6F4PL{*Tr)*Q38pSH~8dUj8NLL$oE&bVZ>FY!L4gC;(9?~Yj{$4+}SDN8=M@E(Ur+`7PyOQ9&1yFd)m+^FVFqz;A2ByTDS50MF_GQrQrUYcM@(E z#EEbU0c1wVXYJMgUa2RnQxx<`Ei)-Jh57nu*{GG_UjRq>192Vw=yPMvY!=d~O9xZP zFOuLy6>oP(BSh_P#$F^5TZBjOz1Mn$S$#^u68l8lgho{;>MT)Npw<>2y>b1d?tg#Z z{@3G~)Yiw2qA!x#_`3-7y%g$r3U*;Q7y-YERtl*sSIbKW?S5~tnBaDERYBYTFf{Ew zbMD&*Pglk__gpPL@4ZWP7>0WYbu(c4O@#Vi>)X@cwUKE1o35flrpCxCG2Q;W)FqM` z!YrjH6#{&kV7%#Fc9JcIlODDh%@hTw^oyKxBZ<~BTm5vmJ5 zmyHwelZVX|mq|w4 zRCFvz^>`t*G9c|@ie*e2tK{yM$yDj8>(fK`O2@BZ zJ}9yc2gRm-s8RePp((KErY8P&467&A(f%5K7476?Dv?fSlx;3+JkAiOn6PP-2Pri) z`R$(976d!KIIj+>FKu_I8E5c-;yttuznFNu)+E6_?-U^1(L+Gb0J;dLJzLiKqc(m~ z*QpNLdIZU+{_`886G6rL5C7AlUixuYc=^7k`X+r&pf_Q33)&)#4aGhukQkh;#OjH2 z1p*~4%bi40}KhH*6wdq6WC`GXl1j zL80v&HGoD%T$Z|6}K^G0WT>9!rs*RH&Bd*dyeio7Ln*d`MiJ|naV zUn4c~@V#D))X_4;0aTgO+HuqCuo^O9HkYX_#!U(9WT0xUZ5*_7kb(Z#jLmBfq!(|O z2j%dhwTwZWmeG_YNSkOKRR7Bu>^Z2eg?kP^_bIh2msPP04i{T4wCD2)X*TJ5uI8Cm z1``|Z{=|4Ea)z0FoO&{?h3P+1+IYG_^><9bX4jEv-9raZ9haZg6rG|@l_4dRXSIBG zPrle+>*tM6G21Rp>$06XJ!#J|!-X9WA??BoFr%4sVG#D!5EOZ$0c8`elDcw2rdMw5 ziC4PKNoJQaZ&V5UphnJJ69(YBKHnZ0%X@5+>6sHBzVUv~j-MzE3+ZjVcSzK_R!Bf< z3mv?vI!q2?v`E~>@=Dcqu~QcIIP+Q`E0FGKBsFrnCR~FTDrQCCM}MDjPf)K(Uygj1 zA~v|tHt{W#b`C}wLK{Eo3Tg`aEKZi+Uhyl-)}9n69f~T|`0c|ZPJCYkC->Pe5Bm6h zj$Cr7_saa1c?caNwTWLPHFK_@*gT*r{~DgB=uI4bsaVRTxII2o$?uMM#9Tp|*vPpZ ze~1Y`>ULhAygKo{h&*D!YT?N%Xsf`1f}DpA`9@;T(}*p68e;&}TST@_e?}zEo1YZJfP?Cb1fZ`XQ{Xg+K#kmQ1?9 z*M`ysl`D|q@O2)mSYdZQP&2VFEq!P7z|^6g;cw=;zX zMP%K#jo$Cx*JS+Y1l)k1R>=vtNhF7{kErApAyCFm`SLP_UMNVFDy}XrSDG%cEr$IV z{xG2Xv1*Hyw(;H7v?uOP&hEQ_P0lnnyiCI0Yk}(PrvjW9MuHG04Lyl3qzQ3?ZVo^0 z_xeQ4iXtsF^6jZL5FT9u+-2+Eclqxcn|bcqwNa;`&j+nM3De&mHbuaFozX0j0eve< z3m>c!;cBy1S0K(aT8nH}B$MEoMTwIzSOq^Zi+oIJXW)kqSqHAf{*&AC5X$BC&!*s@#2>y*oq z@xWdJ#w0*=C)&jSfQns2iO7i&=yCia%l#IgLRQjAJ-jf>6SHT$YN2m+?IPEG@I7s0 z9lYxB-z&a+v3GM~2HYxMfMD%Z+;7Z)2ZKPj2359;Ic8H@l#LrB@`R16K z=WqS$Smc*?Y4ayqzco$Uwg5qT2$VRnjlY}FB$xqX&D1*DM8W_mB9`$aszhg=7maev zQx3bc7wL!*M>8SLaC?-38iL0*a4^@B5JkfHE#FfN%ZrqhJR*@ zH@$-$?(Tz-j~j8v481@Q2hxQV0%|XWT^fTY{51R)Bz$<7?2d=R{xVM~ z5f~~QwKT1$-R&n{(SNq2ea@$)!9VFgvCdwzxEcAOsw}y8GE{&6Q0xp`N8{oC!_?y_ zCl$<2wuGDLa!2x7JHK1Eu4Z&a{NGnhfAYYO>+W2f96fd7ST&5?M5N#!2e%H*l3ZhZ zXc?1)JPud=p)#)bR{WZTJ;PGDOd7T**Hbpz(Ha5GAlcI&zkfovX_sUFpTB(i`_2C$ zH}b9k3<3571%E?-p?HE!VhE~adjlkAHsh9QtnO&2Gv@Bn1Wh4x&W6^Q7;KwY5c4CJ zT;D7AQvvt{jBTe9kTRiKAJ)nJ6>mCWh~$OIqE(=Eu-GY*mFiP0UJ^tpPnUFqI$O_vgh;o2PHJ488Q=wFz&GbfC!3 zbkN3x1o$TXhYT-5pnGBLeJWIcu4#Z&ump5z1XZJ(cYD=u(0CG!$j;` z3WQy+hw6TB!!vUt&4LHIl}u7pR*Ee#Jtw5#heJ_`NmRO}Hfp^0#VsG2v1KpcjV*3p zdf?voJ|ZBiP|D9R_A&#i-!=??4E!?6z_kt)eX6|1qs1*FRZ$i@6H__ukcg)yC-Rwd?v<_pRw&+t-hOc{To})qTC|s%_HVe*8Pu^sT~w zc+IL_{O?t3SM{!0jsGbAJ)k`bR=~e$b^o8O@SOjv8_Hq-FO65;e`8T04x9b|(ht=` zql7~JfA>SPp>9i_Wpr9|nOL_^VB}btwp{qX`k@@)ie4TtzkP9tY;XU%?S_Nc^4F>= zuRM{nrt->P15M!ZRzvKLYU>d<<)g%crYg*kEa!)LRU7G3#N0k;qKI4TK?rIT!~#G;45mo6PNBvu zEDLy^VuYU$_xSn6o=zovy=p&4n-)#mQ2FthBS*jG<(6Lb;E9r?nlH6+PQWaYxE~tA z5IqK07xGwa>SDxQwDxeKieOG)PzEJ_6?~NpxSRris9oao|D{Ac04m1cN$F9aV!C};5eav9RZSHWh`F<*_p zJBip0gK`U5X_R*>%6y(b6O3{EUVg5W^Kzv#YFw0!@4bk%iL%f4AN=T@*Xx_VU5wO( zS=;#2;fY+qbQp9#j=`iBUN`PNO3aW?5l<^h$x_5^@oGg1cX!x4E`V0umKH9)ixz98Nq7Qo?@+^(NkZ;fd?~9i z>Si)i?(P7O6-moNVHU4YJX@P!lfSUy+L>?H{rK;~*>}(T`;AX3S|CAxC{>&ScEpg+oTB`0gJ%s_^LJ-Ai0L(=Ce!Z4mixVAlvKm zY5Xdg$mcWP7+gP;?JDq=->Z5eQ6iDw`d&PP!Vv$2;2ANVJ3!Pt5t>ZE_7MmzoId=x z8fOPBTPn?PgB5Yi>d=dG=FBHI zWJRt#*H)Mftq;HU2Y)d06Z(rMVTRCw)SZNZK3EfX92F!k@E=gsOE?TgOPtBMF@2H&RT)mVJ-?`nrFd`S&}nIcFk^@P+s*f#hDJ;3Jr-hN<)FfHMa&zoN_d zi)WV-zEmXSGISfvEQ_QfkBSQZ1+`4`K;7-n4nH{c(a*2{o25?uCA){vDtd*~CXvDu z`J6ojm{+w7Ku~@Fji@|auD#+mMIu&_KbO)6!e0D)szvGfuHvD|D}*B~7d{z3@!RIF zUM7Kx(G0ka^D?O!AR$8aWZHV#0ICp{^gWtVFspYP-DL$wVC@p;fr>1CB(V+DPiMAm zc#h_8ymW$e{Y`k5u7SbLg04uTa3@SX3ga;;05;EnA)0`K8&j<^We*EWJuIozU(m{# z%n~~U%I9E;EHG!hj{eHN-=OSKd3$27}5H*Xl>TK9cj)7P_AWA(UQ63wRoVo=T< z_3Z*>!%YJusbAoc`FrxAu&?O1u?#_JSI-(+ZFCI&+)euQ!u{y4nf6c0q>F24tzt5% zk-H1uXN1LPe?%CB*hwf#!abanmvx9G(twQ1^9f9fVyLUr^bvRr{4oF7aL&AMM!xm! z^kedEm%ipNM38B8P!|;7rw>1Ypec-eORg5kWiFS`U`|P0`GQC-Gk9FgfEm1zYH^U{ zynXVS_gvp^88PTk=PA=NHHzF`nCTmEn4rpv? zjxrqg@|_8`GLur&B2fYPdei>>{nk;-HxBuabkfT4gi$>HRdSVIcRoVL-lXCou@ZEG zgF?17!q4(0-i)2E7APvdqDom)A2R;(hOCWw?CY2GbKb?q{W??$H|!;}i!cV%B)}I; zr@pCHCR!vwfj!C7X~fx*pvU2i+A4WdXTofr-cZ9bJd;)bu~qV!phL|xdh8Ev)uYIF z;04p=F!dt>e6@<~uit?0Q^KGT=qj0zp{yyFP2rfWoV4)jNHrA03dBE%8aC zu?$a`=Q~E#m~d91Esr7xq4e#@gtI@dFB_UMI)T(C+)1ccqtFH_HVYvlT~u@}7#m{R zB`Ouaz*Pm+yl|w@t?|3{@|xcYAKy0QuBqR4F8qm-T3Z@1@y~h&$4>?9gAWdd+IfH0 z4`=XKBUKaX6N{rfmCh*Cd!2qwC9ANz3YFKXHGZ}9zK$PkhSSeI-aT`D!%eBZQDUp) zG>jiQJl`Epzy{&_EqaM@6;VWp}cnXRBeM6&o7F5roQ1=or3JGoDNI{HY({*)m+)hs*kd|?c z-JFoJEV%*asSN78+chNkO@!>9c<^`5FJBoqg?iZ8q}^fV3eqlXCZ1LSDo}uXJgjY)bu06!v0r@u^ibK06NaaV z^ac{J;uFQ(b10S~Qd$IHFvsrX$_hG;I1-85l-7_ZDHp|aDPm2vS!McC(@7YaH3W3FB`4t~ z0gVJL0o=a?+O16ks7n>m$PHcTyxuK{+n5n&!Y4?btjTydy3~|6Prvb;WO;k9&2@G4 zMiMd$3{?ajD#kgHP6i`tQxV#5tMcMQ$>Eav}OlSe`$F; zHgCo(vhlDhd+(L!@Cz?FR3-oA8PO)e0t8w{1nv%bb7-|qZPM6^DRqynXk#;T8IeB{ z5Zc+d!2`e?^7HAHiT&GJZ+mUy4-0;J^uX&>`Yro(OK*J?FRFL{*_+J1NRV-TgY|a!}KB4 zChjeR+B-g>kP!m|{TY}-TVAHDD7qthLAIFp8}O5vP~70pIoKi{JnGE+jq&Z@)N76F zXrOyP1VB97BwDmxazDaA>S~PRmGDi{G=J61T-EjUUF8eYM?RdOr>qX zu9Dndr`ghDO=~5dm|a?ABh70>2-_Q0%n&Y6_2MqW(})oV6tsD(yr*k{8J}Bcvd?haOB^uJKj() zM(7F@TStQG@g!nX2MVewRZq_E$|iILx7_9w%MyN1XHT}=!(Lot9X!%=V%$kYX@;Np z$o2Ww+?NR=L;OAwaD_`2BNMrN{Be{pnYMyD}cnU{EYXMY~ zQ6d?BrXHP(=bT+imS1A(=JaqpMz*f(44EoQ&fFWak9U}V`18BMK9}K(m5!M|%>Q~W zLjMKD$Q^i)vVnyCg5p;BCc+i8+n(zwxno&vG-dI0cIwyx4_ERSt&#gp+W@kT$$NkC zxsHkZg25&Gzb;;ai{t1tQk!@=xqineY&QX=HIKSVa*0^UvM<*y;D;nSzCh(Ss7;oU z_>lp$Ae}o-?%Fc()S|z~Kcwt_^}$6-nBHC2&iNfr(t7Cl5@ZIio`#+VJp!SQCCjq3 zJYPxg4_hR%irzDc03B+(2Alh4p*!~cxRshE{7vq7YKs!qad;h*Z=W!V!+wc?JI{$c zahwP*fj}>XAu`tZqrx#(Mr#+bQweprE2*)tqmC{;lTf1vnLGXL=&64`Yh81fci)#| zzhRnCWGRKhB+wNwOC(tX4WgOQ%j8s7j;W0Z^vZZHk`ZKbJ;I7VES=d7J{*3MSKWJC zN1JYte)RgMH|*Q6ayD)(By<=QStk#{6YK=B>bNH2AybbcCX>000!2BhR4Eh*rIXL< z#?Lr7%AbngC)p*~i1|a4ZpI=(HC_ zd9FUnQltRG*?0CGrR-6qM?nO1aGJ&3a8qQ(#rC z)3o%9-h-ECPn06Wlj|dg9xMci@P~DPJT>1AHH#-vpu;fs9)U=E0ynDGlGCH;?&1V& z5l%{#VX|zB{5K8PNgTimB_}n}pSV;QQCR8O{&(YrX=9)SD44cOro#}{PGHbR47q}) zyKG*&A{8x)q-hD)VO8tYQr#@zKLNp5Z{DMk9NqECgv)_1Ml7E_4kjU^nIJ&n7wHrE z;@O}mQwL8$s*>MkQz4PAq*W4;D5wev1MHyL$OBDOE|?`2f4{DO66x~xpN9UkYX7li zKh8m5+>7=>jUq2Xjn*}Bj)7Ljlu-0T4ni4+0_dIBku{4;8l8bz)RoivZim#b5lCxW zAHDhWi}1&+b02>+_eBGle9}uqek9kehUo7R>QA8XW^%PMI;go?85Jfw^HG&AAyKA6 z8l57eFNG3;8$!%ygD)?3o%mDx^TT6CZRI}8t^ zjl>-nlb`MJM{O!;PuazaN5wsQuirMkW;Cuna4|bpNw~%Nk7nzq&#hG9pGbg!Pb5XD zGe^K36ikK^T7+_R04-&MQC(DMi^=l5E`>5>=dw(S{u)`q&bRx8{S)7KDg?g`+qTp?z#II%n%-?ffw`~38HT1U?1RTu@}#djiPXlqm3qa%duQ@ji`Q9^ds2;eZV^!z9E0hfHna&pAk@D#3QN;#%5Z>n%VyVx zdomW6kC%~jby~zyPt>bE27@DwKa8?t)aU&N(>>en^k0WpJbj&ubl-&EutrXjMkSFj z&6L6Lrn+ieX!UA*U1eEL>DI-|xhT`D3~_^ic8LQf$E3aA-`$vC%nf|BXS-Pa*S)(a z4Z*rL?z2#fBtXQ*3DFi2?iNV8bT>~RDRYHIZ@k21g*7Iwdl#iPH#Y8p(y1ScXa8FJ zLHh8>A6FddgOCaVNTdpP6Pm>-I(8hUP=RmPhPxdRcHC^V2YOf$cPM1EDTE6V@Fe&R z>!8{fY3ZEtxqpVW&-_q+<|YDtcM~>SfM>^a2JP6$tE8x2!B1E+p+Jad3s^MVm?*1# z9@urz@m##-%Hsp83xDs=|Nhs*oA)p#QW#urO{w*HQvK~iv56E4(n$iGDrih1@3f@( zL8j8{^rf6eOTgyTj3ZpH_B$W`;>p_{YP+I(JXN6G`jw;Y1l(Y#$Hq*8*gVP9JBfr} zCD?hCjIbwGV?w>%q6)=3c{x+wBS?03%F|kh;Rc)a#4)?>SHAtq_wJLyC#iD>Pwm5x zn&f7LqQVW~dQ!c2XbpI_fdtVSNLSE8oGmDcT{;K9E79f52TUxNwX_HrAV9~z*(mv7 zm)5-C#GU#(MVBn^0};mKWH4?m@r}by3?GU#jw1tp_Ygl?)a3+{c$c~0lDO<$E>T%3 z83g?@|i-2SDO)k!qtSWEKk9 zVJ=H(*Kh=d5Hn>d>DWrRrVKIjm(5=`>^L*}(#cc*th`>gUkSJJ{zGdQ`G-PvpW?RS z6pYf&4#zL3GFtZY*v@#+k#Z-ZEVnl0@ZHcSeNXdh?!F<*kDu~&?YL*jfh3>OD#o8> z1O`!yb&QTlFt!+hkT*ote1hZ6d4;JwH!ex?Bnr045G=4bcC?ZEEB?6-u5UhjZ}G81 zZ~u8~!<2J7zBvigmrTUA-U>!(hSGlIUqKatvRdoNTXa^f!NKxJDn^#grKsKl+~PF+ zpc*~Rd=pY{zC3!*zWvUVa4YvSLK}B5iF!vPmL`&s-=M1YN3`M;scgxBM=0a?O(}k7 z)bEos0ZIfQahBdRZD`Y~@R~yRp(~#8EffdZis#grhyqdZ0DdG1aPT8_x1pdAX<|A& z-D;mX6wz^#1x1OevxiI@bux|t~|cF?Kq5F8CJKKK!1YV#1&Jp zJL)Md5;0|fWcI3L0(GG5v4&K7R=1%fkd-G8ej@=qj-*$5Je&!VcQFU_A3Tyt?yI}O z3f#MT<=WLX+TYcySNE?4tyO?M?d@H=y1#Gjs{X#d{#AW_t5@P*T-Og+f${Ide_~bd z>b2|EtipeOU0*-`ouK>L-@B%NZEyct{8w*(wGj*cSqC&*=l$QfRPq1PY~}t}C-(QD z?Eep>z(%N%Yy9t4Y=Nsu+BlgQKcW$fS(a|2$zEdrH#C(4;96U}C)J+*qm1hpFP>q4 z^~wQwE`AY3qFT>XB zR!PGVR)TBbc8V+^i%A(4=6!i7{uK()?-)WE{m-3C52g0ayr`JB{`NQE+3<0Alt55b zM{N_!;0Z$ffq`xcVHCeM4Sod`%ha4=wjwj96nV8W?hNIl8T}FXDuoLqEq`OYxqa)L zo4YTJ&xgmvKa0B7~?oR&a>3c4G+OySi^P%7Np86U0s8?&cxBStB zCj2+li`3d2Kf_nZJYFIs(Px8Nhm4*c6q(gy<);= z86UA#FbXhYz$5ra;OQNyv=kr>h4|(IK~Z(g=w~^(27@syE&D=zWiBiTzE7y>A~f?x z{%u@3c@MPf?JI-!NP6BUz)u36@u_056sDel;Wt5v47b2H8qT7GxU<9#8RF7zc_&xn zWO55KvAhLNKlt5cIG zNzHbdPN<8Ni#Bhl2x_OeD|!9luiNf`B%`#aS7f#gTfH5gKc2u4`~_7B_f+UaF6Vf) zd-^F{li__Ft+r2-22qb!=P2rP`W|PABk;%UX5)>4j}_Z5Mjrol($3fI4V5haA+7>O zo`UQ8p;nO!ZW2X^m>NY90lE5Uj#$j;;ib#Dq%^?|>1~C8ND|#$tFW8IUxj-(uibOg zp+A_rlxMIs0Nw@OAUNsHtuP0{U0n_PxmN$0sy zVP7<9@m~+^TzT)erg_e(pVoziS1acUQ5{bwVVzrLg#Z_0#N|!Y$Q1i_) zg|MqjSago7DJ9(PeNC*iJhS{AapS<9_iwU-#=+wR%2BvYg1>K(6o$MoHV1)dU<%#l z&WZgjli8*YcMIM2xV8{yGQluBn21m=y(M?iq=-yBGkDKP9$6j2&8@u=c{(6E$ zP-mwuB2zygU?C!fArX_S`=vF>OS)npmMb}PmV!}})CKL9{+hQO^~EdWF8g06O}_0I zdBuMoo&F#hxu1gXKlTC%|7b5$u?NX@46(b`%LrPcNuHa-@frPb4=YoU3Bt1M+8W7m z`O0%YK3GWX9vdFmNL~SNg6B|4^p6N_Vtj!UF#bGv%y%Beqa=N|+)+%~L+Yf>E;VJ9 zrieKKO~Tf29U}LnNDXE zNwX@wUf|8@R7Gvv9^Xy4LI#tKj}OxFUSSUDdpGu)a_f>o%i&oVwUq#Zl-Sw;nfVP8wt<5F!ot1EpvPbMvCd}HnJk2cytg0Im zjz;# ziWEVg!Nl&GSCcb*Z0^ooPfmIF+gt!^ zHm$_g3fRHUL`tn?b!i>!kec6RiV$lUkq=~&{U_F~^i98G{+}~b!$tcj4DtGU{K7Yk zBTV9mlrXlNK&lQ*XvATW#*kE1x_nu)zDJX^dkxG2y%z1a@0m98-31>P&xtqeernv( zE4bH#(YiIDCjy)wCW9#qp%JP=BfVmuDwA}pRXKLpWa^HkJX%{A2*`nWKmO6KmqO^U z&qndT^RIf8F}D}Lt2ZNccmQ|;hDHebA#6PfZQ+7`k5I((io!96*54&f`|?b;+vyH0 zyg@*GZrgXiP!5dV^PXbf^;K4WhzQ!BG*AX+*^;Gv+2M+c z%c_juq0N*%3HiyIo&LM&mA8i+T>7c<#Siza9~Pt>q%b%gM2Zrg!j&vWCJ1==8_L%a z8Iq6a)q!X>DKkxnZ5#J~3U{ciR?^e%Sok>8U7N&_A~NdyZKD*lciBw{xapcZb7IDq=CT7@@g z7Rh4zkd4*lElCaC{#Xs_ltBp5V_Q(}YW1b>-b2o07a~ZMN?C}siSdtOMu)X z1)e~_;zVNoHuMco_k{(Hvu^tGRU z{(QQz4{GI&ph8+AzDScON^I}&HIfmu9-WU|LGwAKK~_%L)WM82!%7Ex_+3F0C@S(m zb?B}4>&Z>~o6gQ&KhU;s!B6~CaFr883Ab^5jZH!o4C99w;S_se|1neFnM7!2_1vM6Vo{GNgwXym9e7-F2G}?{f`;Y+&7j$*Ge;?Yj!J{^b z-}*t;_2II)@WN*paBCfI->Z_3?6nZwOTyMc#8EsD0&8%yn$wr%R#AwjZEn}VtsFnG4cvD>5nc;nN|b=SHne)0Q@*sa z;A30)%1qj8R!Ku#yP`)^YwfPuOni4+<6+$kks~u^1?GK@lHl_+iW)zG?_m>!V0s*| zXp3tDHyM^tCl3_aPJx%_H=Cp0PLo&sAc7k){9dg8XGz`nf7+jXyZcyQ)bC+8VVCP6 zx)P>NCScz;;K#UJg_)pTUcQFIQpQc%gs0oYPkE#1O0u^$5O?QB@;ASafz79s@NGV%Z#3yFOdbIDz? zbmdIhq9oX@?=i^XDhI=Z|F-NoKVr|N^TI8E_dNRgls>58b3!}!Ng7mVLfbn|4?-CN z{C=Lm_bU55jHHSs(>rI1kUuqOSi0vMkFV@-ckxuYKV?8lYA z&Du0r7M}htezutuEIJgLzz1YIJE|-+G*9CIDWZxcyn0hANt-%p(z#eoHK^nK zt`m{|@O?|3#xKx{FP+zlbOygn7@l~l1KWb(_5zQoI_4qm9rFcM~aXkq;3KMDn zG+ssdnru{U^O{rENH`R<<;po{(DfpzCbWIH=V;TghgMPUx$k)5qg`#TF@#pZOQc4@ zPMA6esoyvZ<2R7uTWHlRL*d|g4R(!KQ1)`lK3mjS@cO(!>mA?whFiazGWz+C^Nl1H~?nsIy#uai;9GbtD72TdBLVL%?;7w9kA1e2K!1(e zE}2Mys3%ZtCxJq{Wy}Bxj}RifGT+DS$)zg_E1vXbvrBFO>X+^s{c~H(wP_Rf@F&*A z&OJ4Rif{&lNvQ*E&?F9fC>1wCb@iw5Po!g|P4*yDPzotKyGoTjH>|Pdmmt@xxccz} z4ccGdegF4ichg?}4*ue;FPp$T<~pcN5GFPWa38;qg2H;JIy%GhmJ()}x56@Ncs?a3 zSxTsU%8v*GRUzlUH{2v1_S*ILFDHzloz~kMKO!J=D0O%!_8A4Ldx->T2zcNN!NcmR zoff0AOA=$tqjHwG$X6%3bL@gTzN!YjWh?)Fct&6SzpTU27hl&ed<0+V7aEY^>l=or zgm{{PJ0KNGfPW)Z;cjfDMHuIHXM5rvo08+SgcUaB-lpqBfUW;RKJZv9{<+FJ*on>N zEA}?gosHPX_)GqMB=!@GA|373Ii4Pg%B@VLEhUY`WwWZde6QK5+j9d{zi)rnpdReK z+(9K=JktO8O<^K(ClKDi_JH}Ddj|sjJA|A5f)^OwR2V@ExZQ?50DgWR#5Bb$}(YPR%S571lEx2+Xnmy;-3mL|1|ouIWNIX z;%moFkPp6l9FKcb)n1>l7pnie5o^LwzkItb57t%Z=8Wz=W1vN+4w(_Q40 zZHgG+yui&?YyIkJ7pX7I4qX5A%#J&(^o2+RZgEyKAS$1N?Ilp_zolFy35@bCiP)NE zu^onBC7X-9>CUHw zW{!x0rHCj)d^-sRgO+AdH_w*$=tPo~x4_D({Bb`!x%dWC>a@)z)z{iz4}G^~U{_Yn zUvU~nei#foWSmI^YUfChH`!3aD8A@UxH=`0w74}vXQxM12*?%T&Z5?)lycL+dVsFf zioeJe$>^j_qZ!b5r{Pvm*f_?Au+#APD`-imvHBf`Y{VPu3Cb)1d#6q#c?xVB zm~;Q2_lpeo+=3S_mIwc+^{Fqf#dDkuC?2a~JMkQclObY5NOg#0Y!&5V3bI2SDOc?A z$n(}*JZ9iAEv&UQMR^M&3r+HF)kL3d7GQfBJrYSN)J*Q^W55 zia;M2)W%s0HFJkD;0;8qfTtF3GX_wlv`f*&E87c9ExtPeiBgo0@i>HPfZ9m<;O?2W zEk{Q#-+kZUE&iXrA~ZaRvi|rAOn-NSeVi z0tH;Z7{TsDQKUAxMRIf(%w56mgrz&eGxrEtX^lp6AGlQj>1TT8p*zn8Z(C8CcH53M zncat%AoNf07-u3(okYO>44EPPrs^L^IzFpn@x+3vlq(dn@%%Qe%=GNw>-Z}8bsGQh zAGfafX;|Uz`&xfc%r-VEVdzzqqJXiB4N$Xq2nkRVA^{%wRHRNrFk;jzY$?8;Wy`pO zo@CklFA6pDuEAYz_p;aQUp;q+>bbU1!t|XIM&24k>4$C+OD501h59Xq^^xn~MOf8g zakUkjy;I~3b$9#Hp;XXO6a~5`g90ypmA+lJT$}pt{-Gc9xI%drZUEUNZNr7SY-L#)d?vakZchSXPY+C18 zi9gY~DiV1ao-PL`V57%VS_BUu1E@>s3;HXO61T@+WJh{D7GGInd5cs{*Bz>#aud40 z7qPw^dFa-y_uZz1NAe#)=x>nfA8LjMkstwtb)gW0dr#HkkhB7eC(X5M(q5iLn6 z=1^cvMbf>Cwbg%SH)o;hCdH(wOV%^T}ha4di#~Pl7Fqc<1y{CVD9f?O%=m2f!7)x_|W=fPbwvZ&$DF z?_1Xo2!;FCtm^Gw*IVt+uIugV!@qCss{VEOAH9H*82^^l)ei0dt=lU4Usze5Hq1_UK`(&>fwha-F$U9ir?W>kt6Hog#Q~X%LA>u z&u3nl_g%SV#7kDmuU|D6=Jwyfv;x>DuOAwL|NcK*s{IfI^ir;n(NSyY``X@mhlRy38+ zB&sfi$Kc;0Rhw^;dAJ#Ak22yE#X<(Fx*+87J$QCU2Gh3uVZ|3{A1*K*oB0O1<>e~@ zcrNbW4})=L`U6P2WFKKD{7p?ZDr*>w~|ih!-Aw+EGwjv z!l*fKvRVQ26#hiB5-;?9D|zZ+cynd*;&VnRJO_-7^y3jUVWLQ=fZ;1dJO;uo(j#yc ziIWxUm5#Ws(;&!qrgOngrB`axx={RJ;SaQF=X{gQ#d^loxBE%P8b-%LgdzA347i)Q z;=P1=1_|qhNDR^WTCbTKV#;Nekdb9Y!&G!-iHWX|3d z&M3_lv5%dJ;xRgCs{81_3eV4d`osUs?WKS6(Vs8OLt4czlUhYmxEa6DFdwa|9SikU zVsJE4=5+I8W;Px#7CVhOOW2#NbOOL@mHDPMdX)FZhE7)3C)k`9U-Q6=j=~J#w+Q~u zMse65z)gYs>Lzj6FOzUXOkqfR5%jq#63=fd%Q7WxRG$)+0)m3ZZQ-7&g|h-OJm0WqJh#kVib3E@{2VsAp_?wFpF$+qbB$Av0g&31ICN(;hE}7QMH*+lcw5F5- zWm7Qbe#@6*pj|}AAo}48=C|hjw(=AV!eYtHI?xsc61%4WsPz=?U(#U_&m))eEx5f& zN>t7q*A&o8;cCOCkA68YN<5vu^O^mQ*Zp@+{Fng!3Ak_RYXI9dbQH$86hb|ipA{AK zshl9&$xG+6VU?_?w^@Rb)ipxZUyeTi`qN+kv@|~3lAbvDrv-;$2Dbr>gST-PAT7dX zDz=qMf|tS7c?f^fAL()P7WCjw>ZbIEh1W@w&5r|j*NI+MC2+mvNc-aY#LMhQjeQB9WT26a!gKIUS zr`BJJYYQ~9cFAUV2rUx*5jUZ?!EFKxsa*`1F>lZunzWq>w?P@~^rb!da)_xa!EKUJUDOGOlqVSL-t7Nr=9S%l{MmYK-spey zgiWbBQ2(UNL)yfXDHHjU&nVc>4P^L6$edi48Kad6u>8S3nhx?&cp%%q{L19dX4sGFp(y+PgN;%B&I6IaLs0}@` zOhzHMtH4YI;6xo7Hk=vz<+RojTi<_a%}(uS1cu}a1R{9hdeQ`;0DqJOk;vd&sG$#8 z6`qh~XJ=G)TiU=a%Ka&45dWFl{J_6c2L7HhY3Nw|we8Fn+D747=x7X&z3-<_;E2{h zm;oiz4p6S5d8e|w+h!>kT|EVTMv(I`Z9R#7gc>o@o6?Ev2LHmjE!Gh*eZ#o#a?8 zp?Eu}nR)1ze?5nvI(}@=-F1J)hwsUEuA+&oM2T+01+9}o)td_y+;xMJ9E_s*Ta>bDKoPd+)@Ic+||;1f_V?9ShTL3Ps50B11|SZV9o%+0)~4RWEZ`W&y{;0o-R-&rTWL7ND_zgCRTg=a<^(GBU z8mQta_l|Zwy+hKvbN{7>#T(~7*ybl97?^dRBI3M8Z4&%c{b=}~^(yo#S_+#)nV2)j zldAJZv&Ulgv7Nb9H_((j3HQDvnEV6$GW(=ivTev~?F0so3ly~*1@}YLnMjl9M;L1- z)YNB=zyqY5Eg9`r7%D1e!NBzh+Ja+~j==Pt zgjO-`A;m{wFs?4gy@xPehhMH}kRM9LaF=GO*!A{ zlzZ)yjL(e4mwApvch*x!4FM6rDhP((s!cB06M-U6XO^-$!-1GoF6t^+?85DYs^!sj zrhj;B_TQ1#Wzd$pj(vHqkHnC?P+uhuzS3CVOTxAfK^fxZXw`(7-5IyUWwYev`fRqG zH$`P>R{s6kG=$`V6_*_5H#*jDYB4Tup8n=NM26&X!0b(r)U``S6BsQ55WldsNdY(D z=jesZq$(S6+5~9{?*dXC86Gs02vuOp zakxqNTRkR4iNN^)##Rk8%Bkd?EOt0i_LnUBqEL}n2Wq0n?Wf<|dvN-fb(tJt?~XMW znjd4(WmUyvYCj3G7G^Ql})ZFu09urGVQ#z1D+nxG+EXdLA|> zX>ZZKFcsHMhmpmDszQD3FqKGZ;(CeT6z(KIG>9~S`Yh%^-fR@RMQXP_Wbm4-m2COO zkXhp5syB|^;(v98_K)ubwDliSTad@A!)o{uZWhl$;C&6)P>cw_UWdx@H|;1ZI7xMo zEy>`?turMpW`iLonCt>-yynJBE5dL5wf0;^``-4qS6f3wq&ky{Eu+EpVIn3PL_!!f zY6HE-DzP|X?jD&|(`8W0()l1u+gGcBO~~JRtN7bb2a)f1bxz+mJ4S+bno4P)k=g`PDfp`$f^4Z#_Gb;AV3)vWx8}>)iZkNT z=h9x)lA0^u@YY>-Kl!EXhxo@D_oTheLwj+{c?(iyO|_uS!sRshTNK0n4f0HVm7zS7 z&PIv_Ez2%o=PbsqP*EZ-+raoSA0P%_df>?Rqto@PCf~B_=N&(Dl)VuBEv!w9zgdn6 z#U>HR^-1Ct)D|c?%&BC`lj5?CJt@DlyR+a1wPijidMkezd~@ab(0y<4BtO`m*m%z? zq*mT?8b)h@sM$K`Too{iwAP_B08%TVPL+95L#3-?)o~<&a6};U&Zy1aJz#x)cX$2A zGang$_nmXwq)~hw!55Tv?hzQGj%|XZcpyb0B4(&s_T;B}1jTHsB#n9%VrDok^t0uh zwY9Fz(V17bzxx|9Zr;Vy6V7~w9Q+u!ejgLs1iZFp5q<;{_-5bMS}g=iSYETe(`GK{ zx*RG8%cC%L$M`}D_+a?my8PEe+JNbuCk_mqeu(y+|LCivR<0V4!!c45X95BK3&nN~ zApovuaE$G0Z%@jWc53to5cr1~EXNEJ|p}MEYRh`H9(5t9H%yo2{D&}0+$WBTM9#vN-Ck8Tm zP~T>K_E^^s54}ds#zpJdquTdk3-c6*wLSl5}V2nY6p#{Y1w5G+b z%{om+xhS7zcFP?`Nug47-(V)XwS#Fnfc)~^ZEMGV`q%kQ-nj@vvK_6v4W=iERG5Js zf$Qo8lmRrE@8$_2C7IWj;+J)9na&gC1#ZaxJ^fwjxoyXJfZz*kp z1hI)XkB)sy!F}##BB}y%F3N=4tzb%wET>q)E-RUSr7MDwYMdPRXuIy2zKwV3mdggk z)%S-kR5mnxLuuoGL#c12!eziO3(>YyFOzjPJImm*JIVpCI~jIoMPfsgRXRH6Uz86D zoi`2HF~#xNuCG@-jWU1TdGucrqoI+PCsXgPZ^A7LHikf~e+f6^-X2#v>+o{*Szj?` ziu1z4QcAw6maxSIk(Y&UO?vCsWrsxbHjJozcQOmo){pec5^ zGZE|LD{OiX&t`IV<@9>l4Z!wl&9Em0?=%d0>c_#3yLLW6o`cX2P}+na4QUpVNZ2D} zGBDmC;VVm6#fUiPa>z=BP~IpFON$XO(_NJi+Wqsdi#{1@7&-O+1JLaHBTIfk5!pmg zjxORpPTIieSWbrQKv5lry>K-h5@h^6k*=~yqbLWR;Y7feDsU7U_=iRb1Fk%PjJNgo&nV%ktDd9SJSZ%saz~Q9$nQWIy-dS{c zBD!$5$1mxx>29C8Gko-=Z}>NDa<0(Mee#aK%qa9~^%6|Pp!x$%@Fy@p>Ap^1`K%tcqgae{7uU@BC*R(&`^nS{^Up~%Z@PB;&06yv)GX z<35E`b(5)F!eHUJgc&p6&JLO~#fZmuLm+z6y*Ew%+j+MC9liR&m#(` z>qsLti9aJi5Dj~mN@(GOrF`AFVy@&N1J;Qn({phRm?}FHl z<|8U}A%fWlL3AF4ngef#f_#Obpzsi`hZq$y3rqhTt?A3^Iz8c!cyZhNX#AhR_01;47%K9E};RTD4zO5r>UJV@|Cv zXvDY;=7F9pVMIR%LAz&eo&EW;S9}{s;%iC9RGDHow?IuI{O_|c8CKSW2y~`$Mb+h3 z<#LA3gx9X}4{}8y&Nqs>w{Va+o;&x zQ}FQdj(V((N~!Ou8z4E7_DCevsk5maHe1G#GwQfvS<`hQm{&FhdKVU!C<6OcYtOS+ zZIv~(a%4?y!bS%5w=sa+o_5!i0aB8aOJp`i*@Y`uKnpEAvq=wBDE7U1l-Vzi_ zy2cS|l&p#ml=u3srIyXn z-3#~5-!{5?KkxASeaB(s4+yBhi{>Fsf@dLswXR=;TtT~C*3OEtQ=kd6WoZXrl;rnV z;{O5tTmZ*g=NPtU*Sl>EaPMu?o>{izbxM_E+k&}uHG{hRzP2$2e-nMep6IUE6?eU^fstPZQ#3Hvz<`?g8W_SCViE`q ztd=WiDj(rW6`V-G%891LdLCEKv|AP<*HJzgTz>Z3B6O>_(BNCW^X+e0kKQ_&(BMGZ zBunXapA+zqg393I5z@m5ew4$ql#i>+sFW;MmgD9pD?0nb$S6MBg$_VaIPHrKF(2II z5%$j$jf7=J!gXh1tdT^*qrL$&C$;88#*8@;idjS^h0Po;$&xp?c3vS)KEL;& zC$8)}|Hb~BY4B4*3Nnp}zuYN&{{B(ToDZllO2s}UK*-C(t23-U=}tf2Z87RA)}qoP zNwV{Dzu+?h9%unD`{xT*9+~$kb1bXz-_Vy|tUUQRxnX)${hDeW1SYW;AjJAg-4&Ew zkQq(dSU}n1G?Znfp0M9$D2%VJf*C&?<#alJGJ?&vL==D;Ktm{*7H%*^5yrw2>YgNgfH^p z5Q%aE2oXU|l8*_~J2)M3n9#x-Ll{64CYK~H70W_WK``N}L`}G92LgJ)kUoC!zBe#< zCjG7_?&uRKetmu+f}9^)MQvysn)t6VFcF^k@-IMOtc7It%hh?CNMzF+1!9XKlJa?E~qfcd>GKH!byiplq=x_e>pJx5-hbHxWMdYOKT@BI4657NwVCp0c zkRBuVQmW;bPEUj*vHLW(u1r9mlZn{k2n+B@R(oLX6@^RVb}M^UO?vO%1AAtD0036E zlPEvKZJaMC)E$$n?8%ck1E@=8&^dz##)pUL%R z#zU8l8f?+-`rDhr+G!G zuE(!cWfHDTl};JAURx)xoHlJm|Gn>x7g2`2=v)uam%)exsa7dQ!_YaMukET)LPF1UbS_0g)B061-fNMsX>;IvJ%;BP@&EXt$Lw7tg!if{Yq1s@>f24Yin_QFRl+Ea z=$(4MOxzta6dWbV3}7ws-R_JiAKy26qHHC3uYcw-S%eRzdBY zodg0yxEQYK({<`xL2imI>E>9S3SES&kE>lb!ywedE#@Vk^WNy6f331uxZZDHM4dYc z$U#s8rT+j=6p3ab@FM7c2pd6FOzboYIW9vqA(Of!Y>p(r5#+p}>4*FN-{m(Sv>koC zap+~^eWkk+QF!(!LM!JvQX6+Cp-F=KmLGuA2C!$LA>p@bDiUWV(alrG#JF=3`*sk( zBjI~+`rSKb%-m*sG5SN~dgH${)6-#ia8;mQ_#@mTxeXqbO8XIPI_SnV5w4KC%UO=p zC5~}aT^3Qu<6sMudSSQ;$oSz8^?dUs`0g=ZIrm-o%YNX+2hQSW_0KBW?JgqJ%mqyA zFj_y7a0RtVxSF`HEcH8_R!5}Asx$FIzc3q!qDGDY!rb2M+oRFWPoIenF8``IIYwAIV!6sY4GN_ZemR}_%`01)ORL~ zT_hNahNi3+o>>D;<*<7p=pKZ+hBSdM?yHi79R`ZVhjF**lvLDwriLkUvsl@f*lA;W z^qCv<)%G729y{{Sg@^y$IDLrvr0&IbLPHy&m3IfRowuDZn8CXOS9y9O28%AFHE=q0 z`Lf7s=}d|Zp#r`OWZd)4fBoZ`b1!v0ley6U^)owveixn#KSiSdgwlV8ss6eu89o4xU^&3|IB`Mibg3{yOR? z0?a_b=V6Evgz7Y@y<3=*YDHyhS?bj#)J(5Fs1yLx0kA8&XE#6({Vs289%tPid4B#0 zc%c+-<$%8D8d5W7CfN*zNk&mf46dct%S*XJT~>B6qAJ@Y>WV7R)fv>qLv^)+=&H_d z`p1e5hvg-B4aF?-@ zNW#|uAjQ8vDYXhiKaX-E4{RrW>UodC5H=A(NGn)JYT~Y~0s=!s0u7X~4NQ*O%NEk`RE1;@kCrY|h6oAqDye9dlS;BirtS7c4>Gr(oP|HgZl8 zsbu_ldLc%K62>k7F45NjJtdwAE4bnw6)zh0c!L$LMwT}e^yM>ne28z}o?EwmdgswO zPd@vJ_x(4vJn#{Gf09UlkVZd+r;O+8fpYO)0t6qVp)0}Vw|V2)Qg>FH>S6jd*qS)XxQ3K)Og3tf4X~qTH1K~%O4Y}HDf96&)4I*@B%=xL}l=n!&PlSQ@P~T zaEk_yK#`0~nW{=M*Ts_7YU`qr&F%3|!jGJM1Y58-KK!3`5Isd~lPp4-B^U`i3D+@1 zm!Rq@DNVeRVX62=VJMSQaZPz?u&AG0gOjhUaM%A{<~@IQ<(&_{LEbdMfsWz|90)}R z`mbpHItU&_0EIl-gS4wCJFZvxCETuxzSElSDY6{mY!_Q~qv8FfY(ehF|Lj@!NdL5d z&3nK3@-qT*hFl$Bd4trj4ldm_wg2(TF0aRzBfQ?cwqV+8pcFQ2N zek*l=#51^amP|w*x0}^^nbofe3)H`&11RWT{P5P~+g^|@AG2`6kdbY4)kkm(#t>lD z`6bbOgo^JOo+UB(=OAc4Tt{FGbS>J5`yF+W;x8e0y zCi7l8u%5k_^5GKM!7z+@g77m$o6_tvZx|k`!3LA<- zXULt^X~JPCP}K(QM#7FI%MQ(&y=B1q7jtZR6auhtVPc!;B~lZA903Z#Rmj-%h5=L- zQL{|p2(K7bn7UFyxx(4$jIYKIH*VQb#ttE+w0+cPca`QgC?9|HI1GP_fOZ%k|7gcy z>X8QQI9y-Fxx2#dpx%`9cr9FEpqS_DjUFe9PP$&DpLU^b6AnEU`FnHmT6w}pj(-&} z{0|9Koz_B7-3Af}4N3L$kO9;isp!={p@OXqm@8IIvCAn?_e`iU*u_RZ(6oKt7mG|E z!@u5XQhj{$O&MoDr7<+0j;};)WUs4H66h?(9=4`OXAy}F zLAi>}7J4F*8`8*QFY>;a`SJ7}_2tp|1J8N?Qo{7zLx9G3lVBdb9XHizi{KO<*zsd> zc`+dmsdPPsZdrn-4J6#E;WY~3qwKlcEvtvD{AF!ZY4oA4VP>>pA=1VJdeQkhFkV~_ zT7xQfRO^c+%rb{oX|u|eB4(vzA5W;srw`XGPDxB8E>Q{1|MmPUdcgeuDFLuFHFN&1 zZ%>~Zj<$&C_;HEFdR#WW(~#AwwJKH89T9mFva0Ag{%#+-<)C6+{f0SHw zqSj{tF9QB5j}_J36qE};!);5P9c_03`x41Wy)LX{TYtE2(c;*!K2 zL1oDux7wr5u6)$c6~be?G%Lb|us2##8js8oe zlfeCO8wwh_O;r39Jx<0QDw!cX4-&8|XuO-vFYshhcY$y9%eir1Qs0^T1V5YtKvVSS zD)E+XpYNB(?kQ_iz9(PLHY10s>OhG`I)DY%KgYO&O5GlrPR^Ix%tBU@9}|@Yp_u$9 z_&UA>ZYl8iY3x))@ym{97GK-_pJS8`0vJnq7HShdORK+H4>WEW95bkgTtSnLoSD

^Oqavp0%@(j4ZZbFzy}BUqU{7Ar{AYCOjxA0D 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%0Dk+(o#JUmJiC_q< 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
#2`&7u5z=))>IxdmUI{^u5bHcHyt;0dMeQpU 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 Date: Thu, 27 Apr 2017 11:04:57 +0200 Subject: [PATCH 106/619] Add parsing for InternalPercentilesBucket (#24330) --- .../percentiles/ParsedPercentiles.java | 9 +- .../percentile/ParsedPercentilesBucket.java | 88 +++++++++++++++++++ .../InternalAggregationTestCase.java | 3 + .../AbstractPercentilesTestCase.java | 8 +- .../hdr/InternalHDRPercentilesTests.java | 4 +- .../InternalPercentilesBucketTests.java | 28 ++++++ 6 files changed, 133 insertions(+), 7 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/ParsedPercentilesBucket.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentiles.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentiles.java index eee058fc2f861..a96fb2cd13cc3 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentiles.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentiles.java @@ -33,8 +33,8 @@ public abstract class ParsedPercentiles extends ParsedAggregation implements Iterable { - private final Map percentiles = new LinkedHashMap<>(); - private final Map percentilesAsString = new HashMap<>(); + protected final Map percentiles = new LinkedHashMap<>(); + protected final Map percentilesAsString = new HashMap<>(); private boolean keyed; @@ -130,7 +130,6 @@ protected static void declarePercentilesFields(ObjectParser 0) { @@ -140,6 +139,8 @@ protected static void declarePercentilesFields(ObjectParser percent : percentiles.entrySet()) { + double value = percent.getValue(); + boolean hasValue = !(Double.isNaN(value)); + Double key = percent.getKey(); + builder.field(Double.toString(key), hasValue ? value : null); + String valueAsString = percentilesAsString.get(key); + if (hasValue && valueAsString != null) { + builder.field(key + "_as_string", valueAsString); + } + } + builder.endObject(); + return builder; + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedPercentilesBucket.class.getSimpleName(), true, ParsedPercentilesBucket::new); + + static { + ParsedPercentiles.declarePercentilesFields(PARSER); + } + + public static ParsedPercentilesBucket fromXContent(XContentParser parser, String name) throws IOException { + ParsedPercentilesBucket aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index 05a4f0eef8221..d4c293a0e8bb7 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -63,6 +63,8 @@ import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.InternalBucketMetricValue; import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.ParsedBucketMetricValue; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.percentile.ParsedPercentilesBucket; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.percentile.PercentilesBucketPipelineAggregationBuilder; import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.ParsedStatsBucket; import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.StatsBucketPipelineAggregationBuilder; import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.extended.ExtendedStatsBucketPipelineAggregationBuilder; @@ -99,6 +101,7 @@ static List getNamedXContents() { namedXContents.put(InternalHDRPercentileRanks.NAME, (p, c) -> ParsedHDRPercentileRanks.fromXContent(p, (String) c)); namedXContents.put(InternalTDigestPercentiles.NAME, (p, c) -> ParsedTDigestPercentiles.fromXContent(p, (String) c)); namedXContents.put(InternalTDigestPercentileRanks.NAME, (p, c) -> ParsedTDigestPercentileRanks.fromXContent(p, (String) c)); + namedXContents.put(PercentilesBucketPipelineAggregationBuilder.NAME, (p, c) -> ParsedPercentilesBucket.fromXContent(p, (String) c)); namedXContents.put(MinAggregationBuilder.NAME, (p, c) -> ParsedMin.fromXContent(p, (String) c)); namedXContents.put(MaxAggregationBuilder.NAME, (p, c) -> ParsedMax.fromXContent(p, (String) c)); namedXContents.put(SumAggregationBuilder.NAME, (p, c) -> ParsedSum.fromXContent(p, (String) c)); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java index 0223db0359994..72167dac6513f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java @@ -26,6 +26,7 @@ import org.junit.Before; import java.io.IOException; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -39,7 +40,7 @@ public abstract class AbstractPercentilesTestCase randomCdfValues = randomSubsetOf(randomIntBetween(1, 7), 0.01d, 0.05d, 0.25d, 0.50d, 0.75d, 0.95d, 0.99d); double[] percents = new double[randomCdfValues.size()]; for (int i = 0; i < randomCdfValues.size(); i++) { percents[i] = randomCdfValues.get(i); } + if (sorted) { + Arrays.sort(percents); + } return percents; } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesTests.java index 908a1db18c6d8..07c74d78b082e 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesTests.java @@ -23,8 +23,8 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.metrics.percentiles.InternalPercentilesTestCase; -import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentiles; +import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.Arrays; @@ -70,7 +70,7 @@ protected Class implementationClass() { } public void testIterator() { - final double[] percents = randomPercents(); + final double[] percents = randomPercents(false); final double[] values = new double[frequently() ? randomIntBetween(1, 10) : 0]; for (int i = 0; i < values.length; ++i) { values[i] = randomDouble(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucketTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucketTests.java index 0ea28f9838474..3f29caae84d25 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucketTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucketTests.java @@ -22,9 +22,11 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import java.io.IOException; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -66,6 +68,22 @@ protected Writeable.Reader instanceReader() { return InternalPercentilesBucket::new; } + @Override + protected final void assertFromXContent(InternalPercentilesBucket aggregation, ParsedAggregation parsedAggregation) { + assertTrue(parsedAggregation instanceof ParsedPercentilesBucket); + ParsedPercentilesBucket parsedPercentiles = (ParsedPercentilesBucket) parsedAggregation; + + for (Percentile percentile : aggregation) { + Double percent = percentile.getPercent(); + assertEquals(aggregation.percentile(percent), parsedPercentiles.percentile(percent), 0); + // we cannot ensure we get the same as_string output for Double.NaN values since they are rendered as + // null and we don't have a formatted string representation in the rest output + if (Double.isNaN(aggregation.percentile(percent)) == false) { + assertEquals(aggregation.percentileAsString(percent), parsedPercentiles.percentileAsString(percent)); + } + } + } + /** * check that we don't rely on the percent array order and that the iterator returns the values in the original order */ @@ -89,4 +107,14 @@ public void testErrorOnDifferentArgumentSize() { assertEquals("The number of provided percents and percentiles didn't match. percents: [0.1, 0.2, 0.3], percentiles: [0.1, 0.2]", e.getMessage()); } + + public void testParsedAggregationIteratorOrder() throws IOException { + final InternalPercentilesBucket aggregation = createTestInstance(); + final Iterable parsedAggregation = parseAndAssert(aggregation, false); + Iterator it = aggregation.iterator(); + Iterator parsedIt = parsedAggregation.iterator(); + while (it.hasNext()) { + assertEquals(it.next(), parsedIt.next()); + } + } } From 65f90b25e0e2306ed53cad6c01e07875a0000a6c Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Thu, 27 Apr 2017 20:26:10 +1000 Subject: [PATCH 107/619] Pass Context to ConstructingObjectParser's function (#24230) Allow the `Context` to be used in the builder function used within ConstructingObjectParser. This facilitates scenarios where a constructor argument comes from a URL parameter, or from document id. --- .../xcontent/ConstructingObjectParser.java | 34 ++++++++++++++++--- .../ConstructingObjectParserTests.java | 29 +++++++++++++++- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/ConstructingObjectParser.java b/core/src/main/java/org/elasticsearch/common/xcontent/ConstructingObjectParser.java index 95e6d2b97a69d..2b478b9018a3c 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/ConstructingObjectParser.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/ConstructingObjectParser.java @@ -94,7 +94,7 @@ public final class ConstructingObjectParser extends AbstractObje */ private final List constructorArgInfos = new ArrayList<>(); private final ObjectParser objectParser; - private final Function builder; + private final BiFunction builder; /** * The number of fields on the targetObject. This doesn't include any constructor arguments and is the size used for the array backing * the field queue. @@ -130,8 +130,27 @@ public ConstructingObjectParser(String name, Function builder) * allocations. */ public ConstructingObjectParser(String name, boolean ignoreUnknownFields, Function builder) { + this(name, ignoreUnknownFields, (args, context) -> builder.apply(args)); + } + + /** + * Build the parser. + * + * @param name The name given to the delegate ObjectParser for error identification. Use what you'd use if the object worked with + * ObjectParser. + * @param ignoreUnknownFields Should this parser ignore unknown fields? This should generally be set to true only when parsing responses + * from external systems, never when parsing requests from users. + * @param builder A binary function that builds the object from an array of Objects and the parser context. Declare this inline with + * the parser, casting the elements of the array to the arguments so they work with your favorite constructor. The objects in + * the array will be in the same order that you declared the {{@link #constructorArg()}s and none will be null. The second + * argument is the value of the context provided to the {@link #parse(XContentParser, Object) parse function}. If any of the + * constructor arguments aren't defined in the XContent then parsing will throw an error. We use an array here rather than a + * {@code Map} to save on allocations. + */ + public ConstructingObjectParser(String name, boolean ignoreUnknownFields, BiFunction builder) { objectParser = new ObjectParser<>(name, ignoreUnknownFields, null); this.builder = builder; + } /** @@ -148,7 +167,7 @@ public Value apply(XContentParser parser, Context context) { @Override public Value parse(XContentParser parser, Context context) throws IOException { - return objectParser.parse(parser, new Target(parser), context).finish(); + return objectParser.parse(parser, new Target(parser, context), context).finish(); } /** @@ -334,6 +353,12 @@ private class Target { * location of each field so that we can give a useful error message when replaying the queue. */ private final XContentParser parser; + + /** + * The parse context that is used for this invocation. Stored here so that it can be passed to the {@link #builder}. + */ + private Context context; + /** * How many of the constructor parameters have we collected? We keep track of this so we don't have to count the * {@link #constructorArgs} array looking for nulls when we receive another constructor parameter. When this is equal to the size of @@ -358,8 +383,9 @@ private class Target { */ private Value targetObject; - Target(XContentParser parser) { + Target(XContentParser parser, Context context) { this.parser = parser; + this.context = context; } /** @@ -433,7 +459,7 @@ private Value finish() { private void buildTarget() { try { - targetObject = builder.apply(constructorArgs); + targetObject = builder.apply(constructorArgs, context); if (queuedOrderedModeCallback != null) { queuedOrderedModeCallback.accept(targetObject); } diff --git a/core/src/test/java/org/elasticsearch/common/xcontent/ConstructingObjectParserTests.java b/core/src/test/java/org/elasticsearch/common/xcontent/ConstructingObjectParserTests.java index 7f6187800c2e4..3f3e8496b2abe 100644 --- a/core/src/test/java/org/elasticsearch/common/xcontent/ConstructingObjectParserTests.java +++ b/core/src/test/java/org/elasticsearch/common/xcontent/ConstructingObjectParserTests.java @@ -303,6 +303,18 @@ class TestStruct { assertEquals(s.test, "foo"); } + public void testConstructObjectUsingContext() throws IOException { + XContentParser parser = createParser(JsonXContent.jsonXContent, + "{\n" + + " \"animal\": \"dropbear\",\n" + + " \"mineral\": -8\n" + + "}"); + HasCtorArguments parsed = HasCtorArguments.PARSER_INT_CONTEXT.apply(parser, 42); + assertEquals(Integer.valueOf(42), parsed.vegetable); + assertEquals("dropbear", parsed.animal); + assertEquals(-8, parsed.mineral); + } + private static class HasCtorArguments implements ToXContent { @Nullable final String animal; @@ -381,22 +393,37 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws public static final ConstructingObjectParser PARSER = buildParser(true, true); public static final ConstructingObjectParser PARSER_VEGETABLE_OPTIONAL = buildParser(true, false); public static final ConstructingObjectParser PARSER_ALL_OPTIONAL = buildParser(false, false); + public static final List> ALL_PARSERS = unmodifiableList( Arrays.asList(PARSER, PARSER_VEGETABLE_OPTIONAL, PARSER_ALL_OPTIONAL)); + public static final ConstructingObjectParser PARSER_INT_CONTEXT = buildContextParser(); + private static ConstructingObjectParser buildParser(boolean animalRequired, boolean vegetableRequired) { ConstructingObjectParser parser = new ConstructingObjectParser<>( "has_required_arguments", a -> new HasCtorArguments((String) a[0], (Integer) a[1])); parser.declareString(animalRequired ? constructorArg() : optionalConstructorArg(), new ParseField("animal")); parser.declareInt(vegetableRequired ? constructorArg() : optionalConstructorArg(), new ParseField("vegetable")); + declareSetters(parser); + return parser; + } + + private static ConstructingObjectParser buildContextParser() { + ConstructingObjectParser parser = new ConstructingObjectParser<>( + "has_required_arguments", false, (args, ctx) -> new HasCtorArguments((String) args[0], ctx)); + parser.declareString(constructorArg(), new ParseField("animal")); + declareSetters(parser); + return parser; + } + + private static void declareSetters(ConstructingObjectParser parser) { parser.declareInt(HasCtorArguments::setMineral, new ParseField("mineral")); parser.declareInt(HasCtorArguments::setFruit, new ParseField("fruit")); parser.declareString(HasCtorArguments::setA, new ParseField("a")); parser.declareString(HasCtorArguments::setB, new ParseField("b")); parser.declareString(HasCtorArguments::setC, new ParseField("c")); parser.declareBoolean(HasCtorArguments::setD, new ParseField("d")); - return parser; } } From 2fa1c9fff14adbdbbd373c5b39b553c55e3738f7 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Thu, 27 Apr 2017 14:45:44 +0200 Subject: [PATCH 108/619] Provide target allocation id as part of start recovery request (#24333) This makes it possible for the recovery source to verify that it is talking to the shard it thinks it is talking to. Closes #24167 --- .../recovery/PeerRecoverySourceService.java | 22 ++++++++++------ .../recovery/PeerRecoveryTargetService.java | 1 + .../recovery/RecoverySourceHandler.java | 4 +-- .../indices/recovery/RecoveryTarget.java | 5 ---- .../recovery/RecoveryTargetHandler.java | 5 ---- .../recovery/RemoteRecoveryTargetHandler.java | 10 +------ .../recovery/StartRecoveryRequest.java | 26 ++++++++++++------- .../recovery/RecoverySourceHandlerTests.java | 6 +++++ .../recovery/StartRecoveryRequestTests.java | 3 +++ .../index/shard/IndexShardTestCase.java | 5 ++-- 10 files changed, 46 insertions(+), 41 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoverySourceService.java b/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoverySourceService.java index 93de86193b5c9..7191e4517ab09 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoverySourceService.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoverySourceService.java @@ -113,7 +113,14 @@ private RecoveryResponse recover(final StartRecoveryRequest request) throws IOEx throw new DelayRecoveryException("source node has the state of the target shard to be [" + targetShardRouting.state() + "], expecting to be [initializing]"); } - RecoverySourceHandler handler = ongoingRecoveries.addNewRecovery(request, targetShardRouting.allocationId().getId(), shard); + if (request.targetAllocationId().equals(targetShardRouting.allocationId().getId()) == false) { + logger.debug("delaying recovery of {} due to target allocation id mismatch (expected: [{}], but was: [{}])", + request.shardId(), request.targetAllocationId(), targetShardRouting.allocationId().getId()); + throw new DelayRecoveryException("source node has the state of the target shard to have allocation id [" + + targetShardRouting.allocationId().getId() + "], expecting to be [" + request.targetAllocationId() + "]"); + } + + RecoverySourceHandler handler = ongoingRecoveries.addNewRecovery(request, shard); logger.trace("[{}][{}] starting recovery to {}", request.shardId().getIndex().getName(), request.shardId().id(), request.targetNode()); try { return handler.recoverToTarget(); @@ -133,9 +140,9 @@ public void messageReceived(final StartRecoveryRequest request, final TransportC private final class OngoingRecoveries { private final Map ongoingRecoveries = new HashMap<>(); - synchronized RecoverySourceHandler addNewRecovery(StartRecoveryRequest request, String targetAllocationId, IndexShard shard) { + synchronized RecoverySourceHandler addNewRecovery(StartRecoveryRequest request, IndexShard shard) { final ShardRecoveryContext shardContext = ongoingRecoveries.computeIfAbsent(shard, s -> new ShardRecoveryContext()); - RecoverySourceHandler handler = shardContext.addNewRecovery(request, targetAllocationId, shard); + RecoverySourceHandler handler = shardContext.addNewRecovery(request, shard); shard.recoveryStats().incCurrentAsSource(); return handler; } @@ -181,20 +188,19 @@ private final class ShardRecoveryContext { * Adds recovery source handler if recoveries are not delayed from starting (see also {@link #delayNewRecoveries}. * Throws {@link DelayRecoveryException} if new recoveries are delayed from starting. */ - synchronized RecoverySourceHandler addNewRecovery(StartRecoveryRequest request, String targetAllocationId, IndexShard shard) { + synchronized RecoverySourceHandler addNewRecovery(StartRecoveryRequest request, IndexShard shard) { if (onNewRecoveryException != null) { throw onNewRecoveryException; } - RecoverySourceHandler handler = createRecoverySourceHandler(request, targetAllocationId, shard); + RecoverySourceHandler handler = createRecoverySourceHandler(request, shard); recoveryHandlers.add(handler); return handler; } - private RecoverySourceHandler createRecoverySourceHandler(StartRecoveryRequest request, String targetAllocationId, - IndexShard shard) { + private RecoverySourceHandler createRecoverySourceHandler(StartRecoveryRequest request, IndexShard shard) { RecoverySourceHandler handler; final RemoteRecoveryTargetHandler recoveryTarget = - new RemoteRecoveryTargetHandler(request.recoveryId(), request.shardId(), targetAllocationId, transportService, + new RemoteRecoveryTargetHandler(request.recoveryId(), request.shardId(), transportService, request.targetNode(), recoverySettings, throttleTime -> shard.recoveryStats().addThrottleTime(throttleTime)); Supplier currentClusterStateVersionSupplier = () -> clusterService.state().getVersion(); handler = new RecoverySourceHandler(shard, recoveryTarget, request, currentClusterStateVersionSupplier, diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java b/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java index f449f9ffbe42d..400395d1b20f5 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java @@ -329,6 +329,7 @@ private StartRecoveryRequest getStartRecoveryRequest(final RecoveryTarget recove request = new StartRecoveryRequest( recoveryTarget.shardId(), + recoveryTarget.indexShard().routingEntry().allocationId().getId(), recoveryTarget.sourceNode(), clusterService.localNode(), metadataSnapshot, diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java b/core/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java index 40f9f7f74f895..c53a46ef222fe 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java @@ -78,7 +78,6 @@ public class RecoverySourceHandler { protected final Logger logger; // Shard that is going to be recovered (the "source") private final IndexShard shard; - private final String indexName; private final int shardId; // Request containing source and target node information private final StartRecoveryRequest request; @@ -116,7 +115,6 @@ public RecoverySourceHandler(final IndexShard shard, RecoveryTargetHandler recov this.request = request; this.currentClusterStateVersionSupplier = currentClusterStateVersionSupplier; this.delayNewRecoveries = delayNewRecoveries; - this.indexName = this.request.shardId().getIndex().getName(); this.shardId = this.request.shardId().id(); this.logger = Loggers.getLogger(getClass(), nodeSettings, request.shardId(), "recover to " + request.targetNode().getName()); this.chunkSizeInBytes = fileChunkSizeInBytes; @@ -443,7 +441,7 @@ public void finalizeRecovery() { StopWatch stopWatch = new StopWatch().start(); logger.trace("finalizing recovery"); cancellableThreads.execute(() -> { - shard.markAllocationIdAsInSync(recoveryTarget.getTargetAllocationId()); + shard.markAllocationIdAsInSync(request.targetAllocationId()); recoveryTarget.finalizeRecovery(shard.getGlobalCheckpoint()); }); diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java index b12006bbd3ce5..7b48edfa0424f 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java @@ -368,11 +368,6 @@ public void finalizeRecovery(final long globalCheckpoint) { indexShard.finalizeRecovery(); } - @Override - public String getTargetAllocationId() { - return indexShard().routingEntry().allocationId().getId(); - } - @Override public void ensureClusterStateVersion(long clusterStateVersion) { ensureClusterStateVersionCallback.handle(clusterStateVersion); diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTargetHandler.java b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTargetHandler.java index bdace02d218ba..dec6387744820 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTargetHandler.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTargetHandler.java @@ -77,9 +77,4 @@ void receiveFileInfo(List phase1FileNames, void writeFileChunk(StoreFileMetaData fileMetaData, long position, BytesReference content, boolean lastChunk, int totalTranslogOps) throws IOException; - /*** - * @return the allocation id of the target shard. - */ - String getTargetAllocationId(); - } diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RemoteRecoveryTargetHandler.java b/core/src/main/java/org/elasticsearch/indices/recovery/RemoteRecoveryTargetHandler.java index 959522d297db2..f21d61f2f718d 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RemoteRecoveryTargetHandler.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RemoteRecoveryTargetHandler.java @@ -50,14 +50,10 @@ public class RemoteRecoveryTargetHandler implements RecoveryTargetHandler { private final AtomicLong bytesSinceLastPause = new AtomicLong(); private final Consumer onSourceThrottle; - private String targetAllocationId; - public RemoteRecoveryTargetHandler(long recoveryId, ShardId shardId, String targetAllocationId, TransportService transportService, + public RemoteRecoveryTargetHandler(long recoveryId, ShardId shardId, TransportService transportService, DiscoveryNode targetNode, RecoverySettings recoverySettings, Consumer onSourceThrottle) { - this.targetAllocationId = targetAllocationId; this.transportService = transportService; - - this.recoveryId = recoveryId; this.shardId = shardId; this.targetNode = targetNode; @@ -164,8 +160,4 @@ public void writeFileChunk(StoreFileMetaData fileMetaData, long position, BytesR throttleTimeInNanos), fileChunkRequestOptions, EmptyTransportResponseHandler.INSTANCE_SAME).txGet(); } - @Override - public String getTargetAllocationId() { - return targetAllocationId; - } } diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/StartRecoveryRequest.java b/core/src/main/java/org/elasticsearch/indices/recovery/StartRecoveryRequest.java index 46a29d654640c..a2a578cc7229a 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/StartRecoveryRequest.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/StartRecoveryRequest.java @@ -21,10 +21,8 @@ import org.elasticsearch.Version; import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.routing.RecoverySource; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.index.seqno.SequenceNumbers; import org.elasticsearch.index.seqno.SequenceNumbersService; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.store.Store; @@ -39,6 +37,7 @@ public class StartRecoveryRequest extends TransportRequest { private long recoveryId; private ShardId shardId; + private String targetAllocationId; private DiscoveryNode sourceNode; private DiscoveryNode targetNode; private Store.MetadataSnapshot metadataSnapshot; @@ -51,15 +50,17 @@ public StartRecoveryRequest() { /** * Construct a request for starting a peer recovery. * - * @param shardId the shard ID to recover - * @param sourceNode the source node to remover from - * @param targetNode the target node to recover to - * @param metadataSnapshot the Lucene metadata - * @param primaryRelocation whether or not the recovery is a primary relocation - * @param recoveryId the recovery ID - * @param startingSeqNo the starting sequence number + * @param shardId the shard ID to recover + * @param targetAllocationId the allocation id of the target shard + * @param sourceNode the source node to remover from + * @param targetNode the target node to recover to + * @param metadataSnapshot the Lucene metadata + * @param primaryRelocation whether or not the recovery is a primary relocation + * @param recoveryId the recovery ID + * @param startingSeqNo the starting sequence number */ public StartRecoveryRequest(final ShardId shardId, + final String targetAllocationId, final DiscoveryNode sourceNode, final DiscoveryNode targetNode, final Store.MetadataSnapshot metadataSnapshot, @@ -68,6 +69,7 @@ public StartRecoveryRequest(final ShardId shardId, final long startingSeqNo) { this.recoveryId = recoveryId; this.shardId = shardId; + this.targetAllocationId = targetAllocationId; this.sourceNode = sourceNode; this.targetNode = targetNode; this.metadataSnapshot = metadataSnapshot; @@ -83,6 +85,10 @@ public ShardId shardId() { return shardId; } + public String targetAllocationId() { + return targetAllocationId; + } + public DiscoveryNode sourceNode() { return sourceNode; } @@ -108,6 +114,7 @@ public void readFrom(StreamInput in) throws IOException { super.readFrom(in); recoveryId = in.readLong(); shardId = ShardId.readShardId(in); + targetAllocationId = in.readString(); sourceNode = new DiscoveryNode(in); targetNode = new DiscoveryNode(in); metadataSnapshot = new Store.MetadataSnapshot(in); @@ -124,6 +131,7 @@ public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeLong(recoveryId); shardId.writeTo(out); + out.writeString(targetAllocationId); sourceNode.writeTo(out); targetNode.writeTo(out); metadataSnapshot.writeTo(out); diff --git a/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java b/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java index e424eb399329e..468a5a5500e5c 100644 --- a/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java +++ b/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java @@ -99,6 +99,7 @@ public void testSendFiles() throws Throwable { final RecoverySettings recoverySettings = new RecoverySettings(settings, service); final StartRecoveryRequest request = new StartRecoveryRequest( shardId, + null, new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT), new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT), null, @@ -155,6 +156,7 @@ public void testSendSnapshotSendsOps() throws IOException { final long startingSeqNo = randomBoolean() ? SequenceNumbersService.UNASSIGNED_SEQ_NO : randomIntBetween(0, 16); final StartRecoveryRequest request = new StartRecoveryRequest( shardId, + null, new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT), new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT), null, @@ -223,6 +225,7 @@ public void testHandleCorruptedIndexOnSendSendFiles() throws Throwable { final StartRecoveryRequest request = new StartRecoveryRequest( shardId, + null, new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT), new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT), null, @@ -292,6 +295,7 @@ public void testHandleExceptinoOnSendSendFiles() throws Throwable { final StartRecoveryRequest request = new StartRecoveryRequest( shardId, + null, new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT), new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT), null, @@ -358,6 +362,7 @@ public void testThrowExceptionOnPrimaryRelocatedBeforePhase1Completed() throws I final StartRecoveryRequest request = new StartRecoveryRequest( shardId, + null, new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT), new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT), null, @@ -417,6 +422,7 @@ public void testWaitForClusterStateOnPrimaryRelocation() throws IOException, Int final StartRecoveryRequest request = new StartRecoveryRequest( shardId, + null, new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT), new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT), null, diff --git a/core/src/test/java/org/elasticsearch/indices/recovery/StartRecoveryRequestTests.java b/core/src/test/java/org/elasticsearch/indices/recovery/StartRecoveryRequestTests.java index c176b7c3d291c..6981ebdae4d22 100644 --- a/core/src/test/java/org/elasticsearch/indices/recovery/StartRecoveryRequestTests.java +++ b/core/src/test/java/org/elasticsearch/indices/recovery/StartRecoveryRequestTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.Version; import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.common.io.stream.OutputStreamStreamOutput; import org.elasticsearch.index.seqno.SequenceNumbers; @@ -44,6 +45,7 @@ public void testSerialization() throws Exception { final Version targetNodeVersion = randomVersion(random()); final StartRecoveryRequest outRequest = new StartRecoveryRequest( new ShardId("test", "_na_", 0), + UUIDs.base64UUID(), new DiscoveryNode("a", buildNewFakeTransportAddress(), emptyMap(), emptySet(), targetNodeVersion), new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), targetNodeVersion), Store.MetadataSnapshot.EMPTY, @@ -63,6 +65,7 @@ public void testSerialization() throws Exception { inRequest.readFrom(in); assertThat(outRequest.shardId(), equalTo(inRequest.shardId())); + assertThat(outRequest.targetAllocationId(), equalTo(inRequest.targetAllocationId())); assertThat(outRequest.sourceNode(), equalTo(inRequest.sourceNode())); assertThat(outRequest.targetNode(), equalTo(inRequest.targetNode())); assertThat(outRequest.metadataSnapshot().asMap(), equalTo(inRequest.metadataSnapshot().asMap())); diff --git a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java index 65576dcf0a2b5..95013ee964999 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java @@ -405,6 +405,7 @@ protected final void recoverReplica(final IndexShard replica, } replica.prepareForIndexRecovery(); final RecoveryTarget recoveryTarget = targetSupplier.apply(replica, pNode); + final String targetAllocationId = recoveryTarget.indexShard().routingEntry().allocationId().getId(); final Store.MetadataSnapshot snapshot = getMetadataSnapshotOrEmpty(replica); final long startingSeqNo; @@ -414,8 +415,8 @@ protected final void recoverReplica(final IndexShard replica, startingSeqNo = SequenceNumbersService.UNASSIGNED_SEQ_NO; } - final StartRecoveryRequest request = - new StartRecoveryRequest(replica.shardId(), pNode, rNode, snapshot, false, 0, startingSeqNo); + final StartRecoveryRequest request = new StartRecoveryRequest(replica.shardId(), targetAllocationId, + pNode, rNode, snapshot, false, 0, startingSeqNo); final RecoverySourceHandler recovery = new RecoverySourceHandler( primary, recoveryTarget, From 2facc42a5576ad22104db3043cd6d826b0094ad1 Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Thu, 27 Apr 2017 09:36:26 -0400 Subject: [PATCH 109/619] Change snapshot status error to use generic SnapshotException (#24355) Changes the snapshot status read exception from the (misleading) IndexShardRestoreFailedException to the generic SnapshotException Closes #24225 --- .../repositories/blobstore/BlobStoreRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index b1fdc20117ba4..a0d7ece8c0df5 100644 --- a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -954,7 +954,7 @@ public BlobStoreIndexShardSnapshot loadSnapshot() { try { return indexShardSnapshotFormat(version).read(blobContainer, snapshotId.getUUID()); } catch (IOException ex) { - throw new IndexShardRestoreFailedException(shardId, "failed to read shard snapshot file", ex); + throw new SnapshotException(metadata.name(), snapshotId, "failed to read shard snapshot file for " + shardId, ex); } } From 06aeb2307fc97cb5eaef8e59911f2e1d1887606e Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 27 Apr 2017 10:36:28 -0400 Subject: [PATCH 110/619] Docs: remove experimental tag from update-by-query We meant to remove it in #23621 but we removed it from the documentation for the java client but not the rest documetnaiton. --- docs/reference/docs/update-by-query.asciidoc | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/reference/docs/update-by-query.asciidoc b/docs/reference/docs/update-by-query.asciidoc index d6fb08a2adbb8..23ce6589f3d58 100644 --- a/docs/reference/docs/update-by-query.asciidoc +++ b/docs/reference/docs/update-by-query.asciidoc @@ -1,8 +1,6 @@ [[docs-update-by-query]] == Update By Query API -experimental[The update-by-query API is new and should still be considered experimental. The API may change in ways that are not backwards compatible] - The simplest usage of `_update_by_query` just performs an update on every document in the index without changing the source. This is useful to <> or some other online From b4cff198429dad2912b640fe6f7564ee157b2890 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Thu, 27 Apr 2017 17:29:15 +0200 Subject: [PATCH 111/619] Run gradle vagrantUpdateVersions to add version 5.3.2 --- qa/vagrant/versions | 1 + 1 file changed, 1 insertion(+) diff --git a/qa/vagrant/versions b/qa/vagrant/versions index 51a86ee9f0964..996b8f7e8a29e 100644 --- a/qa/vagrant/versions +++ b/qa/vagrant/versions @@ -8,3 +8,4 @@ 5.2.2 5.3.0 5.3.1 +5.3.2 From cdcc75dd2a5bf2adf4f98bf789688f70d1a2bd33 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 27 Apr 2017 11:27:29 -0700 Subject: [PATCH 112/619] Plugins: Add support for platform specific plugins (#24265) This commit adds support for plugins having a platform specific variant. It also adds unit tests for all official and maven urls. --- .../org/elasticsearch/plugins/Platforms.java | 3 +- .../plugins/InstallPluginCommand.java | 74 +++++++-- .../plugins/InstallPluginCommandTests.java | 146 ++++++++++++------ 3 files changed, 163 insertions(+), 60 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/plugins/Platforms.java b/core/src/main/java/org/elasticsearch/plugins/Platforms.java index 62bb32a4e9a1a..91af58ebec46e 100644 --- a/core/src/main/java/org/elasticsearch/plugins/Platforms.java +++ b/core/src/main/java/org/elasticsearch/plugins/Platforms.java @@ -30,8 +30,7 @@ public class Platforms { private static final String PROGRAM_NAME = Constants.WINDOWS ? "controller.exe" : "controller"; - private static final String PLATFORM_NAME = - Platforms.platformName(Constants.OS_NAME, Constants.OS_ARCH); + public static final String PLATFORM_NAME = Platforms.platformName(Constants.OS_NAME, Constants.OS_ARCH); private Platforms() {} diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java index afe4593e62779..dd7e39f7c797c 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java @@ -41,6 +41,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.net.URLDecoder; @@ -214,18 +215,7 @@ void execute(Terminal terminal, String pluginId, boolean isBatch, Environment en /** Downloads the plugin and returns the file it was downloaded to. */ private Path download(Terminal terminal, String pluginId, Path tmpDir) throws Exception { if (OFFICIAL_PLUGINS.contains(pluginId)) { - final String version = Version.CURRENT.toString(); - final String url; - final String stagingHash = System.getProperty(PROPERTY_STAGING_ID); - if (stagingHash != null) { - url = String.format(Locale.ROOT, - "https://staging.elastic.co/%3$s-%1$s/downloads/elasticsearch-plugins/%2$s/%2$s-%3$s.zip", - stagingHash, pluginId, version); - } else { - url = String.format(Locale.ROOT, - "https://artifacts.elastic.co/downloads/elasticsearch-plugins/%1$s/%1$s-%2$s.zip", - pluginId, version); - } + final String url = getElasticUrl(terminal, getStagingHash(), Version.CURRENT, pluginId, Platforms.PLATFORM_NAME); terminal.println("-> Downloading " + pluginId + " from elastic"); return downloadZipAndChecksum(terminal, url, tmpDir); } @@ -233,8 +223,7 @@ private Path download(Terminal terminal, String pluginId, Path tmpDir) throws Ex // now try as maven coordinates, a valid URL would only have a colon and slash String[] coordinates = pluginId.split(":"); if (coordinates.length == 3 && pluginId.contains("/") == false) { - String mavenUrl = String.format(Locale.ROOT, "https://repo1.maven.org/maven2/%1$s/%2$s/%3$s/%2$s-%3$s.zip", - coordinates[0].replace(".", "/") /* groupId */, coordinates[1] /* artifactId */, coordinates[2] /* version */); + String mavenUrl = getMavenUrl(terminal, coordinates, Platforms.PLATFORM_NAME); terminal.println("-> Downloading " + pluginId + " from maven central"); return downloadZipAndChecksum(terminal, mavenUrl, tmpDir); } @@ -253,6 +242,60 @@ private Path download(Terminal terminal, String pluginId, Path tmpDir) throws Ex return downloadZip(terminal, pluginId, tmpDir); } + // pkg private so tests can override + String getStagingHash() { + return System.getProperty(PROPERTY_STAGING_ID); + } + + /** Returns the url for an official elasticsearch plugin. */ + private String getElasticUrl(Terminal terminal, String stagingHash, Version version, + String pluginId, String platform) throws IOException { + final String baseUrl; + if (stagingHash != null) { + baseUrl = String.format(Locale.ROOT, + "https://staging.elastic.co/%s-%s/downloads/elasticsearch-plugins/%s", version, stagingHash, pluginId); + } else { + baseUrl = String.format(Locale.ROOT, + "https://artifacts.elastic.co/downloads/elasticsearch-plugins/%s", pluginId); + } + final String platformUrl = String.format(Locale.ROOT, "%s/%s-%s-%s.zip", baseUrl, pluginId, platform, version); + if (urlExists(terminal, platformUrl)) { + return platformUrl; + } + return String.format(Locale.ROOT, "%s/%s-%s.zip", baseUrl, pluginId, version); + } + + /** Returns the url for an elasticsearch plugin in maven. */ + private String getMavenUrl(Terminal terminal, String[] coordinates, String platform) throws IOException { + final String groupId = coordinates[0].replace(".", "/"); + final String artifactId = coordinates[1]; + final String version = coordinates[2]; + final String baseUrl = String.format(Locale.ROOT, "https://repo1.maven.org/maven2/%s/%s/%s", groupId, artifactId, version); + final String platformUrl = String.format(Locale.ROOT, "%s/%s-%s-%s.zip", baseUrl, artifactId, platform, version); + if (urlExists(terminal, platformUrl)) { + return platformUrl; + } + return String.format(Locale.ROOT, "%s/%s-%s.zip", baseUrl, artifactId, version); + } + + /** + * Returns {@code true} if the given url exists, and {@code false} otherwise. + * + * The given url must be {@code https} and existing means a {@code HEAD} request returns 200. + */ + // pkg private for tests to manipulate + @SuppressForbidden(reason = "Make HEAD request using URLConnection.connect()") + boolean urlExists(Terminal terminal, String urlString) throws IOException { + terminal.println(VERBOSE, "Checking if url exists: " + urlString); + URL url = new URL(urlString); + assert "https".equals(url.getProtocol()) : "Only http urls can be checked"; + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.addRequestProperty("User-Agent", "elasticsearch-plugin-installer"); + urlConnection.setRequestMethod("HEAD"); + urlConnection.connect(); + return urlConnection.getResponseCode() == 200; + } + /** Returns all the official plugin names that look similar to pluginId. **/ private List checkMisspelledPlugin(String pluginId) { LevensteinDistance ld = new LevensteinDistance(); @@ -318,8 +361,9 @@ public void onProgress(int percent) { } /** Downloads a zip from the url, as well as a SHA1 checksum, and checks the checksum. */ + // pkg private for tests @SuppressForbidden(reason = "We use openStream to download plugins") - private Path downloadZipAndChecksum(Terminal terminal, String urlString, Path tmpDir) throws Exception { + Path downloadZipAndChecksum(Terminal terminal, String urlString, Path tmpDir) throws Exception { Path zip = downloadZip(terminal, urlString, tmpDir); pathsToDeleteOnShutdown.add(zip); URL checksumUrl = new URL(urlString + ".sha1"); diff --git a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java index a70b8a8d3d4a0..57dfec308073c 100644 --- a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java +++ b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java @@ -52,6 +52,7 @@ import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; @@ -77,6 +78,14 @@ @LuceneTestCase.SuppressFileSystems("*") public class InstallPluginCommandTests extends ESTestCase { + private static InstallPluginCommand SKIP_JARHELL_COMMAND = new InstallPluginCommand() { + @Override + void jarHellCheck(Path candidate, Path pluginsDir) throws Exception { + // no jarhell check + } + }; + private static InstallPluginCommand DEFAULT_COMMAND = new InstallPluginCommand(); + private final Function temp; private final FileSystem fs; @@ -164,7 +173,7 @@ static void writeJar(Path jar, String... classes) throws IOException { } } - static String writeZip(Path structure, String prefix) throws IOException { + static Path writeZip(Path structure, String prefix) throws IOException { Path zip = createTempDir().resolve(structure.getFileName() + ".zip"); try (ZipOutputStream stream = new ZipOutputStream(Files.newOutputStream(zip))) { Files.walkFileTree(structure, new SimpleFileVisitor() { @@ -177,15 +186,15 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO } }); } - return zip.toUri().toURL().toString(); + return zip; } /** creates a plugin .zip and returns the url for testing */ - static String createPlugin(String name, Path structure) throws IOException { - return createPlugin(name, structure, false); + static String createPluginUrl(String name, Path structure) throws IOException { + return createPlugin(name, structure, false).toUri().toURL().toString(); } - static String createPlugin(String name, Path structure, boolean createSecurityPolicyFile) throws IOException { + static Path createPlugin(String name, Path structure, boolean createSecurityPolicyFile) throws IOException { PluginTestUtil.writeProperties(structure, "description", "fake desc", "name", name, @@ -202,20 +211,13 @@ static String createPlugin(String name, Path structure, boolean createSecurityPo } static MockTerminal installPlugin(String pluginUrl, Path home) throws Exception { - return installPlugin(pluginUrl, home, false); + return installPlugin(pluginUrl, home, SKIP_JARHELL_COMMAND); } - static MockTerminal installPlugin(String pluginUrl, Path home, boolean jarHellCheck) throws Exception { + static MockTerminal installPlugin(String pluginUrl, Path home, InstallPluginCommand command) throws Exception { Environment env = new Environment(Settings.builder().put("path.home", home).build()); MockTerminal terminal = new MockTerminal(); - new InstallPluginCommand() { - @Override - void jarHellCheck(Path candidate, Path pluginsDir) throws Exception { - if (jarHellCheck) { - super.jarHellCheck(candidate, pluginsDir); - } - } - }.execute(terminal, pluginUrl, true, env); + command.execute(terminal, pluginUrl, true, env); return terminal; } @@ -315,7 +317,7 @@ public void testMissingPluginId() throws IOException { public void testSomethingWorks() throws Exception { Tuple env = createEnv(fs, temp); Path pluginDir = createPluginDir(temp); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); installPlugin(pluginZip, env.v1()); assertPlugin("fake", pluginDir, env.v2()); } @@ -323,7 +325,7 @@ public void testSomethingWorks() throws Exception { public void testSpaceInUrl() throws Exception { Tuple env = createEnv(fs, temp); Path pluginDir = createPluginDir(temp); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); Path pluginZipWithSpaces = createTempFile("foo bar", ".zip"); try (InputStream in = FileSystemUtils.openFileURLStream(new URL(pluginZip))) { Files.copy(in, pluginZipWithSpaces, StandardCopyOption.REPLACE_EXISTING); @@ -351,7 +353,7 @@ public void testPluginsDirReadOnly() throws Exception { Path pluginDir = createPluginDir(temp); try (PosixPermissionsResetter pluginsAttrs = new PosixPermissionsResetter(env.v2().pluginsFile())) { pluginsAttrs.setPermissions(new HashSet<>()); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); IOException e = expectThrows(IOException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains(env.v2().pluginsFile().toString())); } @@ -361,7 +363,7 @@ public void testPluginsDirReadOnly() throws Exception { public void testBuiltinModule() throws Exception { Tuple env = createEnv(fs, temp); Path pluginDir = createPluginDir(temp); - String pluginZip = createPlugin("lang-painless", pluginDir); + String pluginZip = createPluginUrl("lang-painless", pluginDir); UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains("is a system module")); assertInstallCleaned(env.v2()); @@ -373,8 +375,9 @@ public void testJarHell() throws Exception { Tuple environment = createEnv(fs, temp); Path pluginDirectory = createPluginDir(temp); writeJar(pluginDirectory.resolve("other.jar"), "FakePlugin"); - String pluginZip = createPlugin("fake", pluginDirectory); // adds plugin.jar with FakePlugin - IllegalStateException e = expectThrows(IllegalStateException.class, () -> installPlugin(pluginZip, environment.v1(), true)); + String pluginZip = createPluginUrl("fake", pluginDirectory); // adds plugin.jar with FakePlugin + IllegalStateException e = expectThrows(IllegalStateException.class, + () -> installPlugin(pluginZip, environment.v1(), DEFAULT_COMMAND)); assertTrue(e.getMessage(), e.getMessage().contains("jar hell")); assertInstallCleaned(environment.v2()); } @@ -383,10 +386,10 @@ public void testIsolatedPlugins() throws Exception { Tuple env = createEnv(fs, temp); // these both share the same FakePlugin class Path pluginDir1 = createPluginDir(temp); - String pluginZip1 = createPlugin("fake1", pluginDir1); + String pluginZip1 = createPluginUrl("fake1", pluginDir1); installPlugin(pluginZip1, env.v1()); Path pluginDir2 = createPluginDir(temp); - String pluginZip2 = createPlugin("fake2", pluginDir2); + String pluginZip2 = createPluginUrl("fake2", pluginDir2); installPlugin(pluginZip2, env.v1()); assertPlugin("fake1", pluginDir1, env.v2()); assertPlugin("fake2", pluginDir2, env.v2()); @@ -395,7 +398,7 @@ public void testIsolatedPlugins() throws Exception { public void testExistingPlugin() throws Exception { Tuple env = createEnv(fs, temp); Path pluginDir = createPluginDir(temp); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); installPlugin(pluginZip, env.v1()); UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains("already exists")); @@ -408,7 +411,7 @@ public void testBin() throws Exception { Path binDir = pluginDir.resolve("bin"); Files.createDirectory(binDir); Files.createFile(binDir.resolve("somescript")); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); installPlugin(pluginZip, env.v1()); assertPlugin("fake", pluginDir, env.v2()); } @@ -418,7 +421,7 @@ public void testBinNotDir() throws Exception { Path pluginDir = createPluginDir(temp); Path binDir = pluginDir.resolve("bin"); Files.createFile(binDir); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains("not a directory")); assertInstallCleaned(env.v2()); @@ -430,7 +433,7 @@ public void testBinContainsDir() throws Exception { Path dirInBinDir = pluginDir.resolve("bin").resolve("foo"); Files.createDirectories(dirInBinDir); Files.createFile(dirInBinDir.resolve("somescript")); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains("Directories not allowed in bin dir for plugin")); assertInstallCleaned(env.v2()); @@ -442,7 +445,7 @@ public void testBinConflict() throws Exception { Path binDir = pluginDir.resolve("bin"); Files.createDirectory(binDir); Files.createFile(binDir.resolve("somescript")); - String pluginZip = createPlugin("elasticsearch", pluginDir); + String pluginZip = createPluginUrl("elasticsearch", pluginDir); FileAlreadyExistsException e = expectThrows(FileAlreadyExistsException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains(env.v2().binFile().resolve("elasticsearch").toString())); assertInstallCleaned(env.v2()); @@ -455,7 +458,7 @@ public void testBinPermissions() throws Exception { Path binDir = pluginDir.resolve("bin"); Files.createDirectory(binDir); Files.createFile(binDir.resolve("somescript")); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); try (PosixPermissionsResetter binAttrs = new PosixPermissionsResetter(env.v2().binFile())) { Set perms = binAttrs.getCopyPermissions(); // make sure at least one execute perm is missing, so we know we forced it during installation @@ -480,7 +483,7 @@ public void testPlatformBinPermissions() throws Exception { assertFalse(sourcePerms.contains(PosixFilePermission.OWNER_EXECUTE)); assertFalse(sourcePerms.contains(PosixFilePermission.GROUP_EXECUTE)); assertFalse(sourcePerms.contains(PosixFilePermission.OTHERS_EXECUTE)); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); installPlugin(pluginZip, env.v1()); assertPlugin("fake", pluginDir, env.v2()); // check that the installed program has execute permissions, even though the one added to the plugin didn't @@ -500,7 +503,7 @@ public void testConfig() throws Exception { Path configDir = pluginDir.resolve("config"); Files.createDirectory(configDir); Files.createFile(configDir.resolve("custom.yaml")); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); installPlugin(pluginZip, env.v1()); assertPlugin("fake", pluginDir, env.v2()); } @@ -515,7 +518,7 @@ public void testExistingConfig() throws Exception { Files.createDirectory(configDir); Files.write(configDir.resolve("custom.yaml"), "new config".getBytes(StandardCharsets.UTF_8)); Files.createFile(configDir.resolve("other.yaml")); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); installPlugin(pluginZip, env.v1()); assertPlugin("fake", pluginDir, env.v2()); List configLines = Files.readAllLines(envConfigDir.resolve("custom.yaml"), StandardCharsets.UTF_8); @@ -529,7 +532,7 @@ public void testConfigNotDir() throws Exception { Path pluginDir = createPluginDir(temp); Path configDir = pluginDir.resolve("config"); Files.createFile(configDir); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains("not a directory")); assertInstallCleaned(env.v2()); @@ -541,7 +544,7 @@ public void testConfigContainsDir() throws Exception { Path dirInConfigDir = pluginDir.resolve("config").resolve("foo"); Files.createDirectories(dirInConfigDir); Files.createFile(dirInConfigDir.resolve("myconfig.yml")); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains("Directories not allowed in config dir for plugin")); assertInstallCleaned(env.v2()); @@ -553,7 +556,7 @@ public void testConfigConflict() throws Exception { Path configDir = pluginDir.resolve("config"); Files.createDirectory(configDir); Files.createFile(configDir.resolve("myconfig.yml")); - String pluginZip = createPlugin("elasticsearch.yml", pluginDir); + String pluginZip = createPluginUrl("elasticsearch.yml", pluginDir); FileAlreadyExistsException e = expectThrows(FileAlreadyExistsException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains(env.v2().configFile().resolve("elasticsearch.yml").toString())); assertInstallCleaned(env.v2()); @@ -563,7 +566,7 @@ public void testMissingDescriptor() throws Exception { Tuple env = createEnv(fs, temp); Path pluginDir = createPluginDir(temp); Files.createFile(pluginDir.resolve("fake.yml")); - String pluginZip = writeZip(pluginDir, "elasticsearch"); + String pluginZip = writeZip(pluginDir, "elasticsearch").toUri().toURL().toString(); NoSuchFileException e = expectThrows(NoSuchFileException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains("plugin-descriptor.properties")); assertInstallCleaned(env.v2()); @@ -573,7 +576,7 @@ public void testMissingDirectory() throws Exception { Tuple env = createEnv(fs, temp); Path pluginDir = createPluginDir(temp); Files.createFile(pluginDir.resolve(PluginInfo.ES_PLUGIN_PROPERTIES)); - String pluginZip = writeZip(pluginDir, null); + String pluginZip = writeZip(pluginDir, null).toUri().toURL().toString(); UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains("`elasticsearch` directory is missing in the plugin zip")); assertInstallCleaned(env.v2()); @@ -666,9 +669,10 @@ public void testQuietFlagEnabled() throws Exception { public void testPluginAlreadyInstalled() throws Exception { Tuple env = createEnv(fs, temp); Path pluginDir = createPluginDir(temp); - String pluginZip = createPlugin("fake", pluginDir); + String pluginZip = createPluginUrl("fake", pluginDir); installPlugin(pluginZip, env.v1()); - final UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1(), randomBoolean())); + final UserException e = expectThrows(UserException.class, + () -> installPlugin(pluginZip, env.v1(), randomFrom(SKIP_JARHELL_COMMAND, DEFAULT_COMMAND))); assertThat( e.getMessage(), equalTo("plugin directory [" + env.v2().pluginsFile().resolve("fake") + "] already exists; " + @@ -679,15 +683,71 @@ private void installPlugin(MockTerminal terminal, boolean isBatch) throws Except Tuple env = createEnv(fs, temp); Path pluginDir = createPluginDir(temp); // if batch is enabled, we also want to add a security policy - String pluginZip = createPlugin("fake", pluginDir, isBatch); + String pluginZip = createPlugin("fake", pluginDir, isBatch).toUri().toURL().toString(); + SKIP_JARHELL_COMMAND.execute(terminal, pluginZip, isBatch, env.v2()); + } - new InstallPluginCommand() { + public void assertInstallPluginFromUrl(String pluginId, String name, String url, String stagingHash) throws Exception { + Tuple env = createEnv(fs, temp); + Path pluginDir = createPluginDir(temp); + Path pluginZip = createPlugin(name, pluginDir, false); + InstallPluginCommand command = new InstallPluginCommand() { + @Override + Path downloadZipAndChecksum(Terminal terminal, String urlString, Path tmpDir) throws Exception { + assertEquals(url, urlString); + Path downloadedPath = tmpDir.resolve("downloaded.zip"); + Files.copy(pluginZip, downloadedPath); + return downloadedPath; + } + @Override + boolean urlExists(Terminal terminal, String urlString) throws IOException { + return urlString.equals(url); + } + @Override + String getStagingHash() { + return stagingHash; + } @Override void jarHellCheck(Path candidate, Path pluginsDir) throws Exception { + // no jarhell check } - }.execute(terminal, pluginZip, isBatch, env.v2()); + }; + installPlugin(pluginId, env.v1(), command); + assertPlugin(name, pluginDir, env.v2()); + } + + public void testOfficalPlugin() throws Exception { + String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Version.CURRENT + ".zip"; + assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, null); + } + + public void testOfficalPluginStaging() throws Exception { + String url = "https://staging.elastic.co/" + Version.CURRENT + "-abc123/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + + Version.CURRENT + ".zip"; + assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, "abc123"); + } + + public void testOfficalPlatformPlugin() throws Exception { + String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Platforms.PLATFORM_NAME + + "-" + Version.CURRENT + ".zip"; + assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, null); + } + + public void testOfficalPlatformPluginStaging() throws Exception { + String url = "https://staging.elastic.co/" + Version.CURRENT + "-abc123/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + + Platforms.PLATFORM_NAME + "-"+ Version.CURRENT + ".zip"; + assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, "abc123"); + } + + public void testMavenPlugin() throws Exception { + String url = "https://repo1.maven.org/maven2/mygroup/myplugin/1.0.0/myplugin-1.0.0.zip"; + assertInstallPluginFromUrl("mygroup:myplugin:1.0.0", "myplugin", url, null); + } + + public void testMavenPlatformPlugin() throws Exception { + String url = "https://repo1.maven.org/maven2/mygroup/myplugin/1.0.0/myplugin-" + Platforms.PLATFORM_NAME + "-1.0.0.zip"; + assertInstallPluginFromUrl("mygroup:myplugin:1.0.0", "myplugin", url, null); } // TODO: test checksum (need maven/official below) - // TODO: test maven, official, and staging install...need tests with fixtures... } From 350573290fb123a5057f427e8465d76a0e19e4e0 Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Thu, 27 Apr 2017 16:50:59 -0400 Subject: [PATCH 113/619] Agg builder accessibility fixes (#24323) - Getters for DateHisto `interval` and `offset` should return a long, not double - Add getter for the filter in a FilterAgg - Add getters for subaggs / pipelines in base AggregationBuilder --- .../search/aggregations/AggregationBuilder.java | 11 +++++++++++ .../bucket/filter/FilterAggregationBuilder.java | 4 ++++ .../histogram/DateHistogramAggregationBuilder.java | 4 ++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java index 14875895b77e1..16f8ef2444fec 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java @@ -27,6 +27,7 @@ import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; +import java.util.List; import java.util.Map; /** @@ -69,6 +70,16 @@ public String getName() { /** Add a sub aggregation to this builder. */ public abstract AggregationBuilder subAggregation(PipelineAggregationBuilder aggregation); + /** Return the configured set of subaggregations **/ + public List getSubAggregations() { + return factoriesBuilder.getAggregatorFactories(); + } + + /** Return the configured set of pipeline aggregations **/ + public List getPipelineAggregations() { + return factoriesBuilder.getPipelineAggregatorFactories(); + } + /** * Internal: Registers sub-factories with this factory. The sub-factory will be * responsible for the creation of sub-aggregators under the aggregator diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/FilterAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/FilterAggregationBuilder.java index 9fb8b368e9dd7..19d3f32f39dd6 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/FilterAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/FilterAggregationBuilder.java @@ -102,4 +102,8 @@ protected boolean doEquals(Object obj) { public String getType() { return NAME; } + + public QueryBuilder getFilter() { + return filter; + } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregationBuilder.java index e805abd86c981..96b08e3bada12 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregationBuilder.java @@ -164,7 +164,7 @@ protected void innerWriteTo(StreamOutput out) throws IOException { } /** Get the current interval in milliseconds that is set on this builder. */ - public double interval() { + public long interval() { return interval; } @@ -196,7 +196,7 @@ public DateHistogramAggregationBuilder dateHistogramInterval(DateHistogramInterv } /** Get the offset to use when rounding, which is a number of milliseconds. */ - public double offset() { + public long offset() { return offset; } From e4bb360ae0fae753747eebec9ef88cf2530334e0 Mon Sep 17 00:00:00 2001 From: Toby McLaughlin Date: Fri, 28 Apr 2017 14:48:52 +1000 Subject: [PATCH 114/619] Fix typo in node environment exception message This commit fixes a typo in an exception message when trying to create a node environment. Relates #24381 --- core/src/main/java/org/elasticsearch/node/Node.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index 01ccf68dc36aa..764ece5b868a2 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -260,7 +260,7 @@ protected Node(final Environment environment, Collection nodeEnvironment = new NodeEnvironment(tmpSettings, environment); resourcesToClose.add(nodeEnvironment); } catch (IOException ex) { - throw new IllegalStateException("Failed to created node environment", ex); + throw new IllegalStateException("Failed to create node environment", ex); } final boolean hadPredefinedNodeName = NODE_NAME_SETTING.exists(tmpSettings); Logger logger = Loggers.getLogger(Node.class, tmpSettings); From 35f78d098a45cee6b26998a8d0f11343ca67dada Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Fri, 28 Apr 2017 09:34:31 +0200 Subject: [PATCH 115/619] Separate publishing from applying cluster states (#24236) Separates cluster state publishing from applying cluster states: - ClusterService is split into two classes MasterService and ClusterApplierService. MasterService has the responsibility to calculate cluster state updates for actions that want to change the cluster state (create index, update shard routing table, etc.). ClusterApplierService has the responsibility to apply cluster states that have been successfully published and invokes the cluster state appliers and listeners. - ClusterApplierService keeps track of the last applied state, but MasterService is stateless and uses the last cluster state that is provided by the discovery module to calculate the next prospective state. The ClusterService class is still kept around, which now just delegates actions to ClusterApplierService and MasterService. - The discovery implementation is now responsible for managing the last cluster state that is used by the consensus layer and the master service. It also exposes the initial cluster state which is used by the ClusterApplierService. The discovery implementation is also responsible for adding the right cluster-level blocks to the initial state. - NoneDiscovery has been renamed to TribeDiscovery as it is exclusively used by TribeService. It adds the tribe blocks to the initial state. - ZenDiscovery is synchronized on state changes to the last cluster state that is used by the consensus layer and the master service, and does not submit cluster state update tasks anymore to make changes to the disco state (except when becoming master). Control flow for cluster state updates is now as follows: - State updates are sent to MasterService - MasterService gets the latest committed cluster state from the discovery implementation and calculates the next cluster state to publish - MasterService submits the new prospective cluster state to the discovery implementation for publishing - Discovery implementation publishes cluster states to all nodes and, once the state is committed, asks the ClusterApplierService to apply the newly committed state. - ClusterApplierService applies state to local node. --- .../resources/checkstyle_suppressions.xml | 2 +- .../health/TransportClusterHealthAction.java | 8 +- .../TransportPendingClusterTasksAction.java | 2 +- .../elasticsearch/cluster/ClusterState.java | 1 - .../cluster/ClusterStateObserver.java | 30 +- .../cluster/ClusterStateTaskExecutor.java | 9 +- .../cluster/ClusterStateTaskListener.java | 5 +- .../cluster/LocalClusterUpdateTask.java | 16 +- .../routing/DelayedAllocationService.java | 8 +- .../cluster/service/ClusterApplier.java | 37 + .../service/ClusterApplierService.java | 655 ++++++++++ .../cluster/service/ClusterService.java | 1070 +---------------- .../cluster/service/MasterService.java | 752 ++++++++++++ .../org/elasticsearch/common/Randomness.java | 2 +- .../common/util/concurrent/BaseFuture.java | 9 +- .../elasticsearch/discovery/Discovery.java | 21 +- .../discovery/DiscoveryModule.java | 17 +- .../discovery/NoneDiscovery.java | 102 -- .../discovery/TribeDiscovery.java | 73 ++ .../discovery/single/SingleNodeDiscovery.java | 133 +- .../discovery/zen/MasterFaultDetection.java | 20 +- .../discovery/zen/NodeJoinController.java | 26 +- .../discovery/zen/ZenDiscovery.java | 613 +++++----- .../elasticsearch/gateway/GatewayService.java | 3 - .../indices/store/IndicesStore.java | 19 +- .../java/org/elasticsearch/node/Node.java | 26 +- .../org/elasticsearch/node/NodeService.java | 4 +- .../plugins/DiscoveryPlugin.java | 14 +- .../repositories/RepositoriesService.java | 7 +- .../org/elasticsearch/tribe/TribeService.java | 26 +- .../cluster/ClusterModuleTests.java | 6 +- .../health/ClusterStateHealthTests.java | 18 +- .../DelayedAllocationServiceTests.java | 4 +- .../service/ClusterApplierServiceTests.java | 427 +++++++ .../cluster/service/ClusterServiceIT.java | 6 +- ...viceTests.java => MasterServiceTests.java} | 600 +++------ .../cluster/settings/ClusterSettingsIT.java | 3 +- .../discovery/DiscoveryModuleTests.java | 42 +- .../DiscoveryWithServiceDisruptionsIT.java | 43 +- .../discovery/ZenFaultDetectionTests.java | 22 +- .../single/SingleNodeDiscoveryTests.java | 38 +- .../zen/NodeJoinControllerTests.java | 102 +- .../discovery/zen/ZenDiscoveryIT.java | 1 + .../discovery/zen/ZenDiscoveryUnitTests.java | 66 +- .../gateway/GatewayServiceTests.java | 5 +- .../IndexLifecycleActionIT.java | 5 +- .../store/IndicesStoreIntegrationIT.java | 39 +- .../AbstractSnapshotIntegTestCase.java | 130 -- .../DedicatedClusterSnapshotRestoreIT.java | 43 +- .../SharedClusterSnapshotRestoreIT.java | 53 - .../org/elasticsearch/test/NoopDiscovery.java | 21 +- .../azure/classic/AzureDiscoveryPlugin.java | 25 +- .../discovery/ec2/Ec2DiscoveryPlugin.java | 44 +- .../discovery/gce/GceDiscoveryPlugin.java | 19 +- .../test/ClusterServiceUtils.java | 168 ++- .../elasticsearch/test/ESIntegTestCase.java | 3 +- .../test/discovery/TestZenDiscovery.java | 17 +- .../BlockClusterStateProcessing.java | 25 +- .../SlowClusterStateProcessing.java | 39 +- 59 files changed, 3154 insertions(+), 2570 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/cluster/service/ClusterApplier.java create mode 100644 core/src/main/java/org/elasticsearch/cluster/service/ClusterApplierService.java create mode 100644 core/src/main/java/org/elasticsearch/cluster/service/MasterService.java delete mode 100644 core/src/main/java/org/elasticsearch/discovery/NoneDiscovery.java create mode 100644 core/src/main/java/org/elasticsearch/discovery/TribeDiscovery.java create mode 100644 core/src/test/java/org/elasticsearch/cluster/service/ClusterApplierServiceTests.java rename core/src/test/java/org/elasticsearch/cluster/service/{ClusterServiceTests.java => MasterServiceTests.java} (59%) diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 6e62b8ec34675..2ed1b68fb8ac2 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -1047,7 +1047,7 @@ - + diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthAction.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthAction.java index 44c604dc8b845..8924f81a86cea 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthAction.java @@ -194,14 +194,14 @@ public void onTimeout(TimeValue timeout) { } private boolean validateRequest(final ClusterHealthRequest request, ClusterState clusterState, final int waitFor) { - ClusterHealthResponse response = clusterHealth(request, clusterState, clusterService.numberOfPendingTasks(), - gatewayAllocator.getNumberOfInFlightFetch(), clusterService.getMaxTaskWaitTime()); + ClusterHealthResponse response = clusterHealth(request, clusterState, clusterService.getMasterService().numberOfPendingTasks(), + gatewayAllocator.getNumberOfInFlightFetch(), clusterService.getMasterService().getMaxTaskWaitTime()); return prepareResponse(request, response, clusterState, waitFor); } private ClusterHealthResponse getResponse(final ClusterHealthRequest request, ClusterState clusterState, final int waitFor, boolean timedOut) { - ClusterHealthResponse response = clusterHealth(request, clusterState, clusterService.numberOfPendingTasks(), - gatewayAllocator.getNumberOfInFlightFetch(), clusterService.getMaxTaskWaitTime()); + ClusterHealthResponse response = clusterHealth(request, clusterState, clusterService.getMasterService().numberOfPendingTasks(), + gatewayAllocator.getNumberOfInFlightFetch(), clusterService.getMasterService().getMaxTaskWaitTime()); boolean valid = prepareResponse(request, response, clusterState, waitFor); assert valid || timedOut; // we check for a timeout here since this method might be called from the wait_for_events diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/tasks/TransportPendingClusterTasksAction.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/tasks/TransportPendingClusterTasksAction.java index c15758de3cb43..cd58bb8d6d43e 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/tasks/TransportPendingClusterTasksAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/tasks/TransportPendingClusterTasksAction.java @@ -65,7 +65,7 @@ protected PendingClusterTasksResponse newResponse() { @Override protected void masterOperation(PendingClusterTasksRequest request, ClusterState state, ActionListener listener) { logger.trace("fetching pending tasks from cluster service"); - final List pendingTasks = clusterService.pendingTasks(); + final List pendingTasks = clusterService.getMasterService().pendingTasks(); logger.trace("done fetching pending tasks from cluster service"); listener.onResponse(new PendingClusterTasksResponse(pendingTasks)); } diff --git a/core/src/main/java/org/elasticsearch/cluster/ClusterState.java b/core/src/main/java/org/elasticsearch/cluster/ClusterState.java index 5ef06f178a832..89b3727c9f467 100644 --- a/core/src/main/java/org/elasticsearch/cluster/ClusterState.java +++ b/core/src/main/java/org/elasticsearch/cluster/ClusterState.java @@ -22,7 +22,6 @@ import com.carrotsearch.hppc.cursors.IntObjectCursor; import com.carrotsearch.hppc.cursors.ObjectCursor; import com.carrotsearch.hppc.cursors.ObjectObjectCursor; -import org.elasticsearch.Version; import org.elasticsearch.cluster.block.ClusterBlock; import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.metadata.IndexMetaData; diff --git a/core/src/main/java/org/elasticsearch/cluster/ClusterStateObserver.java b/core/src/main/java/org/elasticsearch/cluster/ClusterStateObserver.java index e0c35a12c22a4..d191bc0175606 100644 --- a/core/src/main/java/org/elasticsearch/cluster/ClusterStateObserver.java +++ b/core/src/main/java/org/elasticsearch/cluster/ClusterStateObserver.java @@ -21,6 +21,7 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.cluster.service.ClusterApplierService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.unit.TimeValue; @@ -42,7 +43,7 @@ public class ClusterStateObserver { private final Predicate MATCH_ALL_CHANGES_PREDICATE = state -> true; - private final ClusterService clusterService; + private final ClusterApplierService clusterApplierService; private final ThreadContext contextHolder; volatile TimeValue timeOutValue; @@ -74,7 +75,12 @@ public ClusterStateObserver(ClusterService clusterService, @Nullable TimeValue t */ public ClusterStateObserver(ClusterState initialState, ClusterService clusterService, @Nullable TimeValue timeout, Logger logger, ThreadContext contextHolder) { - this.clusterService = clusterService; + this(initialState, clusterService.getClusterApplierService(), timeout, logger, contextHolder); + } + + public ClusterStateObserver(ClusterState initialState, ClusterApplierService clusterApplierService, @Nullable TimeValue timeout, + Logger logger, ThreadContext contextHolder) { + this.clusterApplierService = clusterApplierService; this.lastObservedState = new AtomicReference<>(new StoredState(initialState)); this.timeOutValue = timeout; if (timeOutValue != null) { @@ -89,7 +95,7 @@ public ClusterState setAndGetObservedState() { if (observingContext.get() != null) { throw new ElasticsearchException("cannot set current cluster state while waiting for a cluster state change"); } - ClusterState clusterState = clusterService.state(); + ClusterState clusterState = clusterApplierService.state(); lastObservedState.set(new StoredState(clusterState)); return clusterState; } @@ -135,7 +141,7 @@ public void waitForNextChange(Listener listener, Predicate statePr logger.trace("observer timed out. notifying listener. timeout setting [{}], time since start [{}]", timeOutValue, new TimeValue(timeSinceStartMS)); // update to latest, in case people want to retry timedOut = true; - lastObservedState.set(new StoredState(clusterService.state())); + lastObservedState.set(new StoredState(clusterApplierService.state())); listener.onTimeout(timeOutValue); return; } @@ -151,7 +157,7 @@ public void waitForNextChange(Listener listener, Predicate statePr // sample a new state. This state maybe *older* than the supplied state if we are called from an applier, // which wants to wait for something else to happen - ClusterState newState = clusterService.state(); + ClusterState newState = clusterApplierService.state(); if (lastObservedState.get().isOlderOrDifferentMaster(newState) && statePredicate.test(newState)) { // good enough, let's go. logger.trace("observer: sampled state accepted by predicate ({})", newState); @@ -163,7 +169,7 @@ public void waitForNextChange(Listener listener, Predicate statePr if (!observingContext.compareAndSet(null, context)) { throw new ElasticsearchException("already waiting for a cluster state change"); } - clusterService.addTimeoutListener(timeoutTimeLeftMS == null ? null : new TimeValue(timeoutTimeLeftMS), clusterStateListener); + clusterApplierService.addTimeoutListener(timeoutTimeLeftMS == null ? null : new TimeValue(timeoutTimeLeftMS), clusterStateListener); } } @@ -179,7 +185,7 @@ public void clusterChanged(ClusterChangedEvent event) { final ClusterState state = event.state(); if (context.statePredicate.test(state)) { if (observingContext.compareAndSet(context, null)) { - clusterService.removeTimeoutListener(this); + clusterApplierService.removeTimeoutListener(this); logger.trace("observer: accepting cluster state change ({})", state); lastObservedState.set(new StoredState(state)); context.listener.onNewClusterState(state); @@ -198,12 +204,12 @@ public void postAdded() { // No need to remove listener as it is the responsibility of the thread that set observingContext to null return; } - ClusterState newState = clusterService.state(); + ClusterState newState = clusterApplierService.state(); if (lastObservedState.get().isOlderOrDifferentMaster(newState) && context.statePredicate.test(newState)) { // double check we're still listening if (observingContext.compareAndSet(context, null)) { logger.trace("observer: post adding listener: accepting current cluster state ({})", newState); - clusterService.removeTimeoutListener(this); + clusterApplierService.removeTimeoutListener(this); lastObservedState.set(new StoredState(newState)); context.listener.onNewClusterState(newState); } else { @@ -220,7 +226,7 @@ public void onClose() { if (context != null) { logger.trace("observer: cluster service closed. notifying listener."); - clusterService.removeTimeoutListener(this); + clusterApplierService.removeTimeoutListener(this); context.listener.onClusterServiceClose(); } } @@ -229,11 +235,11 @@ public void onClose() { public void onTimeout(TimeValue timeout) { ObservingContext context = observingContext.getAndSet(null); if (context != null) { - clusterService.removeTimeoutListener(this); + clusterApplierService.removeTimeoutListener(this); long timeSinceStartMS = TimeValue.nsecToMSec(System.nanoTime() - startTimeNS); logger.trace("observer: timeout notification from cluster service. timeout setting [{}], time since start [{}]", timeOutValue, new TimeValue(timeSinceStartMS)); // update to latest, in case people want to retry - lastObservedState.set(new StoredState(clusterService.state())); + lastObservedState.set(new StoredState(clusterApplierService.state())); timedOut = true; context.listener.onTimeout(timeOutValue); } diff --git a/core/src/main/java/org/elasticsearch/cluster/ClusterStateTaskExecutor.java b/core/src/main/java/org/elasticsearch/cluster/ClusterStateTaskExecutor.java index 3693447cfb6d2..8e50fddb9b17e 100644 --- a/core/src/main/java/org/elasticsearch/cluster/ClusterStateTaskExecutor.java +++ b/core/src/main/java/org/elasticsearch/cluster/ClusterStateTaskExecutor.java @@ -71,21 +71,18 @@ default String describeTasks(List tasks) { * @param the type of the cluster state update task */ class ClusterTasksResult { - public final boolean noMaster; @Nullable public final ClusterState resultingState; public final Map executionResults; /** * Construct an execution result instance with a correspondence between the tasks and their execution result - * @param noMaster whether this node steps down as master or has lost connection to the master * @param resultingState the resulting cluster state * @param executionResults the correspondence between tasks and their outcome */ - ClusterTasksResult(boolean noMaster, ClusterState resultingState, Map executionResults) { + ClusterTasksResult(ClusterState resultingState, Map executionResults) { this.resultingState = resultingState; this.executionResults = executionResults; - this.noMaster = noMaster; } public static Builder builder() { @@ -124,11 +121,11 @@ private Builder result(T task, TaskResult executionResult) { } public ClusterTasksResult build(ClusterState resultingState) { - return new ClusterTasksResult<>(false, resultingState, executionResults); + return new ClusterTasksResult<>(resultingState, executionResults); } ClusterTasksResult build(ClusterTasksResult result, ClusterState previousState) { - return new ClusterTasksResult<>(result.noMaster, result.resultingState == null ? previousState : result.resultingState, + return new ClusterTasksResult<>(result.resultingState == null ? previousState : result.resultingState, executionResults); } } diff --git a/core/src/main/java/org/elasticsearch/cluster/ClusterStateTaskListener.java b/core/src/main/java/org/elasticsearch/cluster/ClusterStateTaskListener.java index 757c8b0c82e17..b2ab13fac2aba 100644 --- a/core/src/main/java/org/elasticsearch/cluster/ClusterStateTaskListener.java +++ b/core/src/main/java/org/elasticsearch/cluster/ClusterStateTaskListener.java @@ -18,6 +18,8 @@ */ package org.elasticsearch.cluster; +import org.elasticsearch.cluster.service.MasterService; + import java.util.List; public interface ClusterStateTaskListener { @@ -28,7 +30,8 @@ public interface ClusterStateTaskListener { void onFailure(String source, Exception e); /** - * called when the task was rejected because the local node is no longer master + * called when the task was rejected because the local node is no longer master. + * Used only for tasks submitted to {@link MasterService}. */ default void onNoLongerMaster(String source) { onFailure(source, new NotMasterException("no longer master. source: [" + source + "]")); diff --git a/core/src/main/java/org/elasticsearch/cluster/LocalClusterUpdateTask.java b/core/src/main/java/org/elasticsearch/cluster/LocalClusterUpdateTask.java index 9692ff8d4e1ed..89d85fa2e1479 100644 --- a/core/src/main/java/org/elasticsearch/cluster/LocalClusterUpdateTask.java +++ b/core/src/main/java/org/elasticsearch/cluster/LocalClusterUpdateTask.java @@ -50,25 +50,11 @@ public final ClusterTasksResult execute(ClusterState cur return ClusterTasksResult.builder().successes(tasks).build(result, currentState); } - /** - * node stepped down as master or has lost connection to the master - */ - public static ClusterTasksResult noMaster() { - return new ClusterTasksResult(true, null, null); - } - /** * no changes were made to the cluster state. Useful to execute a runnable on the cluster state applier thread */ public static ClusterTasksResult unchanged() { - return new ClusterTasksResult(false, null, null); - } - - /** - * locally apply cluster state received from a master - */ - public static ClusterTasksResult newState(ClusterState clusterState) { - return new ClusterTasksResult(false, clusterState, null); + return new ClusterTasksResult<>(null, null); } @Override diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/DelayedAllocationService.java b/core/src/main/java/org/elasticsearch/cluster/routing/DelayedAllocationService.java index 4522dfcf98f18..fd7f8f6811fdf 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/DelayedAllocationService.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/DelayedAllocationService.java @@ -178,8 +178,8 @@ private void removeIfSameTask(DelayedRerouteTask expectedTask) { /** * Figure out if an existing scheduled reroute is good enough or whether we need to cancel and reschedule. */ - private void scheduleIfNeeded(long currentNanoTime, ClusterState state) { - assertClusterStateThread(); + private synchronized void scheduleIfNeeded(long currentNanoTime, ClusterState state) { + assertClusterOrMasterStateThread(); long nextDelayNanos = UnassignedInfo.findNextDelayedAllocation(currentNanoTime, state); if (nextDelayNanos < 0) { logger.trace("no need to schedule reroute - no delayed unassigned shards"); @@ -214,7 +214,7 @@ private void scheduleIfNeeded(long currentNanoTime, ClusterState state) { } // protected so that it can be overridden (and disabled) by unit tests - protected void assertClusterStateThread() { - ClusterService.assertClusterStateThread(); + protected void assertClusterOrMasterStateThread() { + assert ClusterService.assertClusterOrMasterStateThread(); } } diff --git a/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplier.java b/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplier.java new file mode 100644 index 0000000000000..ef3135af24d4a --- /dev/null +++ b/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplier.java @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.cluster.service; + +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateTaskListener; + +import java.util.function.Supplier; + +@FunctionalInterface +public interface ClusterApplier { + /** + * Method to invoke when a new cluster state is available to be applied + * + * @param source information where the cluster state came from + * @param clusterStateSupplier the cluster state supplier which provides the latest cluster state to apply + * @param listener callback that is invoked after cluster state is applied + */ + void onNewClusterState(String source, Supplier clusterStateSupplier, ClusterStateTaskListener listener); +} diff --git a/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplierService.java b/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplierService.java new file mode 100644 index 0000000000000..540881718fc34 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplierService.java @@ -0,0 +1,655 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.cluster.service; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.logging.log4j.util.Supplier; +import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateApplier; +import org.elasticsearch.cluster.ClusterStateListener; +import org.elasticsearch.cluster.ClusterStateObserver; +import org.elasticsearch.cluster.ClusterStateTaskConfig; +import org.elasticsearch.cluster.ClusterStateTaskListener; +import org.elasticsearch.cluster.LocalNodeMasterListener; +import org.elasticsearch.cluster.NodeConnectionsService; +import org.elasticsearch.cluster.TimeoutClusterStateListener; +import org.elasticsearch.cluster.metadata.ProcessClusterEventTimeoutException; +import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.Priority; +import org.elasticsearch.common.component.AbstractLifecycleComponent; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.util.concurrent.ConcurrentCollections; +import org.elasticsearch.common.util.concurrent.EsExecutors; +import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; +import org.elasticsearch.common.util.concurrent.FutureUtils; +import org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor; +import org.elasticsearch.common.util.iterable.Iterables; +import org.elasticsearch.threadpool.ThreadPool; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Stream; + +import static org.elasticsearch.cluster.service.ClusterService.CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING; +import static org.elasticsearch.common.util.concurrent.EsExecutors.daemonThreadFactory; + +public class ClusterApplierService extends AbstractLifecycleComponent implements ClusterApplier { + + public static final String CLUSTER_UPDATE_THREAD_NAME = "clusterApplierService#updateTask"; + + private final ClusterSettings clusterSettings; + protected final ThreadPool threadPool; + + private volatile TimeValue slowTaskLoggingThreshold; + + private volatile PrioritizedEsThreadPoolExecutor threadPoolExecutor; + + /** + * Those 3 state listeners are changing infrequently - CopyOnWriteArrayList is just fine + */ + private final Collection highPriorityStateAppliers = new CopyOnWriteArrayList<>(); + private final Collection normalPriorityStateAppliers = new CopyOnWriteArrayList<>(); + private final Collection lowPriorityStateAppliers = new CopyOnWriteArrayList<>(); + private final Iterable clusterStateAppliers = Iterables.concat(highPriorityStateAppliers, + normalPriorityStateAppliers, lowPriorityStateAppliers); + + private final Collection clusterStateListeners = new CopyOnWriteArrayList<>(); + private final Collection timeoutClusterStateListeners = + Collections.newSetFromMap(new ConcurrentHashMap()); + + private final LocalNodeMasterListeners localNodeMasterListeners; + + private final Queue onGoingTimeouts = ConcurrentCollections.newQueue(); + + private final AtomicReference state; // last applied state + + private NodeConnectionsService nodeConnectionsService; + + public ClusterApplierService(Settings settings, ClusterSettings clusterSettings, ThreadPool threadPool) { + super(settings); + this.clusterSettings = clusterSettings; + this.threadPool = threadPool; + this.state = new AtomicReference<>(); + this.slowTaskLoggingThreshold = CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING.get(settings); + this.localNodeMasterListeners = new LocalNodeMasterListeners(threadPool); + } + + public void setSlowTaskLoggingThreshold(TimeValue slowTaskLoggingThreshold) { + this.slowTaskLoggingThreshold = slowTaskLoggingThreshold; + } + + public synchronized void setNodeConnectionsService(NodeConnectionsService nodeConnectionsService) { + assert this.nodeConnectionsService == null : "nodeConnectionsService is already set"; + this.nodeConnectionsService = nodeConnectionsService; + } + + public void setInitialState(ClusterState initialState) { + if (lifecycle.started()) { + throw new IllegalStateException("can't set initial state when started"); + } + assert state.get() == null : "state is already set"; + state.set(initialState); + } + + @Override + protected synchronized void doStart() { + Objects.requireNonNull(nodeConnectionsService, "please set the node connection service before starting"); + Objects.requireNonNull(state.get(), "please set initial state before starting"); + addListener(localNodeMasterListeners); + threadPoolExecutor = EsExecutors.newSinglePrioritizing(CLUSTER_UPDATE_THREAD_NAME, + daemonThreadFactory(settings, CLUSTER_UPDATE_THREAD_NAME), threadPool.getThreadContext(), threadPool.scheduler()); + } + + class UpdateTask extends SourcePrioritizedRunnable implements Function { + final ClusterStateTaskListener listener; + final Function updateFunction; + + UpdateTask(Priority priority, String source, ClusterStateTaskListener listener, + Function updateFunction) { + super(priority, source); + this.listener = listener; + this.updateFunction = updateFunction; + } + + @Override + public ClusterState apply(ClusterState clusterState) { + return updateFunction.apply(clusterState); + } + + @Override + public void run() { + runTask(this); + } + } + + @Override + protected synchronized void doStop() { + for (NotifyTimeout onGoingTimeout : onGoingTimeouts) { + onGoingTimeout.cancel(); + try { + onGoingTimeout.cancel(); + onGoingTimeout.listener.onClose(); + } catch (Exception ex) { + logger.debug("failed to notify listeners on shutdown", ex); + } + } + ThreadPool.terminate(threadPoolExecutor, 10, TimeUnit.SECONDS); + // close timeout listeners that did not have an ongoing timeout + timeoutClusterStateListeners.forEach(TimeoutClusterStateListener::onClose); + removeListener(localNodeMasterListeners); + } + + @Override + protected synchronized void doClose() { + } + + /** + * The current cluster state. + * Should be renamed to appliedClusterState + */ + public ClusterState state() { + assert assertNotCalledFromClusterStateApplier("the applied cluster state is not yet available"); + ClusterState clusterState = this.state.get(); + assert clusterState != null : "initial cluster state not set yet"; + return clusterState; + } + + /** + * Adds a high priority applier of updated cluster states. + */ + public void addHighPriorityApplier(ClusterStateApplier applier) { + highPriorityStateAppliers.add(applier); + } + + /** + * Adds an applier which will be called after all high priority and normal appliers have been called. + */ + public void addLowPriorityApplier(ClusterStateApplier applier) { + lowPriorityStateAppliers.add(applier); + } + + /** + * Adds a applier of updated cluster states. + */ + public void addStateApplier(ClusterStateApplier applier) { + normalPriorityStateAppliers.add(applier); + } + + /** + * Removes an applier of updated cluster states. + */ + public void removeApplier(ClusterStateApplier applier) { + normalPriorityStateAppliers.remove(applier); + highPriorityStateAppliers.remove(applier); + lowPriorityStateAppliers.remove(applier); + } + + /** + * Add a listener for updated cluster states + */ + public void addListener(ClusterStateListener listener) { + clusterStateListeners.add(listener); + } + + /** + * Removes a listener for updated cluster states. + */ + public void removeListener(ClusterStateListener listener) { + clusterStateListeners.remove(listener); + } + + /** + * Removes a timeout listener for updated cluster states. + */ + public void removeTimeoutListener(TimeoutClusterStateListener listener) { + timeoutClusterStateListeners.remove(listener); + for (Iterator it = onGoingTimeouts.iterator(); it.hasNext(); ) { + NotifyTimeout timeout = it.next(); + if (timeout.listener.equals(listener)) { + timeout.cancel(); + it.remove(); + } + } + } + + /** + * Add a listener for on/off local node master events + */ + public void addLocalNodeMasterListener(LocalNodeMasterListener listener) { + localNodeMasterListeners.add(listener); + } + + /** + * Remove the given listener for on/off local master events + */ + public void removeLocalNodeMasterListener(LocalNodeMasterListener listener) { + localNodeMasterListeners.remove(listener); + } + + /** + * Adds a cluster state listener that is expected to be removed during a short period of time. + * If provided, the listener will be notified once a specific time has elapsed. + * + * NOTE: the listener is not removed on timeout. This is the responsibility of the caller. + */ + public void addTimeoutListener(@Nullable final TimeValue timeout, final TimeoutClusterStateListener listener) { + if (lifecycle.stoppedOrClosed()) { + listener.onClose(); + return; + } + // call the post added notification on the same event thread + try { + threadPoolExecutor.execute(new SourcePrioritizedRunnable(Priority.HIGH, "_add_listener_") { + @Override + public void run() { + if (timeout != null) { + NotifyTimeout notifyTimeout = new NotifyTimeout(listener, timeout); + notifyTimeout.future = threadPool.schedule(timeout, ThreadPool.Names.GENERIC, notifyTimeout); + onGoingTimeouts.add(notifyTimeout); + } + timeoutClusterStateListeners.add(listener); + listener.postAdded(); + } + }); + } catch (EsRejectedExecutionException e) { + if (lifecycle.stoppedOrClosed()) { + listener.onClose(); + } else { + throw e; + } + } + } + + public void runOnApplierThread(final String source, Consumer clusterStateConsumer, + final ClusterStateTaskListener listener, Priority priority) { + submitStateUpdateTask(source, ClusterStateTaskConfig.build(priority), + (clusterState) -> { + clusterStateConsumer.accept(clusterState); + return clusterState; + }, + listener); + } + + public void runOnApplierThread(final String source, Consumer clusterStateConsumer, + final ClusterStateTaskListener listener) { + runOnApplierThread(source, clusterStateConsumer, listener, Priority.HIGH); + } + + @Override + public void onNewClusterState(final String source, final java.util.function.Supplier clusterStateSupplier, + final ClusterStateTaskListener listener) { + Function applyFunction = currentState -> { + ClusterState nextState = clusterStateSupplier.get(); + if (nextState != null) { + return nextState; + } else { + return currentState; + } + }; + submitStateUpdateTask(source, ClusterStateTaskConfig.build(Priority.HIGH), applyFunction, listener); + } + + private void submitStateUpdateTask(final String source, final ClusterStateTaskConfig config, + final Function executor, + final ClusterStateTaskListener listener) { + if (!lifecycle.started()) { + return; + } + try { + UpdateTask updateTask = new UpdateTask(config.priority(), source, new SafeClusterStateTaskListener(listener, logger), executor); + if (config.timeout() != null) { + threadPoolExecutor.execute(updateTask, config.timeout(), + () -> threadPool.generic().execute( + () -> listener.onFailure(source, new ProcessClusterEventTimeoutException(config.timeout(), source)))); + } else { + threadPoolExecutor.execute(updateTask); + } + } catch (EsRejectedExecutionException e) { + // ignore cases where we are shutting down..., there is really nothing interesting + // to be done here... + if (!lifecycle.stoppedOrClosed()) { + throw e; + } + } + } + + /** asserts that the current thread is the cluster state update thread */ + public static boolean assertClusterStateUpdateThread() { + assert Thread.currentThread().getName().contains(ClusterApplierService.CLUSTER_UPDATE_THREAD_NAME) : + "not called from the cluster state update thread"; + return true; + } + + /** asserts that the current thread is NOT the cluster state update thread */ + public static boolean assertNotClusterStateUpdateThread(String reason) { + assert Thread.currentThread().getName().contains(CLUSTER_UPDATE_THREAD_NAME) == false : + "Expected current thread [" + Thread.currentThread() + "] to not be the cluster state update thread. Reason: [" + reason + "]"; + return true; + } + + /** asserts that the current stack trace does NOT involve a cluster state applier */ + private static boolean assertNotCalledFromClusterStateApplier(String reason) { + if (Thread.currentThread().getName().contains(CLUSTER_UPDATE_THREAD_NAME)) { + for (StackTraceElement element : Thread.currentThread().getStackTrace()) { + final String className = element.getClassName(); + final String methodName = element.getMethodName(); + if (className.equals(ClusterStateObserver.class.getName())) { + // people may start an observer from an applier + return true; + } else if (className.equals(ClusterApplierService.class.getName()) + && methodName.equals("callClusterStateAppliers")) { + throw new AssertionError("should not be called by a cluster state applier. reason [" + reason + "]"); + } + } + } + return true; + } + + protected void runTask(UpdateTask task) { + if (!lifecycle.started()) { + logger.debug("processing [{}]: ignoring, cluster applier service not started", task.source); + return; + } + + logger.debug("processing [{}]: execute", task.source); + final ClusterState previousClusterState = state.get(); + + long startTimeNS = currentTimeInNanos(); + final ClusterState newClusterState; + try { + newClusterState = task.apply(previousClusterState); + } catch (Exception e) { + TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS))); + if (logger.isTraceEnabled()) { + logger.trace( + (Supplier) () -> new ParameterizedMessage( + "failed to execute cluster state applier in [{}], state:\nversion [{}], source [{}]\n{}{}{}", + executionTime, + previousClusterState.version(), + task.source, + previousClusterState.nodes(), + previousClusterState.routingTable(), + previousClusterState.getRoutingNodes()), + e); + } + warnAboutSlowTaskIfNeeded(executionTime, task.source); + task.listener.onFailure(task.source, e); + return; + } + + if (previousClusterState == newClusterState) { + task.listener.clusterStateProcessed(task.source, newClusterState, newClusterState); + TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS))); + logger.debug("processing [{}]: took [{}] no change in cluster state", task.source, executionTime); + warnAboutSlowTaskIfNeeded(executionTime, task.source); + } else { + if (logger.isTraceEnabled()) { + logger.trace("cluster state updated, source [{}]\n{}", task.source, newClusterState); + } else if (logger.isDebugEnabled()) { + logger.debug("cluster state updated, version [{}], source [{}]", newClusterState.version(), task.source); + } + try { + applyChanges(task, previousClusterState, newClusterState); + TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS))); + logger.debug("processing [{}]: took [{}] done applying updated cluster state (version: {}, uuid: {})", task.source, + executionTime, newClusterState.version(), + newClusterState.stateUUID()); + warnAboutSlowTaskIfNeeded(executionTime, task.source); + } catch (Exception e) { + TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS))); + final long version = newClusterState.version(); + final String stateUUID = newClusterState.stateUUID(); + final String fullState = newClusterState.toString(); + logger.warn( + (Supplier) () -> new ParameterizedMessage( + "failed to apply updated cluster state in [{}]:\nversion [{}], uuid [{}], source [{}]\n{}", + executionTime, + version, + stateUUID, + task.source, + fullState), + e); + // TODO: do we want to call updateTask.onFailure here? + } + } + } + + private void applyChanges(UpdateTask task, ClusterState previousClusterState, ClusterState newClusterState) { + ClusterChangedEvent clusterChangedEvent = new ClusterChangedEvent(task.source, newClusterState, previousClusterState); + // new cluster state, notify all listeners + final DiscoveryNodes.Delta nodesDelta = clusterChangedEvent.nodesDelta(); + if (nodesDelta.hasChanges() && logger.isInfoEnabled()) { + String summary = nodesDelta.shortSummary(); + if (summary.length() > 0) { + logger.info("{}, reason: {}", summary, task.source); + } + } + + nodeConnectionsService.connectToNodes(newClusterState.nodes()); + + logger.debug("applying cluster state version {}", newClusterState.version()); + try { + // nothing to do until we actually recover from the gateway or any other block indicates we need to disable persistency + if (clusterChangedEvent.state().blocks().disableStatePersistence() == false && clusterChangedEvent.metaDataChanged()) { + final Settings incomingSettings = clusterChangedEvent.state().metaData().settings(); + clusterSettings.applySettings(incomingSettings); + } + } catch (Exception ex) { + logger.warn("failed to apply cluster settings", ex); + } + + logger.debug("apply cluster state with version {}", newClusterState.version()); + callClusterStateAppliers(clusterChangedEvent); + + nodeConnectionsService.disconnectFromNodesExcept(newClusterState.nodes()); + + logger.debug("set locally applied cluster state to version {}", newClusterState.version()); + state.set(newClusterState); + + callClusterStateListeners(clusterChangedEvent); + + task.listener.clusterStateProcessed(task.source, previousClusterState, newClusterState); + } + + private void callClusterStateAppliers(ClusterChangedEvent clusterChangedEvent) { + clusterStateAppliers.forEach(applier -> { + try { + logger.trace("calling [{}] with change to version [{}]", applier, clusterChangedEvent.state().version()); + applier.applyClusterState(clusterChangedEvent); + } catch (Exception ex) { + logger.warn("failed to notify ClusterStateApplier", ex); + } + }); + } + + private void callClusterStateListeners(ClusterChangedEvent clusterChangedEvent) { + Stream.concat(clusterStateListeners.stream(), timeoutClusterStateListeners.stream()).forEach(listener -> { + try { + logger.trace("calling [{}] with change to version [{}]", listener, clusterChangedEvent.state().version()); + listener.clusterChanged(clusterChangedEvent); + } catch (Exception ex) { + logger.warn("failed to notify ClusterStateListener", ex); + } + }); + } + + private static class SafeClusterStateTaskListener implements ClusterStateTaskListener { + private final ClusterStateTaskListener listener; + private final Logger logger; + + SafeClusterStateTaskListener(ClusterStateTaskListener listener, Logger logger) { + this.listener = listener; + this.logger = logger; + } + + @Override + public void onFailure(String source, Exception e) { + try { + listener.onFailure(source, e); + } catch (Exception inner) { + inner.addSuppressed(e); + logger.error( + (Supplier) () -> new ParameterizedMessage( + "exception thrown by listener notifying of failure from [{}]", source), inner); + } + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + try { + listener.clusterStateProcessed(source, oldState, newState); + } catch (Exception e) { + logger.error( + (Supplier) () -> new ParameterizedMessage( + "exception thrown by listener while notifying of cluster state processed from [{}], old cluster state:\n" + + "{}\nnew cluster state:\n{}", + source, oldState, newState), + e); + } + } + } + + protected void warnAboutSlowTaskIfNeeded(TimeValue executionTime, String source) { + if (executionTime.getMillis() > slowTaskLoggingThreshold.getMillis()) { + logger.warn("cluster state applier task [{}] took [{}] above the warn threshold of {}", source, executionTime, + slowTaskLoggingThreshold); + } + } + + class NotifyTimeout implements Runnable { + final TimeoutClusterStateListener listener; + final TimeValue timeout; + volatile ScheduledFuture future; + + NotifyTimeout(TimeoutClusterStateListener listener, TimeValue timeout) { + this.listener = listener; + this.timeout = timeout; + } + + public void cancel() { + FutureUtils.cancel(future); + } + + @Override + public void run() { + if (future != null && future.isCancelled()) { + return; + } + if (lifecycle.stoppedOrClosed()) { + listener.onClose(); + } else { + listener.onTimeout(this.timeout); + } + // note, we rely on the listener to remove itself in case of timeout if needed + } + } + + private static class LocalNodeMasterListeners implements ClusterStateListener { + + private final List listeners = new CopyOnWriteArrayList<>(); + private final ThreadPool threadPool; + private volatile boolean master = false; + + private LocalNodeMasterListeners(ThreadPool threadPool) { + this.threadPool = threadPool; + } + + @Override + public void clusterChanged(ClusterChangedEvent event) { + if (!master && event.localNodeMaster()) { + master = true; + for (LocalNodeMasterListener listener : listeners) { + java.util.concurrent.Executor executor = threadPool.executor(listener.executorName()); + executor.execute(new OnMasterRunnable(listener)); + } + return; + } + + if (master && !event.localNodeMaster()) { + master = false; + for (LocalNodeMasterListener listener : listeners) { + java.util.concurrent.Executor executor = threadPool.executor(listener.executorName()); + executor.execute(new OffMasterRunnable(listener)); + } + } + } + + private void add(LocalNodeMasterListener listener) { + listeners.add(listener); + } + + private void remove(LocalNodeMasterListener listener) { + listeners.remove(listener); + } + + private void clear() { + listeners.clear(); + } + } + + private static class OnMasterRunnable implements Runnable { + + private final LocalNodeMasterListener listener; + + private OnMasterRunnable(LocalNodeMasterListener listener) { + this.listener = listener; + } + + @Override + public void run() { + listener.onMaster(); + } + } + + private static class OffMasterRunnable implements Runnable { + + private final LocalNodeMasterListener listener; + + private OffMasterRunnable(LocalNodeMasterListener listener) { + this.listener = listener; + } + + @Override + public void run() { + listener.offMaster(); + } + } + + // this one is overridden in tests so we can control time + protected long currentTimeInNanos() { + return System.nanoTime(); + } +} diff --git a/core/src/main/java/org/elasticsearch/cluster/service/ClusterService.java b/core/src/main/java/org/elasticsearch/cluster/service/ClusterService.java index dd7d7bcbd60b3..18445e62c7eb1 100644 --- a/core/src/main/java/org/elasticsearch/cluster/service/ClusterService.java +++ b/core/src/main/java/org/elasticsearch/cluster/service/ClusterService.java @@ -19,264 +19,82 @@ package org.elasticsearch.cluster.service; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.apache.logging.log4j.util.Supplier; -import org.elasticsearch.cluster.AckedClusterStateTaskListener; -import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.ClusterState.Builder; import org.elasticsearch.cluster.ClusterStateApplier; import org.elasticsearch.cluster.ClusterStateListener; -import org.elasticsearch.cluster.ClusterStateObserver; import org.elasticsearch.cluster.ClusterStateTaskConfig; import org.elasticsearch.cluster.ClusterStateTaskExecutor; -import org.elasticsearch.cluster.ClusterStateTaskExecutor.ClusterTasksResult; import org.elasticsearch.cluster.ClusterStateTaskListener; import org.elasticsearch.cluster.LocalNodeMasterListener; import org.elasticsearch.cluster.NodeConnectionsService; import org.elasticsearch.cluster.TimeoutClusterStateListener; -import org.elasticsearch.cluster.block.ClusterBlock; -import org.elasticsearch.cluster.block.ClusterBlocks; -import org.elasticsearch.cluster.metadata.MetaData; -import org.elasticsearch.cluster.metadata.ProcessClusterEventTimeoutException; import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.routing.OperationRouting; -import org.elasticsearch.cluster.routing.RoutingTable; import org.elasticsearch.common.Nullable; -import org.elasticsearch.common.Priority; import org.elasticsearch.common.component.AbstractLifecycleComponent; -import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.text.Text; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.util.concurrent.ConcurrentCollections; -import org.elasticsearch.common.util.concurrent.CountDown; -import org.elasticsearch.common.util.concurrent.EsExecutors; -import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; -import org.elasticsearch.common.util.concurrent.FutureUtils; -import org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor; -import org.elasticsearch.common.util.iterable.Iterables; -import org.elasticsearch.discovery.Discovery; -import org.elasticsearch.discovery.DiscoverySettings; import org.elasticsearch.threadpool.ThreadPool; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.Objects; -import java.util.Queue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Executor; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiConsumer; -import java.util.function.UnaryOperator; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.elasticsearch.common.util.concurrent.EsExecutors.daemonThreadFactory; public class ClusterService extends AbstractLifecycleComponent { + private final MasterService masterService; + + private final ClusterApplierService clusterApplierService; + public static final Setting CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING = Setting.positiveTimeSetting("cluster.service.slow_task_logging_threshold", TimeValue.timeValueSeconds(30), Property.Dynamic, Property.NodeScope); - public static final String UPDATE_THREAD_NAME = "clusterService#updateTask"; - private final ThreadPool threadPool; private final ClusterName clusterName; - private final Supplier localNodeSupplier; - - private BiConsumer clusterStatePublisher; private final OperationRouting operationRouting; private final ClusterSettings clusterSettings; - private TimeValue slowTaskLoggingThreshold; - - private volatile PrioritizedEsThreadPoolExecutor threadPoolExecutor; - private volatile ClusterServiceTaskBatcher taskBatcher; - - /** - * Those 3 state listeners are changing infrequently - CopyOnWriteArrayList is just fine - */ - private final Collection highPriorityStateAppliers = new CopyOnWriteArrayList<>(); - private final Collection normalPriorityStateAppliers = new CopyOnWriteArrayList<>(); - private final Collection lowPriorityStateAppliers = new CopyOnWriteArrayList<>(); - private final Iterable clusterStateAppliers = Iterables.concat(highPriorityStateAppliers, - normalPriorityStateAppliers, lowPriorityStateAppliers); - - private final Collection clusterStateListeners = new CopyOnWriteArrayList<>(); - private final Collection timeoutClusterStateListeners = - Collections.newSetFromMap(new ConcurrentHashMap()); - - private final LocalNodeMasterListeners localNodeMasterListeners; - - private final Queue onGoingTimeouts = ConcurrentCollections.newQueue(); - - private final AtomicReference state; - - private final ClusterBlocks.Builder initialBlocks; - - private NodeConnectionsService nodeConnectionsService; - - private DiscoverySettings discoverySettings; - - public ClusterService(Settings settings, - ClusterSettings clusterSettings, ThreadPool threadPool, Supplier localNodeSupplier) { + public ClusterService(Settings settings, ClusterSettings clusterSettings, ThreadPool threadPool) { super(settings); - this.localNodeSupplier = localNodeSupplier; + this.clusterApplierService = new ClusterApplierService(settings, clusterSettings, threadPool); + this.masterService = new MasterService(settings, threadPool); this.operationRouting = new OperationRouting(settings, clusterSettings); - this.threadPool = threadPool; this.clusterSettings = clusterSettings; this.clusterName = ClusterName.CLUSTER_NAME_SETTING.get(settings); - // will be replaced on doStart. - this.state = new AtomicReference<>(ClusterState.builder(clusterName).build()); - this.clusterSettings.addSettingsUpdateConsumer(CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING, - this::setSlowTaskLoggingThreshold); - - this.slowTaskLoggingThreshold = CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING.get(settings); - - localNodeMasterListeners = new LocalNodeMasterListeners(threadPool); - - initialBlocks = ClusterBlocks.builder(); + this::setSlowTaskLoggingThreshold); } private void setSlowTaskLoggingThreshold(TimeValue slowTaskLoggingThreshold) { - this.slowTaskLoggingThreshold = slowTaskLoggingThreshold; - } - - public synchronized void setClusterStatePublisher(BiConsumer publisher) { - clusterStatePublisher = publisher; - } - - private void updateState(UnaryOperator updateFunction) { - this.state.getAndUpdate(updateFunction); + masterService.setSlowTaskLoggingThreshold(slowTaskLoggingThreshold); + clusterApplierService.setSlowTaskLoggingThreshold(slowTaskLoggingThreshold); } public synchronized void setNodeConnectionsService(NodeConnectionsService nodeConnectionsService) { - assert this.nodeConnectionsService == null : "nodeConnectionsService is already set"; - this.nodeConnectionsService = nodeConnectionsService; - } - - /** - * Adds an initial block to be set on the first cluster state created. - */ - public synchronized void addInitialStateBlock(ClusterBlock block) throws IllegalStateException { - if (lifecycle.started()) { - throw new IllegalStateException("can't set initial block when started"); - } - initialBlocks.addGlobalBlock(block); - } - - /** - * Remove an initial block to be set on the first cluster state created. - */ - public synchronized void removeInitialStateBlock(ClusterBlock block) throws IllegalStateException { - removeInitialStateBlock(block.id()); - } - - /** - * Remove an initial block to be set on the first cluster state created. - */ - public synchronized void removeInitialStateBlock(int blockId) throws IllegalStateException { - if (lifecycle.started()) { - throw new IllegalStateException("can't set initial block when started"); - } - initialBlocks.removeGlobalBlock(blockId); + clusterApplierService.setNodeConnectionsService(nodeConnectionsService); } @Override protected synchronized void doStart() { - Objects.requireNonNull(clusterStatePublisher, "please set a cluster state publisher before starting"); - Objects.requireNonNull(nodeConnectionsService, "please set the node connection service before starting"); - Objects.requireNonNull(discoverySettings, "please set discovery settings before starting"); - addListener(localNodeMasterListeners); - DiscoveryNode localNode = localNodeSupplier.get(); - assert localNode != null; - updateState(state -> { - assert state.nodes().getLocalNodeId() == null : "local node is already set"; - DiscoveryNodes nodes = DiscoveryNodes.builder(state.nodes()).add(localNode).localNodeId(localNode.getId()).build(); - return ClusterState.builder(state).nodes(nodes).blocks(initialBlocks).build(); - }); - this.threadPoolExecutor = EsExecutors.newSinglePrioritizing(UPDATE_THREAD_NAME, - daemonThreadFactory(settings, UPDATE_THREAD_NAME), threadPool.getThreadContext(), threadPool.scheduler()); - this.taskBatcher = new ClusterServiceTaskBatcher(logger, threadPoolExecutor); + clusterApplierService.start(); + masterService.start(); } @Override protected synchronized void doStop() { - for (NotifyTimeout onGoingTimeout : onGoingTimeouts) { - onGoingTimeout.cancel(); - try { - onGoingTimeout.cancel(); - onGoingTimeout.listener.onClose(); - } catch (Exception ex) { - logger.debug("failed to notify listeners on shutdown", ex); - } - } - ThreadPool.terminate(threadPoolExecutor, 10, TimeUnit.SECONDS); - // close timeout listeners that did not have an ongoing timeout - timeoutClusterStateListeners.forEach(TimeoutClusterStateListener::onClose); - removeListener(localNodeMasterListeners); + masterService.stop(); + clusterApplierService.stop(); } @Override protected synchronized void doClose() { - } - - class ClusterServiceTaskBatcher extends TaskBatcher { - - ClusterServiceTaskBatcher(Logger logger, PrioritizedEsThreadPoolExecutor threadExecutor) { - super(logger, threadExecutor); - } - - @Override - protected void onTimeout(List tasks, TimeValue timeout) { - threadPool.generic().execute( - () -> tasks.forEach( - task -> ((UpdateTask) task).listener.onFailure(task.source, - new ProcessClusterEventTimeoutException(timeout, task.source)))); - } - - @Override - protected void run(Object batchingKey, List tasks, String tasksSummary) { - ClusterStateTaskExecutor taskExecutor = (ClusterStateTaskExecutor) batchingKey; - List updateTasks = (List) tasks; - runTasks(new ClusterService.TaskInputs(taskExecutor, updateTasks, tasksSummary)); - } - - class UpdateTask extends BatchedTask { - final ClusterStateTaskListener listener; - - UpdateTask(Priority priority, String source, Object task, ClusterStateTaskListener listener, - ClusterStateTaskExecutor executor) { - super(priority, source, executor, task); - this.listener = listener; - } - - @Override - public String describeTasks(List tasks) { - return ((ClusterStateTaskExecutor) batchingKey).describeTasks( - tasks.stream().map(BatchedTask::getTask).collect(Collectors.toList())); - } - } + masterService.close(); + clusterApplierService.close(); } /** @@ -295,83 +113,74 @@ public OperationRouting operationRouting() { } /** - * The current cluster state. + * The currently applied cluster state. + * TODO: Should be renamed to appliedState / appliedClusterState */ public ClusterState state() { - assert assertNotCalledFromClusterStateApplier("the applied cluster state is not yet available"); - return this.state.get(); + return clusterApplierService.state(); } /** * Adds a high priority applier of updated cluster states. */ public void addHighPriorityApplier(ClusterStateApplier applier) { - highPriorityStateAppliers.add(applier); + clusterApplierService.addHighPriorityApplier(applier); } /** * Adds an applier which will be called after all high priority and normal appliers have been called. */ public void addLowPriorityApplier(ClusterStateApplier applier) { - lowPriorityStateAppliers.add(applier); + clusterApplierService.addLowPriorityApplier(applier); } /** * Adds a applier of updated cluster states. */ public void addStateApplier(ClusterStateApplier applier) { - normalPriorityStateAppliers.add(applier); + clusterApplierService.addStateApplier(applier); } /** * Removes an applier of updated cluster states. */ public void removeApplier(ClusterStateApplier applier) { - normalPriorityStateAppliers.remove(applier); - highPriorityStateAppliers.remove(applier); - lowPriorityStateAppliers.remove(applier); + clusterApplierService.removeApplier(applier); } /** * Add a listener for updated cluster states */ public void addListener(ClusterStateListener listener) { - clusterStateListeners.add(listener); + clusterApplierService.addListener(listener); } /** * Removes a listener for updated cluster states. */ public void removeListener(ClusterStateListener listener) { - clusterStateListeners.remove(listener); + clusterApplierService.removeListener(listener); } /** * Removes a timeout listener for updated cluster states. */ public void removeTimeoutListener(TimeoutClusterStateListener listener) { - timeoutClusterStateListeners.remove(listener); - for (Iterator it = onGoingTimeouts.iterator(); it.hasNext(); ) { - NotifyTimeout timeout = it.next(); - if (timeout.listener.equals(listener)) { - timeout.cancel(); - it.remove(); - } - } + clusterApplierService.removeTimeoutListener(listener); } /** * Add a listener for on/off local node master events */ public void addLocalNodeMasterListener(LocalNodeMasterListener listener) { - localNodeMasterListeners.add(listener); + clusterApplierService.addLocalNodeMasterListener(listener); } /** * Remove the given listener for on/off local master events */ public void removeLocalNodeMasterListener(LocalNodeMasterListener listener) { - localNodeMasterListeners.remove(listener); + clusterApplierService.removeLocalNodeMasterListener(listener); } /** @@ -381,32 +190,34 @@ public void removeLocalNodeMasterListener(LocalNodeMasterListener listener) { * NOTE: the listener is not removed on timeout. This is the responsibility of the caller. */ public void addTimeoutListener(@Nullable final TimeValue timeout, final TimeoutClusterStateListener listener) { - if (lifecycle.stoppedOrClosed()) { - listener.onClose(); - return; - } + clusterApplierService.addTimeoutListener(timeout, listener); + } - // call the post added notification on the same event thread - try { - threadPoolExecutor.execute(new SourcePrioritizedRunnable(Priority.HIGH, "_add_listener_") { - @Override - public void run() { - if (timeout != null) { - NotifyTimeout notifyTimeout = new NotifyTimeout(listener, timeout); - notifyTimeout.future = threadPool.schedule(timeout, ThreadPool.Names.GENERIC, notifyTimeout); - onGoingTimeouts.add(notifyTimeout); - } - timeoutClusterStateListeners.add(listener); - listener.postAdded(); - } - }); - } catch (EsRejectedExecutionException e) { - if (lifecycle.stoppedOrClosed()) { - listener.onClose(); - } else { - throw e; - } - } + public MasterService getMasterService() { + return masterService; + } + + public ClusterApplierService getClusterApplierService() { + return clusterApplierService; + } + + public static boolean assertClusterOrMasterStateThread() { + assert Thread.currentThread().getName().contains(ClusterApplierService.CLUSTER_UPDATE_THREAD_NAME) || + Thread.currentThread().getName().contains(MasterService.MASTER_UPDATE_THREAD_NAME) : + "not called from the master/cluster state update thread"; + return true; + } + + public ClusterName getClusterName() { + return clusterName; + } + + public ClusterSettings getClusterSettings() { + return clusterSettings; + } + + public Settings getSettings() { + return settings; } /** @@ -418,8 +229,8 @@ public void run() { * task * */ - public & ClusterStateTaskListener> void submitStateUpdateTask( - final String source, final T updateTask) { + public & ClusterStateTaskListener> + void submitStateUpdateTask(String source, T updateTask) { submitStateUpdateTask(source, updateTask, updateTask, updateTask, updateTask); } @@ -442,10 +253,10 @@ public & Cluster * @param the type of the cluster state update task state * */ - public void submitStateUpdateTask(final String source, final T task, - final ClusterStateTaskConfig config, - final ClusterStateTaskExecutor executor, - final ClusterStateTaskListener listener) { + public void submitStateUpdateTask(String source, T task, + ClusterStateTaskConfig config, + ClusterStateTaskExecutor executor, + ClusterStateTaskListener listener) { submitStateUpdateTasks(source, Collections.singletonMap(task, listener), config, executor); } @@ -465,761 +276,6 @@ public void submitStateUpdateTask(final String source, final T task, public void submitStateUpdateTasks(final String source, final Map tasks, final ClusterStateTaskConfig config, final ClusterStateTaskExecutor executor) { - if (!lifecycle.started()) { - return; - } - try { - List safeTasks = tasks.entrySet().stream() - .map(e -> taskBatcher.new UpdateTask(config.priority(), source, e.getKey(), safe(e.getValue(), logger), executor)) - .collect(Collectors.toList()); - taskBatcher.submitTasks(safeTasks, config.timeout()); - } catch (EsRejectedExecutionException e) { - // ignore cases where we are shutting down..., there is really nothing interesting - // to be done here... - if (!lifecycle.stoppedOrClosed()) { - throw e; - } - } - } - - /** - * Returns the tasks that are pending. - */ - public List pendingTasks() { - return Arrays.stream(threadPoolExecutor.getPending()).map(pending -> { - assert pending.task instanceof SourcePrioritizedRunnable : - "thread pool executor should only use SourcePrioritizedRunnable instances but found: " + pending.task.getClass().getName(); - SourcePrioritizedRunnable task = (SourcePrioritizedRunnable) pending.task; - return new PendingClusterTask(pending.insertionOrder, pending.priority, new Text(task.source()), - task.getAgeInMillis(), pending.executing); - }).collect(Collectors.toList()); - } - - /** - * Returns the number of currently pending tasks. - */ - public int numberOfPendingTasks() { - return threadPoolExecutor.getNumberOfPendingTasks(); - } - - /** - * Returns the maximum wait time for tasks in the queue - * - * @return A zero time value if the queue is empty, otherwise the time value oldest task waiting in the queue - */ - public TimeValue getMaxTaskWaitTime() { - return threadPoolExecutor.getMaxTaskWaitTime(); - } - - /** asserts that the current thread is the cluster state update thread */ - public static boolean assertClusterStateThread() { - assert Thread.currentThread().getName().contains(ClusterService.UPDATE_THREAD_NAME) : - "not called from the cluster state update thread"; - return true; - } - - /** asserts that the current thread is NOT the cluster state update thread */ - public static boolean assertNotClusterStateUpdateThread(String reason) { - assert Thread.currentThread().getName().contains(UPDATE_THREAD_NAME) == false : - "Expected current thread [" + Thread.currentThread() + "] to not be the cluster state update thread. Reason: [" + reason + "]"; - return true; - } - - /** asserts that the current stack trace does NOT involve a cluster state applier */ - private static boolean assertNotCalledFromClusterStateApplier(String reason) { - if (Thread.currentThread().getName().contains(UPDATE_THREAD_NAME)) { - for (StackTraceElement element : Thread.currentThread().getStackTrace()) { - final String className = element.getClassName(); - final String methodName = element.getMethodName(); - if (className.equals(ClusterStateObserver.class.getName())) { - // people may start an observer from an applier - return true; - } else if (className.equals(ClusterService.class.getName()) - && methodName.equals("callClusterStateAppliers")) { - throw new AssertionError("should not be called by a cluster state applier. reason [" + reason + "]"); - } - } - } - return true; - } - - public ClusterName getClusterName() { - return clusterName; - } - - public void setDiscoverySettings(DiscoverySettings discoverySettings) { - this.discoverySettings = discoverySettings; - } - - void runTasks(TaskInputs taskInputs) { - if (!lifecycle.started()) { - logger.debug("processing [{}]: ignoring, cluster service not started", taskInputs.summary); - return; - } - - logger.debug("processing [{}]: execute", taskInputs.summary); - ClusterState previousClusterState = state(); - - if (!previousClusterState.nodes().isLocalNodeElectedMaster() && taskInputs.runOnlyOnMaster()) { - logger.debug("failing [{}]: local node is no longer master", taskInputs.summary); - taskInputs.onNoLongerMaster(); - return; - } - - long startTimeNS = currentTimeInNanos(); - TaskOutputs taskOutputs = calculateTaskOutputs(taskInputs, previousClusterState, startTimeNS); - taskOutputs.notifyFailedTasks(); - - if (taskOutputs.clusterStateUnchanged()) { - taskOutputs.notifySuccessfulTasksOnUnchangedClusterState(); - TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS))); - logger.debug("processing [{}]: took [{}] no change in cluster_state", taskInputs.summary, executionTime); - warnAboutSlowTaskIfNeeded(executionTime, taskInputs.summary); - } else { - ClusterState newClusterState = taskOutputs.newClusterState; - if (logger.isTraceEnabled()) { - logger.trace("cluster state updated, source [{}]\n{}", taskInputs.summary, newClusterState); - } else if (logger.isDebugEnabled()) { - logger.debug("cluster state updated, version [{}], source [{}]", newClusterState.version(), taskInputs.summary); - } - try { - publishAndApplyChanges(taskInputs, taskOutputs); - TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS))); - logger.debug("processing [{}]: took [{}] done applying updated cluster_state (version: {}, uuid: {})", taskInputs.summary, - executionTime, newClusterState.version(), newClusterState.stateUUID()); - warnAboutSlowTaskIfNeeded(executionTime, taskInputs.summary); - } catch (Exception e) { - TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS))); - final long version = newClusterState.version(); - final String stateUUID = newClusterState.stateUUID(); - final String fullState = newClusterState.toString(); - logger.warn( - (Supplier) () -> new ParameterizedMessage( - "failed to apply updated cluster state in [{}]:\nversion [{}], uuid [{}], source [{}]\n{}", - executionTime, - version, - stateUUID, - taskInputs.summary, - fullState), - e); - // TODO: do we want to call updateTask.onFailure here? - } - } - } - - public TaskOutputs calculateTaskOutputs(TaskInputs taskInputs, ClusterState previousClusterState, long startTimeNS) { - ClusterTasksResult clusterTasksResult = executeTasks(taskInputs, startTimeNS, previousClusterState); - // extract those that are waiting for results - List nonFailedTasks = new ArrayList<>(); - for (ClusterServiceTaskBatcher.UpdateTask updateTask : taskInputs.updateTasks) { - assert clusterTasksResult.executionResults.containsKey(updateTask.task) : "missing " + updateTask; - final ClusterStateTaskExecutor.TaskResult taskResult = - clusterTasksResult.executionResults.get(updateTask.task); - if (taskResult.isSuccess()) { - nonFailedTasks.add(updateTask); - } - } - ClusterState newClusterState = patchVersionsAndNoMasterBlocks(previousClusterState, clusterTasksResult); - - return new TaskOutputs(taskInputs, previousClusterState, newClusterState, nonFailedTasks, - clusterTasksResult.executionResults); - } - - private ClusterTasksResult executeTasks(TaskInputs taskInputs, long startTimeNS, ClusterState previousClusterState) { - ClusterTasksResult clusterTasksResult; - try { - List inputs = taskInputs.updateTasks.stream() - .map(ClusterServiceTaskBatcher.UpdateTask::getTask).collect(Collectors.toList()); - clusterTasksResult = taskInputs.executor.execute(previousClusterState, inputs); - } catch (Exception e) { - TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS))); - if (logger.isTraceEnabled()) { - logger.trace( - (Supplier) () -> new ParameterizedMessage( - "failed to execute cluster state update in [{}], state:\nversion [{}], source [{}]\n{}{}{}", - executionTime, - previousClusterState.version(), - taskInputs.summary, - previousClusterState.nodes(), - previousClusterState.routingTable(), - previousClusterState.getRoutingNodes()), - e); - } - warnAboutSlowTaskIfNeeded(executionTime, taskInputs.summary); - clusterTasksResult = ClusterTasksResult.builder() - .failures(taskInputs.updateTasks.stream().map(ClusterServiceTaskBatcher.UpdateTask::getTask)::iterator, e) - .build(previousClusterState); - } - - assert clusterTasksResult.executionResults != null; - assert clusterTasksResult.executionResults.size() == taskInputs.updateTasks.size() - : String.format(Locale.ROOT, "expected [%d] task result%s but was [%d]", taskInputs.updateTasks.size(), - taskInputs.updateTasks.size() == 1 ? "" : "s", clusterTasksResult.executionResults.size()); - boolean assertsEnabled = false; - assert (assertsEnabled = true); - if (assertsEnabled) { - for (ClusterServiceTaskBatcher.UpdateTask updateTask : taskInputs.updateTasks) { - assert clusterTasksResult.executionResults.containsKey(updateTask.task) : - "missing task result for " + updateTask; - } - } - - return clusterTasksResult; - } - - private ClusterState patchVersionsAndNoMasterBlocks(ClusterState previousClusterState, ClusterTasksResult executionResult) { - ClusterState newClusterState = executionResult.resultingState; - - if (executionResult.noMaster) { - assert newClusterState == previousClusterState : "state can only be changed by ClusterService when noMaster = true"; - if (previousClusterState.nodes().getMasterNodeId() != null) { - // remove block if it already exists before adding new one - assert previousClusterState.blocks().hasGlobalBlock(discoverySettings.getNoMasterBlock().id()) == false : - "NO_MASTER_BLOCK should only be added by ClusterService"; - ClusterBlocks clusterBlocks = ClusterBlocks.builder().blocks(previousClusterState.blocks()) - .addGlobalBlock(discoverySettings.getNoMasterBlock()) - .build(); - - DiscoveryNodes discoveryNodes = new DiscoveryNodes.Builder(previousClusterState.nodes()).masterNodeId(null).build(); - newClusterState = ClusterState.builder(previousClusterState) - .blocks(clusterBlocks) - .nodes(discoveryNodes) - .build(); - } - } else if (newClusterState.nodes().isLocalNodeElectedMaster() && previousClusterState != newClusterState) { - // only the master controls the version numbers - Builder builder = ClusterState.builder(newClusterState).incrementVersion(); - if (previousClusterState.routingTable() != newClusterState.routingTable()) { - builder.routingTable(RoutingTable.builder(newClusterState.routingTable()) - .version(newClusterState.routingTable().version() + 1).build()); - } - if (previousClusterState.metaData() != newClusterState.metaData()) { - builder.metaData(MetaData.builder(newClusterState.metaData()).version(newClusterState.metaData().version() + 1)); - } - - // remove the no master block, if it exists - if (newClusterState.blocks().hasGlobalBlock(discoverySettings.getNoMasterBlock().id())) { - builder.blocks(ClusterBlocks.builder().blocks(newClusterState.blocks()) - .removeGlobalBlock(discoverySettings.getNoMasterBlock().id())); - } - - newClusterState = builder.build(); - } - - assert newClusterState.nodes().getMasterNodeId() == null || - newClusterState.blocks().hasGlobalBlock(discoverySettings.getNoMasterBlock().id()) == false : - "cluster state with master node must not have NO_MASTER_BLOCK"; - - return newClusterState; - } - - private void publishAndApplyChanges(TaskInputs taskInputs, TaskOutputs taskOutputs) { - ClusterState previousClusterState = taskOutputs.previousClusterState; - ClusterState newClusterState = taskOutputs.newClusterState; - - ClusterChangedEvent clusterChangedEvent = new ClusterChangedEvent(taskInputs.summary, newClusterState, previousClusterState); - // new cluster state, notify all listeners - final DiscoveryNodes.Delta nodesDelta = clusterChangedEvent.nodesDelta(); - if (nodesDelta.hasChanges() && logger.isInfoEnabled()) { - String summary = nodesDelta.shortSummary(); - if (summary.length() > 0) { - logger.info("{}, reason: {}", summary, taskInputs.summary); - } - } - - final Discovery.AckListener ackListener = newClusterState.nodes().isLocalNodeElectedMaster() ? - taskOutputs.createAckListener(threadPool, newClusterState) : - null; - - nodeConnectionsService.connectToNodes(newClusterState.nodes()); - - // if we are the master, publish the new state to all nodes - // we publish here before we send a notification to all the listeners, since if it fails - // we don't want to notify - if (newClusterState.nodes().isLocalNodeElectedMaster()) { - logger.debug("publishing cluster state version [{}]", newClusterState.version()); - try { - clusterStatePublisher.accept(clusterChangedEvent, ackListener); - } catch (Discovery.FailedToCommitClusterStateException t) { - final long version = newClusterState.version(); - logger.warn( - (Supplier) () -> new ParameterizedMessage( - "failing [{}]: failed to commit cluster state version [{}]", taskInputs.summary, version), - t); - // ensure that list of connected nodes in NodeConnectionsService is in-sync with the nodes of the current cluster state - nodeConnectionsService.connectToNodes(previousClusterState.nodes()); - nodeConnectionsService.disconnectFromNodesExcept(previousClusterState.nodes()); - taskOutputs.publishingFailed(t); - return; - } - } - - logger.debug("applying cluster state version {}", newClusterState.version()); - try { - // nothing to do until we actually recover from the gateway or any other block indicates we need to disable persistency - if (clusterChangedEvent.state().blocks().disableStatePersistence() == false && clusterChangedEvent.metaDataChanged()) { - final Settings incomingSettings = clusterChangedEvent.state().metaData().settings(); - clusterSettings.applySettings(incomingSettings); - } - } catch (Exception ex) { - logger.warn("failed to apply cluster settings", ex); - } - - logger.debug("set local cluster state to version {}", newClusterState.version()); - callClusterStateAppliers(newClusterState, clusterChangedEvent); - - nodeConnectionsService.disconnectFromNodesExcept(newClusterState.nodes()); - - updateState(css -> newClusterState); - - Stream.concat(clusterStateListeners.stream(), timeoutClusterStateListeners.stream()).forEach(listener -> { - try { - logger.trace("calling [{}] with change to version [{}]", listener, newClusterState.version()); - listener.clusterChanged(clusterChangedEvent); - } catch (Exception ex) { - logger.warn("failed to notify ClusterStateListener", ex); - } - }); - - //manual ack only from the master at the end of the publish - if (newClusterState.nodes().isLocalNodeElectedMaster()) { - try { - ackListener.onNodeAck(newClusterState.nodes().getLocalNode(), null); - } catch (Exception e) { - final DiscoveryNode localNode = newClusterState.nodes().getLocalNode(); - logger.debug( - (Supplier) () -> new ParameterizedMessage("error while processing ack for master node [{}]", localNode), - e); - } - } - - taskOutputs.processedDifferentClusterState(previousClusterState, newClusterState); - - if (newClusterState.nodes().isLocalNodeElectedMaster()) { - try { - taskOutputs.clusterStatePublished(clusterChangedEvent); - } catch (Exception e) { - logger.error( - (Supplier) () -> new ParameterizedMessage( - "exception thrown while notifying executor of new cluster state publication [{}]", - taskInputs.summary), - e); - } - } - } - - private void callClusterStateAppliers(ClusterState newClusterState, ClusterChangedEvent clusterChangedEvent) { - for (ClusterStateApplier applier : clusterStateAppliers) { - try { - logger.trace("calling [{}] with change to version [{}]", applier, newClusterState.version()); - applier.applyClusterState(clusterChangedEvent); - } catch (Exception ex) { - logger.warn("failed to notify ClusterStateApplier", ex); - } - } - } - - /** - * Represents a set of tasks to be processed together with their executor - */ - class TaskInputs { - public final String summary; - public final List updateTasks; - public final ClusterStateTaskExecutor executor; - - TaskInputs(ClusterStateTaskExecutor executor, List updateTasks, String summary) { - this.summary = summary; - this.executor = executor; - this.updateTasks = updateTasks; - } - - public boolean runOnlyOnMaster() { - return executor.runOnlyOnMaster(); - } - - public void onNoLongerMaster() { - updateTasks.stream().forEach(task -> task.listener.onNoLongerMaster(task.source)); - } - } - - /** - * Output created by executing a set of tasks provided as TaskInputs - */ - class TaskOutputs { - public final TaskInputs taskInputs; - public final ClusterState previousClusterState; - public final ClusterState newClusterState; - public final List nonFailedTasks; - public final Map executionResults; - - TaskOutputs(TaskInputs taskInputs, ClusterState previousClusterState, - ClusterState newClusterState, List nonFailedTasks, - Map executionResults) { - this.taskInputs = taskInputs; - this.previousClusterState = previousClusterState; - this.newClusterState = newClusterState; - this.nonFailedTasks = nonFailedTasks; - this.executionResults = executionResults; - } - - public void publishingFailed(Discovery.FailedToCommitClusterStateException t) { - nonFailedTasks.forEach(task -> task.listener.onFailure(task.source, t)); - } - - public void processedDifferentClusterState(ClusterState previousClusterState, ClusterState newClusterState) { - nonFailedTasks.forEach(task -> task.listener.clusterStateProcessed(task.source, previousClusterState, newClusterState)); - } - - public void clusterStatePublished(ClusterChangedEvent clusterChangedEvent) { - taskInputs.executor.clusterStatePublished(clusterChangedEvent); - } - - public Discovery.AckListener createAckListener(ThreadPool threadPool, ClusterState newClusterState) { - ArrayList ackListeners = new ArrayList<>(); - - //timeout straightaway, otherwise we could wait forever as the timeout thread has not started - nonFailedTasks.stream().filter(task -> task.listener instanceof AckedClusterStateTaskListener).forEach(task -> { - final AckedClusterStateTaskListener ackedListener = (AckedClusterStateTaskListener) task.listener; - if (ackedListener.ackTimeout() == null || ackedListener.ackTimeout().millis() == 0) { - ackedListener.onAckTimeout(); - } else { - try { - ackListeners.add(new AckCountDownListener(ackedListener, newClusterState.version(), newClusterState.nodes(), - threadPool)); - } catch (EsRejectedExecutionException ex) { - if (logger.isDebugEnabled()) { - logger.debug("Couldn't schedule timeout thread - node might be shutting down", ex); - } - //timeout straightaway, otherwise we could wait forever as the timeout thread has not started - ackedListener.onAckTimeout(); - } - } - }); - - return new DelegetingAckListener(ackListeners); - } - - public boolean clusterStateUnchanged() { - return previousClusterState == newClusterState; - } - - public void notifyFailedTasks() { - // fail all tasks that have failed - for (ClusterServiceTaskBatcher.UpdateTask updateTask : taskInputs.updateTasks) { - assert executionResults.containsKey(updateTask.task) : "missing " + updateTask; - final ClusterStateTaskExecutor.TaskResult taskResult = executionResults.get(updateTask.task); - if (taskResult.isSuccess() == false) { - updateTask.listener.onFailure(updateTask.source, taskResult.getFailure()); - } - } - } - - public void notifySuccessfulTasksOnUnchangedClusterState() { - nonFailedTasks.forEach(task -> { - if (task.listener instanceof AckedClusterStateTaskListener) { - //no need to wait for ack if nothing changed, the update can be counted as acknowledged - ((AckedClusterStateTaskListener) task.listener).onAllNodesAcked(null); - } - task.listener.clusterStateProcessed(task.source, newClusterState, newClusterState); - }); - } - } - - // this one is overridden in tests so we can control time - protected long currentTimeInNanos() { - return System.nanoTime(); - } - - private static SafeClusterStateTaskListener safe(ClusterStateTaskListener listener, Logger logger) { - if (listener instanceof AckedClusterStateTaskListener) { - return new SafeAckedClusterStateTaskListener((AckedClusterStateTaskListener) listener, logger); - } else { - return new SafeClusterStateTaskListener(listener, logger); - } - } - - private static class SafeClusterStateTaskListener implements ClusterStateTaskListener { - private final ClusterStateTaskListener listener; - private final Logger logger; - - SafeClusterStateTaskListener(ClusterStateTaskListener listener, Logger logger) { - this.listener = listener; - this.logger = logger; - } - - @Override - public void onFailure(String source, Exception e) { - try { - listener.onFailure(source, e); - } catch (Exception inner) { - inner.addSuppressed(e); - logger.error( - (Supplier) () -> new ParameterizedMessage( - "exception thrown by listener notifying of failure from [{}]", source), inner); - } - } - - @Override - public void onNoLongerMaster(String source) { - try { - listener.onNoLongerMaster(source); - } catch (Exception e) { - logger.error( - (Supplier) () -> new ParameterizedMessage( - "exception thrown by listener while notifying no longer master from [{}]", source), e); - } - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - try { - listener.clusterStateProcessed(source, oldState, newState); - } catch (Exception e) { - logger.error( - (Supplier) () -> new ParameterizedMessage( - "exception thrown by listener while notifying of cluster state processed from [{}], old cluster state:\n" + - "{}\nnew cluster state:\n{}", - source, oldState, newState), - e); - } - } - } - - private static class SafeAckedClusterStateTaskListener extends SafeClusterStateTaskListener implements AckedClusterStateTaskListener { - private final AckedClusterStateTaskListener listener; - private final Logger logger; - - SafeAckedClusterStateTaskListener(AckedClusterStateTaskListener listener, Logger logger) { - super(listener, logger); - this.listener = listener; - this.logger = logger; - } - - @Override - public boolean mustAck(DiscoveryNode discoveryNode) { - return listener.mustAck(discoveryNode); - } - - @Override - public void onAllNodesAcked(@Nullable Exception e) { - try { - listener.onAllNodesAcked(e); - } catch (Exception inner) { - inner.addSuppressed(e); - logger.error("exception thrown by listener while notifying on all nodes acked", inner); - } - } - - @Override - public void onAckTimeout() { - try { - listener.onAckTimeout(); - } catch (Exception e) { - logger.error("exception thrown by listener while notifying on ack timeout", e); - } - } - - @Override - public TimeValue ackTimeout() { - return listener.ackTimeout(); - } - } - - private void warnAboutSlowTaskIfNeeded(TimeValue executionTime, String source) { - if (executionTime.getMillis() > slowTaskLoggingThreshold.getMillis()) { - logger.warn("cluster state update task [{}] took [{}] above the warn threshold of {}", source, executionTime, - slowTaskLoggingThreshold); - } - } - - class NotifyTimeout implements Runnable { - final TimeoutClusterStateListener listener; - final TimeValue timeout; - volatile ScheduledFuture future; - - NotifyTimeout(TimeoutClusterStateListener listener, TimeValue timeout) { - this.listener = listener; - this.timeout = timeout; - } - - public void cancel() { - FutureUtils.cancel(future); - } - - @Override - public void run() { - if (future != null && future.isCancelled()) { - return; - } - if (lifecycle.stoppedOrClosed()) { - listener.onClose(); - } else { - listener.onTimeout(this.timeout); - } - // note, we rely on the listener to remove itself in case of timeout if needed - } - } - - private static class LocalNodeMasterListeners implements ClusterStateListener { - - private final List listeners = new CopyOnWriteArrayList<>(); - private final ThreadPool threadPool; - private volatile boolean master = false; - - private LocalNodeMasterListeners(ThreadPool threadPool) { - this.threadPool = threadPool; - } - - @Override - public void clusterChanged(ClusterChangedEvent event) { - if (!master && event.localNodeMaster()) { - master = true; - for (LocalNodeMasterListener listener : listeners) { - Executor executor = threadPool.executor(listener.executorName()); - executor.execute(new OnMasterRunnable(listener)); - } - return; - } - - if (master && !event.localNodeMaster()) { - master = false; - for (LocalNodeMasterListener listener : listeners) { - Executor executor = threadPool.executor(listener.executorName()); - executor.execute(new OffMasterRunnable(listener)); - } - } - } - - private void add(LocalNodeMasterListener listener) { - listeners.add(listener); - } - - private void remove(LocalNodeMasterListener listener) { - listeners.remove(listener); - } - - private void clear() { - listeners.clear(); - } - } - - private static class OnMasterRunnable implements Runnable { - - private final LocalNodeMasterListener listener; - - private OnMasterRunnable(LocalNodeMasterListener listener) { - this.listener = listener; - } - - @Override - public void run() { - listener.onMaster(); - } - } - - private static class OffMasterRunnable implements Runnable { - - private final LocalNodeMasterListener listener; - - private OffMasterRunnable(LocalNodeMasterListener listener) { - this.listener = listener; - } - - @Override - public void run() { - listener.offMaster(); - } - } - - private static class DelegetingAckListener implements Discovery.AckListener { - - private final List listeners; - - private DelegetingAckListener(List listeners) { - this.listeners = listeners; - } - - @Override - public void onNodeAck(DiscoveryNode node, @Nullable Exception e) { - for (Discovery.AckListener listener : listeners) { - listener.onNodeAck(node, e); - } - } - - @Override - public void onTimeout() { - throw new UnsupportedOperationException("no timeout delegation"); - } - } - - private static class AckCountDownListener implements Discovery.AckListener { - - private static final Logger logger = Loggers.getLogger(AckCountDownListener.class); - - private final AckedClusterStateTaskListener ackedTaskListener; - private final CountDown countDown; - private final DiscoveryNodes nodes; - private final long clusterStateVersion; - private final Future ackTimeoutCallback; - private Exception lastFailure; - - AckCountDownListener(AckedClusterStateTaskListener ackedTaskListener, long clusterStateVersion, DiscoveryNodes nodes, - ThreadPool threadPool) { - this.ackedTaskListener = ackedTaskListener; - this.clusterStateVersion = clusterStateVersion; - this.nodes = nodes; - int countDown = 0; - for (DiscoveryNode node : nodes) { - if (ackedTaskListener.mustAck(node)) { - countDown++; - } - } - //we always wait for at least 1 node (the master) - countDown = Math.max(1, countDown); - logger.trace("expecting {} acknowledgements for cluster_state update (version: {})", countDown, clusterStateVersion); - this.countDown = new CountDown(countDown); - this.ackTimeoutCallback = threadPool.schedule(ackedTaskListener.ackTimeout(), ThreadPool.Names.GENERIC, () -> onTimeout()); - } - - @Override - public void onNodeAck(DiscoveryNode node, @Nullable Exception e) { - if (!ackedTaskListener.mustAck(node)) { - //we always wait for the master ack anyway - if (!node.equals(nodes.getMasterNode())) { - return; - } - } - if (e == null) { - logger.trace("ack received from node [{}], cluster_state update (version: {})", node, clusterStateVersion); - } else { - this.lastFailure = e; - logger.debug( - (Supplier) () -> new ParameterizedMessage( - "ack received from node [{}], cluster_state update (version: {})", node, clusterStateVersion), - e); - } - - if (countDown.countDown()) { - logger.trace("all expected nodes acknowledged cluster_state update (version: {})", clusterStateVersion); - FutureUtils.cancel(ackTimeoutCallback); - ackedTaskListener.onAllNodesAcked(lastFailure); - } - } - - @Override - public void onTimeout() { - if (countDown.fastForward()) { - logger.trace("timeout waiting for acknowledgement for cluster_state update (version: {})", clusterStateVersion); - ackedTaskListener.onAckTimeout(); - } - } - } - - public ClusterSettings getClusterSettings() { - return clusterSettings; - } - - public Settings getSettings() { - return settings; + masterService.submitStateUpdateTasks(source, tasks, config, executor); } } diff --git a/core/src/main/java/org/elasticsearch/cluster/service/MasterService.java b/core/src/main/java/org/elasticsearch/cluster/service/MasterService.java new file mode 100644 index 0000000000000..46d9eaef7ab34 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/cluster/service/MasterService.java @@ -0,0 +1,752 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.cluster.service; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.logging.log4j.util.Supplier; +import org.elasticsearch.cluster.AckedClusterStateTaskListener; +import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterState.Builder; +import org.elasticsearch.cluster.ClusterStateTaskConfig; +import org.elasticsearch.cluster.ClusterStateTaskExecutor; +import org.elasticsearch.cluster.ClusterStateTaskExecutor.ClusterTasksResult; +import org.elasticsearch.cluster.ClusterStateTaskListener; +import org.elasticsearch.cluster.metadata.MetaData; +import org.elasticsearch.cluster.metadata.ProcessClusterEventTimeoutException; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.cluster.routing.RoutingTable; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.Priority; +import org.elasticsearch.common.component.AbstractLifecycleComponent; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.text.Text; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.util.concurrent.CountDown; +import org.elasticsearch.common.util.concurrent.EsExecutors; +import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; +import org.elasticsearch.common.util.concurrent.FutureUtils; +import org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor; +import org.elasticsearch.discovery.Discovery; +import org.elasticsearch.threadpool.ThreadPool; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +import static org.elasticsearch.cluster.service.ClusterService.CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING; +import static org.elasticsearch.common.util.concurrent.EsExecutors.daemonThreadFactory; + +public class MasterService extends AbstractLifecycleComponent { + + public static final String MASTER_UPDATE_THREAD_NAME = "masterService#updateTask"; + + private BiConsumer clusterStatePublisher; + + private java.util.function.Supplier clusterStateSupplier; + + private volatile TimeValue slowTaskLoggingThreshold; + + protected final ThreadPool threadPool; + + private volatile PrioritizedEsThreadPoolExecutor threadPoolExecutor; + private volatile Batcher taskBatcher; + + public MasterService(Settings settings, ThreadPool threadPool) { + super(settings); + // TODO: introduce a dedicated setting for master service + this.slowTaskLoggingThreshold = CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING.get(settings); + this.threadPool = threadPool; + } + + public void setSlowTaskLoggingThreshold(TimeValue slowTaskLoggingThreshold) { + this.slowTaskLoggingThreshold = slowTaskLoggingThreshold; + } + + public synchronized void setClusterStatePublisher(BiConsumer publisher) { + clusterStatePublisher = publisher; + } + + public synchronized void setClusterStateSupplier(java.util.function.Supplier clusterStateSupplier) { + this.clusterStateSupplier = clusterStateSupplier; + } + + @Override + protected synchronized void doStart() { + Objects.requireNonNull(clusterStatePublisher, "please set a cluster state publisher before starting"); + Objects.requireNonNull(clusterStateSupplier, "please set a cluster state supplier before starting"); + threadPoolExecutor = EsExecutors.newSinglePrioritizing(MASTER_UPDATE_THREAD_NAME, + daemonThreadFactory(settings, MASTER_UPDATE_THREAD_NAME), threadPool.getThreadContext(), threadPool.scheduler()); + taskBatcher = new Batcher(logger, threadPoolExecutor); + } + + class Batcher extends TaskBatcher { + + Batcher(Logger logger, PrioritizedEsThreadPoolExecutor threadExecutor) { + super(logger, threadExecutor); + } + + @Override + protected void onTimeout(List tasks, TimeValue timeout) { + threadPool.generic().execute( + () -> tasks.forEach( + task -> ((UpdateTask) task).listener.onFailure(task.source, + new ProcessClusterEventTimeoutException(timeout, task.source)))); + } + + @Override + protected void run(Object batchingKey, List tasks, String tasksSummary) { + ClusterStateTaskExecutor taskExecutor = (ClusterStateTaskExecutor) batchingKey; + List updateTasks = (List) tasks; + runTasks(new TaskInputs(taskExecutor, updateTasks, tasksSummary)); + } + + class UpdateTask extends BatchedTask { + final ClusterStateTaskListener listener; + + UpdateTask(Priority priority, String source, Object task, ClusterStateTaskListener listener, + ClusterStateTaskExecutor executor) { + super(priority, source, executor, task); + this.listener = listener; + } + + @Override + public String describeTasks(List tasks) { + return ((ClusterStateTaskExecutor) batchingKey).describeTasks( + tasks.stream().map(BatchedTask::getTask).collect(Collectors.toList())); + } + } + } + + @Override + protected synchronized void doStop() { + ThreadPool.terminate(threadPoolExecutor, 10, TimeUnit.SECONDS); + } + + @Override + protected synchronized void doClose() { + } + + /** + * The current cluster state exposed by the discovery layer. Package-visible for tests. + */ + ClusterState state() { + return clusterStateSupplier.get(); + } + + public static boolean assertMasterUpdateThread() { + assert Thread.currentThread().getName().contains(MASTER_UPDATE_THREAD_NAME) : + "not called from the master service thread"; + return true; + } + + public static boolean assertNotMasterUpdateThread(String reason) { + assert Thread.currentThread().getName().contains(MASTER_UPDATE_THREAD_NAME) == false : + "Expected current thread [" + Thread.currentThread() + "] to not be the master service thread. Reason: [" + reason + "]"; + return true; + } + + protected void runTasks(TaskInputs taskInputs) { + final String summary = taskInputs.summary; + if (!lifecycle.started()) { + logger.debug("processing [{}]: ignoring, master service not started", summary); + return; + } + + logger.debug("processing [{}]: execute", summary); + final ClusterState previousClusterState = state(); + + if (!previousClusterState.nodes().isLocalNodeElectedMaster() && taskInputs.runOnlyWhenMaster()) { + logger.debug("failing [{}]: local node is no longer master", summary); + taskInputs.onNoLongerMaster(); + return; + } + + long startTimeNS = currentTimeInNanos(); + TaskOutputs taskOutputs = calculateTaskOutputs(taskInputs, previousClusterState, startTimeNS); + taskOutputs.notifyFailedTasks(); + + if (taskOutputs.clusterStateUnchanged()) { + taskOutputs.notifySuccessfulTasksOnUnchangedClusterState(); + TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS))); + logger.debug("processing [{}]: took [{}] no change in cluster state", summary, executionTime); + warnAboutSlowTaskIfNeeded(executionTime, summary); + } else { + ClusterState newClusterState = taskOutputs.newClusterState; + if (logger.isTraceEnabled()) { + logger.trace("cluster state updated, source [{}]\n{}", summary, newClusterState); + } else if (logger.isDebugEnabled()) { + logger.debug("cluster state updated, version [{}], source [{}]", newClusterState.version(), summary); + } + try { + ClusterChangedEvent clusterChangedEvent = new ClusterChangedEvent(summary, newClusterState, previousClusterState); + // new cluster state, notify all listeners + final DiscoveryNodes.Delta nodesDelta = clusterChangedEvent.nodesDelta(); + if (nodesDelta.hasChanges() && logger.isInfoEnabled()) { + String nodeSummary = nodesDelta.shortSummary(); + if (nodeSummary.length() > 0) { + logger.info("{}, reason: {}", summary, nodeSummary); + } + } + + logger.debug("publishing cluster state version [{}]", newClusterState.version()); + try { + clusterStatePublisher.accept(clusterChangedEvent, taskOutputs.createAckListener(threadPool, newClusterState)); + } catch (Discovery.FailedToCommitClusterStateException t) { + final long version = newClusterState.version(); + logger.warn( + (Supplier) () -> new ParameterizedMessage( + "failing [{}]: failed to commit cluster state version [{}]", summary, version), + t); + taskOutputs.publishingFailed(t); + return; + } + + taskOutputs.processedDifferentClusterState(previousClusterState, newClusterState); + + try { + taskOutputs.clusterStatePublished(clusterChangedEvent); + } catch (Exception e) { + logger.error( + (Supplier) () -> new ParameterizedMessage( + "exception thrown while notifying executor of new cluster state publication [{}]", + summary), + e); + } + TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS))); + logger.debug("processing [{}]: took [{}] done publishing updated cluster state (version: {}, uuid: {})", summary, + executionTime, newClusterState.version(), + newClusterState.stateUUID()); + warnAboutSlowTaskIfNeeded(executionTime, summary); + } catch (Exception e) { + TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS))); + final long version = newClusterState.version(); + final String stateUUID = newClusterState.stateUUID(); + final String fullState = newClusterState.toString(); + logger.warn( + (Supplier) () -> new ParameterizedMessage( + "failed to publish updated cluster state in [{}]:\nversion [{}], uuid [{}], source [{}]\n{}", + executionTime, + version, + stateUUID, + summary, + fullState), + e); + // TODO: do we want to call updateTask.onFailure here? + } + } + } + + public TaskOutputs calculateTaskOutputs(TaskInputs taskInputs, ClusterState previousClusterState, long startTimeNS) { + ClusterTasksResult clusterTasksResult = executeTasks(taskInputs, startTimeNS, previousClusterState); + ClusterState newClusterState = patchVersions(previousClusterState, clusterTasksResult); + return new TaskOutputs(taskInputs, previousClusterState, newClusterState, getNonFailedTasks(taskInputs, clusterTasksResult), + clusterTasksResult.executionResults); + } + + private ClusterState patchVersions(ClusterState previousClusterState, ClusterTasksResult executionResult) { + ClusterState newClusterState = executionResult.resultingState; + + if (previousClusterState != newClusterState) { + // only the master controls the version numbers + Builder builder = ClusterState.builder(newClusterState).incrementVersion(); + if (previousClusterState.routingTable() != newClusterState.routingTable()) { + builder.routingTable(RoutingTable.builder(newClusterState.routingTable()) + .version(newClusterState.routingTable().version() + 1).build()); + } + if (previousClusterState.metaData() != newClusterState.metaData()) { + builder.metaData(MetaData.builder(newClusterState.metaData()).version(newClusterState.metaData().version() + 1)); + } + + newClusterState = builder.build(); + } + + return newClusterState; + } + + /** + * Submits a cluster state update task; unlike {@link #submitStateUpdateTask(String, Object, ClusterStateTaskConfig, + * ClusterStateTaskExecutor, ClusterStateTaskListener)}, submitted updates will not be batched. + * + * @param source the source of the cluster state update task + * @param updateTask the full context for the cluster state update + * task + * + */ + public & ClusterStateTaskListener> + void submitStateUpdateTask( + String source, T updateTask) { + submitStateUpdateTask(source, updateTask, updateTask, updateTask, updateTask); + } + + /** + * Submits a cluster state update task; submitted updates will be + * batched across the same instance of executor. The exact batching + * semantics depend on the underlying implementation but a rough + * guideline is that if the update task is submitted while there + * are pending update tasks for the same executor, these update + * tasks will all be executed on the executor in a single batch + * + * @param source the source of the cluster state update task + * @param task the state needed for the cluster state update task + * @param config the cluster state update task configuration + * @param executor the cluster state update task executor; tasks + * that share the same executor will be executed + * batches on this executor + * @param listener callback after the cluster state update task + * completes + * @param the type of the cluster state update task state + * + */ + public void submitStateUpdateTask(String source, T task, + ClusterStateTaskConfig config, + ClusterStateTaskExecutor executor, + ClusterStateTaskListener listener) { + submitStateUpdateTasks(source, Collections.singletonMap(task, listener), config, executor); + } + + /** + * Output created by executing a set of tasks provided as TaskInputs + */ + class TaskOutputs { + public final TaskInputs taskInputs; + public final ClusterState previousClusterState; + public final ClusterState newClusterState; + public final List nonFailedTasks; + public final Map executionResults; + + TaskOutputs(TaskInputs taskInputs, ClusterState previousClusterState, + ClusterState newClusterState, + List nonFailedTasks, + Map executionResults) { + this.taskInputs = taskInputs; + this.previousClusterState = previousClusterState; + this.newClusterState = newClusterState; + this.nonFailedTasks = nonFailedTasks; + this.executionResults = executionResults; + } + + public void publishingFailed(Discovery.FailedToCommitClusterStateException t) { + nonFailedTasks.forEach(task -> task.listener.onFailure(task.source(), t)); + } + + public void processedDifferentClusterState(ClusterState previousClusterState, ClusterState newClusterState) { + nonFailedTasks.forEach(task -> task.listener.clusterStateProcessed(task.source(), previousClusterState, newClusterState)); + } + + public void clusterStatePublished(ClusterChangedEvent clusterChangedEvent) { + taskInputs.executor.clusterStatePublished(clusterChangedEvent); + } + + public Discovery.AckListener createAckListener(ThreadPool threadPool, ClusterState newClusterState) { + ArrayList ackListeners = new ArrayList<>(); + + //timeout straightaway, otherwise we could wait forever as the timeout thread has not started + nonFailedTasks.stream().filter(task -> task.listener instanceof AckedClusterStateTaskListener).forEach(task -> { + final AckedClusterStateTaskListener ackedListener = (AckedClusterStateTaskListener) task.listener; + if (ackedListener.ackTimeout() == null || ackedListener.ackTimeout().millis() == 0) { + ackedListener.onAckTimeout(); + } else { + try { + ackListeners.add(new AckCountDownListener(ackedListener, newClusterState.version(), newClusterState.nodes(), + threadPool)); + } catch (EsRejectedExecutionException ex) { + if (logger.isDebugEnabled()) { + logger.debug("Couldn't schedule timeout thread - node might be shutting down", ex); + } + //timeout straightaway, otherwise we could wait forever as the timeout thread has not started + ackedListener.onAckTimeout(); + } + } + }); + + return new DelegetingAckListener(ackListeners); + } + + public boolean clusterStateUnchanged() { + return previousClusterState == newClusterState; + } + + public void notifyFailedTasks() { + // fail all tasks that have failed + for (Batcher.UpdateTask updateTask : taskInputs.updateTasks) { + assert executionResults.containsKey(updateTask.task) : "missing " + updateTask; + final ClusterStateTaskExecutor.TaskResult taskResult = executionResults.get(updateTask.task); + if (taskResult.isSuccess() == false) { + updateTask.listener.onFailure(updateTask.source(), taskResult.getFailure()); + } + } + } + + public void notifySuccessfulTasksOnUnchangedClusterState() { + nonFailedTasks.forEach(task -> { + if (task.listener instanceof AckedClusterStateTaskListener) { + //no need to wait for ack if nothing changed, the update can be counted as acknowledged + ((AckedClusterStateTaskListener) task.listener).onAllNodesAcked(null); + } + task.listener.clusterStateProcessed(task.source(), newClusterState, newClusterState); + }); + } + } + + /** + * Returns the tasks that are pending. + */ + public List pendingTasks() { + return Arrays.stream(threadPoolExecutor.getPending()).map(pending -> { + assert pending.task instanceof SourcePrioritizedRunnable : + "thread pool executor should only use SourcePrioritizedRunnable instances but found: " + pending.task.getClass().getName(); + SourcePrioritizedRunnable task = (SourcePrioritizedRunnable) pending.task; + return new PendingClusterTask(pending.insertionOrder, pending.priority, new Text(task.source()), + task.getAgeInMillis(), pending.executing); + }).collect(Collectors.toList()); + } + + /** + * Returns the number of currently pending tasks. + */ + public int numberOfPendingTasks() { + return threadPoolExecutor.getNumberOfPendingTasks(); + } + + /** + * Returns the maximum wait time for tasks in the queue + * + * @return A zero time value if the queue is empty, otherwise the time value oldest task waiting in the queue + */ + public TimeValue getMaxTaskWaitTime() { + return threadPoolExecutor.getMaxTaskWaitTime(); + } + + private SafeClusterStateTaskListener safe(ClusterStateTaskListener listener) { + if (listener instanceof AckedClusterStateTaskListener) { + return new SafeAckedClusterStateTaskListener((AckedClusterStateTaskListener) listener, logger); + } else { + return new SafeClusterStateTaskListener(listener, logger); + } + } + + private static class SafeClusterStateTaskListener implements ClusterStateTaskListener { + private final ClusterStateTaskListener listener; + private final Logger logger; + + SafeClusterStateTaskListener(ClusterStateTaskListener listener, Logger logger) { + this.listener = listener; + this.logger = logger; + } + + @Override + public void onFailure(String source, Exception e) { + try { + listener.onFailure(source, e); + } catch (Exception inner) { + inner.addSuppressed(e); + logger.error( + (Supplier) () -> new ParameterizedMessage( + "exception thrown by listener notifying of failure from [{}]", source), inner); + } + } + + @Override + public void onNoLongerMaster(String source) { + try { + listener.onNoLongerMaster(source); + } catch (Exception e) { + logger.error( + (Supplier) () -> new ParameterizedMessage( + "exception thrown by listener while notifying no longer master from [{}]", source), e); + } + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + try { + listener.clusterStateProcessed(source, oldState, newState); + } catch (Exception e) { + logger.error( + (Supplier) () -> new ParameterizedMessage( + "exception thrown by listener while notifying of cluster state processed from [{}], old cluster state:\n" + + "{}\nnew cluster state:\n{}", + source, oldState, newState), + e); + } + } + } + + private static class SafeAckedClusterStateTaskListener extends SafeClusterStateTaskListener implements AckedClusterStateTaskListener { + private final AckedClusterStateTaskListener listener; + private final Logger logger; + + SafeAckedClusterStateTaskListener(AckedClusterStateTaskListener listener, Logger logger) { + super(listener, logger); + this.listener = listener; + this.logger = logger; + } + + @Override + public boolean mustAck(DiscoveryNode discoveryNode) { + return listener.mustAck(discoveryNode); + } + + @Override + public void onAllNodesAcked(@Nullable Exception e) { + try { + listener.onAllNodesAcked(e); + } catch (Exception inner) { + inner.addSuppressed(e); + logger.error("exception thrown by listener while notifying on all nodes acked", inner); + } + } + + @Override + public void onAckTimeout() { + try { + listener.onAckTimeout(); + } catch (Exception e) { + logger.error("exception thrown by listener while notifying on ack timeout", e); + } + } + + @Override + public TimeValue ackTimeout() { + return listener.ackTimeout(); + } + } + + protected void warnAboutSlowTaskIfNeeded(TimeValue executionTime, String source) { + if (executionTime.getMillis() > slowTaskLoggingThreshold.getMillis()) { + logger.warn("cluster state update task [{}] took [{}] above the warn threshold of {}", source, executionTime, + slowTaskLoggingThreshold); + } + } + + private static class DelegetingAckListener implements Discovery.AckListener { + + private final List listeners; + + private DelegetingAckListener(List listeners) { + this.listeners = listeners; + } + + @Override + public void onNodeAck(DiscoveryNode node, @Nullable Exception e) { + for (Discovery.AckListener listener : listeners) { + listener.onNodeAck(node, e); + } + } + + @Override + public void onTimeout() { + throw new UnsupportedOperationException("no timeout delegation"); + } + } + + private static class AckCountDownListener implements Discovery.AckListener { + + private static final Logger logger = Loggers.getLogger(AckCountDownListener.class); + + private final AckedClusterStateTaskListener ackedTaskListener; + private final CountDown countDown; + private final DiscoveryNodes nodes; + private final long clusterStateVersion; + private final Future ackTimeoutCallback; + private Exception lastFailure; + + AckCountDownListener(AckedClusterStateTaskListener ackedTaskListener, long clusterStateVersion, DiscoveryNodes nodes, + ThreadPool threadPool) { + this.ackedTaskListener = ackedTaskListener; + this.clusterStateVersion = clusterStateVersion; + this.nodes = nodes; + int countDown = 0; + for (DiscoveryNode node : nodes) { + if (ackedTaskListener.mustAck(node)) { + countDown++; + } + } + //we always wait for at least 1 node (the master) + countDown = Math.max(1, countDown); + logger.trace("expecting {} acknowledgements for cluster_state update (version: {})", countDown, clusterStateVersion); + this.countDown = new CountDown(countDown); + this.ackTimeoutCallback = threadPool.schedule(ackedTaskListener.ackTimeout(), ThreadPool.Names.GENERIC, () -> onTimeout()); + } + + @Override + public void onNodeAck(DiscoveryNode node, @Nullable Exception e) { + if (!ackedTaskListener.mustAck(node)) { + //we always wait for the master ack anyway + if (!node.equals(nodes.getMasterNode())) { + return; + } + } + if (e == null) { + logger.trace("ack received from node [{}], cluster_state update (version: {})", node, clusterStateVersion); + } else { + this.lastFailure = e; + logger.debug( + (Supplier) () -> new ParameterizedMessage( + "ack received from node [{}], cluster_state update (version: {})", node, clusterStateVersion), + e); + } + + if (countDown.countDown()) { + logger.trace("all expected nodes acknowledged cluster_state update (version: {})", clusterStateVersion); + FutureUtils.cancel(ackTimeoutCallback); + ackedTaskListener.onAllNodesAcked(lastFailure); + } + } + + @Override + public void onTimeout() { + if (countDown.fastForward()) { + logger.trace("timeout waiting for acknowledgement for cluster_state update (version: {})", clusterStateVersion); + ackedTaskListener.onAckTimeout(); + } + } + } + + protected ClusterTasksResult executeTasks(TaskInputs taskInputs, long startTimeNS, ClusterState previousClusterState) { + ClusterTasksResult clusterTasksResult; + try { + List inputs = taskInputs.updateTasks.stream().map(tUpdateTask -> tUpdateTask.task).collect(Collectors.toList()); + clusterTasksResult = taskInputs.executor.execute(previousClusterState, inputs); + if (previousClusterState != clusterTasksResult.resultingState && + previousClusterState.nodes().isLocalNodeElectedMaster() && + (clusterTasksResult.resultingState.nodes().isLocalNodeElectedMaster() == false)) { + throw new AssertionError("update task submitted to MasterService cannot remove master"); + } + } catch (Exception e) { + TimeValue executionTime = TimeValue.timeValueMillis(Math.max(0, TimeValue.nsecToMSec(currentTimeInNanos() - startTimeNS))); + if (logger.isTraceEnabled()) { + logger.trace( + (Supplier) () -> new ParameterizedMessage( + "failed to execute cluster state update in [{}], state:\nversion [{}], source [{}]\n{}{}{}", + executionTime, + previousClusterState.version(), + taskInputs.summary, + previousClusterState.nodes(), + previousClusterState.routingTable(), + previousClusterState.getRoutingNodes()), + e); + } + warnAboutSlowTaskIfNeeded(executionTime, taskInputs.summary); + clusterTasksResult = ClusterTasksResult.builder() + .failures(taskInputs.updateTasks.stream().map(updateTask -> updateTask.task)::iterator, e) + .build(previousClusterState); + } + + assert clusterTasksResult.executionResults != null; + assert clusterTasksResult.executionResults.size() == taskInputs.updateTasks.size() + : String.format(Locale.ROOT, "expected [%d] task result%s but was [%d]", taskInputs.updateTasks.size(), + taskInputs.updateTasks.size() == 1 ? "" : "s", clusterTasksResult.executionResults.size()); + boolean assertsEnabled = false; + assert (assertsEnabled = true); + if (assertsEnabled) { + ClusterTasksResult finalClusterTasksResult = clusterTasksResult; + taskInputs.updateTasks.forEach(updateTask -> { + assert finalClusterTasksResult.executionResults.containsKey(updateTask.task) : + "missing task result for " + updateTask; + }); + } + + return clusterTasksResult; + } + + public List getNonFailedTasks(TaskInputs taskInputs, + ClusterTasksResult clusterTasksResult) { + return taskInputs.updateTasks.stream().filter(updateTask -> { + assert clusterTasksResult.executionResults.containsKey(updateTask.task) : "missing " + updateTask; + final ClusterStateTaskExecutor.TaskResult taskResult = + clusterTasksResult.executionResults.get(updateTask.task); + return taskResult.isSuccess(); + }).collect(Collectors.toList()); + } + + /** + * Represents a set of tasks to be processed together with their executor + */ + protected class TaskInputs { + public final String summary; + public final List updateTasks; + public final ClusterStateTaskExecutor executor; + + TaskInputs(ClusterStateTaskExecutor executor, List updateTasks, String summary) { + this.summary = summary; + this.executor = executor; + this.updateTasks = updateTasks; + } + + public boolean runOnlyWhenMaster() { + return executor.runOnlyOnMaster(); + } + + public void onNoLongerMaster() { + updateTasks.forEach(task -> task.listener.onNoLongerMaster(task.source())); + } + } + + /** + * Submits a batch of cluster state update tasks; submitted updates are guaranteed to be processed together, + * potentially with more tasks of the same executor. + * + * @param source the source of the cluster state update task + * @param tasks a map of update tasks and their corresponding listeners + * @param config the cluster state update task configuration + * @param executor the cluster state update task executor; tasks + * that share the same executor will be executed + * batches on this executor + * @param the type of the cluster state update task state + * + */ + public void submitStateUpdateTasks(final String source, + final Map tasks, final ClusterStateTaskConfig config, + final ClusterStateTaskExecutor executor) { + if (!lifecycle.started()) { + return; + } + try { + List safeTasks = tasks.entrySet().stream() + .map(e -> taskBatcher.new UpdateTask(config.priority(), source, e.getKey(), safe(e.getValue()), executor)) + .collect(Collectors.toList()); + taskBatcher.submitTasks(safeTasks, config.timeout()); + } catch (EsRejectedExecutionException e) { + // ignore cases where we are shutting down..., there is really nothing interesting + // to be done here... + if (!lifecycle.stoppedOrClosed()) { + throw e; + } + } + } + + // this one is overridden in tests so we can control time + protected long currentTimeInNanos() { + return System.nanoTime(); + } +} diff --git a/core/src/main/java/org/elasticsearch/common/Randomness.java b/core/src/main/java/org/elasticsearch/common/Randomness.java index 295d7a6bcda5f..05ebe1a7377f9 100644 --- a/core/src/main/java/org/elasticsearch/common/Randomness.java +++ b/core/src/main/java/org/elasticsearch/common/Randomness.java @@ -41,7 +41,7 @@ * setting a reproducible seed. When running the Elasticsearch server * process, non-reproducible sources of randomness are provided (unless * a setting is provided for a module that exposes a seed setting (e.g., - * DiscoveryService#NODE_ID_SEED_SETTING)). + * NodeEnvironment#NODE_ID_SEED_SETTING)). */ public final class Randomness { private static final Method currentMethod; diff --git a/core/src/main/java/org/elasticsearch/common/util/concurrent/BaseFuture.java b/core/src/main/java/org/elasticsearch/common/util/concurrent/BaseFuture.java index ee9aea9ed70c3..3436ccdf7ad7c 100644 --- a/core/src/main/java/org/elasticsearch/common/util/concurrent/BaseFuture.java +++ b/core/src/main/java/org/elasticsearch/common/util/concurrent/BaseFuture.java @@ -19,7 +19,8 @@ package org.elasticsearch.common.util.concurrent; -import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.cluster.service.ClusterApplierService; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.Nullable; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.Transports; @@ -63,7 +64,8 @@ public V get(long timeout, TimeUnit unit) throws InterruptedException, assert timeout <= 0 || (Transports.assertNotTransportThread(BLOCKING_OP_REASON) && ThreadPool.assertNotScheduleThread(BLOCKING_OP_REASON) && - ClusterService.assertNotClusterStateUpdateThread(BLOCKING_OP_REASON)); + ClusterApplierService.assertNotClusterStateUpdateThread(BLOCKING_OP_REASON) && + MasterService.assertNotMasterUpdateThread(BLOCKING_OP_REASON)); return sync.get(unit.toNanos(timeout)); } @@ -87,7 +89,8 @@ public V get(long timeout, TimeUnit unit) throws InterruptedException, public V get() throws InterruptedException, ExecutionException { assert Transports.assertNotTransportThread(BLOCKING_OP_REASON) && ThreadPool.assertNotScheduleThread(BLOCKING_OP_REASON) && - ClusterService.assertNotClusterStateUpdateThread(BLOCKING_OP_REASON); + ClusterApplierService.assertNotClusterStateUpdateThread(BLOCKING_OP_REASON) && + MasterService.assertNotMasterUpdateThread(BLOCKING_OP_REASON); return sync.get(); } diff --git a/core/src/main/java/org/elasticsearch/discovery/Discovery.java b/core/src/main/java/org/elasticsearch/discovery/Discovery.java index e4addfdd02f1c..4c28b51bc4f16 100644 --- a/core/src/main/java/org/elasticsearch/discovery/Discovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/Discovery.java @@ -21,6 +21,7 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.common.Nullable; @@ -36,10 +37,6 @@ */ public interface Discovery extends LifecycleComponent { - DiscoveryNode localNode(); - - String nodeDescription(); - /** * Another hack to solve dep injection problem..., note, this will be called before * any start is called. @@ -48,7 +45,7 @@ public interface Discovery extends LifecycleComponent { /** * Publish all the changes to the cluster from the master (can be called just by the master). The publish - * process should not publish this state to the master as well! (the master is sending it...). + * process should apply this state to the master as well! * * The {@link AckListener} allows to keep track of the ack received from nodes, and verify whether * they updated their own cluster state or not. @@ -58,6 +55,18 @@ public interface Discovery extends LifecycleComponent { */ void publish(ClusterChangedEvent clusterChangedEvent, AckListener ackListener); + /** + * Returns the initial cluster state provided by the discovery module. Used by + * {@link org.elasticsearch.cluster.service.ClusterApplierService} as initial applied state. + */ + ClusterState getInitialClusterState(); + + /** + * Returns latest cluster state used by the discovery module. Used by {@link org.elasticsearch.cluster.service.MasterService} to + * calculate the next prospective state to publish. + */ + ClusterState clusterState(); + interface AckListener { void onNodeAck(DiscoveryNode node, @Nullable Exception e); void onTimeout(); @@ -83,8 +92,6 @@ public FailedToCommitClusterStateException(String msg, Throwable cause, Object.. */ DiscoveryStats stats(); - DiscoverySettings getDiscoverySettings(); - /** * Triggers the first join cycle */ diff --git a/core/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java b/core/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java index 2328b5a861675..43fccab1a284a 100644 --- a/core/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java +++ b/core/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java @@ -19,10 +19,12 @@ package org.elasticsearch.discovery; -import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.cluster.service.ClusterApplier; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.network.NetworkService; +import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; @@ -55,8 +57,8 @@ public class DiscoveryModule { private final Discovery discovery; public DiscoveryModule(Settings settings, ThreadPool threadPool, TransportService transportService, - NamedWriteableRegistry namedWriteableRegistry, NetworkService networkService, ClusterService clusterService, - List plugins) { + NamedWriteableRegistry namedWriteableRegistry, NetworkService networkService, MasterService masterService, + ClusterApplier clusterApplier, ClusterSettings clusterSettings, List plugins) { final UnicastHostsProvider hostsProvider; Map> hostProviders = new HashMap<>(); @@ -80,12 +82,13 @@ public DiscoveryModule(Settings settings, ThreadPool threadPool, TransportServic Map> discoveryTypes = new HashMap<>(); discoveryTypes.put("zen", - () -> new ZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, clusterService, hostsProvider)); - discoveryTypes.put("none", () -> new NoneDiscovery(settings, clusterService, clusterService.getClusterSettings())); - discoveryTypes.put("single-node", () -> new SingleNodeDiscovery(settings, clusterService)); + () -> new ZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, masterService, clusterApplier, + clusterSettings, hostsProvider)); + discoveryTypes.put("tribe", () -> new TribeDiscovery(settings, transportService, clusterApplier)); + discoveryTypes.put("single-node", () -> new SingleNodeDiscovery(settings, transportService, clusterApplier)); for (DiscoveryPlugin plugin : plugins) { plugin.getDiscoveryTypes(threadPool, transportService, namedWriteableRegistry, - clusterService, hostsProvider).entrySet().forEach(entry -> { + masterService, clusterApplier, clusterSettings, hostsProvider).entrySet().forEach(entry -> { if (discoveryTypes.put(entry.getKey(), entry.getValue()) != null) { throw new IllegalArgumentException("Cannot register discovery type [" + entry.getKey() + "] twice"); } diff --git a/core/src/main/java/org/elasticsearch/discovery/NoneDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/NoneDiscovery.java deleted file mode 100644 index 91b04ce396ba6..0000000000000 --- a/core/src/main/java/org/elasticsearch/discovery/NoneDiscovery.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -package org.elasticsearch.discovery; - -import org.elasticsearch.cluster.ClusterChangedEvent; -import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.component.AbstractLifecycleComponent; -import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.settings.ClusterSettings; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.discovery.zen.ElectMasterService; - -/** - * A {@link Discovery} implementation that is used by {@link org.elasticsearch.tribe.TribeService}. This implementation - * doesn't support any clustering features. Most notably {@link #startInitialJoin()} does nothing and - * {@link #publish(ClusterChangedEvent, AckListener)} is not supported. - */ -public class NoneDiscovery extends AbstractLifecycleComponent implements Discovery { - - private final ClusterService clusterService; - private final DiscoverySettings discoverySettings; - - @Inject - public NoneDiscovery(Settings settings, ClusterService clusterService, ClusterSettings clusterSettings) { - super(settings); - this.clusterService = clusterService; - this.discoverySettings = new DiscoverySettings(settings, clusterSettings); - } - - @Override - public DiscoveryNode localNode() { - return clusterService.localNode(); - } - - @Override - public String nodeDescription() { - return clusterService.getClusterName().value() + "/" + clusterService.localNode().getId(); - } - - @Override - public void setAllocationService(AllocationService allocationService) { - - } - - @Override - public void publish(ClusterChangedEvent clusterChangedEvent, AckListener ackListener) { - throw new UnsupportedOperationException(); - } - - @Override - public DiscoveryStats stats() { - return null; - } - - @Override - public DiscoverySettings getDiscoverySettings() { - return discoverySettings; - } - - @Override - public void startInitialJoin() { - - } - - @Override - public int getMinimumMasterNodes() { - return ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING.get(settings); - } - - @Override - protected void doStart() { - - } - - @Override - protected void doStop() { - - } - - @Override - protected void doClose() { - - } -} diff --git a/core/src/main/java/org/elasticsearch/discovery/TribeDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/TribeDiscovery.java new file mode 100644 index 0000000000000..751eb94a9f216 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/discovery/TribeDiscovery.java @@ -0,0 +1,73 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.discovery; + +import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.block.ClusterBlocks; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.cluster.service.ClusterApplier;; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.discovery.single.SingleNodeDiscovery; +import org.elasticsearch.transport.TransportService; + +import static org.elasticsearch.tribe.TribeService.BLOCKS_METADATA_SETTING; +import static org.elasticsearch.tribe.TribeService.BLOCKS_WRITE_SETTING; +import static org.elasticsearch.tribe.TribeService.TRIBE_METADATA_BLOCK; +import static org.elasticsearch.tribe.TribeService.TRIBE_WRITE_BLOCK; + +/** + * A {@link Discovery} implementation that is used by {@link org.elasticsearch.tribe.TribeService}. This implementation + * doesn't support any clustering features. Most notably {@link #startInitialJoin()} does nothing and + * {@link #publish(ClusterChangedEvent, AckListener)} delegates state updates directly to the + * {@link org.elasticsearch.cluster.service.ClusterApplierService}. + */ +public class TribeDiscovery extends SingleNodeDiscovery implements Discovery { + + @Inject + public TribeDiscovery(Settings settings, TransportService transportService, ClusterApplier clusterApplier) { + super(settings, transportService, clusterApplier); + } + + @Override + public synchronized ClusterState getInitialClusterState() { + if (initialState == null) { + ClusterBlocks.Builder clusterBlocks = ClusterBlocks.builder(); // don't add no_master / state recovery block + if (BLOCKS_WRITE_SETTING.get(settings)) { + clusterBlocks.addGlobalBlock(TRIBE_WRITE_BLOCK); + } + if (BLOCKS_METADATA_SETTING.get(settings)) { + clusterBlocks.addGlobalBlock(TRIBE_METADATA_BLOCK); + } + DiscoveryNode localNode = transportService.getLocalNode(); + initialState = ClusterState.builder(ClusterName.CLUSTER_NAME_SETTING.get(settings)) + .nodes(DiscoveryNodes.builder().add(localNode).localNodeId(localNode.getId()).build()) + .blocks(clusterBlocks).build(); + } + return initialState; + } + + @Override + public synchronized void startInitialJoin() { + // no state recovery required as tribe nodes don't persist cluster state + } +} diff --git a/core/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java index f4735c8bf3a0d..d7275e7be91da 100644 --- a/core/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java @@ -19,63 +19,103 @@ package org.elasticsearch.discovery.single; +import org.apache.logging.log4j.message.ParameterizedMessage; import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.ClusterStateTaskConfig; -import org.elasticsearch.cluster.ClusterStateTaskExecutor; +import org.elasticsearch.cluster.ClusterStateTaskListener; +import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.Priority; +import org.elasticsearch.cluster.service.ClusterApplier; import org.elasticsearch.common.component.AbstractLifecycleComponent; -import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.discovery.Discovery; -import org.elasticsearch.discovery.DiscoverySettings; import org.elasticsearch.discovery.DiscoveryStats; import org.elasticsearch.discovery.zen.PendingClusterStateStats; -import org.elasticsearch.discovery.zen.PendingClusterStatesQueue; +import org.elasticsearch.transport.TransportService; import java.io.IOException; -import java.util.Collections; -import java.util.List; import java.util.Objects; +import java.util.concurrent.CountDownLatch; + +import static org.elasticsearch.gateway.GatewayService.STATE_NOT_RECOVERED_BLOCK; /** * A discovery implementation where the only member of the cluster is the local node. */ public class SingleNodeDiscovery extends AbstractLifecycleComponent implements Discovery { - private final ClusterService clusterService; - private final DiscoverySettings discoverySettings; + protected final TransportService transportService; + private final ClusterApplier clusterApplier; + protected volatile ClusterState initialState; + private volatile ClusterState clusterState; - public SingleNodeDiscovery(final Settings settings, final ClusterService clusterService) { + public SingleNodeDiscovery(final Settings settings, final TransportService transportService, + ClusterApplier clusterApplier) { super(Objects.requireNonNull(settings)); - this.clusterService = Objects.requireNonNull(clusterService); - final ClusterSettings clusterSettings = - Objects.requireNonNull(clusterService.getClusterSettings()); - this.discoverySettings = new DiscoverySettings(settings, clusterSettings); + this.transportService = Objects.requireNonNull(transportService); + this.clusterApplier = clusterApplier; } @Override - public DiscoveryNode localNode() { - return clusterService.localNode(); + public void setAllocationService(final AllocationService allocationService) { + } @Override - public String nodeDescription() { - return clusterService.getClusterName().value() + "/" + clusterService.localNode().getId(); + public synchronized void publish(final ClusterChangedEvent event, + final AckListener ackListener) { + clusterState = event.state(); + CountDownLatch latch = new CountDownLatch(1); + + ClusterStateTaskListener listener = new ClusterStateTaskListener() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + ackListener.onNodeAck(transportService.getLocalNode(), null); + } + + @Override + public void onFailure(String source, Exception e) { + latch.countDown(); + ackListener.onNodeAck(transportService.getLocalNode(), e); + logger.warn( + (org.apache.logging.log4j.util.Supplier) () -> new ParameterizedMessage( + "failed while applying cluster state locally [{}]", + event.source()), + e); + } + }; + clusterApplier.onNewClusterState("apply-locally-on-node[" + event.source() + "]", this::clusterState, listener); + + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } } @Override - public void setAllocationService(final AllocationService allocationService) { - + public synchronized ClusterState getInitialClusterState() { + if (initialState == null) { + DiscoveryNode localNode = transportService.getLocalNode(); + initialState = ClusterState.builder(ClusterName.CLUSTER_NAME_SETTING.get(settings)) + .nodes(DiscoveryNodes.builder().add(localNode) + .localNodeId(localNode.getId()) + .masterNodeId(localNode.getId()) + .build()) + .blocks(ClusterBlocks.builder() + .addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)) + .build(); + } + return initialState; } @Override - public void publish(final ClusterChangedEvent event, final AckListener listener) { - + public ClusterState clusterState() { + return clusterState; } @Override @@ -84,41 +124,11 @@ public DiscoveryStats stats() { } @Override - public DiscoverySettings getDiscoverySettings() { - return discoverySettings; - } - - @Override - public void startInitialJoin() { - final ClusterStateTaskExecutor executor = - new ClusterStateTaskExecutor() { - - @Override - public ClusterTasksResult execute( - final ClusterState current, - final List tasks) throws Exception { - assert tasks.size() == 1; - final DiscoveryNodes.Builder nodes = - DiscoveryNodes.builder(current.nodes()); - // always set the local node as master, there will not be other nodes - nodes.masterNodeId(localNode().getId()); - final ClusterState next = - ClusterState.builder(current).nodes(nodes).build(); - final ClusterTasksResult.Builder result = - ClusterTasksResult.builder(); - return result.successes(tasks).build(next); - } - - @Override - public boolean runOnlyOnMaster() { - return false; - } - - }; - final ClusterStateTaskConfig config = ClusterStateTaskConfig.build(Priority.URGENT); - clusterService.submitStateUpdateTasks( - "single-node-start-initial-join", - Collections.singletonMap(localNode(), (s, e) -> {}), config, executor); + public synchronized void startInitialJoin() { + // apply a fresh cluster state just so that state recovery gets triggered by GatewayService + // TODO: give discovery module control over GatewayService + clusterState = ClusterState.builder(getInitialClusterState()).build(); + clusterApplier.onNewClusterState("single-node-start-initial-join", this::clusterState, (source, e) -> {}); } @Override @@ -127,8 +137,9 @@ public int getMinimumMasterNodes() { } @Override - protected void doStart() { - + protected synchronized void doStart() { + initialState = getInitialClusterState(); + clusterState = initialState; } @Override diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/MasterFaultDetection.java b/core/src/main/java/org/elasticsearch/discovery/zen/MasterFaultDetection.java index 81d4c19d33ee2..fff5e7cb5c983 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/MasterFaultDetection.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/MasterFaultDetection.java @@ -28,7 +28,7 @@ import org.elasticsearch.cluster.NotMasterException; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -64,7 +64,8 @@ public interface Listener { } - private final ClusterService clusterService; + private final MasterService masterService; + private final java.util.function.Supplier clusterStateSupplier; private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList<>(); private volatile MasterPinger masterPinger; @@ -78,9 +79,11 @@ public interface Listener { private final AtomicBoolean notifiedMasterFailure = new AtomicBoolean(); public MasterFaultDetection(Settings settings, ThreadPool threadPool, TransportService transportService, - ClusterService clusterService) { - super(settings, threadPool, transportService, clusterService.getClusterName()); - this.clusterService = clusterService; + java.util.function.Supplier clusterStateSupplier, MasterService masterService, + ClusterName clusterName) { + super(settings, threadPool, transportService, clusterName); + this.clusterStateSupplier = clusterStateSupplier; + this.masterService = masterService; logger.debug("[master] uses ping_interval [{}], ping_timeout [{}], ping_retries [{}]", pingInterval, pingRetryTimeout, pingRetryCount); @@ -215,7 +218,8 @@ public void run() { return; } - final MasterPingRequest request = new MasterPingRequest(clusterService.localNode(), masterToPing, clusterName); + final MasterPingRequest request = new MasterPingRequest( + clusterStateSupplier.get().nodes().getLocalNode(), masterToPing, clusterName); final TransportRequestOptions options = TransportRequestOptions.builder().withType(TransportRequestOptions.Type.PING) .withTimeout(pingRetryTimeout).build(); transportService.sendRequest(masterToPing, MASTER_PING_ACTION_NAME, request, options, @@ -323,7 +327,7 @@ private class MasterPingRequestHandler implements TransportRequestHandler {}); // noop listener, the election finished listener determines result tasks.put(FINISH_ELECTION_TASK, electionFinishedListener); - clusterService.submitStateUpdateTasks(source, tasks, ClusterStateTaskConfig.build(Priority.URGENT), joinTaskExecutor); + masterService.submitStateUpdateTasks(source, tasks, ClusterStateTaskConfig.build(Priority.URGENT), joinTaskExecutor); } public synchronized void closeAndProcessPending(String reason) { @@ -287,7 +288,7 @@ public synchronized void closeAndProcessPending(String reason) { Map tasks = getPendingAsTasks(); final String source = "zen-disco-election-stop [" + reason + "]"; tasks.put(FINISH_ELECTION_TASK, electionFinishedListener); - clusterService.submitStateUpdateTasks(source, tasks, ClusterStateTaskConfig.build(Priority.URGENT), joinTaskExecutor); + masterService.submitStateUpdateTasks(source, tasks, ClusterStateTaskConfig.build(Priority.URGENT), joinTaskExecutor); } private void innerClose() { @@ -307,7 +308,7 @@ private synchronized ElectionCallback getCallback() { } private void onElectedAsMaster(ClusterState state) { - ClusterService.assertClusterStateThread(); + assert MasterService.assertMasterUpdateThread(); assert state.nodes().isLocalNodeElectedMaster() : "onElectedAsMaster called but local node is not master"; ElectionCallback callback = getCallback(); // get under lock if (callback != null) { @@ -316,7 +317,7 @@ private void onElectedAsMaster(ClusterState state) { } private void onFailure(Throwable t) { - ClusterService.assertClusterStateThread(); + assert MasterService.assertMasterUpdateThread(); ElectionCallback callback = getCallback(); // get under lock if (callback != null) { callback.onFailure(t); @@ -469,6 +470,7 @@ private ClusterState.Builder becomeMasterAndTrimConflictingNodes(ClusterState cu DiscoveryNodes currentNodes = currentState.nodes(); DiscoveryNodes.Builder nodesBuilder = DiscoveryNodes.builder(currentNodes); nodesBuilder.masterNodeId(currentState.nodes().getLocalNodeId()); + for (final DiscoveryNode joiningNode : joiningNodes) { final DiscoveryNode nodeWithSameId = nodesBuilder.get(joiningNode.getId()); if (nodeWithSameId != null && nodeWithSameId.equals(joiningNode) == false) { @@ -486,7 +488,9 @@ private ClusterState.Builder becomeMasterAndTrimConflictingNodes(ClusterState cu // now trim any left over dead nodes - either left there when the previous master stepped down // or removed by us above - ClusterState tmpState = ClusterState.builder(currentState).nodes(nodesBuilder).build(); + ClusterState tmpState = ClusterState.builder(currentState).nodes(nodesBuilder).blocks(ClusterBlocks.builder() + .blocks(currentState.blocks()) + .removeGlobalBlock(DiscoverySettings.NO_MASTER_BLOCK_ID)).build(); return ClusterState.builder(allocationService.deassociateDeadNodes(tmpState, false, "removed dead nodes on election")); } diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java index f5dbaac62423f..5da446aa5b927 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java @@ -31,28 +31,28 @@ import org.elasticsearch.cluster.ClusterStateTaskConfig; import org.elasticsearch.cluster.ClusterStateTaskExecutor; import org.elasticsearch.cluster.ClusterStateTaskListener; -import org.elasticsearch.cluster.LocalClusterUpdateTask; import org.elasticsearch.cluster.NotMasterException; +import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.cluster.service.ClusterApplier; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.Priority; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.component.Lifecycle; -import org.elasticsearch.common.inject.internal.Nullable; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.lease.Releasables; import org.elasticsearch.common.logging.LoggerMessageFormat; +import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.discovery.Discovery; import org.elasticsearch.discovery.DiscoverySettings; import org.elasticsearch.discovery.DiscoveryStats; @@ -68,8 +68,10 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -78,6 +80,7 @@ import java.util.stream.Collectors; import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds; +import static org.elasticsearch.gateway.GatewayService.STATE_NOT_RECOVERED_BLOCK; public class ZenDiscovery extends AbstractLifecycleComponent implements Discovery, PingContextProvider { @@ -105,8 +108,7 @@ public class ZenDiscovery extends AbstractLifecycleComponent implements Discover public static final String DISCOVERY_REJOIN_ACTION_NAME = "internal:discovery/zen/rejoin"; private final TransportService transportService; - private final NamedWriteableRegistry namedWriteableRegistry; - private final ClusterService clusterService; + private final MasterService masterService; private AllocationService allocationService; private final ClusterName clusterName; private final DiscoverySettings discoverySettings; @@ -142,15 +144,19 @@ public class ZenDiscovery extends AbstractLifecycleComponent implements Discover private volatile NodeJoinController nodeJoinController; private volatile NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; + private final ClusterApplier clusterApplier; + private final AtomicReference state; // last committed cluster state + private final Object stateMutex = new Object(); + private volatile ClusterState initialState; // set lazily when discovery layer is started + public ZenDiscovery(Settings settings, ThreadPool threadPool, TransportService transportService, - NamedWriteableRegistry namedWriteableRegistry, - ClusterService clusterService, UnicastHostsProvider hostsProvider) { + NamedWriteableRegistry namedWriteableRegistry, MasterService masterService, ClusterApplier clusterApplier, + ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { super(settings); - this.clusterService = clusterService; - this.clusterName = clusterService.getClusterName(); + this.masterService = masterService; + this.clusterApplier = clusterApplier; this.transportService = transportService; - this.namedWriteableRegistry = namedWriteableRegistry; - this.discoverySettings = new DiscoverySettings(settings, clusterService.getClusterSettings()); + this.discoverySettings = new DiscoverySettings(settings, clusterSettings); this.zenPing = newZenPing(settings, threadPool, transportService, hostsProvider); this.electMaster = new ElectMasterService(settings); this.pingTimeout = PING_TIMEOUT_SETTING.get(settings); @@ -160,6 +166,8 @@ public ZenDiscovery(Settings settings, ThreadPool threadPool, TransportService t this.maxPingsFromAnotherMaster = MAX_PINGS_FROM_ANOTHER_MASTER_SETTING.get(settings); this.sendLeaveRequest = SEND_LEAVE_REQUEST_SETTING.get(settings); this.threadPool = threadPool; + this.clusterName = ClusterName.CLUSTER_NAME_SETTING.get(settings); + this.state = new AtomicReference<>(); this.masterElectionIgnoreNonMasters = MASTER_ELECTION_IGNORE_NON_MASTER_PINGS_SETTING.get(settings); this.masterElectionWaitForJoinsTimeout = MASTER_ELECTION_WAIT_FOR_JOINS_TIMEOUT_SETTING.get(settings); @@ -167,9 +175,9 @@ public ZenDiscovery(Settings settings, ThreadPool threadPool, TransportService t logger.debug("using ping_timeout [{}], join.timeout [{}], master_election.ignore_non_master [{}]", this.pingTimeout, joinTimeout, masterElectionIgnoreNonMasters); - clusterService.getClusterSettings().addSettingsUpdateConsumer(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING, + clusterSettings.addSettingsUpdateConsumer(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING, this::handleMinimumMasterNodesChanged, (value) -> { - final ClusterState clusterState = clusterService.state(); + final ClusterState clusterState = this.clusterState(); int masterNodes = clusterState.nodes().getMasterNodes().size(); // the purpose of this validation is to make sure that the master doesn't step down // due to a change in master nodes, which also means that there is no way to revert @@ -188,9 +196,9 @@ public ZenDiscovery(Settings settings, ThreadPool threadPool, TransportService t } }); - this.masterFD = new MasterFaultDetection(settings, threadPool, transportService, clusterService); + this.masterFD = new MasterFaultDetection(settings, threadPool, transportService, this::clusterState, masterService, clusterName); this.masterFD.addListener(new MasterNodeFailureListener()); - this.nodesFD = new NodesFaultDetection(settings, threadPool, transportService, clusterService.getClusterName()); + this.nodesFD = new NodesFaultDetection(settings, threadPool, transportService, clusterName); this.nodesFD.addListener(new NodeFaultDetectionListener()); this.publishClusterState = @@ -198,10 +206,10 @@ public ZenDiscovery(Settings settings, ThreadPool threadPool, TransportService t settings, transportService, namedWriteableRegistry, - clusterService::state, + this::clusterState, new NewPendingClusterStateListener(), discoverySettings, - clusterService.getClusterName()); + clusterName); this.membership = new MembershipAction(settings, transportService, new MembershipListener()); this.joinThreadControl = new JoinThreadControl(); @@ -222,30 +230,26 @@ public void setAllocationService(AllocationService allocationService) { @Override protected void doStart() { - nodesFD.setLocalNode(clusterService.localNode()); - joinThreadControl.start(); + DiscoveryNode localNode = transportService.getLocalNode(); + assert localNode != null; + synchronized (stateMutex) { + initialState = getInitialClusterState(); + state.set(initialState); + nodesFD.setLocalNode(localNode); + joinThreadControl.start(); + } zenPing.start(this); - this.nodeJoinController = new NodeJoinController(clusterService, allocationService, electMaster, settings); + this.nodeJoinController = new NodeJoinController(masterService, allocationService, electMaster, settings); this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, electMaster, this::submitRejoin, logger); } @Override public void startInitialJoin() { // start the join thread from a cluster state update. See {@link JoinThreadControl} for details. - clusterService.submitStateUpdateTask("initial_join", new LocalClusterUpdateTask() { - - @Override - public ClusterTasksResult execute(ClusterState currentState) throws Exception { - // do the join on a different thread, the DiscoveryService waits for 30s anyhow till it is discovered - joinThreadControl.startNewThreadIfNotRunning(); - return unchanged(); - } - - @Override - public void onFailure(String source, @org.elasticsearch.common.Nullable Exception e) { - logger.warn("failed to start initial join process", e); - } - }); + synchronized (stateMutex) { + // do the join on a different thread, the caller of this method waits for 30s anyhow till it is discovered + joinThreadControl.startNewThreadIfNotRunning(); + } } @Override @@ -286,58 +290,117 @@ protected void doClose() throws IOException { IOUtils.close(masterFD, nodesFD); } - @Override - public DiscoveryNode localNode() { - return clusterService.localNode(); - } - - @Override - public String nodeDescription() { - return clusterName.value() + "/" + clusterService.localNode().getId(); - } - /** start of {@link PingContextProvider } implementation */ @Override public DiscoveryNodes nodes() { - return clusterService.state().nodes(); + return clusterState().nodes(); } @Override public ClusterState clusterState() { - return clusterService.state(); + ClusterState clusterState = state.get(); + assert clusterState != null : "accessing cluster state before it is set"; + return clusterState; } /** end of {@link PingContextProvider } implementation */ - @Override public void publish(ClusterChangedEvent clusterChangedEvent, AckListener ackListener) { - if (!clusterChangedEvent.state().getNodes().isLocalNodeElectedMaster()) { - throw new IllegalStateException("Shouldn't publish state when not master"); + ClusterState newState = clusterChangedEvent.state(); + assert newState.getNodes().isLocalNodeElectedMaster() : "Shouldn't publish state when not master " + clusterChangedEvent.source(); + + // state got changed locally (maybe because another master published to us) + if (clusterChangedEvent.previousState() != this.state.get()) { + throw new FailedToCommitClusterStateException("state was mutated while calculating new CS update"); } + + publishClusterState.pendingStatesQueue().addPending(newState); + try { publishClusterState.publish(clusterChangedEvent, electMaster.minimumMasterNodes(), ackListener); } catch (FailedToCommitClusterStateException t) { // cluster service logs a WARN message - logger.debug("failed to publish cluster state version [{}] (not enough nodes acknowledged, min master nodes [{}])", clusterChangedEvent.state().version(), electMaster.minimumMasterNodes()); - submitRejoin("zen-disco-failed-to-publish"); + logger.debug("failed to publish cluster state version [{}] (not enough nodes acknowledged, min master nodes [{}])", + newState.version(), electMaster.minimumMasterNodes()); + + synchronized (stateMutex) { + publishClusterState.pendingStatesQueue().failAllStatesAndClear( + new ElasticsearchException("failed to publish cluster state")); + + rejoin("zen-disco-failed-to-publish"); + } throw t; } - // update the set of nodes to ping after the new cluster state has been published - nodesFD.updateNodesAndPing(clusterChangedEvent.state()); + final DiscoveryNode localNode = newState.getNodes().getLocalNode(); + final CountDownLatch latch = new CountDownLatch(1); + publishClusterState.pendingStatesQueue().markAsCommitted(newState.stateUUID(), + new PendingClusterStatesQueue.StateProcessedListener() { + @Override + public void onNewClusterStateProcessed() { + latch.countDown(); + ackListener.onNodeAck(localNode, null); + } + + @Override + public void onNewClusterStateFailed(Exception e) { + latch.countDown(); + ackListener.onNodeAck(localNode, e); + logger.warn( + (org.apache.logging.log4j.util.Supplier) () -> new ParameterizedMessage( + "failed while applying cluster state locally [{}]", + clusterChangedEvent.source()), + e); + } + }); - // clean the pending cluster queue - we are currently master, so any pending cluster state should be failed - // note that we also clean the queue on master failure (see handleMasterGone) but a delayed cluster state publish - // from a stale master can still make it in the queue during the election (but not be committed) - publishClusterState.pendingStatesQueue().failAllStatesAndClear(new ElasticsearchException("elected as master")); + synchronized (stateMutex) { + if (clusterChangedEvent.previousState() != this.state.get()) { + throw new FailedToCommitClusterStateException("local state was mutated while CS update was published to other nodes"); + } + + boolean processed = processNextCommittedClusterState("master " + newState.nodes().getMasterNode() + + " committed version [" + newState.version() + "] source [" + clusterChangedEvent.source() + "]"); + if (processed == false) { + assert false : "CS published to itself not processed"; + return; + } + } + // indefinitely wait for cluster state to be applied locally + try { + latch.await(); + } catch (InterruptedException e) { + logger.debug( + (org.apache.logging.log4j.util.Supplier) () -> new ParameterizedMessage( + "interrupted while applying cluster state locally [{}]", + clusterChangedEvent.source()), + e); + Thread.currentThread().interrupt(); + } + } + + @Override + public synchronized ClusterState getInitialClusterState() { + if (initialState == null) { + assert state.get() == null; + DiscoveryNode localNode = transportService.getLocalNode(); + assert localNode != null; + initialState = ClusterState.builder(clusterName) + .blocks(ClusterBlocks.builder() + .addGlobalBlock(STATE_NOT_RECOVERED_BLOCK) + .addGlobalBlock(discoverySettings.getNoMasterBlock())) + .nodes(DiscoveryNodes.builder().add(localNode).localNodeId(localNode.getId())) + .build(); + } + return initialState; } /** * Gets the current set of nodes involved in the node fault detection. * NB: for testing purposes */ - public Set getFaultDetectionNodes() { + Set getFaultDetectionNodes() { return nodesFD.getNodes(); } @@ -347,7 +410,6 @@ public DiscoveryStats stats() { return new DiscoveryStats(queueStats); } - @Override public DiscoverySettings getDiscoverySettings() { return discoverySettings; } @@ -391,22 +453,24 @@ private void innerJoinCluster() { return; } - if (clusterService.localNode().equals(masterNode)) { + if (transportService.getLocalNode().equals(masterNode)) { final int requiredJoins = Math.max(0, electMaster.minimumMasterNodes() - 1); // we count as one logger.debug("elected as master, waiting for incoming joins ([{}] needed)", requiredJoins); nodeJoinController.waitToBeElectedAsMaster(requiredJoins, masterElectionWaitForJoinsTimeout, new NodeJoinController.ElectionCallback() { @Override public void onElectedAsMaster(ClusterState state) { - joinThreadControl.markThreadAsDone(currentThread); - // we only starts nodesFD if we are master (it may be that we received a cluster state while pinging) - nodesFD.updateNodesAndPing(state); // start the nodes FD + synchronized (stateMutex) { + joinThreadControl.markThreadAsDone(currentThread); + } } @Override public void onFailure(Throwable t) { logger.trace("failed while waiting for nodes to join, rejoining", t); - joinThreadControl.markThreadAsDoneAndStartNew(currentThread); + synchronized (stateMutex) { + joinThreadControl.markThreadAsDoneAndStartNew(currentThread); + } } } @@ -418,41 +482,25 @@ public void onFailure(Throwable t) { // send join request final boolean success = joinElectedMaster(masterNode); - // finalize join through the cluster state update thread - final DiscoveryNode finalMasterNode = masterNode; - clusterService.submitStateUpdateTask("finalize_join (" + masterNode + ")", new LocalClusterUpdateTask() { - @Override - public ClusterTasksResult execute(ClusterState currentState) throws Exception { - if (!success) { - // failed to join. Try again... - joinThreadControl.markThreadAsDoneAndStartNew(currentThread); - return unchanged(); - } - - if (currentState.getNodes().getMasterNode() == null) { + synchronized (stateMutex) { + if (success) { + DiscoveryNode currentMasterNode = this.clusterState().getNodes().getMasterNode(); + if (currentMasterNode == null) { // Post 1.3.0, the master should publish a new cluster state before acking our join request. we now should have // a valid master. logger.debug("no master node is set, despite of join request completing. retrying pings."); joinThreadControl.markThreadAsDoneAndStartNew(currentThread); - return unchanged(); + } else if (currentMasterNode.equals(masterNode) == false) { + // update cluster state + joinThreadControl.stopRunningThreadAndRejoin("master_switched_while_finalizing_join"); } - if (!currentState.getNodes().getMasterNode().equals(finalMasterNode)) { - return joinThreadControl.stopRunningThreadAndRejoin(currentState, "master_switched_while_finalizing_join"); - } - - // Note: we do not have to start master fault detection here because it's set at {@link #processNextPendingClusterState } - // when the first cluster state arrives. joinThreadControl.markThreadAsDone(currentThread); - return unchanged(); - } - - @Override - public void onFailure(String source, @Nullable Exception e) { - logger.error("unexpected error while trying to finalize cluster join", e); + } else { + // failed to join. Try again... joinThreadControl.markThreadAsDoneAndStartNew(currentThread); } - }); + } } } @@ -473,7 +521,7 @@ private boolean joinElectedMaster(DiscoveryNode masterNode) { while (true) { try { logger.trace("joining master {}", masterNode); - membership.sendJoinRequestBlocking(masterNode, clusterService.localNode(), joinTimeout); + membership.sendJoinRequestBlocking(masterNode, transportService.getLocalNode(), joinTimeout); return true; } catch (Exception e) { final Throwable unwrap = ExceptionsHelper.unwrapCause(e); @@ -503,18 +551,16 @@ private boolean joinElectedMaster(DiscoveryNode masterNode) { } private void submitRejoin(String source) { - clusterService.submitStateUpdateTask(source, new LocalClusterUpdateTask(Priority.IMMEDIATE) { - @Override - public ClusterTasksResult execute(ClusterState currentState) { - return rejoin(currentState, source); - } - - @Override - public void onFailure(String source, Exception e) { - logger.error((Supplier) () -> new ParameterizedMessage("unexpected failure during [{}]", source), e); - } + synchronized (stateMutex) { + rejoin(source); + } + } - }); + // visible for testing + void setState(ClusterState clusterState) { + synchronized (stateMutex) { + state.set(clusterState); + } } // visible for testing @@ -611,7 +657,7 @@ public void onNoLongerMaster(String source) { } private void removeNode(final DiscoveryNode node, final String source, final String reason) { - clusterService.submitStateUpdateTask( + masterService.submitStateUpdateTask( source + "(" + node + "), reason(" + reason + ")", new NodeRemovalClusterStateTaskExecutor.Task(node, reason), ClusterStateTaskConfig.build(Priority.IMMEDIATE), @@ -654,32 +700,12 @@ private void handleMinimumMasterNodesChanged(final int minimumMasterNodes) { // We only set the new value. If the master doesn't see enough nodes it will revoke it's mastership. return; } - clusterService.submitStateUpdateTask("zen-disco-min-master-nodes-changed", new LocalClusterUpdateTask(Priority.IMMEDIATE) { - @Override - public ClusterTasksResult execute(ClusterState currentState) { - // check if we have enough master nodes, if not, we need to move into joining the cluster again - if (!electMaster.hasEnoughMasterNodes(currentState.nodes())) { - return rejoin(currentState, "not enough master nodes on change of minimum_master_nodes from [" + prevMinimumMasterNode + "] to [" + minimumMasterNodes + "]"); - } - return unchanged(); - } - - - @Override - public void onNoLongerMaster(String source) { - // ignoring (already logged) - } - - @Override - public void onFailure(String source, Exception e) { - logger.error((Supplier) () -> new ParameterizedMessage("unexpected failure during [{}]", source), e); - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - electMaster.logMinimumMasterNodesWarningIfNecessary(oldState, newState); + synchronized (stateMutex) { + // check if we have enough master nodes, if not, we need to move into joining the cluster again + if (!electMaster.hasEnoughMasterNodes(state.get().nodes())) { + rejoin("not enough master nodes on change of minimum_master_nodes from [" + prevMinimumMasterNode + "] to [" + minimumMasterNodes + "]"); } - }); + } } private void handleMasterGone(final DiscoveryNode masterNode, final Throwable cause, final String reason) { @@ -694,116 +720,135 @@ private void handleMasterGone(final DiscoveryNode masterNode, final Throwable ca logger.info((Supplier) () -> new ParameterizedMessage("master_left [{}], reason [{}]", masterNode, reason), cause); - clusterService.submitStateUpdateTask("master_failed (" + masterNode + ")", new LocalClusterUpdateTask(Priority.IMMEDIATE) { - - @Override - public ClusterTasksResult execute(ClusterState currentState) { - if (!masterNode.equals(currentState.nodes().getMasterNode())) { - // master got switched on us, no need to send anything - return unchanged(); - } - + synchronized (stateMutex) { + if (localNodeMaster() == false && masterNode.equals(state.get().nodes().getMasterNode())) { // flush any pending cluster states from old master, so it will not be set as master again publishClusterState.pendingStatesQueue().failAllStatesAndClear(new ElasticsearchException("master left [{}]", reason)); - - return rejoin(currentState, "master left (reason = " + reason + ")"); + rejoin("master left (reason = " + reason + ")"); } + } + } - @Override - public void onFailure(String source, Exception e) { - logger.error((Supplier) () -> new ParameterizedMessage("unexpected failure during [{}]", source), e); - } + // return true if state has been sent to applier + boolean processNextCommittedClusterState(String reason) { + assert Thread.holdsLock(stateMutex); - }); - } + final ClusterState newClusterState = publishClusterState.pendingStatesQueue().getNextClusterStateToProcess(); + final ClusterState currentState = state.get(); + final ClusterState adaptedNewClusterState; + // all pending states have been processed + if (newClusterState == null) { + return false; + } - void processNextPendingClusterState(String reason) { - clusterService.submitStateUpdateTask("zen-disco-receive(from master [" + reason + "])", new LocalClusterUpdateTask(Priority.URGENT) { - ClusterState newClusterState = null; + assert newClusterState.nodes().getMasterNode() != null : "received a cluster state without a master"; + assert !newClusterState.blocks().hasGlobalBlock(discoverySettings.getNoMasterBlock()) : "received a cluster state with a master block"; - @Override - public ClusterTasksResult execute(ClusterState currentState) { - newClusterState = publishClusterState.pendingStatesQueue().getNextClusterStateToProcess(); + if (currentState.nodes().isLocalNodeElectedMaster() && newClusterState.nodes().isLocalNodeElectedMaster() == false) { + handleAnotherMaster(currentState, newClusterState.nodes().getMasterNode(), newClusterState.version(), "via a new cluster state"); + return false; + } - // all pending states have been processed - if (newClusterState == null) { - return unchanged(); - } + try { + if (shouldIgnoreOrRejectNewClusterState(logger, currentState, newClusterState)) { + String message = String.format( + Locale.ROOT, + "rejecting cluster state version [%d] uuid [%s] received from [%s]", + newClusterState.version(), + newClusterState.stateUUID(), + newClusterState.nodes().getMasterNodeId() + ); + throw new IllegalStateException(message); + } + } catch (Exception e) { + try { + publishClusterState.pendingStatesQueue().markAsFailed(newClusterState, e); + } catch (Exception inner) { + inner.addSuppressed(e); + logger.error((Supplier) () -> new ParameterizedMessage("unexpected exception while failing [{}]", reason), inner); + } + return false; + } - assert newClusterState.nodes().getMasterNode() != null : "received a cluster state without a master"; - assert !newClusterState.blocks().hasGlobalBlock(discoverySettings.getNoMasterBlock()) : "received a cluster state with a master block"; + if (newClusterState.nodes().isLocalNodeElectedMaster()) { + // update the set of nodes to ping + nodesFD.updateNodesAndPing(newClusterState); + } else { + // check to see that we monitor the correct master of the cluster + if (masterFD.masterNode() == null || !masterFD.masterNode().equals(newClusterState.nodes().getMasterNode())) { + masterFD.restart(newClusterState.nodes().getMasterNode(), "new cluster state received and we are monitoring the wrong master [" + masterFD.masterNode() + "]"); + } + } - if (currentState.nodes().isLocalNodeElectedMaster()) { - return handleAnotherMaster(currentState, newClusterState.nodes().getMasterNode(), newClusterState.version(), "via a new cluster state"); + if (currentState.blocks().hasGlobalBlock(discoverySettings.getNoMasterBlock())) { + // its a fresh update from the master as we transition from a start of not having a master to having one + logger.debug("got first state from fresh master [{}]", newClusterState.nodes().getMasterNodeId()); + adaptedNewClusterState = newClusterState; + } else if (newClusterState.nodes().isLocalNodeElectedMaster() == false) { + // some optimizations to make sure we keep old objects where possible + ClusterState.Builder builder = ClusterState.builder(newClusterState); + + // if the routing table did not change, use the original one + if (newClusterState.routingTable().version() == currentState.routingTable().version()) { + builder.routingTable(currentState.routingTable()); + } + // same for metadata + if (newClusterState.metaData().version() == currentState.metaData().version()) { + builder.metaData(currentState.metaData()); + } else { + // if its not the same version, only copy over new indices or ones that changed the version + MetaData.Builder metaDataBuilder = MetaData.builder(newClusterState.metaData()).removeAllIndices(); + for (IndexMetaData indexMetaData : newClusterState.metaData()) { + IndexMetaData currentIndexMetaData = currentState.metaData().index(indexMetaData.getIndex()); + if (currentIndexMetaData != null && currentIndexMetaData.isSameUUID(indexMetaData.getIndexUUID()) && + currentIndexMetaData.getVersion() == indexMetaData.getVersion()) { + // safe to reuse + metaDataBuilder.put(currentIndexMetaData, false); + } else { + metaDataBuilder.put(indexMetaData, false); + } } + builder.metaData(metaDataBuilder); + } - if (shouldIgnoreOrRejectNewClusterState(logger, currentState, newClusterState)) { - return unchanged(); - } - if (currentState.blocks().hasGlobalBlock(discoverySettings.getNoMasterBlock())) { - // its a fresh update from the master as we transition from a start of not having a master to having one - logger.debug("got first state from fresh master [{}]", newClusterState.nodes().getMasterNodeId()); - return newState(newClusterState); - } + adaptedNewClusterState = builder.build(); + } else { + adaptedNewClusterState = newClusterState; + } + if (currentState == adaptedNewClusterState) { + return false; + } - // some optimizations to make sure we keep old objects where possible - ClusterState.Builder builder = ClusterState.builder(newClusterState); + state.set(adaptedNewClusterState); - // if the routing table did not change, use the original one - if (newClusterState.routingTable().version() == currentState.routingTable().version()) { - builder.routingTable(currentState.routingTable()); - } - // same for metadata - if (newClusterState.metaData().version() == currentState.metaData().version()) { - builder.metaData(currentState.metaData()); - } else { - // if its not the same version, only copy over new indices or ones that changed the version - MetaData.Builder metaDataBuilder = MetaData.builder(newClusterState.metaData()).removeAllIndices(); - for (IndexMetaData indexMetaData : newClusterState.metaData()) { - IndexMetaData currentIndexMetaData = currentState.metaData().index(indexMetaData.getIndex()); - if (currentIndexMetaData != null && currentIndexMetaData.isSameUUID(indexMetaData.getIndexUUID()) && - currentIndexMetaData.getVersion() == indexMetaData.getVersion()) { - // safe to reuse - metaDataBuilder.put(currentIndexMetaData, false); - } else { - metaDataBuilder.put(indexMetaData, false); - } + clusterApplier.onNewClusterState("apply cluster state (from master [" + reason + "])", + this::clusterState, + new ClusterStateTaskListener() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + try { + publishClusterState.pendingStatesQueue().markAsProcessed(newClusterState); + } catch (Exception e) { + onFailure(source, e); } - builder.metaData(metaDataBuilder); } - return newState(builder.build()); - } - - @Override - public void onFailure(String source, Exception e) { - logger.error((Supplier) () -> new ParameterizedMessage("unexpected failure during [{}]", source), e); - if (newClusterState != null) { + @Override + public void onFailure(String source, Exception e) { + logger.error((Supplier) () -> new ParameterizedMessage("unexpected failure applying [{}]", reason), e); try { + // TODO: use cluster state uuid instead of full cluster state so that we don't keep reference to CS around + // for too long. publishClusterState.pendingStatesQueue().markAsFailed(newClusterState, e); } catch (Exception inner) { inner.addSuppressed(e); - logger.error((Supplier) () -> new ParameterizedMessage("unexpected exception while failing [{}]", source), inner); + logger.error((Supplier) () -> new ParameterizedMessage("unexpected exception while failing [{}]", reason), inner); } } - } + }); - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - try { - if (newClusterState != null) { - // check to see that we monitor the correct master of the cluster - if (masterFD.masterNode() == null || !masterFD.masterNode().equals(newClusterState.nodes().getMasterNode())) { - masterFD.restart(newClusterState.nodes().getMasterNode(), "new cluster state received and we are monitoring the wrong master [" + masterFD.masterNode() + "]"); - } - publishClusterState.pendingStatesQueue().markAsProcessed(newClusterState); - } - } catch (Exception e) { - onFailure(source, e); - } - } - }); + return true; } /** @@ -888,13 +933,13 @@ private DiscoveryNode findMaster() { logger.trace("full ping responses:{}", sb); } - final DiscoveryNode localNode = clusterService.localNode(); + final DiscoveryNode localNode = transportService.getLocalNode(); // add our selves assert fullPingResponses.stream().map(ZenPing.PingResponse::node) .filter(n -> n.equals(localNode)).findAny().isPresent() == false; - fullPingResponses.add(new ZenPing.PingResponse(localNode, null, clusterService.state())); + fullPingResponses.add(new ZenPing.PingResponse(localNode, null, this.clusterState())); // filter responses final List pingResponses = filterPingResponses(fullPingResponses, masterElectionIgnoreNonMasters, logger); @@ -956,10 +1001,9 @@ static List filterPingResponses(List return pingResponses; } - protected ClusterStateTaskExecutor.ClusterTasksResult rejoin(ClusterState clusterState, String reason) { - - // *** called from within an cluster state update task *** // - assert Thread.currentThread().getName().contains(ClusterService.UPDATE_THREAD_NAME); + protected void rejoin(String reason) { + assert Thread.holdsLock(stateMutex); + ClusterState clusterState = state.get(); logger.warn("{}, current nodes: {}", reason, clusterState.nodes()); nodesFD.stop(); @@ -968,44 +1012,54 @@ protected ClusterStateTaskExecutor.ClusterTasksResult re // TODO: do we want to force a new thread if we actively removed the master? this is to give a full pinging cycle // before a decision is made. joinThreadControl.startNewThreadIfNotRunning(); - return LocalClusterUpdateTask.noMaster(); + + if (clusterState.nodes().getMasterNodeId() != null) { + // remove block if it already exists before adding new one + assert clusterState.blocks().hasGlobalBlock(discoverySettings.getNoMasterBlock().id()) == false : + "NO_MASTER_BLOCK should only be added by ZenDiscovery"; + ClusterBlocks clusterBlocks = ClusterBlocks.builder().blocks(clusterState.blocks()) + .addGlobalBlock(discoverySettings.getNoMasterBlock()) + .build(); + + DiscoveryNodes discoveryNodes = new DiscoveryNodes.Builder(clusterState.nodes()).masterNodeId(null).build(); + clusterState = ClusterState.builder(clusterState) + .blocks(clusterBlocks) + .nodes(discoveryNodes) + .build(); + + state.set(clusterState); + clusterApplier.onNewClusterState(reason, this::clusterState, (source, e) -> {}); // don't wait for state to be applied + } } private boolean localNodeMaster() { return nodes().isLocalNodeElectedMaster(); } - private ClusterStateTaskExecutor.ClusterTasksResult handleAnotherMaster(ClusterState localClusterState, final DiscoveryNode otherMaster, long otherClusterStateVersion, String reason) { + private void handleAnotherMaster(ClusterState localClusterState, final DiscoveryNode otherMaster, long otherClusterStateVersion, String reason) { assert localClusterState.nodes().isLocalNodeElectedMaster() : "handleAnotherMaster called but current node is not a master"; - assert Thread.currentThread().getName().contains(ClusterService.UPDATE_THREAD_NAME) : "not called from the cluster state update thread"; + assert Thread.holdsLock(stateMutex); if (otherClusterStateVersion > localClusterState.version()) { - return rejoin(localClusterState, "zen-disco-discovered another master with a new cluster_state [" + otherMaster + "][" + reason + "]"); + rejoin("zen-disco-discovered another master with a new cluster_state [" + otherMaster + "][" + reason + "]"); } else { + // TODO: do this outside mutex logger.warn("discovered [{}] which is also master but with an older cluster_state, telling [{}] to rejoin the cluster ([{}])", otherMaster, otherMaster, reason); - // spawn to a background thread to not do blocking operations on the cluster state thread - threadPool.generic().execute(new AbstractRunnable() { - @Override - public void onFailure(Exception e) { - logger.warn((Supplier) () -> new ParameterizedMessage("failed to send rejoin request to [{}]", otherMaster), e); - } - - @Override - protected void doRun() throws Exception { - // make sure we're connected to this node (connect to node does nothing if we're already connected) - // since the network connections are asymmetric, it may be that we received a state but have disconnected from the node - // in the past (after a master failure, for example) - transportService.connectToNode(otherMaster); - transportService.sendRequest(otherMaster, DISCOVERY_REJOIN_ACTION_NAME, new RejoinClusterRequest(localNode().getId()), new EmptyTransportResponseHandler(ThreadPool.Names.SAME) { - - @Override - public void handleException(TransportException exp) { - logger.warn((Supplier) () -> new ParameterizedMessage("failed to send rejoin request to [{}]", otherMaster), exp); - } - }); - } - }); - return LocalClusterUpdateTask.unchanged(); + try { + // make sure we're connected to this node (connect to node does nothing if we're already connected) + // since the network connections are asymmetric, it may be that we received a state but have disconnected from the node + // in the past (after a master failure, for example) + transportService.connectToNode(otherMaster); + transportService.sendRequest(otherMaster, DISCOVERY_REJOIN_ACTION_NAME, new RejoinClusterRequest(localClusterState.nodes().getLocalNodeId()), new EmptyTransportResponseHandler(ThreadPool.Names.SAME) { + + @Override + public void handleException(TransportException exp) { + logger.warn((Supplier) () -> new ParameterizedMessage("failed to send rejoin request to [{}]", otherMaster), exp); + } + }); + } catch (Exception e) { + logger.warn((Supplier) () -> new ParameterizedMessage("failed to send rejoin request to [{}]", otherMaster), e); + } } } @@ -1033,14 +1087,16 @@ private class NewPendingClusterStateListener implements PublishClusterStateActio @Override public void onNewClusterState(String reason) { - processNextPendingClusterState(reason); + synchronized (stateMutex) { + processNextCommittedClusterState(reason); + } } } private class MembershipListener implements MembershipAction.MembershipListener { @Override public void onJoin(DiscoveryNode node, MembershipAction.JoinCallback callback) { - handleJoinRequest(node, clusterService.state(), callback); + handleJoinRequest(node, ZenDiscovery.this.clusterState(), callback); } @Override @@ -1072,23 +1128,13 @@ public void onPingReceived(final NodesFaultDetection.PingRequest pingRequest) { return; } logger.debug("got a ping from another master {}. resolving who should rejoin. current ping count: [{}]", pingRequest.masterNode(), pingsWhileMaster.get()); - clusterService.submitStateUpdateTask("ping from another master", new LocalClusterUpdateTask(Priority.IMMEDIATE) { - - @Override - public ClusterTasksResult execute(ClusterState currentState) throws Exception { - if (currentState.nodes().isLocalNodeElectedMaster()) { - pingsWhileMaster.set(0); - return handleAnotherMaster(currentState, pingRequest.masterNode(), pingRequest.clusterStateVersion(), "node fd ping"); - } else { - return unchanged(); - } - } - - @Override - public void onFailure(String source, Exception e) { - logger.debug("unexpected error during cluster state update task after pings from another master", e); + synchronized (stateMutex) { + ClusterState currentState = state.get(); + if (currentState.nodes().isLocalNodeElectedMaster()) { + pingsWhileMaster.set(0); + handleAnotherMaster(currentState, pingRequest.masterNode(), pingRequest.clusterStateVersion(), "node fd ping"); } - }); + } } } @@ -1127,23 +1173,14 @@ public void writeTo(StreamOutput out) throws IOException { class RejoinClusterRequestHandler implements TransportRequestHandler { @Override public void messageReceived(final RejoinClusterRequest request, final TransportChannel channel) throws Exception { - clusterService.submitStateUpdateTask("received a request to rejoin the cluster from [" + request.fromNodeId + "]", new LocalClusterUpdateTask(Priority.IMMEDIATE) { - - @Override - public ClusterTasksResult execute(ClusterState currentState) { - try { - channel.sendResponse(TransportResponse.Empty.INSTANCE); - } catch (Exception e) { - logger.warn("failed to send response on rejoin cluster request handling", e); - } - return rejoin(currentState, "received a request to rejoin the cluster from [" + request.fromNodeId + "]"); - } - - @Override - public void onFailure(String source, Exception e) { - logger.error((Supplier) () -> new ParameterizedMessage("unexpected failure during [{}]", source), e); - } - }); + try { + channel.sendResponse(TransportResponse.Empty.INSTANCE); + } catch (Exception e) { + logger.warn("failed to send response on rejoin cluster request handling", e); + } + synchronized (stateMutex) { + rejoin("received a request to rejoin the cluster from [" + request.fromNodeId + "]"); + } } } @@ -1169,15 +1206,15 @@ public boolean joinThreadActive(Thread joinThread) { } /** cleans any running joining thread and calls {@link #rejoin} */ - public ClusterStateTaskExecutor.ClusterTasksResult stopRunningThreadAndRejoin(ClusterState clusterState, String reason) { - ClusterService.assertClusterStateThread(); + public void stopRunningThreadAndRejoin(String reason) { + assert Thread.holdsLock(stateMutex); currentJoinThread.set(null); - return rejoin(clusterState, reason); + rejoin(reason); } /** starts a new joining thread if there is no currently active one and join thread controlling is started */ public void startNewThreadIfNotRunning() { - ClusterService.assertClusterStateThread(); + assert Thread.holdsLock(stateMutex); if (joinThreadActive()) { return; } @@ -1210,7 +1247,7 @@ public void run() { * If the given thread is not the currently running join thread, the command is ignored. */ public void markThreadAsDoneAndStartNew(Thread joinThread) { - ClusterService.assertClusterStateThread(); + assert Thread.holdsLock(stateMutex); if (!markThreadAsDone(joinThread)) { return; } @@ -1219,7 +1256,7 @@ public void markThreadAsDoneAndStartNew(Thread joinThread) { /** marks the given joinThread as completed. Returns false if the supplied thread is not the currently active join thread */ public boolean markThreadAsDone(Thread joinThread) { - ClusterService.assertClusterStateThread(); + assert Thread.holdsLock(stateMutex); return currentJoinThread.compareAndSet(joinThread, null); } diff --git a/core/src/main/java/org/elasticsearch/gateway/GatewayService.java b/core/src/main/java/org/elasticsearch/gateway/GatewayService.java index c215585c45c10..0353deab6e5f1 100644 --- a/core/src/main/java/org/elasticsearch/gateway/GatewayService.java +++ b/core/src/main/java/org/elasticsearch/gateway/GatewayService.java @@ -122,9 +122,6 @@ public GatewayService(Settings settings, AllocationService allocationService, Cl // TODO: change me once the minimum_master_nodes is changed too recoverAfterMasterNodes = settings.getAsInt("discovery.zen.minimum_master_nodes", -1); } - - // Add the not recovered as initial state block, we don't allow anything until - this.clusterService.addInitialStateBlock(STATE_NOT_RECOVERED_BLOCK); } @Override diff --git a/core/src/main/java/org/elasticsearch/indices/store/IndicesStore.java b/core/src/main/java/org/elasticsearch/indices/store/IndicesStore.java index 9c9731bc15508..2ae8d12a9fe9f 100644 --- a/core/src/main/java/org/elasticsearch/indices/store/IndicesStore.java +++ b/core/src/main/java/org/elasticsearch/indices/store/IndicesStore.java @@ -26,7 +26,6 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateListener; import org.elasticsearch.cluster.ClusterStateObserver; -import org.elasticsearch.cluster.LocalClusterUpdateTask; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.IndexRoutingTable; @@ -280,26 +279,20 @@ private void allNodesResponded() { return; } - clusterService.submitStateUpdateTask("indices_store ([" + shardId + "] active fully on other nodes)", new LocalClusterUpdateTask() { - @Override - public ClusterTasksResult execute(ClusterState currentState) throws Exception { + clusterService.getClusterApplierService().runOnApplierThread("indices_store ([" + shardId + "] active fully on other nodes)", + currentState -> { if (clusterStateVersion != currentState.getVersion()) { logger.trace("not deleting shard {}, the update task state version[{}] is not equal to cluster state before shard active api call [{}]", shardId, currentState.getVersion(), clusterStateVersion); - return unchanged(); + return; } try { indicesService.deleteShardStore("no longer used", shardId, currentState); } catch (Exception ex) { logger.debug((Supplier) () -> new ParameterizedMessage("{} failed to delete unallocated shard, ignoring", shardId), ex); } - return unchanged(); - } - - @Override - public void onFailure(String source, Exception e) { - logger.error((Supplier) () -> new ParameterizedMessage("{} unexpected error during deletion of unallocated shard", shardId), e); - } - }); + }, + (source, e) -> logger.error((Supplier) () -> new ParameterizedMessage("{} unexpected error during deletion of unallocated shard", shardId), e) + ); } } diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index 764ece5b868a2..cbb68dc29245e 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -336,8 +336,7 @@ protected Node(final Environment environment, Collection resourcesToClose.add(resourceWatcherService); final NetworkService networkService = new NetworkService(settings, getCustomNameResolvers(pluginsService.filterPlugins(DiscoveryPlugin.class))); - final ClusterService clusterService = new ClusterService(settings, settingsModule.getClusterSettings(), threadPool, - localNodeFactory::getNode); + final ClusterService clusterService = new ClusterService(settings, settingsModule.getClusterSettings(), threadPool); clusterService.addListener(scriptModule.getScriptService()); resourcesToClose.add(clusterService); final IngestService ingestService = new IngestService(settings, threadPool, this.environment, @@ -428,12 +427,13 @@ protected Node(final Environment environment, Collection }; httpServerTransport = null; } - final DiscoveryModule discoveryModule = new DiscoveryModule(this.settings, threadPool, transportService, - namedWriteableRegistry, networkService, clusterService, pluginsService.filterPlugins(DiscoveryPlugin.class)); + + final DiscoveryModule discoveryModule = new DiscoveryModule(this.settings, threadPool, transportService, namedWriteableRegistry, + networkService, clusterService.getMasterService(), clusterService.getClusterApplierService(), + clusterService.getClusterSettings(), pluginsService.filterPlugins(DiscoveryPlugin.class)); NodeService nodeService = new NodeService(settings, threadPool, monitorService, discoveryModule.getDiscovery(), transportService, indicesService, pluginsService, circuitBreakerService, scriptModule.getScriptService(), httpServerTransport, ingestService, clusterService, settingsModule.getSettingsFilter()); - modules.add(b -> { b.bind(NodeService.class).toInstance(nodeService); b.bind(NamedXContentRegistry.class).toInstance(xContentRegistry); @@ -662,9 +662,7 @@ public Node start() throws NodeValidationException { injector.getInstance(ResourceWatcherService.class).start(); injector.getInstance(GatewayService.class).start(); Discovery discovery = injector.getInstance(Discovery.class); - clusterService.setDiscoverySettings(discovery.getDiscoverySettings()); - clusterService.addInitialStateBlock(discovery.getDiscoverySettings().getNoMasterBlock()); - clusterService.setClusterStatePublisher(discovery::publish); + clusterService.getMasterService().setClusterStatePublisher(discovery::publish); // start before the cluster service since it adds/removes initial Cluster state blocks final TribeService tribeService = injector.getInstance(TribeService.class); @@ -674,18 +672,20 @@ public Node start() throws NodeValidationException { TransportService transportService = injector.getInstance(TransportService.class); transportService.getTaskManager().setTaskResultsService(injector.getInstance(TaskResultsService.class)); transportService.start(); + assert localNodeFactory.getNode() != null; + assert transportService.getLocalNode().equals(localNodeFactory.getNode()) + : "transportService has a different local node than the factory provided"; validateNodeBeforeAcceptingRequests(settings, transportService.boundAddress(), pluginsService.filterPlugins(Plugin.class).stream() .flatMap(p -> p.getBootstrapChecks().stream()).collect(Collectors.toList())); clusterService.addStateApplier(transportService.getTaskManager()); + clusterService.getMasterService().setClusterStateSupplier(discovery::clusterState); + clusterService.getClusterApplierService().setInitialState(discovery.getInitialClusterState()); + // start after transport service so the local disco is known clusterService.start(); - assert localNodeFactory.getNode() != null; - assert transportService.getLocalNode().equals(localNodeFactory.getNode()) - : "transportService has a different local node than the factory provided"; + discovery.start(); assert clusterService.localNode().equals(localNodeFactory.getNode()) : "clusterService has a different local node than the factory provided"; - // start after cluster service so the local disco is known - discovery.start(); transportService.acceptIncomingRequests(); discovery.startInitialJoin(); // tribe nodes don't have a master so we shouldn't register an observer s diff --git a/core/src/main/java/org/elasticsearch/node/NodeService.java b/core/src/main/java/org/elasticsearch/node/NodeService.java index cb245487152e3..1c283522cc64f 100644 --- a/core/src/main/java/org/elasticsearch/node/NodeService.java +++ b/core/src/main/java/org/elasticsearch/node/NodeService.java @@ -82,7 +82,7 @@ public class NodeService extends AbstractComponent implements Closeable { public NodeInfo info(boolean settings, boolean os, boolean process, boolean jvm, boolean threadPool, boolean transport, boolean http, boolean plugin, boolean ingest, boolean indices) { - return new NodeInfo(Version.CURRENT, Build.CURRENT, discovery.localNode(), + return new NodeInfo(Version.CURRENT, Build.CURRENT, transportService.getLocalNode(), settings ? settingsFilter.filter(this.settings) : null, os ? monitorService.osService().info() : null, process ? monitorService.processService().info() : null, @@ -101,7 +101,7 @@ public NodeStats stats(CommonStatsFlags indices, boolean os, boolean process, bo boolean script, boolean discoveryStats, boolean ingest) { // for indices stats we want to include previous allocated shards stats as well (it will // only be applied to the sensible ones to use, like refresh/merge/flush/indexing stats) - return new NodeStats(discovery.localNode(), System.currentTimeMillis(), + return new NodeStats(transportService.getLocalNode(), System.currentTimeMillis(), indices.anySet() ? indicesService.stats(true, indices) : null, os ? monitorService.osService().stats() : null, process ? monitorService.processService().stats() : null, diff --git a/core/src/main/java/org/elasticsearch/plugins/DiscoveryPlugin.java b/core/src/main/java/org/elasticsearch/plugins/DiscoveryPlugin.java index 61e87d83a183f..0569547b82240 100644 --- a/core/src/main/java/org/elasticsearch/plugins/DiscoveryPlugin.java +++ b/core/src/main/java/org/elasticsearch/plugins/DiscoveryPlugin.java @@ -23,13 +23,14 @@ import java.util.Map; import java.util.function.Supplier; -import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.cluster.service.ClusterApplier; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.network.NetworkService; +import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.discovery.Discovery; import org.elasticsearch.discovery.zen.UnicastHostsProvider; -import org.elasticsearch.discovery.zen.ZenPing; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -57,12 +58,17 @@ public interface DiscoveryPlugin { * * @param threadPool Use to schedule ping actions * @param transportService Use to communicate with other nodes - * @param clusterService Use to find current nodes in the cluster + * @param masterService Use to submit cluster state update tasks + * @param clusterApplier Use to locally apply cluster state updates + * @param clusterSettings Use to get cluster settings * @param hostsProvider Use to find configured hosts which should be pinged for initial discovery */ default Map> getDiscoveryTypes(ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, - ClusterService clusterService, UnicastHostsProvider hostsProvider) { + MasterService masterService, + ClusterApplier clusterApplier, + ClusterSettings clusterSettings, + UnicastHostsProvider hostsProvider) { return Collections.emptyMap(); } diff --git a/core/src/main/java/org/elasticsearch/repositories/RepositoriesService.java b/core/src/main/java/org/elasticsearch/repositories/RepositoriesService.java index 7aea5d18afb43..2efbae5961e9d 100644 --- a/core/src/main/java/org/elasticsearch/repositories/RepositoriesService.java +++ b/core/src/main/java/org/elasticsearch/repositories/RepositoriesService.java @@ -148,7 +148,8 @@ public void onFailure(String source, Exception e) { @Override public boolean mustAck(DiscoveryNode discoveryNode) { - return discoveryNode.isMasterNode(); + // repository is created on both master and data nodes + return discoveryNode.isMasterNode() || discoveryNode.isDataNode(); } }); } @@ -198,8 +199,8 @@ public ClusterState execute(ClusterState currentState) { @Override public boolean mustAck(DiscoveryNode discoveryNode) { - // Since operation occurs only on masters, it's enough that only master-eligible nodes acked - return discoveryNode.isMasterNode(); + // repository was created on both master and data nodes + return discoveryNode.isMasterNode() || discoveryNode.isDataNode(); } }); } diff --git a/core/src/main/java/org/elasticsearch/tribe/TribeService.java b/core/src/main/java/org/elasticsearch/tribe/TribeService.java index fb9d7babf205c..cec01732c4286 100644 --- a/core/src/main/java/org/elasticsearch/tribe/TribeService.java +++ b/core/src/main/java/org/elasticsearch/tribe/TribeService.java @@ -43,6 +43,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.component.AbstractLifecycleComponent; +import org.elasticsearch.common.component.Lifecycle; import org.elasticsearch.common.hash.MurmurHash3; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput; @@ -73,6 +74,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Function; @@ -129,7 +131,7 @@ public static Settings processSettings(Settings settings) { if (!NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.exists(settings)) { sb.put(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey(), nodesSettings.size()); } - sb.put(DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey(), "none"); // a tribe node should not use zen discovery + sb.put(DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey(), "tribe"); // there is a special discovery implementation for tribe // nothing is going to be discovered, since no master will be elected sb.put(DiscoverySettings.INITIAL_STATE_TIMEOUT_SETTING.getKey(), 0); if (sb.get("cluster.name") == null) { @@ -230,16 +232,6 @@ public TribeService(Settings settings, ClusterService clusterService, final Stri this.blockIndicesMetadata = BLOCKS_METADATA_INDICES_SETTING.get(settings).toArray(Strings.EMPTY_ARRAY); this.blockIndicesRead = BLOCKS_READ_INDICES_SETTING.get(settings).toArray(Strings.EMPTY_ARRAY); this.blockIndicesWrite = BLOCKS_WRITE_INDICES_SETTING.get(settings).toArray(Strings.EMPTY_ARRAY); - - if (!nodes.isEmpty()) { - if (BLOCKS_WRITE_SETTING.get(settings)) { - clusterService.addInitialStateBlock(TRIBE_WRITE_BLOCK); - } - if (BLOCKS_METADATA_SETTING.get(settings)) { - clusterService.addInitialStateBlock(TRIBE_METADATA_BLOCK); - } - } - this.onConflict = ON_CONFLICT_SETTING.get(settings); } @@ -290,12 +282,7 @@ static Settings buildClientSettings(String tribeName, String parentNodeId, Setti @Override protected void doStart() { - if (nodes.isEmpty() == false) { - // remove the initial election / recovery blocks since we are not going to have a - // master elected in this single tribe node local "cluster" - clusterService.removeInitialStateBlock(DiscoverySettings.NO_MASTER_BLOCK_ID); - clusterService.removeInitialStateBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK); - } + } public void startNodes() { @@ -516,7 +503,10 @@ private boolean updateCustoms(ClusterState currentState, List tribeClientNodes = TribeService.this.nodes; Map mergedCustomMetaDataMap = mergeChangedCustomMetaData(changedCustomMetaDataTypeSet, customMetaDataType -> tribeClientNodes.stream() - .map(TribeService::getClusterService).map(ClusterService::state) + .map(TribeService::getClusterService) + // cluster service might not have initial state yet (as tribeClientNodes are started after main node) + .filter(cs -> cs.lifecycleState() == Lifecycle.State.STARTED) + .map(ClusterService::state) .map(ClusterState::metaData) .map(clusterMetaData -> ((MetaData.Custom) clusterMetaData.custom(customMetaDataType))) .filter(custom1 -> custom1 != null && custom1 instanceof MergableCustomMetaData) diff --git a/core/src/test/java/org/elasticsearch/cluster/ClusterModuleTests.java b/core/src/test/java/org/elasticsearch/cluster/ClusterModuleTests.java index 51c66033ca9cf..9e45b0d38280e 100644 --- a/core/src/test/java/org/elasticsearch/cluster/ClusterModuleTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/ClusterModuleTests.java @@ -19,8 +19,6 @@ package org.elasticsearch.cluster; -import org.elasticsearch.Version; -import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.allocation.RoutingAllocation; import org.elasticsearch.cluster.routing.allocation.ShardAllocationDecision; @@ -41,7 +39,6 @@ import org.elasticsearch.cluster.routing.allocation.decider.SnapshotInProgressAllocationDecider; import org.elasticsearch.cluster.routing.allocation.decider.ThrottlingAllocationDecider; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.inject.ModuleTestCase; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; @@ -61,8 +58,7 @@ public class ClusterModuleTests extends ModuleTestCase { private ClusterService clusterService = new ClusterService(Settings.EMPTY, - new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), null, () -> - new DiscoveryNode(UUIDs.randomBase64UUID(), buildNewFakeTransportAddress(), Version.CURRENT)); + new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), null); static class FakeAllocationDecider extends AllocationDecider { protected FakeAllocationDecider(Settings settings) { super(settings); diff --git a/core/src/test/java/org/elasticsearch/cluster/health/ClusterStateHealthTests.java b/core/src/test/java/org/elasticsearch/cluster/health/ClusterStateHealthTests.java index f2cd88a27a7c1..f2537e746ad0c 100644 --- a/core/src/test/java/org/elasticsearch/cluster/health/ClusterStateHealthTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/health/ClusterStateHealthTests.java @@ -29,7 +29,6 @@ import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.LocalClusterUpdateTask; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.MetaData; @@ -130,18 +129,11 @@ public void testClusterHealthWaitsForClusterStateApplication() throws Interrupte }); logger.info("--> submit task to restore master"); - clusterService.submitStateUpdateTask("restore master", new LocalClusterUpdateTask() { - @Override - public ClusterTasksResult execute(ClusterState currentState) throws Exception { - return newState(ClusterState.builder(currentState).nodes( - DiscoveryNodes.builder(currentState.nodes()).masterNodeId(currentState.nodes().getLocalNodeId())).build()); - } - - @Override - public void onFailure(String source, Exception e) { - logger.warn("unexpected failure", e); - } - }); + ClusterState currentState = clusterService.getClusterApplierService().state(); + clusterService.getClusterApplierService().onNewClusterState("restore master", + () -> ClusterState.builder(currentState) + .nodes(DiscoveryNodes.builder(currentState.nodes()).masterNodeId(currentState.nodes().getLocalNodeId())).build(), + (source, e) -> {}); logger.info("--> waiting for listener to be called and cluster state being blocked"); listenerCalled.await(); diff --git a/core/src/test/java/org/elasticsearch/cluster/routing/DelayedAllocationServiceTests.java b/core/src/test/java/org/elasticsearch/cluster/routing/DelayedAllocationServiceTests.java index f4ce40e16dbea..e38f09e567a39 100644 --- a/core/src/test/java/org/elasticsearch/cluster/routing/DelayedAllocationServiceTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/routing/DelayedAllocationServiceTests.java @@ -465,12 +465,12 @@ private static class TestDelayAllocationService extends DelayedAllocationService private volatile long nanoTimeOverride = -1L; TestDelayAllocationService(Settings settings, ThreadPool threadPool, ClusterService clusterService, - AllocationService allocationService) { + AllocationService allocationService) { super(settings, threadPool, clusterService, allocationService); } @Override - protected void assertClusterStateThread() { + protected void assertClusterOrMasterStateThread() { // do not check this in the unit tests } diff --git a/core/src/test/java/org/elasticsearch/cluster/service/ClusterApplierServiceTests.java b/core/src/test/java/org/elasticsearch/cluster/service/ClusterApplierServiceTests.java new file mode 100644 index 0000000000000..9782dcdc858ac --- /dev/null +++ b/core/src/test/java/org/elasticsearch/cluster/service/ClusterApplierServiceTests.java @@ -0,0 +1,427 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.cluster.service; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.Version; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateObserver; +import org.elasticsearch.cluster.ClusterStateTaskListener; +import org.elasticsearch.cluster.LocalNodeMasterListener; +import org.elasticsearch.cluster.NodeConnectionsService; +import org.elasticsearch.cluster.block.ClusterBlocks; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.discovery.DiscoverySettings; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.MockLogAppender; +import org.elasticsearch.test.junit.annotations.TestLogging; +import org.elasticsearch.threadpool.TestThreadPool; +import org.elasticsearch.threadpool.ThreadPool; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; +import static org.elasticsearch.test.ClusterServiceUtils.setState; +import static org.hamcrest.Matchers.is; + +public class ClusterApplierServiceTests extends ESTestCase { + + protected static ThreadPool threadPool; + protected TimedClusterApplierService clusterApplierService; + + @BeforeClass + public static void createThreadPool() { + threadPool = new TestThreadPool(ClusterApplierServiceTests.class.getName()); + } + + @AfterClass + public static void stopThreadPool() { + if (threadPool != null) { + threadPool.shutdownNow(); + threadPool = null; + } + } + + @Before + public void setUp() throws Exception { + super.setUp(); + clusterApplierService = createTimedClusterService(true); + } + + @After + public void tearDown() throws Exception { + clusterApplierService.close(); + super.tearDown(); + } + + TimedClusterApplierService createTimedClusterService(boolean makeMaster) throws InterruptedException { + DiscoveryNode localNode = new DiscoveryNode("node1", buildNewFakeTransportAddress(), emptyMap(), + emptySet(), Version.CURRENT); + TimedClusterApplierService timedClusterApplierService = new TimedClusterApplierService(Settings.builder().put("cluster.name", + "ClusterApplierServiceTests").build(), new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), + threadPool); + timedClusterApplierService.setNodeConnectionsService(new NodeConnectionsService(Settings.EMPTY, null, null) { + @Override + public void connectToNodes(DiscoveryNodes discoveryNodes) { + // skip + } + + @Override + public void disconnectFromNodesExcept(DiscoveryNodes nodesToKeep) { + // skip + } + }); + timedClusterApplierService.setInitialState(ClusterState.builder(new ClusterName("ClusterApplierServiceTests")) + .nodes(DiscoveryNodes.builder() + .add(localNode) + .localNodeId(localNode.getId()) + .masterNodeId(makeMaster ? localNode.getId() : null)) + .blocks(ClusterBlocks.EMPTY_CLUSTER_BLOCK).build()); + timedClusterApplierService.start(); + return timedClusterApplierService; + } + + @TestLogging("org.elasticsearch.cluster.service:TRACE") // To ensure that we log cluster state events on TRACE level + public void testClusterStateUpdateLogging() throws Exception { + MockLogAppender mockAppender = new MockLogAppender(); + mockAppender.start(); + mockAppender.addExpectation( + new MockLogAppender.SeenEventExpectation( + "test1", + clusterApplierService.getClass().getName(), + Level.DEBUG, + "*processing [test1]: took [1s] no change in cluster state")); + mockAppender.addExpectation( + new MockLogAppender.SeenEventExpectation( + "test2", + clusterApplierService.getClass().getName(), + Level.TRACE, + "*failed to execute cluster state applier in [2s]*")); + + Logger clusterLogger = Loggers.getLogger("org.elasticsearch.cluster.service"); + Loggers.addAppender(clusterLogger, mockAppender); + try { + final CountDownLatch latch = new CountDownLatch(3); + clusterApplierService.currentTimeOverride = System.nanoTime(); + clusterApplierService.runOnApplierThread("test1", + currentState -> clusterApplierService.currentTimeOverride += TimeValue.timeValueSeconds(1).nanos(), + new ClusterStateTaskListener() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + } + + @Override + public void onFailure(String source, Exception e) { + fail(); + } + }); + clusterApplierService.runOnApplierThread("test2", + currentState -> { + clusterApplierService.currentTimeOverride += TimeValue.timeValueSeconds(2).nanos(); + throw new IllegalArgumentException("Testing handling of exceptions in the cluster state task"); + }, + new ClusterStateTaskListener() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + fail(); + } + + @Override + public void onFailure(String source, Exception e) { + latch.countDown(); + } + }); + // Additional update task to make sure all previous logging made it to the loggerName + // We don't check logging for this on since there is no guarantee that it will occur before our check + clusterApplierService.runOnApplierThread("test3", + currentState -> {}, + new ClusterStateTaskListener() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + } + + @Override + public void onFailure(String source, Exception e) { + fail(); + } + }); + latch.await(); + } finally { + Loggers.removeAppender(clusterLogger, mockAppender); + mockAppender.stop(); + } + mockAppender.assertAllExpectationsMatched(); + } + + @TestLogging("org.elasticsearch.cluster.service:WARN") // To ensure that we log cluster state events on WARN level + public void testLongClusterStateUpdateLogging() throws Exception { + MockLogAppender mockAppender = new MockLogAppender(); + mockAppender.start(); + mockAppender.addExpectation( + new MockLogAppender.UnseenEventExpectation( + "test1 shouldn't see because setting is too low", + clusterApplierService.getClass().getName(), + Level.WARN, + "*cluster state applier task [test1] took [*] above the warn threshold of *")); + mockAppender.addExpectation( + new MockLogAppender.SeenEventExpectation( + "test2", + clusterApplierService.getClass().getName(), + Level.WARN, + "*cluster state applier task [test2] took [32s] above the warn threshold of *")); + mockAppender.addExpectation( + new MockLogAppender.SeenEventExpectation( + "test4", + clusterApplierService.getClass().getName(), + Level.WARN, + "*cluster state applier task [test3] took [34s] above the warn threshold of *")); + + Logger clusterLogger = Loggers.getLogger("org.elasticsearch.cluster.service"); + Loggers.addAppender(clusterLogger, mockAppender); + try { + final CountDownLatch latch = new CountDownLatch(4); + final CountDownLatch processedFirstTask = new CountDownLatch(1); + clusterApplierService.currentTimeOverride = System.nanoTime(); + clusterApplierService.runOnApplierThread("test1", + currentState -> clusterApplierService.currentTimeOverride += TimeValue.timeValueSeconds(1).nanos(), + new ClusterStateTaskListener() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + processedFirstTask.countDown(); + } + + @Override + public void onFailure(String source, Exception e) { + fail(); + } + }); + processedFirstTask.await(); + clusterApplierService.runOnApplierThread("test2", + currentState -> { + clusterApplierService.currentTimeOverride += TimeValue.timeValueSeconds(32).nanos(); + throw new IllegalArgumentException("Testing handling of exceptions in the cluster state task"); + }, + new ClusterStateTaskListener() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + fail(); + } + + @Override + public void onFailure(String source, Exception e) { + latch.countDown(); + } + }); + clusterApplierService.runOnApplierThread("test3", + currentState -> clusterApplierService.currentTimeOverride += TimeValue.timeValueSeconds(34).nanos(), + new ClusterStateTaskListener() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + } + + @Override + public void onFailure(String source, Exception e) { + fail(); + } + }); + // Additional update task to make sure all previous logging made it to the loggerName + // We don't check logging for this on since there is no guarantee that it will occur before our check + clusterApplierService.runOnApplierThread("test4", + currentState -> {}, + new ClusterStateTaskListener() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + } + + @Override + public void onFailure(String source, Exception e) { + fail(); + } + }); + latch.await(); + } finally { + Loggers.removeAppender(clusterLogger, mockAppender); + mockAppender.stop(); + } + mockAppender.assertAllExpectationsMatched(); + } + + public void testLocalNodeMasterListenerCallbacks() throws Exception { + TimedClusterApplierService timedClusterApplierService = createTimedClusterService(false); + + AtomicBoolean isMaster = new AtomicBoolean(); + timedClusterApplierService.addLocalNodeMasterListener(new LocalNodeMasterListener() { + @Override + public void onMaster() { + isMaster.set(true); + } + + @Override + public void offMaster() { + isMaster.set(false); + } + + @Override + public String executorName() { + return ThreadPool.Names.SAME; + } + }); + + ClusterState state = timedClusterApplierService.state(); + DiscoveryNodes nodes = state.nodes(); + DiscoveryNodes.Builder nodesBuilder = DiscoveryNodes.builder(nodes).masterNodeId(nodes.getLocalNodeId()); + state = ClusterState.builder(state).blocks(ClusterBlocks.EMPTY_CLUSTER_BLOCK).nodes(nodesBuilder).build(); + setState(timedClusterApplierService, state); + assertThat(isMaster.get(), is(true)); + + nodes = state.nodes(); + nodesBuilder = DiscoveryNodes.builder(nodes).masterNodeId(null); + state = ClusterState.builder(state).blocks(ClusterBlocks.builder().addGlobalBlock(DiscoverySettings.NO_MASTER_BLOCK_WRITES)) + .nodes(nodesBuilder).build(); + setState(timedClusterApplierService, state); + assertThat(isMaster.get(), is(false)); + nodesBuilder = DiscoveryNodes.builder(nodes).masterNodeId(nodes.getLocalNodeId()); + state = ClusterState.builder(state).blocks(ClusterBlocks.EMPTY_CLUSTER_BLOCK).nodes(nodesBuilder).build(); + setState(timedClusterApplierService, state); + assertThat(isMaster.get(), is(true)); + + timedClusterApplierService.close(); + } + + public void testClusterStateApplierCantSampleClusterState() throws InterruptedException { + AtomicReference error = new AtomicReference<>(); + AtomicBoolean applierCalled = new AtomicBoolean(); + clusterApplierService.addStateApplier(event -> { + try { + applierCalled.set(true); + clusterApplierService.state(); + error.set(new AssertionError("successfully sampled state")); + } catch (AssertionError e) { + if (e.getMessage().contains("should not be called by a cluster state applier") == false) { + error.set(e); + } + } + }); + + CountDownLatch latch = new CountDownLatch(1); + clusterApplierService.onNewClusterState("test", () -> ClusterState.builder(clusterApplierService.state()).build(), + new ClusterStateTaskListener() { + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + } + + @Override + public void onFailure(String source, Exception e) { + error.compareAndSet(null, e); + } + } + ); + + latch.await(); + assertNull(error.get()); + assertTrue(applierCalled.get()); + } + + public void testClusterStateApplierCanCreateAnObserver() throws InterruptedException { + AtomicReference error = new AtomicReference<>(); + AtomicBoolean applierCalled = new AtomicBoolean(); + clusterApplierService.addStateApplier(event -> { + try { + applierCalled.set(true); + ClusterStateObserver observer = new ClusterStateObserver(event.state(), + clusterApplierService, null, logger, threadPool.getThreadContext()); + observer.waitForNextChange(new ClusterStateObserver.Listener() { + @Override + public void onNewClusterState(ClusterState state) { + + } + + @Override + public void onClusterServiceClose() { + + } + + @Override + public void onTimeout(TimeValue timeout) { + + } + }); + } catch (AssertionError e) { + error.set(e); + } + }); + + CountDownLatch latch = new CountDownLatch(1); + clusterApplierService.onNewClusterState("test", () -> ClusterState.builder(clusterApplierService.state()).build(), + new ClusterStateTaskListener() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + } + + @Override + public void onFailure(String source, Exception e) { + error.compareAndSet(null, e); + } + }); + + latch.await(); + assertNull(error.get()); + assertTrue(applierCalled.get()); + } + + static class TimedClusterApplierService extends ClusterApplierService { + + public volatile Long currentTimeOverride = null; + + TimedClusterApplierService(Settings settings, ClusterSettings clusterSettings, ThreadPool threadPool) { + super(settings, clusterSettings, threadPool); + } + + @Override + protected long currentTimeInNanos() { + if (currentTimeOverride != null) { + return currentTimeOverride; + } + return super.currentTimeInNanos(); + } + } +} diff --git a/core/src/test/java/org/elasticsearch/cluster/service/ClusterServiceIT.java b/core/src/test/java/org/elasticsearch/cluster/service/ClusterServiceIT.java index f056eded34b25..8514cb4ac2e1b 100644 --- a/core/src/test/java/org/elasticsearch/cluster/service/ClusterServiceIT.java +++ b/core/src/test/java/org/elasticsearch/cluster/service/ClusterServiceIT.java @@ -382,7 +382,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS // The tasks can be re-ordered, so we need to check out-of-order Set controlSources = new HashSet<>(Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")); - List pendingClusterTasks = clusterService.pendingTasks(); + List pendingClusterTasks = clusterService.getMasterService().pendingTasks(); assertThat(pendingClusterTasks.size(), greaterThanOrEqualTo(10)); assertThat(pendingClusterTasks.get(0).getSource().string(), equalTo("1")); assertThat(pendingClusterTasks.get(0).isExecuting(), equalTo(true)); @@ -404,7 +404,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS invoked2.await(); // whenever we test for no tasks, we need to awaitBusy since this is a live node - assertTrue(awaitBusy(() -> clusterService.pendingTasks().isEmpty())); + assertTrue(awaitBusy(() -> clusterService.getMasterService().pendingTasks().isEmpty())); waitNoPendingTasksOnAll(); final CountDownLatch block2 = new CountDownLatch(1); @@ -444,7 +444,7 @@ public void onFailure(String source, Exception e) { } Thread.sleep(100); - pendingClusterTasks = clusterService.pendingTasks(); + pendingClusterTasks = clusterService.getMasterService().pendingTasks(); assertThat(pendingClusterTasks.size(), greaterThanOrEqualTo(5)); controlSources = new HashSet<>(Arrays.asList("1", "2", "3", "4", "5")); for (PendingClusterTask task : pendingClusterTasks) { diff --git a/core/src/test/java/org/elasticsearch/cluster/service/ClusterServiceTests.java b/core/src/test/java/org/elasticsearch/cluster/service/MasterServiceTests.java similarity index 59% rename from core/src/test/java/org/elasticsearch/cluster/service/ClusterServiceTests.java rename to core/src/test/java/org/elasticsearch/cluster/service/MasterServiceTests.java index 8afadff082536..1b747f2268747 100644 --- a/core/src/test/java/org/elasticsearch/cluster/service/ClusterServiceTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/service/MasterServiceTests.java @@ -20,32 +20,25 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.Version; import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.ClusterStateObserver; import org.elasticsearch.cluster.ClusterStateTaskConfig; import org.elasticsearch.cluster.ClusterStateTaskExecutor; import org.elasticsearch.cluster.ClusterStateTaskListener; import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.cluster.LocalClusterUpdateTask; -import org.elasticsearch.cluster.LocalNodeMasterListener; -import org.elasticsearch.cluster.NodeConnectionsService; import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.common.Priority; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.logging.Loggers; -import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.BaseFuture; -import org.elasticsearch.common.util.set.Sets; -import org.elasticsearch.discovery.Discovery; -import org.elasticsearch.discovery.DiscoverySettings; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.MockLogAppender; import org.elasticsearch.test.junit.annotations.TestLogging; @@ -75,21 +68,19 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; -import static org.elasticsearch.test.ClusterServiceUtils.setState; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasKey; -import static org.hamcrest.Matchers.is; -public class ClusterServiceTests extends ESTestCase { +public class MasterServiceTests extends ESTestCase { - static ThreadPool threadPool; - TimedClusterService clusterService; + private static ThreadPool threadPool; + private TimedMasterService masterService; @BeforeClass public static void createThreadPool() { - threadPool = new TestThreadPool(ClusterServiceTests.class.getName()); + threadPool = new TestThreadPool(MasterServiceTests.class.getName()); } @AfterClass @@ -103,48 +94,35 @@ public static void stopThreadPool() { @Before public void setUp() throws Exception { super.setUp(); - clusterService = createTimedClusterService(true); + masterService = createTimedMasterService(true); } @After public void tearDown() throws Exception { - clusterService.close(); + masterService.close(); super.tearDown(); } - TimedClusterService createTimedClusterService(boolean makeMaster) throws InterruptedException { - TimedClusterService timedClusterService = new TimedClusterService(Settings.builder().put("cluster.name", - "ClusterServiceTests").build(), new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), - threadPool, () -> new DiscoveryNode("node1", buildNewFakeTransportAddress(), emptyMap(), - emptySet(), Version.CURRENT)); - timedClusterService.setNodeConnectionsService(new NodeConnectionsService(Settings.EMPTY, null, null) { - @Override - public void connectToNodes(DiscoveryNodes discoveryNodes) { - // skip - } - - @Override - public void disconnectFromNodesExcept(DiscoveryNodes nodesToKeep) { - // skip - } - }); - timedClusterService.setClusterStatePublisher((event, ackListener) -> { - }); - timedClusterService.setDiscoverySettings(new DiscoverySettings(Settings.EMPTY, - new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS))); - timedClusterService.start(); - ClusterState state = timedClusterService.state(); - final DiscoveryNodes nodes = state.nodes(); - final DiscoveryNodes.Builder nodesBuilder = DiscoveryNodes.builder(nodes) - .masterNodeId(makeMaster ? nodes.getLocalNodeId() : null); - state = ClusterState.builder(state).blocks(ClusterBlocks.EMPTY_CLUSTER_BLOCK) - .nodes(nodesBuilder).build(); - setState(timedClusterService, state); - return timedClusterService; + private TimedMasterService createTimedMasterService(boolean makeMaster) throws InterruptedException { + DiscoveryNode localNode = new DiscoveryNode("node1", buildNewFakeTransportAddress(), emptyMap(), + emptySet(), Version.CURRENT); + TimedMasterService timedMasterService = new TimedMasterService(Settings.builder().put("cluster.name", + MasterServiceTests.class.getSimpleName()).build(), threadPool); + ClusterState initialClusterState = ClusterState.builder(new ClusterName(MasterServiceTests.class.getSimpleName())) + .nodes(DiscoveryNodes.builder() + .add(localNode) + .localNodeId(localNode.getId()) + .masterNodeId(makeMaster ? localNode.getId() : null)) + .blocks(ClusterBlocks.EMPTY_CLUSTER_BLOCK).build(); + AtomicReference clusterStateRef = new AtomicReference<>(initialClusterState); + timedMasterService.setClusterStatePublisher((event, ackListener) -> clusterStateRef.set(event.state())); + timedMasterService.setClusterStateSupplier(clusterStateRef::get); + timedMasterService.start(); + return timedMasterService; } public void testMasterAwareExecution() throws Exception { - ClusterService nonMaster = createTimedClusterService(false); + TimedMasterService nonMaster = createTimedMasterService(false); final boolean[] taskFailed = {false}; final CountDownLatch latch1 = new CountDownLatch(1); @@ -196,7 +174,7 @@ public void testClusterStateTaskListenerThrowingExceptionIsOkay() throws Interru final CountDownLatch latch = new CountDownLatch(1); AtomicBoolean published = new AtomicBoolean(); - clusterService.submitStateUpdateTask( + masterService.submitStateUpdateTask( "testClusterStateTaskListenerThrowingExceptionIsOkay", new Object(), ClusterStateTaskConfig.build(Priority.NORMAL), @@ -229,49 +207,109 @@ public void onFailure(String source, Exception e) { assertTrue(published.get()); } - public void testBlockingCallInClusterStateTaskListenerFails() throws InterruptedException { - assumeTrue("assertions must be enabled for this test to work", BaseFuture.class.desiredAssertionStatus()); - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference assertionRef = new AtomicReference<>(); + @TestLogging("org.elasticsearch.cluster.service:TRACE") // To ensure that we log cluster state events on TRACE level + public void testClusterStateUpdateLogging() throws Exception { + MockLogAppender mockAppender = new MockLogAppender(); + mockAppender.start(); + mockAppender.addExpectation( + new MockLogAppender.SeenEventExpectation( + "test1", + masterService.getClass().getName(), + Level.DEBUG, + "*processing [test1]: took [1s] no change in cluster state")); + mockAppender.addExpectation( + new MockLogAppender.SeenEventExpectation( + "test2", + masterService.getClass().getName(), + Level.TRACE, + "*failed to execute cluster state update in [2s]*")); + mockAppender.addExpectation( + new MockLogAppender.SeenEventExpectation( + "test3", + masterService.getClass().getName(), + Level.DEBUG, + "*processing [test3]: took [3s] done publishing updated cluster state (version: *, uuid: *)")); - clusterService.submitStateUpdateTask( - "testBlockingCallInClusterStateTaskListenerFails", - new Object(), - ClusterStateTaskConfig.build(Priority.NORMAL), - new ClusterStateTaskExecutor() { + Logger clusterLogger = Loggers.getLogger(masterService.getClass().getPackage().getName()); + Loggers.addAppender(clusterLogger, mockAppender); + try { + final CountDownLatch latch = new CountDownLatch(4); + masterService.currentTimeOverride = System.nanoTime(); + masterService.submitStateUpdateTask("test1", new ClusterStateUpdateTask() { @Override - public ClusterTasksResult execute(ClusterState currentState, List tasks) throws Exception { - ClusterState newClusterState = ClusterState.builder(currentState).build(); - return ClusterTasksResult.builder().successes(tasks).build(newClusterState); + public ClusterState execute(ClusterState currentState) throws Exception { + masterService.currentTimeOverride += TimeValue.timeValueSeconds(1).nanos(); + return currentState; } - }, - new ClusterStateTaskListener() { + @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - BaseFuture future = new BaseFuture() {}; - try { - if (randomBoolean()) { - future.get(1L, TimeUnit.SECONDS); - } else { - future.get(); - } - } catch (Exception e) { - throw new RuntimeException(e); - } catch (AssertionError e) { - assertionRef.set(e); - latch.countDown(); - } + latch.countDown(); } @Override public void onFailure(String source, Exception e) { + fail(); + } + }); + masterService.submitStateUpdateTask("test2", new ClusterStateUpdateTask() { + @Override + public ClusterState execute(ClusterState currentState) { + masterService.currentTimeOverride += TimeValue.timeValueSeconds(2).nanos(); + throw new IllegalArgumentException("Testing handling of exceptions in the cluster state task"); } - } - ); - latch.await(); - assertNotNull(assertionRef.get()); - assertThat(assertionRef.get().getMessage(), containsString("not be the cluster state update thread. Reason: [Blocking operation]")); + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + fail(); + } + + @Override + public void onFailure(String source, Exception e) { + latch.countDown(); + } + }); + masterService.submitStateUpdateTask("test3", new ClusterStateUpdateTask() { + @Override + public ClusterState execute(ClusterState currentState) { + masterService.currentTimeOverride += TimeValue.timeValueSeconds(3).nanos(); + return ClusterState.builder(currentState).incrementVersion().build(); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + } + + @Override + public void onFailure(String source, Exception e) { + fail(); + } + }); + // Additional update task to make sure all previous logging made it to the loggerName + // We don't check logging for this on since there is no guarantee that it will occur before our check + masterService.submitStateUpdateTask("test4", new ClusterStateUpdateTask() { + @Override + public ClusterState execute(ClusterState currentState) { + return currentState; + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + } + + @Override + public void onFailure(String source, Exception e) { + fail(); + } + }); + latch.await(); + } finally { + Loggers.removeAppender(clusterLogger, mockAppender); + mockAppender.stop(); + } + mockAppender.assertAllExpectationsMatched(); } public void testClusterStateBatchedUpdates() throws BrokenBarrierException, InterruptedException { @@ -414,7 +452,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS submittedTasksPerThread.computeIfAbsent(threadName, key -> new AtomicInteger()).addAndGet(tasks.size()); final TaskExecutor executor = assignment.v1(); if (tasks.size() == 1) { - clusterService.submitStateUpdateTask( + masterService.submitStateUpdateTask( threadName, tasks.stream().findFirst().get(), ClusterStateTaskConfig.build(randomFrom(Priority.values())), @@ -423,7 +461,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS } else { Map taskListeners = new HashMap<>(); tasks.stream().forEach(t -> taskListeners.put(t, listener)); - clusterService.submitStateUpdateTasks( + masterService.submitStateUpdateTasks( threadName, taskListeners, ClusterStateTaskConfig.build(randomFrom(Priority.values())), executor @@ -467,109 +505,47 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS } } - @TestLogging("org.elasticsearch.cluster.service:TRACE") // To ensure that we log cluster state events on TRACE level - public void testClusterStateUpdateLogging() throws Exception { - MockLogAppender mockAppender = new MockLogAppender(); - mockAppender.start(); - mockAppender.addExpectation( - new MockLogAppender.SeenEventExpectation( - "test1", - "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService", - Level.DEBUG, - "*processing [test1]: took [1s] no change in cluster_state")); - mockAppender.addExpectation( - new MockLogAppender.SeenEventExpectation( - "test2", - "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService", - Level.TRACE, - "*failed to execute cluster state update in [2s]*")); - mockAppender.addExpectation( - new MockLogAppender.SeenEventExpectation( - "test3", - "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService", - Level.DEBUG, - "*processing [test3]: took [3s] done applying updated cluster_state (version: *, uuid: *)")); - - Logger clusterLogger = Loggers.getLogger("org.elasticsearch.cluster.service"); - Loggers.addAppender(clusterLogger, mockAppender); - try { - final CountDownLatch latch = new CountDownLatch(4); - clusterService.currentTimeOverride = System.nanoTime(); - clusterService.submitStateUpdateTask("test1", new ClusterStateUpdateTask() { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - clusterService.currentTimeOverride += TimeValue.timeValueSeconds(1).nanos(); - return currentState; - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - latch.countDown(); - } - - @Override - public void onFailure(String source, Exception e) { - fail(); - } - }); - clusterService.submitStateUpdateTask("test2", new ClusterStateUpdateTask() { - @Override - public ClusterState execute(ClusterState currentState) { - clusterService.currentTimeOverride += TimeValue.timeValueSeconds(2).nanos(); - throw new IllegalArgumentException("Testing handling of exceptions in the cluster state task"); - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - fail(); - } - - @Override - public void onFailure(String source, Exception e) { - latch.countDown(); - } - }); - clusterService.submitStateUpdateTask("test3", new ClusterStateUpdateTask() { - @Override - public ClusterState execute(ClusterState currentState) { - clusterService.currentTimeOverride += TimeValue.timeValueSeconds(3).nanos(); - return ClusterState.builder(currentState).incrementVersion().build(); - } + public void testBlockingCallInClusterStateTaskListenerFails() throws InterruptedException { + assumeTrue("assertions must be enabled for this test to work", BaseFuture.class.desiredAssertionStatus()); + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference assertionRef = new AtomicReference<>(); + masterService.submitStateUpdateTask( + "testBlockingCallInClusterStateTaskListenerFails", + new Object(), + ClusterStateTaskConfig.build(Priority.NORMAL), + (currentState, tasks) -> { + ClusterState newClusterState = ClusterState.builder(currentState).build(); + return ClusterStateTaskExecutor.ClusterTasksResult.builder().successes(tasks).build(newClusterState); + }, + new ClusterStateTaskListener() { @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - latch.countDown(); + BaseFuture future = new BaseFuture() {}; + try { + if (randomBoolean()) { + future.get(1L, TimeUnit.SECONDS); + } else { + future.get(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } catch (AssertionError e) { + assertionRef.set(e); + latch.countDown(); + } } @Override public void onFailure(String source, Exception e) { - fail(); - } - }); - // Additional update task to make sure all previous logging made it to the loggerName - // We don't check logging for this on since there is no guarantee that it will occur before our check - clusterService.submitStateUpdateTask("test4", new ClusterStateUpdateTask() { - @Override - public ClusterState execute(ClusterState currentState) { - return currentState; - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - latch.countDown(); } + } + ); - @Override - public void onFailure(String source, Exception e) { - fail(); - } - }); - latch.await(); - } finally { - Loggers.removeAppender(clusterLogger, mockAppender); - mockAppender.stop(); - } - mockAppender.assertAllExpectationsMatched(); + latch.await(); + assertNotNull(assertionRef.get()); + assertThat(assertionRef.get().getMessage(), + containsString("Reason: [Blocking operation]")); } @TestLogging("org.elasticsearch.cluster.service:WARN") // To ensure that we log cluster state events on WARN level @@ -577,40 +553,40 @@ public void testLongClusterStateUpdateLogging() throws Exception { MockLogAppender mockAppender = new MockLogAppender(); mockAppender.start(); mockAppender.addExpectation( - new MockLogAppender.UnseenEventExpectation( - "test1 shouldn't see because setting is too low", - "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService", - Level.WARN, - "*cluster state update task [test1] took [*] above the warn threshold of *")); + new MockLogAppender.UnseenEventExpectation( + "test1 shouldn't see because setting is too low", + masterService.getClass().getName(), + Level.WARN, + "*cluster state update task [test1] took [*] above the warn threshold of *")); mockAppender.addExpectation( - new MockLogAppender.SeenEventExpectation( - "test2", - "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService", - Level.WARN, - "*cluster state update task [test2] took [32s] above the warn threshold of *")); + new MockLogAppender.SeenEventExpectation( + "test2", + masterService.getClass().getName(), + Level.WARN, + "*cluster state update task [test2] took [32s] above the warn threshold of *")); mockAppender.addExpectation( - new MockLogAppender.SeenEventExpectation( - "test3", - "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService", - Level.WARN, - "*cluster state update task [test3] took [33s] above the warn threshold of *")); + new MockLogAppender.SeenEventExpectation( + "test3", + masterService.getClass().getName(), + Level.WARN, + "*cluster state update task [test3] took [33s] above the warn threshold of *")); mockAppender.addExpectation( - new MockLogAppender.SeenEventExpectation( - "test4", - "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService", - Level.WARN, - "*cluster state update task [test4] took [34s] above the warn threshold of *")); + new MockLogAppender.SeenEventExpectation( + "test4", + masterService.getClass().getName(), + Level.WARN, + "*cluster state update task [test4] took [34s] above the warn threshold of *")); - Logger clusterLogger = Loggers.getLogger("org.elasticsearch.cluster.service"); + Logger clusterLogger = Loggers.getLogger(masterService.getClass().getPackage().getName()); Loggers.addAppender(clusterLogger, mockAppender); try { final CountDownLatch latch = new CountDownLatch(5); final CountDownLatch processedFirstTask = new CountDownLatch(1); - clusterService.currentTimeOverride = System.nanoTime(); - clusterService.submitStateUpdateTask("test1", new ClusterStateUpdateTask() { + masterService.currentTimeOverride = System.nanoTime(); + masterService.submitStateUpdateTask("test1", new ClusterStateUpdateTask() { @Override public ClusterState execute(ClusterState currentState) throws Exception { - clusterService.currentTimeOverride += TimeValue.timeValueSeconds(1).nanos(); + masterService.currentTimeOverride += TimeValue.timeValueSeconds(1).nanos(); return currentState; } @@ -627,10 +603,10 @@ public void onFailure(String source, Exception e) { }); processedFirstTask.await(); - clusterService.submitStateUpdateTask("test2", new ClusterStateUpdateTask() { + masterService.submitStateUpdateTask("test2", new ClusterStateUpdateTask() { @Override public ClusterState execute(ClusterState currentState) throws Exception { - clusterService.currentTimeOverride += TimeValue.timeValueSeconds(32).nanos(); + masterService.currentTimeOverride += TimeValue.timeValueSeconds(32).nanos(); throw new IllegalArgumentException("Testing handling of exceptions in the cluster state task"); } @@ -644,10 +620,10 @@ public void onFailure(String source, Exception e) { latch.countDown(); } }); - clusterService.submitStateUpdateTask("test3", new ClusterStateUpdateTask() { + masterService.submitStateUpdateTask("test3", new ClusterStateUpdateTask() { @Override public ClusterState execute(ClusterState currentState) throws Exception { - clusterService.currentTimeOverride += TimeValue.timeValueSeconds(33).nanos(); + masterService.currentTimeOverride += TimeValue.timeValueSeconds(33).nanos(); return ClusterState.builder(currentState).incrementVersion().build(); } @@ -661,10 +637,10 @@ public void onFailure(String source, Exception e) { fail(); } }); - clusterService.submitStateUpdateTask("test4", new ClusterStateUpdateTask() { + masterService.submitStateUpdateTask("test4", new ClusterStateUpdateTask() { @Override public ClusterState execute(ClusterState currentState) throws Exception { - clusterService.currentTimeOverride += TimeValue.timeValueSeconds(34).nanos(); + masterService.currentTimeOverride += TimeValue.timeValueSeconds(34).nanos(); return currentState; } @@ -680,7 +656,7 @@ public void onFailure(String source, Exception e) { }); // Additional update task to make sure all previous logging made it to the loggerName // We don't check logging for this on since there is no guarantee that it will occur before our check - clusterService.submitStateUpdateTask("test5", new ClusterStateUpdateTask() { + masterService.submitStateUpdateTask("test5", new ClusterStateUpdateTask() { @Override public ClusterState execute(ClusterState currentState) { return currentState; @@ -704,211 +680,12 @@ public void onFailure(String source, Exception e) { mockAppender.assertAllExpectationsMatched(); } - public void testDisconnectFromNewlyAddedNodesIfClusterStatePublishingFails() throws InterruptedException { - TimedClusterService timedClusterService = new TimedClusterService(Settings.builder().put("cluster.name", - "ClusterServiceTests").build(), new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), - threadPool, () -> new DiscoveryNode("node1", buildNewFakeTransportAddress(), emptyMap(), - emptySet(), Version.CURRENT)); - Set currentNodes = new HashSet<>(); - timedClusterService.setNodeConnectionsService(new NodeConnectionsService(Settings.EMPTY, null, null) { - @Override - public void connectToNodes(DiscoveryNodes discoveryNodes) { - discoveryNodes.forEach(currentNodes::add); - } - - @Override - public void disconnectFromNodesExcept(DiscoveryNodes nodesToKeep) { - Set nodeSet = new HashSet<>(); - nodesToKeep.iterator().forEachRemaining(nodeSet::add); - currentNodes.removeIf(node -> nodeSet.contains(node) == false); - } - }); - AtomicBoolean failToCommit = new AtomicBoolean(); - timedClusterService.setClusterStatePublisher((event, ackListener) -> { - if (failToCommit.get()) { - throw new Discovery.FailedToCommitClusterStateException("just to test this"); - } - }); - timedClusterService.setDiscoverySettings(new DiscoverySettings(Settings.EMPTY, - new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS))); - timedClusterService.start(); - ClusterState state = timedClusterService.state(); - final DiscoveryNodes nodes = state.nodes(); - final DiscoveryNodes.Builder nodesBuilder = DiscoveryNodes.builder(nodes) - .masterNodeId(nodes.getLocalNodeId()); - state = ClusterState.builder(state).blocks(ClusterBlocks.EMPTY_CLUSTER_BLOCK) - .nodes(nodesBuilder).build(); - setState(timedClusterService, state); - - assertThat(currentNodes, equalTo(Sets.newHashSet(timedClusterService.state().getNodes()))); - - final CountDownLatch latch = new CountDownLatch(1); - - // try to add node when cluster state publishing fails - failToCommit.set(true); - timedClusterService.submitStateUpdateTask("test", new ClusterStateUpdateTask() { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - DiscoveryNode newNode = new DiscoveryNode("node2", buildNewFakeTransportAddress(), emptyMap(), - emptySet(), Version.CURRENT); - return ClusterState.builder(currentState).nodes(DiscoveryNodes.builder(currentState.nodes()).add(newNode)).build(); - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - latch.countDown(); - } - - @Override - public void onFailure(String source, Exception e) { - latch.countDown(); - } - }); - - latch.await(); - assertThat(currentNodes, equalTo(Sets.newHashSet(timedClusterService.state().getNodes()))); - timedClusterService.close(); - } - - public void testLocalNodeMasterListenerCallbacks() throws Exception { - TimedClusterService timedClusterService = createTimedClusterService(false); - - AtomicBoolean isMaster = new AtomicBoolean(); - timedClusterService.addLocalNodeMasterListener(new LocalNodeMasterListener() { - @Override - public void onMaster() { - isMaster.set(true); - } - - @Override - public void offMaster() { - isMaster.set(false); - } - - @Override - public String executorName() { - return ThreadPool.Names.SAME; - } - }); - - ClusterState state = timedClusterService.state(); - DiscoveryNodes nodes = state.nodes(); - DiscoveryNodes.Builder nodesBuilder = DiscoveryNodes.builder(nodes).masterNodeId(nodes.getLocalNodeId()); - state = ClusterState.builder(state).blocks(ClusterBlocks.EMPTY_CLUSTER_BLOCK).nodes(nodesBuilder).build(); - setState(timedClusterService, state); - assertThat(isMaster.get(), is(true)); - - nodes = state.nodes(); - nodesBuilder = DiscoveryNodes.builder(nodes).masterNodeId(null); - state = ClusterState.builder(state).blocks(ClusterBlocks.builder().addGlobalBlock(DiscoverySettings.NO_MASTER_BLOCK_WRITES)) - .nodes(nodesBuilder).build(); - setState(timedClusterService, state); - assertThat(isMaster.get(), is(false)); - nodesBuilder = DiscoveryNodes.builder(nodes).masterNodeId(nodes.getLocalNodeId()); - state = ClusterState.builder(state).blocks(ClusterBlocks.EMPTY_CLUSTER_BLOCK).nodes(nodesBuilder).build(); - setState(timedClusterService, state); - assertThat(isMaster.get(), is(true)); - - timedClusterService.close(); - } - - public void testClusterStateApplierCantSampleClusterState() throws InterruptedException { - AtomicReference error = new AtomicReference<>(); - AtomicBoolean applierCalled = new AtomicBoolean(); - clusterService.addStateApplier(event -> { - try { - applierCalled.set(true); - clusterService.state(); - error.set(new AssertionError("successfully sampled state")); - } catch (AssertionError e) { - if (e.getMessage().contains("should not be called by a cluster state applier") == false) { - error.set(e); - } - } - }); - - CountDownLatch latch = new CountDownLatch(1); - clusterService.submitStateUpdateTask("test", new ClusterStateUpdateTask() { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - return ClusterState.builder(currentState).build(); - } - - @Override - public void onFailure(String source, Exception e) { - error.compareAndSet(null, e); - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - latch.countDown(); - } - }); - - latch.await(); - assertNull(error.get()); - assertTrue(applierCalled.get()); - } - - public void testClusterStateApplierCanCreateAnObserver() throws InterruptedException { - AtomicReference error = new AtomicReference<>(); - AtomicBoolean applierCalled = new AtomicBoolean(); - clusterService.addStateApplier(event -> { - try { - applierCalled.set(true); - ClusterStateObserver observer = new ClusterStateObserver(event.state(), - clusterService, null, logger, threadPool.getThreadContext()); - observer.waitForNextChange(new ClusterStateObserver.Listener() { - @Override - public void onNewClusterState(ClusterState state) { - - } - - @Override - public void onClusterServiceClose() { - - } - - @Override - public void onTimeout(TimeValue timeout) { - - } - }); - } catch (AssertionError e) { - error.set(e); - } - }); - - CountDownLatch latch = new CountDownLatch(1); - clusterService.submitStateUpdateTask("test", new ClusterStateUpdateTask() { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - return ClusterState.builder(currentState).build(); - } - - @Override - public void onFailure(String source, Exception e) { - error.compareAndSet(null, e); - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - latch.countDown(); - } - }); - - latch.await(); - assertNull(error.get()); - assertTrue(applierCalled.get()); - } - - static class TimedClusterService extends ClusterService { + static class TimedMasterService extends MasterService { public volatile Long currentTimeOverride = null; - TimedClusterService(Settings settings, ClusterSettings clusterSettings, ThreadPool threadPool, - Supplier localNodeSupplier) { - super(settings, clusterSettings, threadPool, localNodeSupplier); + TimedMasterService(Settings settings, ThreadPool threadPool) { + super(settings, threadPool); } @Override @@ -919,4 +696,11 @@ protected long currentTimeInNanos() { return super.currentTimeInNanos(); } } + + /** + * Returns the cluster state that the master service uses (and that is provided by the discovery layer) + */ + public static ClusterState discoveryState(MasterService masterService) { + return masterService.state(); + } } diff --git a/core/src/test/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java b/core/src/test/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java index c52768d7b7e7f..bab53b8f35c22 100644 --- a/core/src/test/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java +++ b/core/src/test/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.discovery.Discovery; import org.elasticsearch.discovery.DiscoverySettings; +import org.elasticsearch.discovery.zen.ZenDiscovery; import org.elasticsearch.indices.recovery.RecoverySettings; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; @@ -282,7 +283,7 @@ public void testUpdateDiscoveryPublishTimeout() { assertThat(discoverySettings.getPublishTimeout().seconds(), equalTo(1L)); } - private DiscoverySettings getDiscoverySettings() {return internalCluster().getInstance(Discovery.class).getDiscoverySettings();} + private DiscoverySettings getDiscoverySettings() {return ((ZenDiscovery) internalCluster().getInstance(Discovery.class)).getDiscoverySettings();} public void testClusterUpdateSettingsWithBlocks() { String key1 = "cluster.routing.allocation.enable"; diff --git a/core/src/test/java/org/elasticsearch/discovery/DiscoveryModuleTests.java b/core/src/test/java/org/elasticsearch/discovery/DiscoveryModuleTests.java index 378557b4047a9..9460261e547f7 100644 --- a/core/src/test/java/org/elasticsearch/discovery/DiscoveryModuleTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/DiscoveryModuleTests.java @@ -18,47 +18,43 @@ */ package org.elasticsearch.discovery; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Function; -import java.util.function.Supplier; - import org.apache.lucene.util.IOUtils; import org.elasticsearch.Version; -import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.cluster.service.ClusterApplier; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.discovery.zen.UnicastHostsProvider; import org.elasticsearch.discovery.zen.ZenDiscovery; -import org.elasticsearch.discovery.zen.ZenPing; import org.elasticsearch.plugins.DiscoveryPlugin; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.NoopDiscovery; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.junit.After; import org.junit.Before; -import org.mockito.Mock; -import org.mockito.Mockito; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; public class DiscoveryModuleTests extends ESTestCase { private TransportService transportService; private NamedWriteableRegistry namedWriteableRegistry; - private ClusterService clusterService; + private MasterService masterService; + private ClusterApplier clusterApplier; private ThreadPool threadPool; + private ClusterSettings clusterSettings; public interface DummyHostsProviderPlugin extends DiscoveryPlugin { Map> impl(); @@ -74,7 +70,8 @@ public interface DummyDiscoveryPlugin extends DiscoveryPlugin { @Override default Map> getDiscoveryTypes(ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, - ClusterService clusterService, UnicastHostsProvider hostsProvider) { + MasterService masterService, ClusterApplier clusterApplier, + ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { return impl(); } } @@ -82,11 +79,11 @@ default Map> getDiscoveryTypes(ThreadPool threadPool @Before public void setupDummyServices() { transportService = MockTransportService.createNewService(Settings.EMPTY, Version.CURRENT, null, null); - clusterService = mock(ClusterService.class); + masterService = mock(MasterService.class); namedWriteableRegistry = new NamedWriteableRegistry(Collections.emptyList()); - ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); - when(clusterService.getClusterSettings()).thenReturn(clusterSettings); + clusterApplier = mock(ClusterApplier.class); threadPool = mock(ThreadPool.class); + clusterSettings = new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); } @After @@ -95,7 +92,8 @@ public void clearDummyServices() throws IOException { } private DiscoveryModule newModule(Settings settings, List plugins) { - return new DiscoveryModule(settings, threadPool, transportService, namedWriteableRegistry, null, clusterService, plugins); + return new DiscoveryModule(settings, threadPool, transportService, namedWriteableRegistry, null, masterService, + clusterApplier, clusterSettings, plugins); } public void testDefaults() { diff --git a/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java b/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java index 021e2be85edbc..9d46b35377c2c 100644 --- a/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java +++ b/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java @@ -447,29 +447,30 @@ public void testIsolateMasterAndVerifyClusterStateConsensus() throws Exception { ensureGreen("test"); // verify all cluster states are the same - ClusterState state = null; - for (String node : nodes) { - ClusterState nodeState = getNodeClusterState(node); - if (state == null) { - state = nodeState; - continue; - } - // assert nodes are identical - try { - assertEquals("unequal versions", state.version(), nodeState.version()); - assertEquals("unequal node count", state.nodes().getSize(), nodeState.nodes().getSize()); - assertEquals("different masters ", state.nodes().getMasterNodeId(), nodeState.nodes().getMasterNodeId()); - assertEquals("different meta data version", state.metaData().version(), nodeState.metaData().version()); - if (!state.routingTable().toString().equals(nodeState.routingTable().toString())) { - fail("different routing"); + // use assert busy to wait for cluster states to be applied (as publish_timeout has low value) + assertBusy(() -> { + ClusterState state = null; + for (String node : nodes) { + ClusterState nodeState = getNodeClusterState(node); + if (state == null) { + state = nodeState; + continue; + } + // assert nodes are identical + try { + assertEquals("unequal versions", state.version(), nodeState.version()); + assertEquals("unequal node count", state.nodes().getSize(), nodeState.nodes().getSize()); + assertEquals("different masters ", state.nodes().getMasterNodeId(), nodeState.nodes().getMasterNodeId()); + assertEquals("different meta data version", state.metaData().version(), nodeState.metaData().version()); + assertEquals("different routing", state.routingTable().toString(), nodeState.routingTable().toString()); + } catch (AssertionError t) { + fail("failed comparing cluster state: " + t.getMessage() + "\n" + + "--- cluster state of node [" + nodes.get(0) + "]: ---\n" + state + + "\n--- cluster state [" + node + "]: ---\n" + nodeState); } - } catch (AssertionError t) { - fail("failed comparing cluster state: " + t.getMessage() + "\n" + - "--- cluster state of node [" + nodes.get(0) + "]: ---\n" + state + - "\n--- cluster state [" + node + "]: ---\n" + nodeState); - } - } + } + }); } /** diff --git a/core/src/test/java/org/elasticsearch/discovery/ZenFaultDetectionTests.java b/core/src/test/java/org/elasticsearch/discovery/ZenFaultDetectionTests.java index 59fe5872911e1..d32f8cba33471 100644 --- a/core/src/test/java/org/elasticsearch/discovery/ZenFaultDetectionTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/ZenFaultDetectionTests.java @@ -25,7 +25,6 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -58,17 +57,14 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import static java.util.Collections.singleton; -import static org.elasticsearch.test.ClusterServiceUtils.createClusterService; -import static org.elasticsearch.test.ClusterServiceUtils.setState; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; public class ZenFaultDetectionTests extends ESTestCase { protected ThreadPool threadPool; - protected ClusterService clusterServiceA; - protected ClusterService clusterServiceB; private CircuitBreakerService circuitBreakerService; protected static final Version version0 = Version.fromId(/*0*/99); @@ -97,8 +93,6 @@ public void setUp() throws Exception { settingsB = Settings.builder().put("node.name", "TS_B").put(settings).build(); serviceB = build(settingsB, version1); nodeB = serviceB.getLocalDiscoNode(); - clusterServiceA = createClusterService(settingsA, threadPool, nodeA); - clusterServiceB = createClusterService(settingsB, threadPool, nodeB); // wait till all nodes are properly connected and the event has been sent, so tests in this class // will not get this callback called on the connections done in this setup @@ -133,8 +127,6 @@ public void tearDown() throws Exception { super.tearDown(); serviceA.close(); serviceB.close(); - clusterServiceA.close(); - clusterServiceB.close(); terminate(threadPool); } @@ -241,9 +233,9 @@ public void testMasterFaultDetectionConnectOnDisconnect() throws InterruptedExce .put(FaultDetection.PING_INTERVAL_SETTING.getKey(), "5m").put("cluster.name", clusterName.value()); final ClusterState state = ClusterState.builder(clusterName).nodes(buildNodesForA(false)).build(); - setState(clusterServiceA, state); + AtomicReference clusterStateSupplier = new AtomicReference<>(state); MasterFaultDetection masterFD = new MasterFaultDetection(settings.build(), threadPool, serviceA, - clusterServiceA); + clusterStateSupplier::get, null, clusterName); masterFD.restart(nodeB, "test"); final String[] failureReason = new String[1]; @@ -278,7 +270,7 @@ public void testMasterFaultDetectionNotSizeLimited() throws InterruptedException .put(FaultDetection.PING_INTERVAL_SETTING.getKey(), "1s") .put("cluster.name", clusterName.value()).build(); final ClusterState stateNodeA = ClusterState.builder(clusterName).nodes(buildNodesForA(false)).build(); - setState(clusterServiceA, stateNodeA); + AtomicReference clusterStateSupplierA = new AtomicReference<>(stateNodeA); int minExpectedPings = 2; @@ -289,14 +281,14 @@ public void testMasterFaultDetectionNotSizeLimited() throws InterruptedException serviceB.addTracer(pingProbeB); MasterFaultDetection masterFDNodeA = new MasterFaultDetection(Settings.builder().put(settingsA).put(settings).build(), - threadPool, serviceA, clusterServiceA); + threadPool, serviceA, clusterStateSupplierA::get, null, clusterName); masterFDNodeA.restart(nodeB, "test"); final ClusterState stateNodeB = ClusterState.builder(clusterName).nodes(buildNodesForB(true)).build(); - setState(clusterServiceB, stateNodeB); + AtomicReference clusterStateSupplierB = new AtomicReference<>(stateNodeB); MasterFaultDetection masterFDNodeB = new MasterFaultDetection(Settings.builder().put(settingsB).put(settings).build(), - threadPool, serviceB, clusterServiceB); + threadPool, serviceB, clusterStateSupplierB::get, null, clusterName); masterFDNodeB.restart(nodeB, "test"); // let's do a few pings diff --git a/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryTests.java b/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryTests.java index a0e0b699d78db..917894b60d74e 100644 --- a/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryTests.java @@ -21,14 +21,10 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.Version; -import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.ClusterStateObserver; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.threadpool.TestThreadPool; @@ -36,7 +32,6 @@ import java.io.Closeable; import java.util.Stack; -import java.util.concurrent.CountDownLatch; import static org.elasticsearch.test.ClusterServiceUtils.createClusterService; import static org.hamcrest.Matchers.equalTo; @@ -57,37 +52,10 @@ public void testInitialJoin() throws Exception { final ClusterService clusterService = createClusterService(threadPool, node); stack.push(clusterService); final SingleNodeDiscovery discovery = - new SingleNodeDiscovery(Settings.EMPTY, clusterService); + new SingleNodeDiscovery(Settings.EMPTY, transportService, + clusterService.getClusterApplierService()); discovery.startInitialJoin(); - - // we are racing against the initial join which is asynchronous so we use an observer - final ClusterState state = clusterService.state(); - final ThreadContext threadContext = threadPool.getThreadContext(); - final ClusterStateObserver observer = - new ClusterStateObserver(state, clusterService, null, logger, threadContext); - if (state.nodes().getMasterNodeId() == null) { - final CountDownLatch latch = new CountDownLatch(1); - observer.waitForNextChange(new ClusterStateObserver.Listener() { - @Override - public void onNewClusterState(ClusterState state) { - latch.countDown(); - } - - @Override - public void onClusterServiceClose() { - latch.countDown(); - } - - @Override - public void onTimeout(TimeValue timeout) { - assert false; - } - }, s -> s.nodes().getMasterNodeId() != null); - - latch.await(); - } - - final DiscoveryNodes nodes = clusterService.state().nodes(); + final DiscoveryNodes nodes = discovery.getInitialClusterState().nodes(); assertThat(nodes.getSize(), equalTo(1)); assertThat(nodes.getMasterNode().getId(), equalTo(node.getId())); } finally { diff --git a/core/src/test/java/org/elasticsearch/discovery/zen/NodeJoinControllerTests.java b/core/src/test/java/org/elasticsearch/discovery/zen/NodeJoinControllerTests.java index c5c78189ee107..c25152a442672 100644 --- a/core/src/test/java/org/elasticsearch/discovery/zen/NodeJoinControllerTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/zen/NodeJoinControllerTests.java @@ -22,8 +22,10 @@ import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.Version; +import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.NotMasterException; +import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -35,14 +37,14 @@ import org.elasticsearch.cluster.routing.ShardRoutingState; import org.elasticsearch.cluster.routing.TestShardRouting; import org.elasticsearch.cluster.routing.UnassignedInfo; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.cluster.service.MasterService; +import org.elasticsearch.cluster.service.MasterServiceTests; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.common.util.concurrent.BaseFuture; -import org.elasticsearch.discovery.DiscoverySettings; import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.test.ClusterServiceUtils; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.VersionUtils; import org.elasticsearch.test.junit.annotations.TestLogging; @@ -81,8 +83,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_SHARDS; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_VERSION_CREATED; import static org.elasticsearch.cluster.routing.RoutingTableTests.updateActiveAllocations; -import static org.elasticsearch.test.ClusterServiceUtils.createClusterService; -import static org.elasticsearch.test.ClusterServiceUtils.setState; +import static org.elasticsearch.cluster.service.MasterServiceTests.discoveryState; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; @@ -93,12 +94,12 @@ public class NodeJoinControllerTests extends ESTestCase { private static ThreadPool threadPool; - private ClusterService clusterService; + private MasterService masterService; private NodeJoinController nodeJoinController; @BeforeClass public static void beforeClass() { - threadPool = new TestThreadPool("ShardReplicationTests"); + threadPool = new TestThreadPool("NodeJoinControllerTests"); } @AfterClass @@ -110,25 +111,39 @@ public static void afterClass() { @Before public void setUp() throws Exception { super.setUp(); - clusterService = createClusterService(threadPool); - final DiscoveryNodes initialNodes = clusterService.state().nodes(); - final DiscoveryNode localNode = initialNodes.getLocalNode(); - // make sure we have a master - setState(clusterService, ClusterState.builder(clusterService.state()).nodes( - DiscoveryNodes.builder(initialNodes).masterNodeId(localNode.getId()))); - nodeJoinController = new NodeJoinController(clusterService, createAllocationService(Settings.EMPTY), - new ElectMasterService(Settings.EMPTY), Settings.EMPTY); } @After public void tearDown() throws Exception { super.tearDown(); - clusterService.close(); + masterService.close(); + } + + private static ClusterState initialState(boolean withMaster) { + DiscoveryNode localNode = new DiscoveryNode("node", ESTestCase.buildNewFakeTransportAddress(), Collections.emptyMap(), + new HashSet<>(Arrays.asList(DiscoveryNode.Role.values())),Version.CURRENT); + ClusterState initialClusterState = ClusterState.builder(new ClusterName(ClusterServiceUtils.class.getSimpleName())) + .nodes(DiscoveryNodes.builder() + .add(localNode) + .localNodeId(localNode.getId()) + .masterNodeId(withMaster ? localNode.getId() : null)) + .blocks(ClusterBlocks.EMPTY_CLUSTER_BLOCK).build(); + return initialClusterState; + } + + private void setupMasterServiceAndNodeJoinController(ClusterState initialState) { + if (masterService != null || nodeJoinController != null) { + throw new IllegalStateException("method setupMasterServiceAndNodeJoinController can only be called once"); + } + masterService = ClusterServiceUtils.createMasterService(threadPool, initialState); + nodeJoinController = new NodeJoinController(masterService, createAllocationService(Settings.EMPTY), + new ElectMasterService(Settings.EMPTY), Settings.EMPTY); } public void testSimpleJoinAccumulation() throws InterruptedException, ExecutionException { + setupMasterServiceAndNodeJoinController(initialState(true)); List nodes = new ArrayList<>(); - nodes.add(clusterService.localNode()); + nodes.add(discoveryState(masterService).nodes().getLocalNode()); int nodeId = 0; for (int i = randomInt(5); i > 0; i--) { @@ -162,9 +177,7 @@ public void testSimpleJoinAccumulation() throws InterruptedException, ExecutionE } public void testFailingJoinsWhenNotMaster() throws ExecutionException, InterruptedException { - // remove current master flag - DiscoveryNodes.Builder nodes = DiscoveryNodes.builder(clusterService.state().nodes()).masterNodeId(null); - setState(clusterService, ClusterState.builder(clusterService.state()).nodes(nodes)); + setupMasterServiceAndNodeJoinController(initialState(false)); int nodeId = 0; try { joinNode(newNode(nodeId++)); @@ -194,8 +207,7 @@ public void testFailingJoinsWhenNotMaster() throws ExecutionException, Interrupt } public void testSimpleMasterElectionWithoutRequiredJoins() throws InterruptedException, ExecutionException { - DiscoveryNodes.Builder nodes = DiscoveryNodes.builder(clusterService.state().nodes()).masterNodeId(null); - setState(clusterService, ClusterState.builder(clusterService.state()).nodes(nodes)); + setupMasterServiceAndNodeJoinController(initialState(false)); int nodeId = 0; final int requiredJoins = 0; logger.debug("--> using requiredJoins [{}]", requiredJoins); @@ -244,8 +256,7 @@ public void onFailure(Throwable t) { } public void testSimpleMasterElection() throws InterruptedException, ExecutionException { - DiscoveryNodes.Builder nodes = DiscoveryNodes.builder(clusterService.state().nodes()).masterNodeId(null); - setState(clusterService, ClusterState.builder(clusterService.state()).nodes(nodes)); + setupMasterServiceAndNodeJoinController(initialState(false)); int nodeId = 0; final int requiredJoins = 1 + randomInt(5); logger.debug("--> using requiredJoins [{}]", requiredJoins); @@ -356,10 +367,8 @@ public void onFailure(Throwable t) { } - public void testMasterElectionTimeout() throws InterruptedException { - DiscoveryNodes.Builder nodes = DiscoveryNodes.builder(clusterService.state().nodes()).masterNodeId(null); - setState(clusterService, ClusterState.builder(clusterService.state()).nodes(nodes)); + setupMasterServiceAndNodeJoinController(initialState(false)); int nodeId = 0; final int requiredJoins = 1 + randomInt(5); logger.debug("--> using requiredJoins [{}]", requiredJoins); @@ -422,22 +431,23 @@ public void onFailure(Throwable t) { } public void testNewClusterStateOnExistingNodeJoin() throws InterruptedException, ExecutionException { - ClusterState state = clusterService.state(); + ClusterState state = initialState(true); final DiscoveryNodes.Builder nodesBuilder = DiscoveryNodes.builder(state.nodes()); final DiscoveryNode other_node = new DiscoveryNode("other_node", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT); nodesBuilder.add(other_node); - setState(clusterService, ClusterState.builder(state).nodes(nodesBuilder)); + setupMasterServiceAndNodeJoinController(ClusterState.builder(state).nodes(nodesBuilder).build()); - state = clusterService.state(); + state = discoveryState(masterService); joinNode(other_node); - assertTrue("failed to publish a new state upon existing join", clusterService.state() != state); + assertTrue("failed to publish a new state upon existing join", discoveryState(masterService) != state); } public void testNormalConcurrentJoins() throws InterruptedException { + setupMasterServiceAndNodeJoinController(initialState(true)); Thread[] threads = new Thread[3 + randomInt(5)]; ArrayList nodes = new ArrayList<>(); - nodes.add(clusterService.localNode()); + nodes.add(discoveryState(masterService).nodes().getLocalNode()); final CyclicBarrier barrier = new CyclicBarrier(threads.length); final List backgroundExceptions = new CopyOnWriteArrayList<>(); for (int i = 0; i < threads.length; i++) { @@ -472,15 +482,14 @@ protected void doRun() throws Exception { } public void testElectionWithConcurrentJoins() throws InterruptedException, BrokenBarrierException { - DiscoveryNodes.Builder nodesBuilder = DiscoveryNodes.builder(clusterService.state().nodes()).masterNodeId(null); - setState(clusterService, ClusterState.builder(clusterService.state()).nodes(nodesBuilder)); + setupMasterServiceAndNodeJoinController(initialState(false)); nodeJoinController.startElectionContext(); Thread[] threads = new Thread[3 + randomInt(5)]; final int requiredJoins = randomInt(threads.length); ArrayList nodes = new ArrayList<>(); - nodes.add(clusterService.localNode()); + nodes.add(discoveryState(masterService).nodes().getLocalNode()); final CyclicBarrier barrier = new CyclicBarrier(threads.length + 1); final List backgroundExceptions = new CopyOnWriteArrayList<>(); for (int i = 0; i < threads.length; i++) { @@ -539,7 +548,7 @@ public void onFailure(Throwable t) { public void testRejectingJoinWithSameAddressButDifferentId() throws InterruptedException, ExecutionException { addNodes(randomInt(5)); - ClusterState state = clusterService.state(); + ClusterState state = discoveryState(masterService); final DiscoveryNode existing = randomFrom(StreamSupport.stream(state.nodes().spliterator(), false).collect(Collectors.toList())); final DiscoveryNode other_node = new DiscoveryNode("other_node", existing.getAddress(), emptyMap(), emptySet(), Version.CURRENT); @@ -549,7 +558,7 @@ public void testRejectingJoinWithSameAddressButDifferentId() throws InterruptedE public void testRejectingJoinWithSameIdButDifferentNode() throws InterruptedException, ExecutionException { addNodes(randomInt(5)); - ClusterState state = clusterService.state(); + ClusterState state = discoveryState(masterService); final DiscoveryNode existing = randomFrom(StreamSupport.stream(state.nodes().spliterator(), false).collect(Collectors.toList())); final DiscoveryNode other_node = new DiscoveryNode( randomBoolean() ? existing.getName() : "other_name", @@ -565,7 +574,7 @@ public void testRejectingJoinWithSameIdButDifferentNode() throws InterruptedExce public void testRejectingRestartedNodeJoinsBeforeProcessingNodeLeft() throws InterruptedException, ExecutionException { addNodes(randomInt(5)); - ClusterState state = clusterService.state(); + ClusterState state = discoveryState(masterService); final DiscoveryNode existing = randomFrom(StreamSupport.stream(state.nodes().spliterator(), false).collect(Collectors.toList())); joinNode(existing); // OK @@ -581,15 +590,16 @@ public void testRejectingRestartedNodeJoinsBeforeProcessingNodeLeft() throws Int * nodes that conflict with the joins it got and needs to become a master */ public void testElectionBasedOnConflictingNodes() throws InterruptedException, ExecutionException { - final DiscoveryNode masterNode = clusterService.localNode(); + ClusterState initialState = initialState(true); + final DiscoveryNode masterNode = initialState.nodes().getLocalNode(); final DiscoveryNode otherNode = new DiscoveryNode("other_node", buildNewFakeTransportAddress(), emptyMap(), EnumSet.allOf(DiscoveryNode.Role.class), Version.CURRENT); // simulate master going down with stale nodes in it's cluster state (for example when min master nodes is set to 2) // also add some shards to that node - DiscoveryNodes.Builder discoBuilder = DiscoveryNodes.builder(clusterService.state().nodes()); + DiscoveryNodes.Builder discoBuilder = DiscoveryNodes.builder(initialState.nodes()); discoBuilder.masterNodeId(null); discoBuilder.add(otherNode); - ClusterState.Builder stateBuilder = ClusterState.builder(clusterService.state()).nodes(discoBuilder); + ClusterState.Builder stateBuilder = ClusterState.builder(initialState).nodes(discoBuilder); if (randomBoolean()) { IndexMetaData indexMetaData = IndexMetaData.builder("test").settings(Settings.builder() .put(SETTING_VERSION_CREATED, Version.CURRENT) @@ -623,7 +633,7 @@ public void testElectionBasedOnConflictingNodes() throws InterruptedException, E .routingTable(RoutingTable.builder().add(indexRoutingTable).build()); } - setState(clusterService, stateBuilder.build()); + setupMasterServiceAndNodeJoinController(stateBuilder.build()); // conflict on node id or address final DiscoveryNode conflictingNode = randomBoolean() ? @@ -652,7 +662,7 @@ public void onFailure(Throwable t) { joinFuture.get(); // throw any exception - final ClusterState finalState = clusterService.state(); + final ClusterState finalState = discoveryState(masterService); final DiscoveryNodes finalNodes = finalState.nodes(); assertTrue(finalNodes.isLocalNodeElectedMaster()); assertThat(finalNodes.getLocalNode(), equalTo(masterNode)); @@ -666,18 +676,18 @@ public void onFailure(Throwable t) { private void addNodes(int count) { - ClusterState state = clusterService.state(); + ClusterState state = initialState(true); final DiscoveryNodes.Builder nodesBuilder = DiscoveryNodes.builder(state.nodes()); for (int i = 0;i< count;i++) { final DiscoveryNode node = new DiscoveryNode("node_" + state.nodes().getSize() + i, buildNewFakeTransportAddress(), emptyMap(), new HashSet<>(randomSubsetOf(Arrays.asList(DiscoveryNode.Role.values()))), Version.CURRENT); nodesBuilder.add(node); } - setState(clusterService, ClusterState.builder(state).nodes(nodesBuilder)); + setupMasterServiceAndNodeJoinController(ClusterState.builder(state).nodes(nodesBuilder).build()); } protected void assertNodesInCurrentState(List expectedNodes) { - final ClusterState state = clusterService.state(); + final ClusterState state = discoveryState(masterService); logger.info("assert for [{}] in:\n{}", expectedNodes, state); DiscoveryNodes discoveryNodes = state.nodes(); for (DiscoveryNode node : expectedNodes) { diff --git a/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryIT.java b/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryIT.java index dfe1078aaa0a2..7ac463616fcf2 100644 --- a/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryIT.java +++ b/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryIT.java @@ -260,6 +260,7 @@ public void testDiscoveryStats() throws IOException { "}"; internalCluster().startNode(); + ensureGreen(); // ensures that all events are processed (in particular state recovery fully completed) logger.info("--> request node discovery stats"); NodesStatsResponse statsResponse = client().admin().cluster().prepareNodesStats().clear().setDiscovery(true).get(); diff --git a/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java b/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java index 5bbd0f19d2f9b..0d0d391663d0b 100644 --- a/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java @@ -37,12 +37,14 @@ import org.elasticsearch.cluster.routing.ShardRoutingState; import org.elasticsearch.cluster.routing.TestShardRouting; import org.elasticsearch.cluster.routing.UnassignedInfo; -import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.discovery.Discovery; import org.elasticsearch.discovery.zen.PublishClusterStateActionTests.AssertingAckListener; import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.test.ClusterServiceUtils; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.VersionUtils; import org.elasticsearch.test.transport.MockTransportService; @@ -65,6 +67,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import static java.util.Collections.emptyMap; @@ -74,9 +77,9 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_SHARDS; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_VERSION_CREATED; import static org.elasticsearch.cluster.routing.RoutingTableTests.updateActiveAllocations; +import static org.elasticsearch.cluster.service.MasterServiceTests.discoveryState; import static org.elasticsearch.discovery.zen.ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING; import static org.elasticsearch.discovery.zen.ZenDiscovery.shouldIgnoreOrRejectNewClusterState; -import static org.elasticsearch.test.ClusterServiceUtils.createClusterService; import static org.elasticsearch.test.ClusterServiceUtils.setState; import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.containsString; @@ -182,26 +185,31 @@ public void testNodesUpdatedAfterClusterStatePublished() throws Exception { DiscoveryNode masterNode = masterTransport.getLocalNode(); toClose.addFirst(masterTransport); ClusterState state = ClusterStateCreationUtils.state(masterNode, masterNode, masterNode); - // build the zen discovery and cluster service - ClusterService masterClusterService = createClusterService(threadPool, masterNode); - toClose.addFirst(masterClusterService); + // build the zen discovery and discovery service + MasterService masterMasterService = ClusterServiceUtils.createMasterService(threadPool, masterNode); + toClose.addFirst(masterMasterService); // TODO: clustername shouldn't be stored twice in cluster service, but for now, work around it - state = ClusterState.builder(masterClusterService.getClusterName()).nodes(state.nodes()).build(); - setState(masterClusterService, state); - ZenDiscovery masterZen = buildZenDiscovery(settings, masterTransport, masterClusterService, threadPool); + state = ClusterState.builder(discoveryState(masterMasterService).getClusterName()).nodes(state.nodes()).build(); + Settings settingsWithClusterName = Settings.builder().put(settings).put( + ClusterName.CLUSTER_NAME_SETTING.getKey(), discoveryState(masterMasterService).getClusterName().value()).build(); + ZenDiscovery masterZen = buildZenDiscovery( + settingsWithClusterName, + masterTransport, masterMasterService, threadPool); + masterZen.setState(state); toClose.addFirst(masterZen); masterTransport.acceptIncomingRequests(); final MockTransportService otherTransport = MockTransportService.createNewService(settings, Version.CURRENT, threadPool, null); otherTransport.start(); toClose.addFirst(otherTransport); + DiscoveryNode otherNode = otherTransport.getLocalNode(); - final ClusterState otherState = ClusterState.builder(masterClusterService.getClusterName()) + final ClusterState otherState = ClusterState.builder(discoveryState(masterMasterService).getClusterName()) .nodes(DiscoveryNodes.builder().add(otherNode).localNodeId(otherNode.getId())).build(); - ClusterService otherClusterService = createClusterService(threadPool, masterNode); - toClose.addFirst(otherClusterService); - setState(otherClusterService, otherState); - ZenDiscovery otherZen = buildZenDiscovery(settings, otherTransport, otherClusterService, threadPool); + MasterService otherMasterService = ClusterServiceUtils.createMasterService(threadPool, otherNode); + toClose.addFirst(otherMasterService); + ZenDiscovery otherZen = buildZenDiscovery(settingsWithClusterName, otherTransport, otherMasterService, threadPool); + otherZen.setState(otherState); toClose.addFirst(otherZen); otherTransport.acceptIncomingRequests(); @@ -210,7 +218,7 @@ public void testNodesUpdatedAfterClusterStatePublished() throws Exception { // a new cluster state with a new discovery node (we will test if the cluster state // was updated by the presence of this node in NodesFaultDetection) - ClusterState newState = ClusterState.builder(masterClusterService.state()).incrementVersion().nodes( + ClusterState newState = ClusterState.builder(discoveryState(masterMasterService)).incrementVersion().nodes( DiscoveryNodes.builder(state.nodes()).add(otherNode).masterNodeId(masterNode.getId()) ).build(); @@ -220,7 +228,7 @@ public void testNodesUpdatedAfterClusterStatePublished() throws Exception { AssertingAckListener listener = new AssertingAckListener(newState.nodes().getSize() - 1); expectedFDNodes = masterZen.getFaultDetectionNodes(); masterZen.publish(clusterChangedEvent, listener); - listener.await(1, TimeUnit.HOURS); + listener.await(10, TimeUnit.SECONDS); // publish was a success, update expected FD nodes based on new cluster state expectedFDNodes = fdNodesForState(newState, masterNode); } catch (Discovery.FailedToCommitClusterStateException e) { @@ -249,12 +257,12 @@ public void testPendingCSQueueIsClearedWhenClusterStatePublished() throws Except DiscoveryNode masterNode = masterTransport.getLocalNode(); toClose.addFirst(masterTransport); ClusterState state = ClusterStateCreationUtils.state(masterNode, null, masterNode); - // build the zen discovery and cluster service - ClusterService masterClusterService = createClusterService(threadPool, masterNode); - toClose.addFirst(masterClusterService); - state = ClusterState.builder(masterClusterService.getClusterName()).nodes(state.nodes()).build(); - setState(masterClusterService, state); - ZenDiscovery masterZen = buildZenDiscovery(settings, masterTransport, masterClusterService, threadPool); + // build the zen discovery and master service for the master node + MasterService masterMasterService = ClusterServiceUtils.createMasterService(threadPool, masterNode); + toClose.addFirst(masterMasterService); + state = ClusterState.builder(discoveryState(masterMasterService).getClusterName()).nodes(state.nodes()).build(); + ZenDiscovery masterZen = buildZenDiscovery(settings, masterTransport, masterMasterService, threadPool); + masterZen.setState(state); toClose.addFirst(masterZen); masterTransport.acceptIncomingRequests(); @@ -263,8 +271,8 @@ public void testPendingCSQueueIsClearedWhenClusterStatePublished() throws Except // a new cluster state with a new discovery node (we will test if the cluster state // was updated by the presence of this node in NodesFaultDetection) - ClusterState newState = ClusterState.builder(masterClusterService.state()).incrementVersion().nodes( - DiscoveryNodes.builder(masterClusterService.state().nodes()).masterNodeId(masterNode.getId()) + ClusterState newState = ClusterState.builder(discoveryState(masterMasterService)).incrementVersion().nodes( + DiscoveryNodes.builder(discoveryState(masterMasterService).nodes()).masterNodeId(masterNode.getId()) ).build(); @@ -277,9 +285,8 @@ public void testPendingCSQueueIsClearedWhenClusterStatePublished() throws Except // publish was a success, check that queue as cleared assertThat(masterZen.pendingClusterStates(), emptyArray()); } catch (Discovery.FailedToCommitClusterStateException e) { - // not successful, so the pending queue should stay - assertThat(masterZen.pendingClusterStates(), arrayWithSize(1)); - assertThat(masterZen.pendingClusterStates()[0].getClusterName().value(), equalTo("foreign")); + // not successful, so the pending queue should be cleaned + assertThat(Arrays.toString(masterZen.pendingClusterStates()), masterZen.pendingClusterStates(), arrayWithSize(0)); } } finally { IOUtils.close(toClose); @@ -287,9 +294,12 @@ public void testPendingCSQueueIsClearedWhenClusterStatePublished() throws Except } } - private ZenDiscovery buildZenDiscovery(Settings settings, TransportService service, ClusterService clusterService, ThreadPool threadPool) { + private ZenDiscovery buildZenDiscovery(Settings settings, TransportService service, MasterService masterService, + ThreadPool threadPool) { + ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); ZenDiscovery zenDiscovery = new ZenDiscovery(settings, threadPool, service, new NamedWriteableRegistry(ClusterModule.getNamedWriteables()), - clusterService, Collections::emptyList); + masterService, (source, clusterStateSupplier, listener) -> listener.clusterStateProcessed(source, clusterStateSupplier.get(), clusterStateSupplier.get()), + clusterSettings, Collections::emptyList); zenDiscovery.start(); return zenDiscovery; } diff --git a/core/src/test/java/org/elasticsearch/gateway/GatewayServiceTests.java b/core/src/test/java/org/elasticsearch/gateway/GatewayServiceTests.java index a4e3aad8bec21..2bec3d5eded89 100644 --- a/core/src/test/java/org/elasticsearch/gateway/GatewayServiceTests.java +++ b/core/src/test/java/org/elasticsearch/gateway/GatewayServiceTests.java @@ -19,10 +19,7 @@ package org.elasticsearch.gateway; -import org.elasticsearch.Version; -import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; @@ -37,7 +34,7 @@ public class GatewayServiceTests extends ESTestCase { private GatewayService createService(Settings.Builder settings) { ClusterService clusterService = new ClusterService(Settings.builder().put("cluster.name", "GatewayServiceTests").build(), new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), - null, () -> new DiscoveryNode(UUIDs.randomBase64UUID(), buildNewFakeTransportAddress(), Version.CURRENT)); + null); return new GatewayService(settings.build(), null, clusterService, null, null, null, new NoopDiscovery(), null); } diff --git a/core/src/test/java/org/elasticsearch/indexlifecycle/IndexLifecycleActionIT.java b/core/src/test/java/org/elasticsearch/indexlifecycle/IndexLifecycleActionIT.java index db03e51d4d9c0..1bd255349ca44 100644 --- a/core/src/test/java/org/elasticsearch/indexlifecycle/IndexLifecycleActionIT.java +++ b/core/src/test/java/org/elasticsearch/indexlifecycle/IndexLifecycleActionIT.java @@ -32,6 +32,7 @@ import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.ESIntegTestCase.Scope; import org.elasticsearch.test.InternalTestCluster; +import org.elasticsearch.transport.TransportService; import java.util.Set; import java.util.stream.Collectors; @@ -204,8 +205,8 @@ public void testIndexLifecycleActionsWith11Shards1Backup() throws Exception { } private String getLocalNodeId(String name) { - Discovery discovery = internalCluster().getInstance(Discovery.class, name); - String nodeId = discovery.localNode().getId(); + TransportService transportService = internalCluster().getInstance(TransportService.class, name); + String nodeId = transportService.getLocalNode().getId(); assertThat(nodeId, not(nullValue())); return nodeId; } diff --git a/core/src/test/java/org/elasticsearch/indices/store/IndicesStoreIntegrationIT.java b/core/src/test/java/org/elasticsearch/indices/store/IndicesStoreIntegrationIT.java index 13f00b152dfa7..4e1be614fa5d6 100644 --- a/core/src/test/java/org/elasticsearch/indices/store/IndicesStoreIntegrationIT.java +++ b/core/src/test/java/org/elasticsearch/indices/store/IndicesStoreIntegrationIT.java @@ -20,9 +20,12 @@ package org.elasticsearch.indices.store; import org.apache.logging.log4j.Logger; +import org.elasticsearch.ExceptionsHelper; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateTaskListener; import org.elasticsearch.cluster.LocalClusterUpdateTask; import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.cluster.metadata.IndexMetaData; @@ -35,6 +38,7 @@ import org.elasticsearch.cluster.routing.TestShardRouting; import org.elasticsearch.cluster.routing.allocation.command.MoveAllocationCommand; import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider; +import org.elasticsearch.cluster.service.ClusterApplierService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Priority; import org.elasticsearch.common.settings.Settings; @@ -434,26 +438,35 @@ public void testShardActiveElseWhere() throws Exception { // disable relocations when we do this, to make sure the shards are not relocated from node2 // due to rebalancing, and delete its content client().admin().cluster().prepareUpdateSettings().setTransientSettings(Settings.builder().put(EnableAllocationDecider.CLUSTER_ROUTING_REBALANCE_ENABLE_SETTING.getKey(), EnableAllocationDecider.Rebalance.NONE)).get(); - internalCluster().getInstance(ClusterService.class, nonMasterNode).submitStateUpdateTask("test", new LocalClusterUpdateTask(Priority.IMMEDIATE) { + + ClusterApplierService clusterApplierService = internalCluster().getInstance(ClusterService.class, nonMasterNode).getClusterApplierService(); + ClusterState currentState = clusterApplierService.state(); + IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(index); + for (int j = 0; j < numShards; j++) { + indexRoutingTableBuilder.addIndexShard( + new IndexShardRoutingTable.Builder(new ShardId(index, j)) + .addShard(TestShardRouting.newShardRouting("test", j, masterId, true, ShardRoutingState.STARTED)) + .build() + ); + } + ClusterState newState = ClusterState.builder(currentState) + .incrementVersion() + .routingTable(RoutingTable.builder().add(indexRoutingTableBuilder).build()) + .build(); + CountDownLatch latch = new CountDownLatch(1); + clusterApplierService.onNewClusterState("test", () -> newState, new ClusterStateTaskListener() { @Override - public ClusterTasksResult execute(ClusterState currentState) throws Exception { - IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(index); - for (int i = 0; i < numShards; i++) { - indexRoutingTableBuilder.addIndexShard( - new IndexShardRoutingTable.Builder(new ShardId(index, i)) - .addShard(TestShardRouting.newShardRouting("test", i, masterId, true, ShardRoutingState.STARTED)) - .build() - ); - } - return newState(ClusterState.builder(currentState) - .routingTable(RoutingTable.builder().add(indexRoutingTableBuilder).build()) - .build()); + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); } @Override public void onFailure(String source, Exception e) { + latch.countDown(); + fail("Excepted proper response " + ExceptionsHelper.detailedMessage(e)); } }); + latch.await(); waitNoPendingTasksOnAll(); logger.info("Checking if shards aren't removed"); for (int shard : node2Shards) { diff --git a/core/src/test/java/org/elasticsearch/snapshots/AbstractSnapshotIntegTestCase.java b/core/src/test/java/org/elasticsearch/snapshots/AbstractSnapshotIntegTestCase.java index 5562e2d4026fa..aa9b4ba4a1629 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/AbstractSnapshotIntegTestCase.java +++ b/core/src/test/java/org/elasticsearch/snapshots/AbstractSnapshotIntegTestCase.java @@ -18,19 +18,9 @@ */ package org.elasticsearch.snapshots; -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; -import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksResponse; -import org.elasticsearch.cluster.ClusterChangedEvent; -import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.ClusterStateListener; -import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.cluster.SnapshotsInProgress; import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.cluster.service.PendingClusterTask; -import org.elasticsearch.common.Priority; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.plugins.Plugin; @@ -47,13 +37,10 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Predicate; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; public abstract class AbstractSnapshotIntegTestCase extends ESIntegTestCase { @@ -180,121 +167,4 @@ public void waitForBlockOnAnyDataNode(String repository, TimeValue timeout) thro public static void unblockNode(final String repository, final String node) { ((MockRepository)internalCluster().getInstance(RepositoriesService.class, node).repository(repository)).unblock(); } - - protected void assertBusyPendingTasks(final String taskPrefix, final int expectedCount) throws Exception { - assertBusy(new Runnable() { - @Override - public void run() { - PendingClusterTasksResponse tasks = client().admin().cluster().preparePendingClusterTasks().get(); - int count = 0; - for(PendingClusterTask task : tasks) { - if (task.getSource().toString().startsWith(taskPrefix)) { - count++; - } - } - assertThat(count, greaterThanOrEqualTo(expectedCount)); - } - }, 1, TimeUnit.MINUTES); - } - - /** - * Cluster state task that blocks waits for the blockOn task to show up and then blocks execution not letting - * any cluster state update task to be performed unless they have priority higher then passThroughPriority. - * - * This class is useful to testing of cluster state update task batching for lower priority tasks. - */ - protected class BlockingClusterStateListener implements ClusterStateListener { - - private final Predicate blockOn; - private final Predicate countOn; - private final ClusterService clusterService; - private final CountDownLatch latch; - private final Priority passThroughPriority; - private int count; - private boolean timedOut; - private final TimeValue timeout; - private long stopWaitingAt = -1; - - public BlockingClusterStateListener(ClusterService clusterService, String blockOn, String countOn, Priority passThroughPriority) { - // Waiting for the 70 seconds here to make sure that the last check at 65 sec mark in assertBusyPendingTasks has a chance - // to finish before we timeout on the cluster state block. Otherwise the last check in assertBusyPendingTasks kicks in - // after the cluster state block clean up takes place and it's assert doesn't reflect the actual failure - this(clusterService, blockOn, countOn, passThroughPriority, TimeValue.timeValueSeconds(70)); - } - - public BlockingClusterStateListener(ClusterService clusterService, final String blockOn, final String countOn, Priority passThroughPriority, TimeValue timeout) { - this.clusterService = clusterService; - this.blockOn = clusterChangedEvent -> clusterChangedEvent.source().startsWith(blockOn); - this.countOn = clusterChangedEvent -> clusterChangedEvent.source().startsWith(countOn); - this.latch = new CountDownLatch(1); - this.passThroughPriority = passThroughPriority; - this.timeout = timeout; - - } - - public void unblock() { - latch.countDown(); - } - - @Override - public void clusterChanged(ClusterChangedEvent event) { - if (blockOn.test(event)) { - logger.info("blocking cluster state tasks on [{}]", event.source()); - assert stopWaitingAt < 0; // Make sure we are the first time here - stopWaitingAt = System.currentTimeMillis() + timeout.getMillis(); - addBlock(); - } - if (countOn.test(event)) { - count++; - } - } - - private void addBlock() { - // We should block after this task - add blocking cluster state update task - clusterService.submitStateUpdateTask("test_block", new ClusterStateUpdateTask(passThroughPriority) { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - while(System.currentTimeMillis() < stopWaitingAt) { - for (PendingClusterTask task : clusterService.pendingTasks()) { - if (task.getSource().string().equals("test_block") == false && passThroughPriority.sameOrAfter(task.getPriority())) { - // There are other higher priority tasks in the queue and let them pass through and then set the block again - logger.info("passing through cluster state task {}", task.getSource()); - addBlock(); - return currentState; - } - } - try { - logger.info("waiting...."); - if (latch.await(Math.min(100, timeout.millis()), TimeUnit.MILLISECONDS)){ - // Done waiting - unblock - logger.info("unblocked"); - return currentState; - } - logger.info("done waiting...."); - } catch (InterruptedException ex) { - logger.info("interrupted...."); - Thread.currentThread().interrupt(); - return currentState; - } - } - timedOut = true; - return currentState; - } - - @Override - public void onFailure(String source, Exception e) { - logger.warn((Supplier) () -> new ParameterizedMessage("failed to execute [{}]", source), e); - } - }); - - } - - public int count() { - return count; - } - - public boolean timedOut() { - return timedOut; - } - } } diff --git a/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java b/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java index 2c1dfc899b690..355b75219a0b1 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java +++ b/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java @@ -724,14 +724,9 @@ public void sendResponse(RestResponse response) { } public void testMasterShutdownDuringSnapshot() throws Exception { - Settings masterSettings = Settings.builder().put(Node.NODE_DATA_SETTING.getKey(), false).build(); - Settings dataSettings = Settings.builder().put(Node.NODE_MASTER_SETTING.getKey(), false).build(); - logger.info("--> starting two master nodes and two data nodes"); - internalCluster().startNode(masterSettings); - internalCluster().startNode(masterSettings); - internalCluster().startNode(dataSettings); - internalCluster().startNode(dataSettings); + internalCluster().startMasterOnlyNodes(2); + internalCluster().startDataOnlyNodes(2); final Client client = client(); @@ -758,35 +753,17 @@ public void testMasterShutdownDuringSnapshot() throws Exception { final int numberOfShards = getNumShards("test-idx").numPrimaries; logger.info("number of shards: {}", numberOfShards); - final ClusterService clusterService = internalCluster().clusterService(internalCluster().getMasterName()); - BlockingClusterStateListener snapshotListener = new BlockingClusterStateListener(clusterService, "update_snapshot [", "update snapshot state", Priority.HIGH); - try { - clusterService.addListener(snapshotListener); - logger.info("--> snapshot"); - dataNodeClient().admin().cluster().prepareCreateSnapshot("test-repo", "test-snap").setWaitForCompletion(false).setIndices("test-idx").get(); - - // Await until some updates are in pending state. - assertBusyPendingTasks("update snapshot state", 1); + dataNodeClient().admin().cluster().prepareCreateSnapshot("test-repo", "test-snap").setWaitForCompletion(false).setIndices("test-idx").get(); - logger.info("--> stopping master node"); - internalCluster().stopCurrentMasterNode(); - - logger.info("--> unblocking snapshot execution"); - snapshotListener.unblock(); - - } finally { - clusterService.removeListener(snapshotListener); - } + logger.info("--> stopping master node"); + internalCluster().stopCurrentMasterNode(); logger.info("--> wait until the snapshot is done"); - assertBusy(new Runnable() { - @Override - public void run() { - GetSnapshotsResponse snapshotsStatusResponse = client().admin().cluster().prepareGetSnapshots("test-repo").setSnapshots("test-snap").get(); - SnapshotInfo snapshotInfo = snapshotsStatusResponse.getSnapshots().get(0); - assertTrue(snapshotInfo.state().completed()); - } + assertBusy(() -> { + GetSnapshotsResponse snapshotsStatusResponse = client().admin().cluster().prepareGetSnapshots("test-repo").setSnapshots("test-snap").get(); + SnapshotInfo snapshotInfo = snapshotsStatusResponse.getSnapshots().get(0); + assertTrue(snapshotInfo.state().completed()); }, 1, TimeUnit.MINUTES); logger.info("--> verify that snapshot was succesful"); @@ -865,7 +842,7 @@ public void testRestoreShrinkIndex() throws Exception { restoreResponse.getRestoreInfo().successfulShards()); ensureYellow(); } - + public static class SnapshottableMetadata extends TestCustomMetaData { public static final String TYPE = "test_snapshottable"; diff --git a/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java b/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java index d8a0036a45c7c..1783363e7cebc 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java +++ b/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java @@ -2286,59 +2286,6 @@ private boolean waitForRelocationsToStart(final String index, TimeValue timeout) return awaitBusy(() -> client().admin().cluster().prepareHealth(index).execute().actionGet().getRelocatingShards() > 0, timeout.millis(), TimeUnit.MILLISECONDS); } - public void testBatchingShardUpdateTask() throws Exception { - final Client client = client(); - - logger.info("--> creating repository"); - assertAcked(client.admin().cluster().preparePutRepository("test-repo") - .setType("fs").setSettings(Settings.builder() - .put("location", randomRepoPath()) - .put("compress", randomBoolean()) - .put("chunk_size", randomIntBetween(100, 1000), ByteSizeUnit.BYTES))); - - assertAcked(prepareCreate("test-idx", 0, Settings.builder().put("number_of_shards", between(1, 10)) - .put("number_of_replicas", 0))); - ensureGreen(); - - logger.info("--> indexing some data"); - final int numdocs = randomIntBetween(10, 100); - IndexRequestBuilder[] builders = new IndexRequestBuilder[numdocs]; - for (int i = 0; i < builders.length; i++) { - builders[i] = client().prepareIndex("test-idx", "type1", Integer.toString(i)).setSource("field1", "bar " + i); - } - indexRandom(true, builders); - flushAndRefresh(); - - final int numberOfShards = getNumShards("test-idx").numPrimaries; - logger.info("number of shards: {}", numberOfShards); - - final ClusterService clusterService = internalCluster().clusterService(internalCluster().getMasterName()); - BlockingClusterStateListener snapshotListener = new BlockingClusterStateListener(clusterService, "update_snapshot [", "update snapshot state", Priority.HIGH); - try { - clusterService.addListener(snapshotListener); - logger.info("--> snapshot"); - ListenableActionFuture snapshotFuture = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap").setWaitForCompletion(true).setIndices("test-idx").execute(); - - // Await until shard updates are in pending state. - assertBusyPendingTasks("update snapshot state", numberOfShards); - snapshotListener.unblock(); - - // Check that the snapshot was successful - CreateSnapshotResponse createSnapshotResponse = snapshotFuture.actionGet(); - assertEquals(SnapshotState.SUCCESS, createSnapshotResponse.getSnapshotInfo().state()); - assertEquals(numberOfShards, createSnapshotResponse.getSnapshotInfo().totalShards()); - assertEquals(numberOfShards, createSnapshotResponse.getSnapshotInfo().successfulShards()); - - } finally { - clusterService.removeListener(snapshotListener); - } - - // Check that we didn't timeout - assertFalse(snapshotListener.timedOut()); - // Check that cluster state update task was called only once - assertEquals(1, snapshotListener.count()); - } - public void testSnapshotName() throws Exception { final Client client = client(); diff --git a/core/src/test/java/org/elasticsearch/test/NoopDiscovery.java b/core/src/test/java/org/elasticsearch/test/NoopDiscovery.java index c36082f147595..1a661b509a2bb 100644 --- a/core/src/test/java/org/elasticsearch/test/NoopDiscovery.java +++ b/core/src/test/java/org/elasticsearch/test/NoopDiscovery.java @@ -19,44 +19,37 @@ package org.elasticsearch.test; import org.elasticsearch.cluster.ClusterChangedEvent; -import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.common.component.Lifecycle; import org.elasticsearch.common.component.LifecycleListener; import org.elasticsearch.discovery.Discovery; -import org.elasticsearch.discovery.DiscoverySettings; import org.elasticsearch.discovery.DiscoveryStats; public class NoopDiscovery implements Discovery { - @Override - public DiscoveryNode localNode() { - return null; - } + public void setAllocationService(AllocationService allocationService) { - @Override - public String nodeDescription() { - return null; } @Override - public void setAllocationService(AllocationService allocationService) { + public void publish(ClusterChangedEvent clusterChangedEvent, AckListener ackListener) { } @Override - public void publish(ClusterChangedEvent clusterChangedEvent, AckListener ackListener) { - + public ClusterState getInitialClusterState() { + return null; } @Override - public DiscoveryStats stats() { + public ClusterState clusterState() { return null; } @Override - public DiscoverySettings getDiscoverySettings() { + public DiscoveryStats stats() { return null; } diff --git a/plugins/discovery-azure-classic/src/main/java/org/elasticsearch/plugin/discovery/azure/classic/AzureDiscoveryPlugin.java b/plugins/discovery-azure-classic/src/main/java/org/elasticsearch/plugin/discovery/azure/classic/AzureDiscoveryPlugin.java index 38e29e0458344..6bff34a667c19 100644 --- a/plugins/discovery-azure-classic/src/main/java/org/elasticsearch/plugin/discovery/azure/classic/AzureDiscoveryPlugin.java +++ b/plugins/discovery-azure-classic/src/main/java/org/elasticsearch/plugin/discovery/azure/classic/AzureDiscoveryPlugin.java @@ -19,22 +19,16 @@ package org.elasticsearch.plugin.discovery.azure.classic; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; - import org.apache.logging.log4j.Logger; -import org.elasticsearch.ElasticsearchException; import org.elasticsearch.cloud.azure.classic.management.AzureComputeService; import org.elasticsearch.cloud.azure.classic.management.AzureComputeServiceImpl; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.Strings; +import org.elasticsearch.cluster.service.ClusterApplier; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.network.NetworkService; +import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.discovery.Discovery; @@ -42,12 +36,17 @@ import org.elasticsearch.discovery.azure.classic.AzureUnicastHostsProvider; import org.elasticsearch.discovery.zen.UnicastHostsProvider; import org.elasticsearch.discovery.zen.ZenDiscovery; -import org.elasticsearch.discovery.zen.ZenPing; import org.elasticsearch.plugins.DiscoveryPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + public class AzureDiscoveryPlugin extends Plugin implements DiscoveryPlugin { public static final String AZURE = "azure"; @@ -76,10 +75,12 @@ public Map> getZenHostsProviders(Transpor @Override public Map> getDiscoveryTypes(ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, - ClusterService clusterService, UnicastHostsProvider hostsProvider) { + MasterService masterService, ClusterApplier clusterApplier, + ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { // this is for backcompat with pre 5.1, where users would set discovery.type to use ec2 hosts provider return Collections.singletonMap(AZURE, () -> - new ZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, clusterService, hostsProvider)); + new ZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, masterService, clusterApplier, + clusterSettings, hostsProvider)); } @Override diff --git a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java index d9923fe0f8193..3c80d0eda0623 100644 --- a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java +++ b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java @@ -20,37 +20,22 @@ package org.elasticsearch.discovery.ec2; import com.amazonaws.util.json.Jackson; -import java.io.BufferedReader; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UncheckedIOException; -import java.net.URL; -import java.net.URLConnection; -import java.nio.charset.StandardCharsets; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; - import org.apache.logging.log4j.Logger; import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.SetOnce; import org.elasticsearch.SpecialPermission; -import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.cluster.service.ClusterApplier; import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.network.NetworkService; +import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.discovery.Discovery; import org.elasticsearch.discovery.DiscoveryModule; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.discovery.zen.UnicastHostsProvider; import org.elasticsearch.discovery.zen.ZenDiscovery; import org.elasticsearch.node.Node; @@ -59,6 +44,23 @@ import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UncheckedIOException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + public class Ec2DiscoveryPlugin extends Plugin implements DiscoveryPlugin, Closeable { private static Logger logger = Loggers.getLogger(Ec2DiscoveryPlugin.class); @@ -95,10 +97,12 @@ public Ec2DiscoveryPlugin(Settings settings) { @Override public Map> getDiscoveryTypes(ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, - ClusterService clusterService, UnicastHostsProvider hostsProvider) { + MasterService masterService, ClusterApplier clusterApplier, + ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { // this is for backcompat with pre 5.1, where users would set discovery.type to use ec2 hosts provider return Collections.singletonMap(EC2, () -> - new ZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, clusterService, hostsProvider)); + new ZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, masterService, clusterApplier, + clusterSettings, hostsProvider)); } @Override diff --git a/plugins/discovery-gce/src/main/java/org/elasticsearch/plugin/discovery/gce/GceDiscoveryPlugin.java b/plugins/discovery-gce/src/main/java/org/elasticsearch/plugin/discovery/gce/GceDiscoveryPlugin.java index acad7e099f533..44fb5c8d15ec8 100644 --- a/plugins/discovery-gce/src/main/java/org/elasticsearch/plugin/discovery/gce/GceDiscoveryPlugin.java +++ b/plugins/discovery-gce/src/main/java/org/elasticsearch/plugin/discovery/gce/GceDiscoveryPlugin.java @@ -24,28 +24,25 @@ import org.apache.logging.log4j.Logger; import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.SetOnce; -import org.elasticsearch.SpecialPermission; import org.elasticsearch.cloud.gce.GceInstancesService; import org.elasticsearch.cloud.gce.GceInstancesServiceImpl; import org.elasticsearch.cloud.gce.GceMetadataService; -import org.elasticsearch.cloud.gce.GceModule; import org.elasticsearch.cloud.gce.network.GceNameResolver; import org.elasticsearch.cloud.gce.util.Access; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.component.LifecycleComponent; -import org.elasticsearch.common.inject.Module; +import org.elasticsearch.cluster.service.ClusterApplier; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.network.NetworkService; +import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.discovery.Discovery; import org.elasticsearch.discovery.DiscoveryModule; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.discovery.gce.GceUnicastHostsProvider; import org.elasticsearch.discovery.zen.UnicastHostsProvider; import org.elasticsearch.discovery.zen.ZenDiscovery; -import org.elasticsearch.discovery.zen.ZenPing; import org.elasticsearch.plugins.DiscoveryPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.threadpool.ThreadPool; @@ -53,11 +50,7 @@ import java.io.Closeable; import java.io.IOException; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -92,10 +85,12 @@ public GceDiscoveryPlugin(Settings settings) { @Override public Map> getDiscoveryTypes(ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, - ClusterService clusterService, UnicastHostsProvider hostsProvider) { + MasterService masterService, ClusterApplier clusterApplier, + ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { // this is for backcompat with pre 5.1, where users would set discovery.type to use ec2 hosts provider return Collections.singletonMap(GCE, () -> - new ZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, clusterService, hostsProvider)); + new ZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, masterService, clusterApplier, + clusterSettings, hostsProvider)); } @Override diff --git a/test/framework/src/main/java/org/elasticsearch/test/ClusterServiceUtils.java b/test/framework/src/main/java/org/elasticsearch/test/ClusterServiceUtils.java index b2eced6fd3545..f6c595b9b9792 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ClusterServiceUtils.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ClusterServiceUtils.java @@ -18,28 +18,109 @@ */ package org.elasticsearch.test; +import org.apache.logging.log4j.core.util.Throwables; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; +import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.LocalClusterUpdateTask; +import org.elasticsearch.cluster.ClusterStateTaskListener; +import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.cluster.NodeConnectionsService; +import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.cluster.service.ClusterApplier; +import org.elasticsearch.cluster.service.ClusterApplierService; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.discovery.DiscoverySettings; +import org.elasticsearch.discovery.Discovery.AckListener; import org.elasticsearch.threadpool.ThreadPool; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; import static junit.framework.TestCase.fail; public class ClusterServiceUtils { + public static MasterService createMasterService(ThreadPool threadPool, ClusterState initialClusterState) { + MasterService masterService = new MasterService(Settings.EMPTY, threadPool); + AtomicReference clusterStateRef = new AtomicReference<>(initialClusterState); + masterService.setClusterStatePublisher((event, ackListener) -> clusterStateRef.set(event.state())); + masterService.setClusterStateSupplier(clusterStateRef::get); + masterService.start(); + return masterService; + } + + public static MasterService createMasterService(ThreadPool threadPool, DiscoveryNode localNode) { + ClusterState initialClusterState = ClusterState.builder(new ClusterName(ClusterServiceUtils.class.getSimpleName())) + .nodes(DiscoveryNodes.builder() + .add(localNode) + .localNodeId(localNode.getId()) + .masterNodeId(localNode.getId())) + .blocks(ClusterBlocks.EMPTY_CLUSTER_BLOCK).build(); + return createMasterService(threadPool, initialClusterState); + } + + public static void setState(ClusterApplierService executor, ClusterState clusterState) { + CountDownLatch latch = new CountDownLatch(1); + AtomicReference exception = new AtomicReference<>(); + executor.onNewClusterState("test setting state", + () -> ClusterState.builder(clusterState).version(clusterState.version() + 1).build(), new ClusterStateTaskListener() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + } + + @Override + public void onFailure(String source, Exception e) { + exception.set(e); + latch.countDown(); + } + }); + try { + latch.await(); + if (exception.get() != null) { + Throwables.rethrow(exception.get()); + } + } catch (InterruptedException e) { + throw new ElasticsearchException("unexpected exception", e); + } + } + + public static void setState(MasterService executor, ClusterState clusterState) { + CountDownLatch latch = new CountDownLatch(1); + executor.submitStateUpdateTask("test setting state", new ClusterStateUpdateTask() { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + // make sure we increment versions as listener may depend on it for change + return ClusterState.builder(clusterState).build(); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + } + + @Override + public void onFailure(String source, Exception e) { + fail("unexpected exception" + e); + } + }); + try { + latch.await(); + } catch (InterruptedException e) { + throw new ElasticsearchException("unexpected interruption", e); + } + } + public static ClusterService createClusterService(ThreadPool threadPool) { DiscoveryNode discoveryNode = new DiscoveryNode("node", ESTestCase.buildNewFakeTransportAddress(), Collections.emptyMap(), EnumSet.allOf(DiscoveryNode.Role.class), Version.CURRENT); @@ -47,14 +128,12 @@ public static ClusterService createClusterService(ThreadPool threadPool) { } public static ClusterService createClusterService(ThreadPool threadPool, DiscoveryNode localNode) { - return createClusterService(Settings.EMPTY, threadPool, localNode); + return createClusterService(threadPool, localNode, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)); } - public static ClusterService createClusterService(Settings settings, ThreadPool threadPool, DiscoveryNode localNode) { - ClusterService clusterService = new ClusterService( - Settings.builder().put("cluster.name", "ClusterServiceTests").put(settings).build(), - new ClusterSettings(settings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), - threadPool, () -> localNode); + public static ClusterService createClusterService(ThreadPool threadPool, DiscoveryNode localNode, ClusterSettings clusterSettings) { + ClusterService clusterService = new ClusterService(Settings.builder().put("cluster.name", "ClusterServiceTests").build(), + clusterSettings, threadPool); clusterService.setNodeConnectionsService(new NodeConnectionsService(Settings.EMPTY, null, null) { @Override public void connectToNodes(DiscoveryNodes discoveryNodes) { @@ -66,17 +145,49 @@ public void disconnectFromNodesExcept(DiscoveryNodes nodesToKeep) { // skip } }); - clusterService.setClusterStatePublisher((event, ackListener) -> { - }); - clusterService.setDiscoverySettings(new DiscoverySettings(Settings.EMPTY, - new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS))); + ClusterState initialClusterState = ClusterState.builder(new ClusterName(ClusterServiceUtils.class.getSimpleName())) + .nodes(DiscoveryNodes.builder() + .add(localNode) + .localNodeId(localNode.getId()) + .masterNodeId(localNode.getId())) + .blocks(ClusterBlocks.EMPTY_CLUSTER_BLOCK).build(); + clusterService.getClusterApplierService().setInitialState(initialClusterState); + clusterService.getMasterService().setClusterStatePublisher( + createClusterStatePublisher(clusterService.getClusterApplierService())); + clusterService.getMasterService().setClusterStateSupplier(clusterService.getClusterApplierService()::state); clusterService.start(); - final DiscoveryNodes.Builder nodes = DiscoveryNodes.builder(clusterService.state().nodes()); - nodes.masterNodeId(clusterService.localNode().getId()); - setState(clusterService, ClusterState.builder(clusterService.state()).nodes(nodes)); return clusterService; } + public static BiConsumer createClusterStatePublisher(ClusterApplier clusterApplier) { + return (event, ackListener) -> { + CountDownLatch latch = new CountDownLatch(1); + AtomicReference ex = new AtomicReference<>(); + clusterApplier.onNewClusterState("mock_publish_to_self[" + event.source() + "]", () -> event.state(), + new ClusterStateTaskListener() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + } + + @Override + public void onFailure(String source, Exception e) { + ex.set(e); + latch.countDown(); + } + } + ); + try { + latch.await(); + } catch (InterruptedException e) { + Throwables.rethrow(e); + } + if (ex.get() != null) { + Throwables.rethrow(ex.get()); + } + }; + } + public static ClusterService createClusterService(ClusterState initialState, ThreadPool threadPool) { ClusterService clusterService = createClusterService(threadPool); setState(clusterService, initialState); @@ -87,29 +198,10 @@ public static void setState(ClusterService clusterService, ClusterState.Builder setState(clusterService, clusterStateBuilder.build()); } + /** + * Sets the state on the cluster applier service + */ public static void setState(ClusterService clusterService, ClusterState clusterState) { - CountDownLatch latch = new CountDownLatch(1); - clusterService.submitStateUpdateTask("test setting state", new LocalClusterUpdateTask() { - @Override - public ClusterTasksResult execute(ClusterState currentState) throws Exception { - // make sure we increment versions as listener may depend on it for change - return newState(ClusterState.builder(clusterState).version(currentState.version() + 1).build()); - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - latch.countDown(); - } - - @Override - public void onFailure(String source, Exception e) { - fail("unexpected exception" + e); - } - }); - try { - latch.await(); - } catch (InterruptedException e) { - throw new ElasticsearchException("unexpected interruption", e); - } + setState(clusterService.getClusterApplierService(), clusterState); } } diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java index 0240a8c4315e1..bd5a809d8cde1 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java @@ -570,7 +570,8 @@ protected final void afterInternal(boolean afterClass) throws Exception { final ZenDiscovery zenDiscovery = (ZenDiscovery) discovery; assertBusy(() -> { final ClusterState[] states = zenDiscovery.pendingClusterStates(); - assertThat(zenDiscovery.localNode().getName() + " still having pending states:\n" + + assertThat(zenDiscovery.clusterState().nodes().getLocalNode().getName() + + " still having pending states:\n" + Stream.of(states).map(ClusterState::toString).collect(Collectors.joining("\n")), states, emptyArray()); }); diff --git a/test/framework/src/main/java/org/elasticsearch/test/discovery/TestZenDiscovery.java b/test/framework/src/main/java/org/elasticsearch/test/discovery/TestZenDiscovery.java index c2a237ef337d5..cb4b8e098aeda 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/discovery/TestZenDiscovery.java +++ b/test/framework/src/main/java/org/elasticsearch/test/discovery/TestZenDiscovery.java @@ -24,8 +24,10 @@ import java.util.Map; import java.util.function.Supplier; -import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.cluster.service.ClusterApplier; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.discovery.Discovery; @@ -56,9 +58,11 @@ public TestPlugin(Settings settings) { @Override public Map> getDiscoveryTypes(ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, - ClusterService clusterService, UnicastHostsProvider hostsProvider) { + MasterService masterService, ClusterApplier clusterApplier, + ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { return Collections.singletonMap("test-zen", - () -> new TestZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, clusterService, hostsProvider)); + () -> new TestZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, masterService, + clusterApplier, clusterSettings, hostsProvider)); } @Override @@ -73,9 +77,10 @@ public Settings additionalSettings() { } private TestZenDiscovery(Settings settings, ThreadPool threadPool, TransportService transportService, - NamedWriteableRegistry namedWriteableRegistry, ClusterService clusterService, - UnicastHostsProvider hostsProvider) { - super(settings, threadPool, transportService, namedWriteableRegistry, clusterService, hostsProvider); + NamedWriteableRegistry namedWriteableRegistry, MasterService masterService, + ClusterApplier clusterApplier, ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { + super(settings, threadPool, transportService, namedWriteableRegistry, masterService, clusterApplier, clusterSettings, + hostsProvider); } @Override diff --git a/test/framework/src/main/java/org/elasticsearch/test/disruption/BlockClusterStateProcessing.java b/test/framework/src/main/java/org/elasticsearch/test/disruption/BlockClusterStateProcessing.java index 8a6be290502c4..f144cb0b118a0 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/disruption/BlockClusterStateProcessing.java +++ b/test/framework/src/main/java/org/elasticsearch/test/disruption/BlockClusterStateProcessing.java @@ -18,8 +18,7 @@ */ package org.elasticsearch.test.disruption; -import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.LocalClusterUpdateTask; +import org.apache.logging.log4j.core.util.Throwables; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Priority; import org.elasticsearch.common.unit.TimeValue; @@ -58,23 +57,19 @@ public void startDisrupting() { boolean success = disruptionLatch.compareAndSet(null, new CountDownLatch(1)); assert success : "startDisrupting called without waiting on stopDisrupting to complete"; final CountDownLatch started = new CountDownLatch(1); - clusterService.submitStateUpdateTask("service_disruption_block", new LocalClusterUpdateTask(Priority.IMMEDIATE) { - - @Override - public ClusterTasksResult execute(ClusterState currentState) throws Exception { + clusterService.getClusterApplierService().runOnApplierThread("service_disruption_block", + currentState -> { started.countDown(); CountDownLatch latch = disruptionLatch.get(); if (latch != null) { - latch.await(); + try { + latch.await(); + } catch (InterruptedException e) { + Throwables.rethrow(e); + } } - return unchanged(); - } - - @Override - public void onFailure(String source, Exception e) { - logger.error("unexpected error during disruption", e); - } - }); + }, (source, e) -> logger.error("unexpected error during disruption", e), + Priority.IMMEDIATE); try { started.await(); } catch (InterruptedException e) { diff --git a/test/framework/src/main/java/org/elasticsearch/test/disruption/SlowClusterStateProcessing.java b/test/framework/src/main/java/org/elasticsearch/test/disruption/SlowClusterStateProcessing.java index 61afa4f77f35f..03ffe1d690ae5 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/disruption/SlowClusterStateProcessing.java +++ b/test/framework/src/main/java/org/elasticsearch/test/disruption/SlowClusterStateProcessing.java @@ -18,8 +18,7 @@ */ package org.elasticsearch.test.disruption; -import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.LocalClusterUpdateTask; +import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Priority; import org.elasticsearch.common.unit.TimeValue; @@ -102,27 +101,23 @@ private boolean interruptClusterStateProcessing(final TimeValue duration) throws return false; } final AtomicBoolean stopped = new AtomicBoolean(false); - clusterService.submitStateUpdateTask("service_disruption_delay", new LocalClusterUpdateTask(Priority.IMMEDIATE) { - - @Override - public ClusterTasksResult execute(ClusterState currentState) throws Exception { - long count = duration.millis() / 200; - // wait while checking for a stopped - for (; count > 0 && !stopped.get(); count--) { - Thread.sleep(200); - } - if (!stopped.get()) { - Thread.sleep(duration.millis() % 200); + clusterService.getClusterApplierService().runOnApplierThread("service_disruption_delay", + currentState -> { + try { + long count = duration.millis() / 200; + // wait while checking for a stopped + for (; count > 0 && !stopped.get(); count--) { + Thread.sleep(200); + } + if (!stopped.get()) { + Thread.sleep(duration.millis() % 200); + } + countDownLatch.countDown(); + } catch (InterruptedException e) { + ExceptionsHelper.reThrowIfNotNull(e); } - countDownLatch.countDown(); - return unchanged(); - } - - @Override - public void onFailure(String source, Exception e) { - countDownLatch.countDown(); - } - }); + }, (source, e) -> countDownLatch.countDown(), + Priority.IMMEDIATE); try { countDownLatch.await(); } catch (InterruptedException e) { From b77254871b714a558d39f268f2beaf5a7516302a Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Fri, 28 Apr 2017 11:09:24 +0200 Subject: [PATCH 116/619] docs: document alternative for nested inner hits source Closes #24110 --- .../search/request/inner-hits.asciidoc | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/docs/reference/search/request/inner-hits.asciidoc b/docs/reference/search/request/inner-hits.asciidoc index 28914cd9b5e02..1b118a419ed22 100644 --- a/docs/reference/search/request/inner-hits.asciidoc +++ b/docs/reference/search/request/inner-hits.asciidoc @@ -148,6 +148,55 @@ An important default is that the `_source` returned in hits inside `inner_hits` So in the above example only the comment part is returned per nested hit and not the entire source of the top level document that contained the comment. +[[nested-inner-hits-source]] +==== Nested inner hits and _source + +Nested document don't have a `_source` field, because the entire source of document is stored with the root document under +its `_source` field. To include the source of just the nested document, the source of the root document is parsed and just +the relevant bit for the nested document is included as source in the inner hit. Doing this for each matching nested document +has an impact on the time it takes to execute the entire search request, especially when `size` and the inner hits' `size` +are set higher than the default. To avoid the relative expensive source extraction for nested inner hits, one can disable +including the source and solely rely on stored fields. + +Enabled stored field for fields under the nested object field in your mapping: + +[source,js] +-------------------------------------------------- +{ + "properties": { + "comment": { + "type": "comments", + "properties" : { + "message" : { + "type" : "text", + "store" : true + } + } + } + } +} +-------------------------------------------------- + +Disable including source and include specific stored fields in the inner hits definition: + +[source,js] +-------------------------------------------------- +{ + "query" : { + "nested" : { + "path" : "comments", + "query" : { + "match" : {"comments.message" : "[actual query]"} + }, + "inner_hits" : { + "_source" : false, + "stored_fields" : ["comments.text"] + } + } + } +} +-------------------------------------------------- + [[hierarchical-nested-inner-hits]] ==== Hierarchical levels of nested object fields and inner hits. From a72db191f296336305ba1e349ec443c75ada3ce1 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Fri, 28 Apr 2017 11:16:07 +0200 Subject: [PATCH 117/619] Weaken assertion in ZenDiscovery.publish The previous commit (35f78d098a) introduced an assertion in ZenDiscovery that was overly restrictive - it could trip when a cluster state that was successfully published would not be applied locally because a master with a better cluster state came along in the meantime. --- .../org/elasticsearch/discovery/zen/ZenDiscovery.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java index 5da446aa5b927..f4bef4b2d91c5 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java @@ -335,16 +335,19 @@ public void publish(ClusterChangedEvent clusterChangedEvent, AckListener ackList final DiscoveryNode localNode = newState.getNodes().getLocalNode(); final CountDownLatch latch = new CountDownLatch(1); + final AtomicBoolean processedOrFailed = new AtomicBoolean(); publishClusterState.pendingStatesQueue().markAsCommitted(newState.stateUUID(), new PendingClusterStatesQueue.StateProcessedListener() { @Override public void onNewClusterStateProcessed() { + processedOrFailed.set(true); latch.countDown(); ackListener.onNodeAck(localNode, null); } @Override public void onNewClusterStateFailed(Exception e) { + processedOrFailed.set(true); latch.countDown(); ackListener.onNodeAck(localNode, e); logger.warn( @@ -360,10 +363,12 @@ public void onNewClusterStateFailed(Exception e) { throw new FailedToCommitClusterStateException("local state was mutated while CS update was published to other nodes"); } - boolean processed = processNextCommittedClusterState("master " + newState.nodes().getMasterNode() + + boolean sentToApplier = processNextCommittedClusterState("master " + newState.nodes().getMasterNode() + " committed version [" + newState.version() + "] source [" + clusterChangedEvent.source() + "]"); - if (processed == false) { - assert false : "CS published to itself not processed"; + if (sentToApplier == false && processedOrFailed.get() == false) { + assert false : "cluster state published locally neither processed nor failed: " + newState; + logger.warn("cluster state with version [{}] that is published locally has neither been processed nor failed", + newState.version()); return; } } From a6c38b8f8a9c95f81c10bd97318d9ab66142108e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Fri, 28 Apr 2017 12:10:16 +0200 Subject: [PATCH 118/619] Add parsing for InternalGeoBounds (#24365) --- .../metrics/geobounds/InternalGeoBounds.java | 23 ++-- .../metrics/geobounds/ParsedGeoBounds.java | 105 ++++++++++++++++++ .../InternalAggregationTestCase.java | 6 +- .../geobounds/InternalGeoBoundsTests.java | 34 ++++-- 4 files changed, 153 insertions(+), 15 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/ParsedGeoBounds.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBounds.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBounds.java index 2a3d03e43e624..37297475a28e3 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBounds.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBounds.java @@ -19,6 +19,7 @@ package org.elasticsearch.search.aggregations.metrics.geobounds; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -32,6 +33,14 @@ import java.util.Objects; public class InternalGeoBounds extends InternalAggregation implements GeoBounds { + + final static ParseField BOUNDS_FIELD = new ParseField("bounds"); + final static ParseField TOP_LEFT_FIELD = new ParseField("top_left"); + final static ParseField BOTTOM_RIGHT_FIELD = new ParseField("bottom_right"); + final static ParseField LAT_FIELD = new ParseField("lat"); + final static ParseField LON_FIELD = new ParseField("lon"); + + final double top; final double bottom; final double posLeft; @@ -170,14 +179,14 @@ public XContentBuilder doXContentBody(XContentBuilder builder, Params params) th GeoPoint topLeft = topLeft(); GeoPoint bottomRight = bottomRight(); if (topLeft != null) { - builder.startObject("bounds"); - builder.startObject("top_left"); - builder.field("lat", topLeft.lat()); - builder.field("lon", topLeft.lon()); + builder.startObject(BOUNDS_FIELD.getPreferredName()); + builder.startObject(TOP_LEFT_FIELD.getPreferredName()); + builder.field(LAT_FIELD.getPreferredName(), topLeft.lat()); + builder.field(LON_FIELD.getPreferredName(), topLeft.lon()); builder.endObject(); - builder.startObject("bottom_right"); - builder.field("lat", bottomRight.lat()); - builder.field("lon", bottomRight.lon()); + builder.startObject(BOTTOM_RIGHT_FIELD.getPreferredName()); + builder.field(LAT_FIELD.getPreferredName(), bottomRight.lat()); + builder.field(LON_FIELD.getPreferredName(), bottomRight.lon()); builder.endObject(); builder.endObject(); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/ParsedGeoBounds.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/ParsedGeoBounds.java new file mode 100644 index 0000000000000..04d2b2448d2e2 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/ParsedGeoBounds.java @@ -0,0 +1,105 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.geobounds; + +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.geo.GeoPoint; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.ParsedAggregation; + +import java.io.IOException; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.search.aggregations.metrics.geobounds.InternalGeoBounds.BOTTOM_RIGHT_FIELD; +import static org.elasticsearch.search.aggregations.metrics.geobounds.InternalGeoBounds.BOUNDS_FIELD; +import static org.elasticsearch.search.aggregations.metrics.geobounds.InternalGeoBounds.LAT_FIELD; +import static org.elasticsearch.search.aggregations.metrics.geobounds.InternalGeoBounds.LON_FIELD; +import static org.elasticsearch.search.aggregations.metrics.geobounds.InternalGeoBounds.TOP_LEFT_FIELD; + +public class ParsedGeoBounds extends ParsedAggregation implements GeoBounds { + private GeoPoint topLeft; + private GeoPoint bottomRight; + + @Override + public String getType() { + return GeoBoundsAggregationBuilder.NAME; + } + + @Override + public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + if (topLeft != null) { + builder.startObject("bounds"); + builder.startObject("top_left"); + builder.field("lat", topLeft.getLat()); + builder.field("lon", topLeft.getLon()); + builder.endObject(); + builder.startObject("bottom_right"); + builder.field("lat", bottomRight.getLat()); + builder.field("lon", bottomRight.getLon()); + builder.endObject(); + builder.endObject(); + } + return builder; + } + + @Override + public GeoPoint topLeft() { + return topLeft; + } + + @Override + public GeoPoint bottomRight() { + return bottomRight; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedGeoBounds.class.getSimpleName(), true, + ParsedGeoBounds::new); + + private static final ConstructingObjectParser, Void> BOUNDS_PARSER = + new ConstructingObjectParser<>(ParsedGeoBounds.class.getSimpleName() + "_BOUNDS", true, + args -> new Tuple<>((GeoPoint) args[0], (GeoPoint) args[1])); + + private static final ObjectParser GEO_POINT_PARSER = new ObjectParser<>( + ParsedGeoBounds.class.getSimpleName() + "_POINT", true, GeoPoint::new); + + static { + declareAggregationFields(PARSER); + PARSER.declareObject((agg, bbox) -> { + agg.topLeft = bbox.v1(); + agg.bottomRight = bbox.v2(); + }, BOUNDS_PARSER, BOUNDS_FIELD); + + BOUNDS_PARSER.declareObject(constructorArg(), GEO_POINT_PARSER, TOP_LEFT_FIELD); + BOUNDS_PARSER.declareObject(constructorArg(), GEO_POINT_PARSER, BOTTOM_RIGHT_FIELD); + + GEO_POINT_PARSER.declareDouble(GeoPoint::resetLat, LAT_FIELD); + GEO_POINT_PARSER.declareDouble(GeoPoint::resetLon, LON_FIELD); + } + + public static ParsedGeoBounds fromXContent(XContentParser parser, final String name) { + ParsedGeoBounds geoBounds = PARSER.apply(parser, null); + geoBounds.setName(name); + return geoBounds; + } + +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index d4c293a0e8bb7..664a2b756df6e 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -38,6 +38,8 @@ import org.elasticsearch.search.aggregations.metrics.avg.ParsedAvg; import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.cardinality.ParsedCardinality; +import org.elasticsearch.search.aggregations.metrics.geobounds.GeoBoundsAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.geobounds.ParsedGeoBounds; import org.elasticsearch.search.aggregations.metrics.max.MaxAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.max.ParsedMax; import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder; @@ -113,7 +115,9 @@ static List getNamedXContents() { namedXContents.put(StatsAggregationBuilder.NAME, (p, c) -> ParsedStats.fromXContent(p, (String) c)); namedXContents.put(StatsBucketPipelineAggregationBuilder.NAME, (p, c) -> ParsedStatsBucket.fromXContent(p, (String) c)); namedXContents.put(ExtendedStatsAggregationBuilder.NAME, (p, c) -> ParsedExtendedStats.fromXContent(p, (String) c)); - namedXContents.put(ExtendedStatsBucketPipelineAggregationBuilder.NAME, (p, c) -> ParsedExtendedStatsBucket.fromXContent(p, (String) c)); + namedXContents.put(ExtendedStatsBucketPipelineAggregationBuilder.NAME, + (p, c) -> ParsedExtendedStatsBucket.fromXContent(p, (String) c)); + namedXContents.put(GeoBoundsAggregationBuilder.NAME, (p, c) -> ParsedGeoBounds.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBoundsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBoundsTests.java index cd5d4d43d1719..611178d1bf20f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBoundsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBoundsTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.Collections; @@ -35,8 +36,10 @@ public class InternalGeoBoundsTests extends InternalAggregationTestCase pipelineAggregators, Map metaData) { + // we occasionally want to test top = Double.NEGATIVE_INFINITY since this triggers empty xContent object + double top = frequently() ? randomDouble() : Double.NEGATIVE_INFINITY; InternalGeoBounds geo = new InternalGeoBounds(name, - randomDouble(), randomDouble(), randomDouble(), randomDouble(), + top, randomDouble(), randomDouble(), randomDouble(), randomDouble(), randomDouble(), randomBoolean(), pipelineAggregators, Collections.emptyMap()); return geo; @@ -70,12 +73,29 @@ protected void assertReduced(InternalGeoBounds reduced, List negRight = bounds.negRight; } } - assertThat(reduced.top, closeTo(top, GEOHASH_TOLERANCE)); - assertThat(reduced.bottom, closeTo(bottom, GEOHASH_TOLERANCE)); - assertThat(reduced.posLeft, closeTo(posLeft, GEOHASH_TOLERANCE)); - assertThat(reduced.posRight, closeTo(posRight, GEOHASH_TOLERANCE)); - assertThat(reduced.negLeft, closeTo(negLeft, GEOHASH_TOLERANCE)); - assertThat(reduced.negRight, closeTo(negRight, GEOHASH_TOLERANCE)); + assertValueClose(reduced.top, top); + assertValueClose(reduced.bottom, bottom); + assertValueClose(reduced.posLeft, posLeft); + assertValueClose(reduced.posRight, posRight); + assertValueClose(reduced.negLeft, negLeft); + assertValueClose(reduced.negRight, negRight); + } + + private static void assertValueClose(double expected, double actual) { + if (Double.isInfinite(expected) == false) { + assertThat(expected, closeTo(actual, GEOHASH_TOLERANCE)); + } else { + assertTrue(Double.isInfinite(actual)); + } + } + + @Override + protected void assertFromXContent(InternalGeoBounds aggregation, ParsedAggregation parsedAggregation) { + assertTrue(parsedAggregation instanceof ParsedGeoBounds); + ParsedGeoBounds parsed = (ParsedGeoBounds) parsedAggregation; + + assertEquals(aggregation.topLeft(), parsed.topLeft()); + assertEquals(aggregation.bottomRight(), parsed.bottomRight()); } @Override From a5bd2012b6046be76fbc6a60b8463e9eb88fdb9c Mon Sep 17 00:00:00 2001 From: Kunal Kapoor Date: Fri, 28 Apr 2017 17:32:09 +0530 Subject: [PATCH 119/619] Added validation for upserd request (#24282) The version on an update request is a syntactic sugar for get of a specific version, doc merge and a version index. This changes it to reject requests with both upsert and a version. If the upsert index request is versioned, we also reject the op. --- .../action/update/UpdateRequest.java | 6 ++ .../action/bulk/BulkRequestTests.java | 36 ++++++++++++ .../action/update/UpdateRequestTests.java | 17 ++++++ .../org/elasticsearch/update/UpdateIT.java | 56 +------------------ 4 files changed, 60 insertions(+), 55 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/update/UpdateRequest.java b/core/src/main/java/org/elasticsearch/action/update/UpdateRequest.java index 2f153cdbef749..fa8c46edf5b7e 100644 --- a/core/src/main/java/org/elasticsearch/action/update/UpdateRequest.java +++ b/core/src/main/java/org/elasticsearch/action/update/UpdateRequest.java @@ -98,6 +98,12 @@ public UpdateRequest(String index, String type, String id) { @Override public ActionRequestValidationException validate() { ActionRequestValidationException validationException = super.validate(); + if (version != Versions.MATCH_ANY && upsertRequest != null) { + validationException = addValidationError("can't provide both upsert request and a version", validationException); + } + if(upsertRequest != null && upsertRequest.version() != Versions.MATCH_ANY) { + validationException = addValidationError("can't provide version in upsert request", validationException); + } if (type == null) { validationException = addValidationError("type is missing", validationException); } diff --git a/core/src/test/java/org/elasticsearch/action/bulk/BulkRequestTests.java b/core/src/test/java/org/elasticsearch/action/bulk/BulkRequestTests.java index d2b63ad396583..76810056485ac 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/BulkRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/BulkRequestTests.java @@ -40,6 +40,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -255,4 +256,39 @@ public void testSmileIsSupported() throws IOException { assertEquals(1, request.sourceAsMap().size()); assertEquals("value", request.sourceAsMap().get("field")); } + + public void testToValidateUpsertRequestAndVersionInBulkRequest() throws IOException { + XContentType xContentType = XContentType.SMILE; + BytesReference data; + try (BytesStreamOutput out = new BytesStreamOutput()) { + try (XContentBuilder builder = XContentFactory.contentBuilder(xContentType, out)) { + builder.startObject(); + builder.startObject("update"); + builder.field("_index", "index"); + builder.field("_type", "type"); + builder.field("_id", "id"); + builder.field("_version", 1L); + builder.endObject(); + builder.endObject(); + } + out.write(xContentType.xContent().streamSeparator()); + try(XContentBuilder builder = XContentFactory.contentBuilder(xContentType, out)) { + builder.startObject(); + builder.field("doc", "{}"); + Map values = new HashMap<>(); + values.put("_version", 2L); + values.put("_index", "index"); + values.put("_type", "type"); + builder.field("upsert", values); + builder.endObject(); + } + out.write(xContentType.xContent().streamSeparator()); + data = out.bytes(); + } + BulkRequest bulkRequest = new BulkRequest(); + bulkRequest.add(data, null, null, xContentType); + assertThat(bulkRequest.validate().validationErrors(), contains("can't provide both upsert request and a version", + "can't provide version in upsert request")); + } + } diff --git a/core/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java b/core/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java index d6a087bec45a4..339976a70865d 100644 --- a/core/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java @@ -59,9 +59,11 @@ import static java.util.Collections.singletonList; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.common.xcontent.XContentHelper.toXContent; +import static org.elasticsearch.common.xcontent.XContentHelper.update; import static org.elasticsearch.script.MockScriptEngine.mockInlineScript; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent; import static org.hamcrest.Matchers.arrayContaining; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.notNullValue; @@ -485,4 +487,19 @@ public void testToAndFromXContent() throws IOException { BytesReference finalBytes = toXContent(parsedUpdateRequest, xContentType, humanReadable); assertToXContentEquivalent(originalBytes, finalBytes, xContentType); } + + public void testToValidateUpsertRequestAndVersion() { + UpdateRequest updateRequest = new UpdateRequest("index", "type", "id"); + updateRequest.version(1L); + updateRequest.doc("{}", XContentType.JSON); + updateRequest.upsert(new IndexRequest("index","type", "id")); + assertThat(updateRequest.validate().validationErrors(), contains("can't provide both upsert request and a version")); + } + + public void testToValidateUpsertRequestWithVersion() { + UpdateRequest updateRequest = new UpdateRequest("index", "type", "id"); + updateRequest.doc("{}", XContentType.JSON); + updateRequest.upsert(new IndexRequest("index", "type", "1").version(1L)); + assertThat(updateRequest.validate().validationErrors(), contains("can't provide version in upsert request")); + } } diff --git a/core/src/test/java/org/elasticsearch/update/UpdateIT.java b/core/src/test/java/org/elasticsearch/update/UpdateIT.java index 893bbbd6115a8..10d235d3a855d 100644 --- a/core/src/test/java/org/elasticsearch/update/UpdateIT.java +++ b/core/src/test/java/org/elasticsearch/update/UpdateIT.java @@ -491,61 +491,7 @@ public void testUpsertFields() throws Exception { assertThat(updateResponse.getGetResult().sourceAsMap().get("bar").toString(), equalTo("baz")); assertThat(updateResponse.getGetResult().sourceAsMap().get("extra").toString(), equalTo("foo")); } - - public void testVersionedUpdate() throws Exception { - assertAcked(prepareCreate("test").addAlias(new Alias("alias"))); - ensureGreen(); - - index("test", "type", "1", "text", "value"); // version is now 1 - - assertThrows(client().prepareUpdate(indexOrAlias(), "type", "1") - .setScript(new Script(ScriptType.INLINE, "put_values", "", Collections.singletonMap("text", "v2"))).setVersion(2) - .execute(), - VersionConflictEngineException.class); - - client().prepareUpdate(indexOrAlias(), "type", "1") - .setScript(new Script(ScriptType.INLINE, "put_values", "", Collections.singletonMap("text", "v2"))).setVersion(1).get(); - assertThat(client().prepareGet("test", "type", "1").get().getVersion(), equalTo(2L)); - - // and again with a higher version.. - client().prepareUpdate(indexOrAlias(), "type", "1") - .setScript(new Script(ScriptType.INLINE, "put_values", "", Collections.singletonMap("text", "v3"))).setVersion(2).get(); - - assertThat(client().prepareGet("test", "type", "1").get().getVersion(), equalTo(3L)); - - // after delete - client().prepareDelete("test", "type", "1").get(); - assertThrows(client().prepareUpdate("test", "type", "1") - .setScript(new Script(ScriptType.INLINE, "put_values", "", Collections.singletonMap("text", "v2"))).setVersion(3) - .execute(), - DocumentMissingException.class); - - // external versioning - client().prepareIndex("test", "type", "2").setSource("text", "value").setVersion(10).setVersionType(VersionType.EXTERNAL).get(); - - assertThrows(client().prepareUpdate(indexOrAlias(), "type", "2") - .setScript(new Script(ScriptType.INLINE, "put_values", "", Collections.singletonMap("text", "v2"))).setVersion(2) - .setVersionType(VersionType.EXTERNAL).execute(), - ActionRequestValidationException.class); - - GetResponse get = get("test", "type", "2"); - assertThat(get.getVersion(), equalTo(10L)); - assertThat((String) get.getSource().get("text"), equalTo("value")); - - // upserts - the combination with versions is a bit weird. Test are here to ensure we do not change our behavior unintentionally - - // With internal versions, tt means "if object is there with version X, update it or explode. If it is not there, index. - client().prepareUpdate(indexOrAlias(), "type", "3") - .setScript(new Script(ScriptType.INLINE, "put_values", "", Collections.singletonMap("text", "v2"))) - .setVersion(10).setUpsert("{ \"text\": \"v0\" }", XContentType.JSON).get(); - get = get("test", "type", "3"); - assertThat(get.getVersion(), equalTo(1L)); - assertThat((String) get.getSource().get("text"), equalTo("v0")); - - // retry on conflict is rejected: - assertThrows(client().prepareUpdate(indexOrAlias(), "type", "1").setVersion(10).setRetryOnConflict(5), ActionRequestValidationException.class); - } - + public void testIndexAutoCreation() throws Exception { UpdateResponse updateResponse = client().prepareUpdate("test", "type1", "1") .setUpsert(XContentFactory.jsonBuilder().startObject().field("bar", "baz").endObject()) From 0d5cca8feb94850d1a6651b95bc09eccf8326c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Fri, 28 Apr 2017 14:19:00 +0200 Subject: [PATCH 120/619] Fixing checktyle error for modifier order --- .../metrics/geobounds/InternalGeoBounds.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBounds.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBounds.java index 37297475a28e3..a64115447be1c 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBounds.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBounds.java @@ -34,11 +34,11 @@ public class InternalGeoBounds extends InternalAggregation implements GeoBounds { - final static ParseField BOUNDS_FIELD = new ParseField("bounds"); - final static ParseField TOP_LEFT_FIELD = new ParseField("top_left"); - final static ParseField BOTTOM_RIGHT_FIELD = new ParseField("bottom_right"); - final static ParseField LAT_FIELD = new ParseField("lat"); - final static ParseField LON_FIELD = new ParseField("lon"); + static final ParseField BOUNDS_FIELD = new ParseField("bounds"); + static final ParseField TOP_LEFT_FIELD = new ParseField("top_left"); + static final ParseField BOTTOM_RIGHT_FIELD = new ParseField("bottom_right"); + static final ParseField LAT_FIELD = new ParseField("lat"); + static final ParseField LON_FIELD = new ParseField("lon"); final double top; From 2412574e49f7bcfc63fda85f21121618aa3d3f79 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Fri, 28 Apr 2017 08:43:46 -0400 Subject: [PATCH 121/619] Docs: Upserts no longer support version Closes #16671 --- docs/reference/migration/migrate_6_0/docs.asciidoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/reference/migration/migrate_6_0/docs.asciidoc b/docs/reference/migration/migrate_6_0/docs.asciidoc index 5d19c000ad726..b7a08ea8930da 100644 --- a/docs/reference/migration/migrate_6_0/docs.asciidoc +++ b/docs/reference/migration/migrate_6_0/docs.asciidoc @@ -5,3 +5,7 @@ Document modification operations may no longer specify the `version_type` of `force` to override any previous version checks. + +==== <> no longer support versions + +Adding a `version` to an upsert request is no longer supported. From 382a617d346ffdcd785e385bc1ec9fd542a0d454 Mon Sep 17 00:00:00 2001 From: Guillaume Le Floch Date: Fri, 28 Apr 2017 15:21:44 +0200 Subject: [PATCH 122/619] Handle multiple aliases in _cat/aliases api (#23698) The alias parameter was documented as a list in our rest-spec, yet only the first value out of a list was getting read and processed. This commit adds support for multiple aliases to _cat/aliases Closes #23661 --- .../rest/action/cat/RestAliasAction.java | 2 +- docs/reference/cat/alias.asciidoc | 5 +- .../test/cat.aliases/10_basic.yaml | 46 +++++++++++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/rest/action/cat/RestAliasAction.java b/core/src/main/java/org/elasticsearch/rest/action/cat/RestAliasAction.java index 120678d2e822c..a783a9c2a8215 100644 --- a/core/src/main/java/org/elasticsearch/rest/action/cat/RestAliasAction.java +++ b/core/src/main/java/org/elasticsearch/rest/action/cat/RestAliasAction.java @@ -46,7 +46,7 @@ public RestAliasAction(Settings settings, RestController controller) { @Override protected RestChannelConsumer doCatRequest(final RestRequest request, final NodeClient client) { final GetAliasesRequest getAliasesRequest = request.hasParam("alias") ? - new GetAliasesRequest(request.param("alias")) : + new GetAliasesRequest(Strings.commaDelimitedListToStringArray(request.param("alias"))) : new GetAliasesRequest(); getAliasesRequest.local(request.paramAsBoolean("local", getAliasesRequest.local())); diff --git a/docs/reference/cat/alias.asciidoc b/docs/reference/cat/alias.asciidoc index 93a1fdf3eb683..84d567d110a60 100644 --- a/docs/reference/cat/alias.asciidoc +++ b/docs/reference/cat/alias.asciidoc @@ -54,5 +54,6 @@ alias4 test1 - 2 1,2 The output shows that `alias2` has configured a filter, and specific routing configurations in `alias3` and `alias4`. -If you only want to get information about a single alias, you can specify -the alias in the URL, for example `/_cat/aliases/alias1`. +If you only want to get information about specific aliases, you can specify +the aliases in comma-delimited format as a URL parameter, e.g., +/_cat/aliases/aliases/alias1,alias2. \ No newline at end of file diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/10_basic.yaml index fc7eb4568920c..12879fa412a1d 100755 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/10_basic.yaml @@ -126,6 +126,52 @@ - match: $body: / (^|\n)test_2 .+ \n/ +--- +"Multiple alias names": + + - skip: + version: " - 5.99.99" + reason: multiple aliases are supported only from 6.0.0 on + + - do: + indices.create: + index: test + + - do: + indices.create: + index: test2 + - do: + indices.create: + index: test3 + + - do: + indices.put_alias: + index: test + name: foo + + - do: + indices.put_alias: + index: test2 + name: bar + - do: + indices.put_alias: + index: test3 + name: baz + + - do: + cat.aliases: + name: foo,bar + v: true + h: [alias, index] + s: [index] + + - match: + $body: | + /^ alias \s+ index \n + foo \s+ test \n + bar \s+ test2 + $/ + --- "Column headers": From e3b7b88756790712717301b34285a1bb2128bd2b Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Fri, 28 Apr 2017 09:52:14 -0400 Subject: [PATCH 123/619] Fix compilation in Ecipse (#24391) Eclipse doesn't allow extra semicolons after an import statement: ``` import foo.Bar;; // <-- syntax error! ``` Here is the Eclipse bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=425140 which the Eclipse folks closed as "the spec doesn't allow these semicolons so why should we?" Which is fair. Here is the bug against javac for allowing them: https://bugs.openjdk.java.net/browse/JDK-8027682 which hasn't been touched since 2013 without explanation. There is, however, a rather educations mailing list thread: http://mail.openjdk.java.net/pipermail/compiler-dev/2013-August/006956.html which contains gems like, "In general, it is better/simpler to change javac to conform to the spec. (Except when it is not.)" I suspect the reason this hasn't been fixed is: ``` FWIW, if we change javac such that the set of programs accepted by javac is changed, we have an process (currently Oracle internal) to get approval for such a change. So, we would not simply change javac on a whim to meet the spec; we would at least have other eyes looking at the behavioral change to determine if it is "acceptable". ``` from http://mail.openjdk.java.net/pipermail/compiler-dev/2013-August/006973.html --- .../main/java/org/elasticsearch/discovery/TribeDiscovery.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/discovery/TribeDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/TribeDiscovery.java index 751eb94a9f216..9f802cc270adf 100644 --- a/core/src/main/java/org/elasticsearch/discovery/TribeDiscovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/TribeDiscovery.java @@ -24,7 +24,7 @@ import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.service.ClusterApplier;; +import org.elasticsearch.cluster.service.ClusterApplier; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.discovery.single.SingleNodeDiscovery; From 94e3796908f7f11c6522fd6f533dd4f0972394f5 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Fri, 28 Apr 2017 09:56:19 -0400 Subject: [PATCH 124/619] Docs tests: cat/health can have max_task_wait_time Make the doc test assertions ok with a non `-` value for `max_task_wait_time`. These are rare, but possible: https://elasticsearch-ci.elastic.co/job/elastic+elasticsearch+master+multijob-unix-compatibility/os=oraclelinux/900/consoleFull --- docs/reference/cat/health.asciidoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/reference/cat/health.asciidoc b/docs/reference/cat/health.asciidoc index a87fe4e5e4a6a..ca2a1838adb02 100644 --- a/docs/reference/cat/health.asciidoc +++ b/docs/reference/cat/health.asciidoc @@ -16,7 +16,8 @@ GET /_cat/health?v epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent 1475871424 16:17:04 elasticsearch green 1 1 5 5 0 0 0 0 - 100.0% -------------------------------------------------- -// TESTRESPONSE[s/1475871424 16:17:04/\\d+ \\d+:\\d+:\\d+/ s/elasticsearch/[^ ]+/ s/0 -/\\d+ -/ _cat] +// TESTRESPONSE[s/1475871424 16:17:04/\\d+ \\d+:\\d+:\\d+/] +// TESTRESPONSE[s/elasticsearch/[^ ]+/ s/0 -/\\d+ (-|\\d+(\\.\\d+)?[ms]+)/ _cat] It has one option `ts` to disable the timestamping: @@ -34,7 +35,7 @@ which looks like: cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent elasticsearch green 1 1 5 5 0 0 0 0 - 100.0% -------------------------------------------------- -// TESTRESPONSE[s/elasticsearch/[^ ]+/ s/0 -/\\d+ -/ _cat] +// TESTRESPONSE[s/elasticsearch/[^ ]+/ s/0 -/\\d+ (-|\\d+(\\.\\d+)?[ms]+)/ _cat] A common use of this command is to verify the health is consistent across nodes: From 16a7cbe4635747909f4112eb4e162fb40cef9da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Fri, 28 Apr 2017 16:25:22 +0200 Subject: [PATCH 125/619] Add `count` value to rest output of `geo_centroid` (#24387) Currently we don't write the count value to the geo_centroid aggregation rest response, but it is provided via the java api and the count() method in the GeoCentroid interface. We should add this parameter to the rest output and also provide it via the getProperty() method. --- .../geocentroid/InternalGeoCentroid.java | 24 +++++++++++++++--- .../aggregations/metrics/GeoCentroidIT.java | 6 +++++ .../geocentroid/InternalGeoCentroidTests.java | 25 ++++++++++++------- .../metrics/geocentroid-aggregation.asciidoc | 12 ++++++--- 4 files changed, 50 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java index da69115ac6b34..c5578813c84c5 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java @@ -20,6 +20,7 @@ package org.elasticsearch.search.aggregations.metrics.geocentroid; import org.apache.lucene.geo.GeoEncodingUtils; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -36,8 +37,8 @@ * Serialization and merge logic for {@link GeoCentroidAggregator}. */ public class InternalGeoCentroid extends InternalAggregation implements GeoCentroid { - protected final GeoPoint centroid; - protected final long count; + private final GeoPoint centroid; + private final long count; public static long encodeLatLon(double lat, double lon) { return (Integer.toUnsignedLong(GeoEncodingUtils.encodeLatitude(lat)) << 32) | Integer.toUnsignedLong(GeoEncodingUtils.encodeLongitude(lon)); @@ -136,6 +137,8 @@ public Object getProperty(List path) { return centroid.lat(); case "lon": return centroid.lon(); + case "count": + return count; default: throw new IllegalArgumentException("Found unknown path element [" + coordinate + "] in [" + getName() + "]"); } @@ -145,14 +148,27 @@ public Object getProperty(List path) { } static class Fields { - static final String CENTROID = "location"; + static final ParseField CENTROID = new ParseField("location"); + static final ParseField CENTROID_LAT = new ParseField("lat"); + static final ParseField CENTROID_LON = new ParseField("lon"); + static final ParseField COUNT = new ParseField("count"); } @Override public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + return renderXContent(builder, params, centroid, count); + } + + static XContentBuilder renderXContent(XContentBuilder builder, Params params, GeoPoint centroid, long count) throws IOException { if (centroid != null) { - builder.startObject(Fields.CENTROID).field("lat", centroid.lat()).field("lon", centroid.lon()).endObject(); + builder.startObject(Fields.CENTROID.getPreferredName()); + { + builder.field(Fields.CENTROID_LAT.getPreferredName(), centroid.lat()); + builder.field(Fields.CENTROID_LON.getPreferredName(), centroid.lon()); + } + builder.endObject(); } + builder.field(Fields.COUNT.getPreferredName(), count); return builder; } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/GeoCentroidIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/GeoCentroidIT.java index 6f2c979936f66..32b036606d399 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/GeoCentroidIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/GeoCentroidIT.java @@ -59,6 +59,7 @@ public void testEmptyAggregation() throws Exception { assertThat(geoCentroid.getName(), equalTo(aggName)); GeoPoint centroid = geoCentroid.centroid(); assertThat(centroid, equalTo(null)); + assertEquals(0, geoCentroid.count()); } public void testUnmapped() throws Exception { @@ -72,6 +73,7 @@ public void testUnmapped() throws Exception { assertThat(geoCentroid.getName(), equalTo(aggName)); GeoPoint centroid = geoCentroid.centroid(); assertThat(centroid, equalTo(null)); + assertEquals(0, geoCentroid.count()); } public void testPartiallyUnmapped() throws Exception { @@ -86,6 +88,7 @@ public void testPartiallyUnmapped() throws Exception { GeoPoint centroid = geoCentroid.centroid(); assertThat(centroid.lat(), closeTo(singleCentroid.lat(), GEOHASH_TOLERANCE)); assertThat(centroid.lon(), closeTo(singleCentroid.lon(), GEOHASH_TOLERANCE)); + assertEquals(numDocs, geoCentroid.count()); } public void testSingleValuedField() throws Exception { @@ -101,6 +104,7 @@ public void testSingleValuedField() throws Exception { GeoPoint centroid = geoCentroid.centroid(); assertThat(centroid.lat(), closeTo(singleCentroid.lat(), GEOHASH_TOLERANCE)); assertThat(centroid.lon(), closeTo(singleCentroid.lon(), GEOHASH_TOLERANCE)); + assertEquals(numDocs, geoCentroid.count()); } public void testSingleValueFieldGetProperty() throws Exception { @@ -130,6 +134,7 @@ public void testSingleValueFieldGetProperty() throws Exception { closeTo(singleCentroid.lon(), GEOHASH_TOLERANCE)); assertThat((double) ((InternalAggregation)global).getProperty(aggName + ".lat"), closeTo(singleCentroid.lat(), GEOHASH_TOLERANCE)); assertThat((double) ((InternalAggregation)global).getProperty(aggName + ".lon"), closeTo(singleCentroid.lon(), GEOHASH_TOLERANCE)); + assertEquals(numDocs, (long) ((InternalAggregation) global).getProperty(aggName + ".count")); } public void testMultiValuedField() throws Exception { @@ -145,6 +150,7 @@ public void testMultiValuedField() throws Exception { GeoPoint centroid = geoCentroid.centroid(); assertThat(centroid.lat(), closeTo(multiCentroid.lat(), GEOHASH_TOLERANCE)); assertThat(centroid.lon(), closeTo(multiCentroid.lon(), GEOHASH_TOLERANCE)); + assertEquals(2 * numDocs, geoCentroid.count()); } public void testSingleValueFieldAsSubAggToGeohashGrid() throws Exception { diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java index c409d2aa7950e..3bbe1a1b462af 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java @@ -42,8 +42,11 @@ protected InternalGeoCentroid createTestInstance(String name, List instanceReader() { @Override protected void assertReduced(InternalGeoCentroid reduced, List inputs) { - GeoPoint expected = new GeoPoint(0, 0); - int i = 0; + double lonSum = 0; + double latSum = 0; + int totalCount = 0; for (InternalGeoCentroid input : inputs) { - expected.reset(expected.lat() + (input.centroid().lat() - expected.lat()) / (i+1), - expected.lon() + (input.centroid().lon() - expected.lon()) / (i+1)); - i++; + if (input.count() > 0) { + lonSum += (input.count() * input.centroid().getLon()); + latSum += (input.count() * input.centroid().getLat()); + } + totalCount += input.count(); } - assertEquals(expected.getLat(), reduced.centroid().getLat(), 1E-5D); - assertEquals(expected.getLon(), reduced.centroid().getLon(), 1E-5D); + assertEquals(latSum/totalCount, reduced.centroid().getLat(), 1E-5D); + assertEquals(lonSum/totalCount, reduced.centroid().getLon(), 1E-5D); + assertEquals(totalCount, reduced.count()); } } diff --git a/docs/reference/aggregations/metrics/geocentroid-aggregation.asciidoc b/docs/reference/aggregations/metrics/geocentroid-aggregation.asciidoc index 4756ebdafc88d..7a355c7b12605 100644 --- a/docs/reference/aggregations/metrics/geocentroid-aggregation.asciidoc +++ b/docs/reference/aggregations/metrics/geocentroid-aggregation.asciidoc @@ -62,7 +62,8 @@ The response for the above aggregation: "location": { "lat": 51.00982963107526, "lon": 3.9662130922079086 - } + }, + "count": 6 } } } @@ -114,7 +115,8 @@ The response for the above aggregation: "location": { "lat": 52.371655656024814, "lon": 4.909563297405839 - } + }, + "count": 3 } }, { @@ -124,7 +126,8 @@ The response for the above aggregation: "location": { "lat": 48.86055548675358, "lon": 2.3316944623366 - } + }, + "count": 2 } }, { @@ -134,7 +137,8 @@ The response for the above aggregation: "location": { "lat": 51.22289997059852, "lon": 4.40519998781383 - } + }, + "count": 1 } } ] From eb002340d7786bb2da54e3b419eefa69ce47a524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Fri, 28 Apr 2017 16:55:01 +0200 Subject: [PATCH 126/619] Don't render InternalGeoCentroid in static helper method This was introduced by a previous commit but is not necessary. --- .../aggregations/metrics/geocentroid/InternalGeoCentroid.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java index c5578813c84c5..accf210345d77 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java @@ -156,10 +156,6 @@ static class Fields { @Override public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { - return renderXContent(builder, params, centroid, count); - } - - static XContentBuilder renderXContent(XContentBuilder builder, Params params, GeoPoint centroid, long count) throws IOException { if (centroid != null) { builder.startObject(Fields.CENTROID.getPreferredName()); { From 4d14143ac637bf11764723319bb37299b2e29ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Fri, 28 Apr 2017 17:08:48 +0200 Subject: [PATCH 127/619] Add parsing for InternalGeoCentroid (#24371) --- .../geocentroid/InternalGeoCentroid.java | 6 +- .../geocentroid/ParsedGeoCentroid.java | 87 +++++++++++++++++++ .../InternalAggregationTestCase.java | 3 + .../geocentroid/InternalGeoCentroidTests.java | 10 +++ 4 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/ParsedGeoCentroid.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java index c5578813c84c5..b8d317ff787de 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroid.java @@ -149,17 +149,13 @@ public Object getProperty(List path) { static class Fields { static final ParseField CENTROID = new ParseField("location"); + static final ParseField COUNT = new ParseField("count"); static final ParseField CENTROID_LAT = new ParseField("lat"); static final ParseField CENTROID_LON = new ParseField("lon"); - static final ParseField COUNT = new ParseField("count"); } @Override public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { - return renderXContent(builder, params, centroid, count); - } - - static XContentBuilder renderXContent(XContentBuilder builder, Params params, GeoPoint centroid, long count) throws IOException { if (centroid != null) { builder.startObject(Fields.CENTROID.getPreferredName()); { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/ParsedGeoCentroid.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/ParsedGeoCentroid.java new file mode 100644 index 0000000000000..ed09c281868d4 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/ParsedGeoCentroid.java @@ -0,0 +1,87 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.geocentroid; + +import org.elasticsearch.common.geo.GeoPoint; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.ParsedAggregation; +import org.elasticsearch.search.aggregations.metrics.geocentroid.InternalGeoCentroid.Fields; + +import java.io.IOException; + +/** + * Serialization and merge logic for {@link GeoCentroidAggregator}. + */ +public class ParsedGeoCentroid extends ParsedAggregation implements GeoCentroid { + private GeoPoint centroid; + private long count; + + @Override + public GeoPoint centroid() { + return centroid; + } + + @Override + public long count() { + return count; + } + + @Override + protected String getType() { + return GeoCentroidAggregationBuilder.NAME; + } + + @Override + public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + if (centroid != null) { + builder.startObject(Fields.CENTROID.getPreferredName()); + { + builder.field(Fields.CENTROID_LAT.getPreferredName(), centroid.lat()); + builder.field(Fields.CENTROID_LON.getPreferredName(), centroid.lon()); + } + builder.endObject(); + } + builder.field(Fields.COUNT.getPreferredName(), count); + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedGeoCentroid.class.getSimpleName(), true, + ParsedGeoCentroid::new); + + private static final ObjectParser GEO_POINT_PARSER = new ObjectParser<>( + ParsedGeoCentroid.class.getSimpleName() + "_POINT", true, GeoPoint::new); + + static { + declareAggregationFields(PARSER); + PARSER.declareObject((agg, centroid) -> agg.centroid = centroid, GEO_POINT_PARSER, Fields.CENTROID); + PARSER.declareLong((agg, count) -> agg.count = count, Fields.COUNT); + + GEO_POINT_PARSER.declareDouble(GeoPoint::resetLat, Fields.CENTROID_LAT); + GEO_POINT_PARSER.declareDouble(GeoPoint::resetLon, Fields.CENTROID_LON); + } + + public static ParsedGeoCentroid fromXContent(XContentParser parser, final String name) { + ParsedGeoCentroid geoCentroid = PARSER.apply(parser, null); + geoCentroid.setName(name); + return geoCentroid; + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index 664a2b756df6e..e99ad8da131fb 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -40,6 +40,8 @@ import org.elasticsearch.search.aggregations.metrics.cardinality.ParsedCardinality; import org.elasticsearch.search.aggregations.metrics.geobounds.GeoBoundsAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.geobounds.ParsedGeoBounds; +import org.elasticsearch.search.aggregations.metrics.geocentroid.GeoCentroidAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.geocentroid.ParsedGeoCentroid; import org.elasticsearch.search.aggregations.metrics.max.MaxAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.max.ParsedMax; import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder; @@ -118,6 +120,7 @@ static List getNamedXContents() { namedXContents.put(ExtendedStatsBucketPipelineAggregationBuilder.NAME, (p, c) -> ParsedExtendedStatsBucket.fromXContent(p, (String) c)); namedXContents.put(GeoBoundsAggregationBuilder.NAME, (p, c) -> ParsedGeoBounds.fromXContent(p, (String) c)); + namedXContents.put(GeoCentroidAggregationBuilder.NAME, (p, c) -> ParsedGeoCentroid.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java index 3bbe1a1b462af..31da19bb91575 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.test.geo.RandomGeoGenerator; @@ -70,4 +71,13 @@ protected void assertReduced(InternalGeoCentroid reduced, List Date: Fri, 28 Apr 2017 17:12:38 +0200 Subject: [PATCH 128/619] Add missing link for the WordDelimiterGraphFilter --- docs/reference/analysis/tokenfilters.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference/analysis/tokenfilters.asciidoc b/docs/reference/analysis/tokenfilters.asciidoc index eae6ceabed7b4..6e77b4498650d 100644 --- a/docs/reference/analysis/tokenfilters.asciidoc +++ b/docs/reference/analysis/tokenfilters.asciidoc @@ -33,6 +33,8 @@ include::tokenfilters/stop-tokenfilter.asciidoc[] include::tokenfilters/word-delimiter-tokenfilter.asciidoc[] +include::tokenfilters/word-delimiter-graph-tokenfilter.asciidoc[] + include::tokenfilters/stemmer-tokenfilter.asciidoc[] include::tokenfilters/stemmer-override-tokenfilter.asciidoc[] From 55daf743d7af32ce1fba3dcfd3733e9615f8fb64 Mon Sep 17 00:00:00 2001 From: olcbean Date: Fri, 28 Apr 2017 17:41:05 +0200 Subject: [PATCH 129/619] Open and close index to honour allow_no_indices option (#24222) Open/close index API when executed providing an index expressions that matched no indices, threw an error even when allow_no_indices was set to true. The APIs should rather honour the option and behave as a no-op in that case. Closes #24031 --- .../close/TransportCloseIndexAction.java | 5 +++ .../open/TransportOpenIndexAction.java | 5 +++ .../indices/state/OpenCloseIndexIT.java | 35 +++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/close/TransportCloseIndexAction.java b/core/src/main/java/org/elasticsearch/action/admin/indices/close/TransportCloseIndexAction.java index d33f37defec1e..244b8a24b9b67 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/close/TransportCloseIndexAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/close/TransportCloseIndexAction.java @@ -22,6 +22,7 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.open.OpenIndexResponse; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.DestructiveOperations; import org.elasticsearch.action.support.master.TransportMasterNodeAction; @@ -97,6 +98,10 @@ protected ClusterBlockException checkBlock(CloseIndexRequest request, ClusterSta @Override protected void masterOperation(final CloseIndexRequest request, final ClusterState state, final ActionListener listener) { final Index[] concreteIndices = indexNameExpressionResolver.concreteIndices(state, request); + if (concreteIndices == null || concreteIndices.length == 0) { + listener.onResponse(new CloseIndexResponse(true)); + return; + } CloseIndexClusterStateUpdateRequest updateRequest = new CloseIndexClusterStateUpdateRequest() .ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout()) .indices(concreteIndices); diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/open/TransportOpenIndexAction.java b/core/src/main/java/org/elasticsearch/action/admin/indices/open/TransportOpenIndexAction.java index 1128ebf9875fd..451b9a280be68 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/open/TransportOpenIndexAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/open/TransportOpenIndexAction.java @@ -22,6 +22,7 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.DestructiveOperations; import org.elasticsearch.action.support.master.TransportMasterNodeAction; @@ -82,6 +83,10 @@ protected ClusterBlockException checkBlock(OpenIndexRequest request, ClusterStat @Override protected void masterOperation(final OpenIndexRequest request, final ClusterState state, final ActionListener listener) { final Index[] concreteIndices = indexNameExpressionResolver.concreteIndices(state, request); + if (concreteIndices == null || concreteIndices.length == 0) { + listener.onResponse(new OpenIndexResponse(true)); + return; + } OpenIndexClusterStateUpdateRequest updateRequest = new OpenIndexClusterStateUpdateRequest() .ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout()) .indices(concreteIndices); diff --git a/core/src/test/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java b/core/src/test/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java index b9eb0abbb7550..e94cdd75acb88 100644 --- a/core/src/test/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java +++ b/core/src/test/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java @@ -190,6 +190,41 @@ public void testCloseOpenAllWildcard() { assertThat(openIndexResponse.isAcknowledged(), equalTo(true)); assertIndexIsOpened("test1", "test2", "test3"); } + + // if there are no indices to open/close throw an exception + public void testOpenCloseWildCardsNoIndicesDefault() { + expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareOpen("test").execute().actionGet()); + expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareClose("test").execute().actionGet()); + + expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareOpen("test*").execute().actionGet()); + expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareClose("test*").execute().actionGet()); + + expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareOpen("*").execute().actionGet()); + expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareClose("*").execute().actionGet()); + + expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareOpen("_all").execute().actionGet()); + expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareClose("_all").execute().actionGet()); + } + + // if there are no indices to open/close and allow_no_indices=true, the open/close is a no-op + public void testOpenCloseWildCardsNoIndicesAllowNoIndices() throws InterruptedException, ExecutionException { + IndicesOptions openIndicesOptions = IndicesOptions.fromOptions(false, true, false, true); + IndicesOptions closeIndicesOptions = IndicesOptions.fromOptions(false, true, true, false); + + expectThrows(IndexNotFoundException.class, + () -> client().admin().indices().prepareOpen("test").setIndicesOptions(openIndicesOptions).execute().actionGet()); + expectThrows(IndexNotFoundException.class, + () -> client().admin().indices().prepareClose("test").setIndicesOptions(closeIndicesOptions).execute().actionGet()); + + assertAcked(client().admin().indices().prepareOpen("test*").setIndicesOptions(openIndicesOptions).execute().get()); + assertAcked(client().admin().indices().prepareClose("test*").setIndicesOptions(closeIndicesOptions).execute().get()); + + assertAcked(client().admin().indices().prepareOpen("*").setIndicesOptions(openIndicesOptions).execute().get()); + assertAcked(client().admin().indices().prepareClose("*").setIndicesOptions(closeIndicesOptions).execute().get()); + + assertAcked(client().admin().indices().prepareOpen("_all").setIndicesOptions(openIndicesOptions).execute().get()); + assertAcked(client().admin().indices().prepareClose("_all").setIndicesOptions(closeIndicesOptions).execute().get()); + } public void testCloseNoIndex() { Client client = client(); From 9c55bca8fbe3c2a37693b2d3c2db7bb326a890de Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Fri, 28 Apr 2017 19:52:27 +0200 Subject: [PATCH 130/619] Fix node failure detection race when updating cluster state Failure detection should only be updated in ZenDiscovery after the current state has been updated to prevent a race condition with handleLeaveRequest and handleNodeFailure as those check the current state to determine whether the failure is to be handled by this node. --- .../discovery/zen/ZenDiscovery.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java index f4bef4b2d91c5..af32afb99c984 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java @@ -775,16 +775,6 @@ boolean processNextCommittedClusterState(String reason) { return false; } - if (newClusterState.nodes().isLocalNodeElectedMaster()) { - // update the set of nodes to ping - nodesFD.updateNodesAndPing(newClusterState); - } else { - // check to see that we monitor the correct master of the cluster - if (masterFD.masterNode() == null || !masterFD.masterNode().equals(newClusterState.nodes().getMasterNode())) { - masterFD.restart(newClusterState.nodes().getMasterNode(), "new cluster state received and we are monitoring the wrong master [" + masterFD.masterNode() + "]"); - } - } - if (currentState.blocks().hasGlobalBlock(discoverySettings.getNoMasterBlock())) { // its a fresh update from the master as we transition from a start of not having a master to having one logger.debug("got first state from fresh master [{}]", newClusterState.nodes().getMasterNodeId()); @@ -827,6 +817,19 @@ boolean processNextCommittedClusterState(String reason) { state.set(adaptedNewClusterState); + // update failure detection only after the state has been updated to prevent race condition with handleLeaveRequest + // and handleNodeFailure as those check the current state to determine whether the failure is to be handled by this node + if (adaptedNewClusterState.nodes().isLocalNodeElectedMaster()) { + // update the set of nodes to ping + nodesFD.updateNodesAndPing(adaptedNewClusterState); + } else { + // check to see that we monitor the correct master of the cluster + if (masterFD.masterNode() == null || !masterFD.masterNode().equals(adaptedNewClusterState.nodes().getMasterNode())) { + masterFD.restart(adaptedNewClusterState.nodes().getMasterNode(), + "new cluster state received and we are monitoring the wrong master [" + masterFD.masterNode() + "]"); + } + } + clusterApplier.onNewClusterState("apply cluster state (from master [" + reason + "])", this::clusterState, new ClusterStateTaskListener() { From 86aab98fde8dc4be8843ddb79216061aca0da406 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Fri, 28 Apr 2017 20:42:56 +0200 Subject: [PATCH 131/619] [TEST] Fix race condition in ZenDiscoveryIT.testDiscoveryStats With #24236, the master now uses the pending queue when publishing to itself. This means that a cluster state update is put into the pending queue, then sent to the ClusterApplierService to be applied. After it has been applied, it is marked as processed and removed from the pending queue. ensureGreen is implemented as a cluster health action that waits on certain conditions, which will lead to a cluster state update task to be submitted on the master. When this task gets to run and the conditions are not satisfied yet, it register a cluster state observer. This observer is registered on the ClusterApplierService and waits on cluster state change events. ClusterApplierService first notifies the observer and then the discovery layer. This means that there is a small time frame where ensureGreen can complete and call the node stats to find the pending queue still containing the last cluster state update. Closes #24388 --- .../java/org/elasticsearch/discovery/zen/ZenDiscoveryIT.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryIT.java b/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryIT.java index 7ac463616fcf2..7821d4fd944fc 100644 --- a/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryIT.java +++ b/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryIT.java @@ -248,7 +248,7 @@ public EnumSet context() { } } - public void testDiscoveryStats() throws IOException { + public void testDiscoveryStats() throws Exception { String expectedStatsJsonResponse = "{\n" + " \"discovery\" : {\n" + " \"cluster_state_queue\" : {\n" + @@ -261,6 +261,9 @@ public void testDiscoveryStats() throws IOException { internalCluster().startNode(); ensureGreen(); // ensures that all events are processed (in particular state recovery fully completed) + assertBusy(() -> + assertThat(internalCluster().clusterService(internalCluster().getMasterName()).getMasterService().numberOfPendingTasks(), + equalTo(0))); // see https://github.com/elastic/elasticsearch/issues/24388 logger.info("--> request node discovery stats"); NodesStatsResponse statsResponse = client().admin().cluster().prepareNodesStats().clear().setDiscovery(true).get(); From a0f4cde905201185857c453c95e32dccd7f86196 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Sun, 30 Apr 2017 12:11:57 -0400 Subject: [PATCH 132/619] Adjust checkstyle suppressions to 140-column limit We are back to having a 140-column limit. While at some point we will apply auto-formatting tools to the code base, that is a down-the-road thing and adjusting the suppressions now shaves minutes off the build time. Relates #24408 --- .../resources/checkstyle_suppressions.xml | 3346 +---------------- 1 file changed, 17 insertions(+), 3329 deletions(-) diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 2ed1b68fb8ac2..72b96d3db2be2 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -15,575 +15,163 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -593,1826 +181,302 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2425,68 +489,34 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2496,8 +526,6 @@ - - @@ -2506,438 +534,127 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2946,1181 +663,152 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From e339d894c5cf6928c92b90a0480ba8535f0bae1e Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Sun, 30 Apr 2017 12:15:54 -0400 Subject: [PATCH 133/619] Remove errant entry in checkstyle suppressions The list of checkstyle suppressions was auto-generated based on the list of files that currently violate the 140-column limit. An errant entry was committed that did no harm, but this commit removes it. --- buildSrc/src/main/resources/checkstyle_suppressions.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 72b96d3db2be2..af6a2cdbf56e5 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -15,7 +15,6 @@ - From 9525c1c4fa1cbdc03fdd807868245a84167bd5ca Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Sun, 30 Apr 2017 12:43:04 -0400 Subject: [PATCH 134/619] Remove duplicates from checkstyle suppressions This commit removes a few duplicates from the list of checkstyle suppressions that accumulated while auto-generating the list of suppressions based on the files that currently exceed the 140-column limit. --- .../resources/checkstyle_suppressions.xml | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index af6a2cdbf56e5..5deb52e53fdba 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -15,6 +15,7 @@ + @@ -752,33 +753,23 @@ - + + + + - - - - - - - - - - - - - @@ -786,10 +777,12 @@ + + From 0fef5acd01c094fa29c0c27fe287b5f73bdb67eb Mon Sep 17 00:00:00 2001 From: Koen De Groote Date: Mon, 1 May 2017 03:26:51 +0200 Subject: [PATCH 135/619] Cleanup collections construction This commit cleans up some cases where a list or map was being constructed, and then an existing collection was copied into the new collection. The clean is to instead use an appropriate constructor to directly copy the existing collection in during collection construction. The advantage of this is that the new collection is sized appropriately. Relates #24409 --- .../action/fieldstats/FieldStatsShardRequest.java | 3 +-- .../elasticsearch/client/transport/TransportClient.java | 9 +++------ .../client/transport/TransportClientNodesService.java | 3 +-- .../java/org/elasticsearch/cluster/DiffableUtils.java | 3 +-- .../org/elasticsearch/common/blobstore/BlobPath.java | 3 +-- .../org/elasticsearch/index/mapper/MapperService.java | 3 +-- core/src/main/java/org/elasticsearch/node/Node.java | 6 ++---- .../rest/action/cat/RestThreadPoolAction.java | 3 +-- .../java/org/elasticsearch/snapshots/SnapshotUtils.java | 6 ++---- .../index/analysis/phonetic/KoelnerPhonetik.java | 3 +-- .../java/org/elasticsearch/script/MockScriptEngine.java | 3 +-- 11 files changed, 15 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStatsShardRequest.java b/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStatsShardRequest.java index 3844895bc2459..b393a3789cf51 100644 --- a/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStatsShardRequest.java +++ b/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStatsShardRequest.java @@ -39,8 +39,7 @@ public FieldStatsShardRequest() { public FieldStatsShardRequest(ShardId shardId, FieldStatsRequest request) { super(shardId, request); - Set fields = new HashSet<>(); - fields.addAll(Arrays.asList(request.getFields())); + Set fields = new HashSet<>(Arrays.asList(request.getFields())); for (IndexConstraint indexConstraint : request.getIndexConstraints()) { fields.add(indexConstraint.getField()); } diff --git a/core/src/main/java/org/elasticsearch/client/transport/TransportClient.java b/core/src/main/java/org/elasticsearch/client/transport/TransportClient.java index 01fdf98ceb427..4942edfc6448f 100644 --- a/core/src/main/java/org/elasticsearch/client/transport/TransportClient.java +++ b/core/src/main/java/org/elasticsearch/client/transport/TransportClient.java @@ -129,10 +129,8 @@ private static ClientTemplate buildTemplate(Settings providedSettings, Settings resourcesToClose.add(() -> ThreadPool.terminate(threadPool, 10, TimeUnit.SECONDS)); final NetworkService networkService = new NetworkService(settings, Collections.emptyList()); try { - final List> additionalSettings = new ArrayList<>(); - final List additionalSettingsFilter = new ArrayList<>(); - additionalSettings.addAll(pluginsService.getPluginSettings()); - additionalSettingsFilter.addAll(pluginsService.getPluginSettingsFilter()); + final List> additionalSettings = new ArrayList<>(pluginsService.getPluginSettings()); + final List additionalSettingsFilter = new ArrayList<>(pluginsService.getPluginSettingsFilter()); for (final ExecutorBuilder builder : threadPool.builders()) { additionalSettings.addAll(builder.getRegisteredSettings()); } @@ -194,8 +192,7 @@ private static ClientTemplate buildTemplate(Settings providedSettings, Settings final TransportProxyClient proxy = new TransportProxyClient(settings, transportService, nodesService, actionModule.getActions().values().stream().map(x -> x.getAction()).collect(Collectors.toList())); - List pluginLifecycleComponents = new ArrayList<>(); - pluginLifecycleComponents.addAll(pluginsService.getGuiceServiceClasses().stream() + List pluginLifecycleComponents = new ArrayList<>(pluginsService.getGuiceServiceClasses().stream() .map(injector::getInstance).collect(Collectors.toList())); resourcesToClose.addAll(pluginLifecycleComponents); diff --git a/core/src/main/java/org/elasticsearch/client/transport/TransportClientNodesService.java b/core/src/main/java/org/elasticsearch/client/transport/TransportClientNodesService.java index dbcf0edef2897..145208baf0db8 100644 --- a/core/src/main/java/org/elasticsearch/client/transport/TransportClientNodesService.java +++ b/core/src/main/java/org/elasticsearch/client/transport/TransportClientNodesService.java @@ -187,8 +187,7 @@ public TransportClientNodesService addTransportAddresses(TransportAddress... tra if (filtered.isEmpty()) { return this; } - List builder = new ArrayList<>(); - builder.addAll(listedNodes()); + List builder = new ArrayList<>(listedNodes); for (TransportAddress transportAddress : filtered) { DiscoveryNode node = new DiscoveryNode("#transport#-" + tempNodeIdGenerator.incrementAndGet(), transportAddress, Collections.emptyMap(), Collections.emptySet(), minCompatibilityVersion); diff --git a/core/src/main/java/org/elasticsearch/cluster/DiffableUtils.java b/core/src/main/java/org/elasticsearch/cluster/DiffableUtils.java index ba769c34d0e16..f7bb42b8dc368 100644 --- a/core/src/main/java/org/elasticsearch/cluster/DiffableUtils.java +++ b/core/src/main/java/org/elasticsearch/cluster/DiffableUtils.java @@ -193,8 +193,7 @@ protected JdkMapDiff(StreamInput in, KeySerializer keySerializer, ValueSerial @Override public Map apply(Map map) { - Map builder = new HashMap<>(); - builder.putAll(map); + Map builder = new HashMap<>(map); for (K part : deletes) { builder.remove(part); diff --git a/core/src/main/java/org/elasticsearch/common/blobstore/BlobPath.java b/core/src/main/java/org/elasticsearch/common/blobstore/BlobPath.java index 0e790c0dc8b76..ea02aebb0aaad 100644 --- a/core/src/main/java/org/elasticsearch/common/blobstore/BlobPath.java +++ b/core/src/main/java/org/elasticsearch/common/blobstore/BlobPath.java @@ -55,8 +55,7 @@ public String[] toArray() { } public BlobPath add(String path) { - List paths = new ArrayList<>(); - paths.addAll(this.paths); + List paths = new ArrayList<>(this.paths); paths.add(path); return new BlobPath(Collections.unmodifiableList(paths)); } diff --git a/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java b/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java index b0761d0df1127..cca285198ef33 100755 --- a/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java @@ -741,8 +741,7 @@ public MappedFieldType unmappedFieldType(String type) { // There is no need to synchronize writes here. In the case of concurrent access, we could just // compute some mappers several times, which is not a big deal - Map newUnmappedFieldTypes = new HashMap<>(); - newUnmappedFieldTypes.putAll(unmappedFieldTypes); + Map newUnmappedFieldTypes = new HashMap<>(unmappedFieldTypes); newUnmappedFieldTypes.put(type, fieldType); unmappedFieldTypes = unmodifiableMap(newUnmappedFieldTypes); } diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index cbb68dc29245e..0b10b16863b07 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -316,10 +316,8 @@ protected Node(final Environment environment, Collection DeprecationLogger.setThreadContext(threadPool.getThreadContext()); resourcesToClose.add(() -> DeprecationLogger.removeThreadContext(threadPool.getThreadContext())); - final List> additionalSettings = new ArrayList<>(); - final List additionalSettingsFilter = new ArrayList<>(); - additionalSettings.addAll(pluginsService.getPluginSettings()); - additionalSettingsFilter.addAll(pluginsService.getPluginSettingsFilter()); + final List> additionalSettings = new ArrayList<>(pluginsService.getPluginSettings()); + final List additionalSettingsFilter = new ArrayList<>(pluginsService.getPluginSettingsFilter()); for (final ExecutorBuilder builder : threadPool.builders()) { additionalSettings.addAll(builder.getRegisteredSettings()); } diff --git a/core/src/main/java/org/elasticsearch/rest/action/cat/RestThreadPoolAction.java b/core/src/main/java/org/elasticsearch/rest/action/cat/RestThreadPoolAction.java index 76e5c40cf424e..74b2a261713e9 100644 --- a/core/src/main/java/org/elasticsearch/rest/action/cat/RestThreadPoolAction.java +++ b/core/src/main/java/org/elasticsearch/rest/action/cat/RestThreadPoolAction.java @@ -95,8 +95,7 @@ public RestResponse buildResponse(NodesStatsResponse nodesStatsResponse) throws private static final Set RESPONSE_PARAMS; static { - final Set responseParams = new HashSet<>(); - responseParams.addAll(AbstractCatAction.RESPONSE_PARAMS); + final Set responseParams = new HashSet<>(AbstractCatAction.RESPONSE_PARAMS); responseParams.add("thread_pool_patterns"); RESPONSE_PARAMS = Collections.unmodifiableSet(responseParams); } diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotUtils.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotUtils.java index 66e9ac88de620..616e5ffecc814 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotUtils.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotUtils.java @@ -82,8 +82,7 @@ public static List filterIndices(List availableIndices, String[] } else { if (result == null) { // add all the previous ones... - result = new HashSet<>(); - result.addAll(availableIndices.subList(0, i)); + result = new HashSet<>(availableIndices.subList(0, i)); } } } else { @@ -99,8 +98,7 @@ public static List filterIndices(List availableIndices, String[] } if (result == null) { // add all the previous ones... - result = new HashSet<>(); - result.addAll(availableIndices.subList(0, i)); + result = new HashSet<>(availableIndices.subList(0, i)); } boolean found = false; for (String index : availableIndices) { diff --git a/plugins/analysis-phonetic/src/main/java/org/elasticsearch/index/analysis/phonetic/KoelnerPhonetik.java b/plugins/analysis-phonetic/src/main/java/org/elasticsearch/index/analysis/phonetic/KoelnerPhonetik.java index 3c658191524db..c7b39a86a2bd7 100644 --- a/plugins/analysis-phonetic/src/main/java/org/elasticsearch/index/analysis/phonetic/KoelnerPhonetik.java +++ b/plugins/analysis-phonetic/src/main/java/org/elasticsearch/index/analysis/phonetic/KoelnerPhonetik.java @@ -142,8 +142,7 @@ private List partition(String str) { List parts = new ArrayList<>(); parts.add(primaryForm.replaceAll("[^\\p{L}\\p{N}]", "")); if (!primary) { - List tmpParts = new ArrayList<>(); - tmpParts.addAll((Arrays.asList(str.split("[\\p{Z}\\p{C}\\p{P}]")))); + List tmpParts = new ArrayList<>(Arrays.asList(str.split("[\\p{Z}\\p{C}\\p{P}]"))); int numberOfParts = tmpParts.size(); while (tmpParts.size() > 0) { StringBuilder part = new StringBuilder(); diff --git a/test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java b/test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java index c2447b4504ee8..62e3d51ad10b4 100644 --- a/test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java +++ b/test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java @@ -184,8 +184,7 @@ public MockSearchScript(SearchLookup lookup, Map vars, Function< public LeafSearchScript getLeafSearchScript(LeafReaderContext context) throws IOException { LeafSearchLookup leafLookup = lookup.getLeafSearchLookup(context); - Map ctx = new HashMap<>(); - ctx.putAll(leafLookup.asMap()); + Map ctx = new HashMap<>(leafLookup.asMap()); if (vars != null) { ctx.putAll(vars); } From 7863407b46b200182d5212011c28198ecda3bd3d Mon Sep 17 00:00:00 2001 From: javanna Date: Mon, 1 May 2017 14:36:23 +0200 Subject: [PATCH 136/619] [TEST] fix _cat/allocation index size check The check expected the size of the index to always be returned in bytes, but that can possibly be kb, mb, gb and tb depending on the actual size. --- .../rest-api-spec/test/cat.allocation/10_basic.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.allocation/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.allocation/10_basic.yaml index 5f0646139b528..bdb328fad79ca 100755 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.allocation/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.allocation/10_basic.yaml @@ -54,7 +54,7 @@ /^ ( \s* #allow leading spaces to account for right-justified text \d+ \s+ - \d+(\.\d+)?b \s+ + \d+(\.\d+)?[kmgt]?b \s+ \d+(\.\d+)?[kmgt]?b \s+ (\d+(\.\d+)?[kmgt]b \s+) #always should return value since we filter out non data nodes by default (\d+(\.\d+)?[kmgt]b \s+) #always should return value since we filter out non data nodes by default @@ -83,7 +83,7 @@ $body: | /^ ( 0 \s+ - 0b \s+ + \d+(\.\d+)?[kmgt]?b \s+ \d+(\.\d+)?[kmgt]?b \s+ (\d+(\.\d+)?[kmgt]b \s+)? #no value from client nodes (\d+(\.\d+)?[kmgt]b \s+)? #no value from client nodes @@ -117,7 +117,7 @@ /^ ( \s* #allow leading spaces to account for right-justified text \d+ \s+ - \d+(\.\d+)?b \s+ + \d+(\.\d+)?[kmgt]?b \s+ \d+(\.\d+)?[kmgt]?b \s+ (\d+(\.\d+)?[kmgt]b \s+)? #no value from client nodes (\d+(\.\d+)?[kmgt]b \s+)? #no value from client nodes From eefcad94b84cb4d14648f8de83ce8290bdcf8220 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 1 May 2017 10:25:32 -0400 Subject: [PATCH 137/619] Upgrade Netty to 4.1.10.Final This commit upgrades the Netty dependency from version 4.1.9.Final to version 4.1.10.Final. Relates #24414 --- modules/transport-netty4/build.gradle | 18 +++++++++++------- .../netty-buffer-4.1.10.Final.jar.sha1 | 1 + .../licenses/netty-buffer-4.1.9.Final.jar.sha1 | 1 - .../licenses/netty-codec-4.1.10.Final.jar.sha1 | 1 + .../licenses/netty-codec-4.1.9.Final.jar.sha1 | 1 - .../netty-codec-http-4.1.10.Final.jar.sha1 | 1 + .../netty-codec-http-4.1.9.Final.jar.sha1 | 1 - .../netty-common-4.1.10.Final.jar.sha1 | 1 + .../licenses/netty-common-4.1.9.Final.jar.sha1 | 1 - .../netty-handler-4.1.10.Final.jar.sha1 | 1 + .../netty-handler-4.1.9.Final.jar.sha1 | 1 - .../netty-resolver-4.1.10.Final.jar.sha1 | 1 + .../netty-resolver-4.1.9.Final.jar.sha1 | 1 - .../netty-transport-4.1.10.Final.jar.sha1 | 1 + .../netty-transport-4.1.9.Final.jar.sha1 | 1 - .../plugin-metadata/plugin-security.policy | 4 ++-- 16 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 modules/transport-netty4/licenses/netty-buffer-4.1.10.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-buffer-4.1.9.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-4.1.10.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-4.1.9.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-http-4.1.10.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-http-4.1.9.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-common-4.1.10.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-common-4.1.9.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-handler-4.1.10.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-handler-4.1.9.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-resolver-4.1.10.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-resolver-4.1.9.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-transport-4.1.10.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-transport-4.1.9.Final.jar.sha1 diff --git a/modules/transport-netty4/build.gradle b/modules/transport-netty4/build.gradle index f671bc5ff66f4..6bc64a178d923 100644 --- a/modules/transport-netty4/build.gradle +++ b/modules/transport-netty4/build.gradle @@ -34,13 +34,13 @@ compileTestJava.options.compilerArgs << "-Xlint:-cast,-deprecation,-rawtypes,-tr dependencies { // network stack - compile "io.netty:netty-buffer:4.1.9.Final" - compile "io.netty:netty-codec:4.1.9.Final" - compile "io.netty:netty-codec-http:4.1.9.Final" - compile "io.netty:netty-common:4.1.9.Final" - compile "io.netty:netty-handler:4.1.9.Final" - compile "io.netty:netty-resolver:4.1.9.Final" - compile "io.netty:netty-transport:4.1.9.Final" + compile "io.netty:netty-buffer:4.1.10.Final" + compile "io.netty:netty-codec:4.1.10.Final" + compile "io.netty:netty-codec-http:4.1.10.Final" + compile "io.netty:netty-common:4.1.10.Final" + compile "io.netty:netty-handler:4.1.10.Final" + compile "io.netty:netty-resolver:4.1.10.Final" + compile "io.netty:netty-transport:4.1.10.Final" } dependencyLicenses { @@ -121,6 +121,7 @@ thirdPartyAudit.excludes = [ 'io.netty.internal.tcnative.CertificateRequestedCallback$KeyMaterial', 'io.netty.internal.tcnative.CertificateVerifier', 'io.netty.internal.tcnative.SessionTicketKey', + 'io.netty.internal.tcnative.SniHostNameMatcher', 'org.eclipse.jetty.alpn.ALPN$ClientProvider', 'org.eclipse.jetty.alpn.ALPN$ServerProvider', 'org.eclipse.jetty.alpn.ALPN', @@ -143,4 +144,7 @@ thirdPartyAudit.excludes = [ 'io.netty.util.internal.shaded.org.jctools.util.JvmInfo', 'io.netty.util.internal.shaded.org.jctools.util.UnsafeAccess', 'io.netty.util.internal.shaded.org.jctools.util.UnsafeRefArrayAccess', + + 'org.conscrypt.Conscrypt$Engines', + 'org.conscrypt.HandshakeListener' ] diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.10.Final.jar.sha1 new file mode 100644 index 0000000000000..147710bd3b438 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-buffer-4.1.10.Final.jar.sha1 @@ -0,0 +1 @@ +62b25bb39c22f5a4c267b9ff1d9915f8c0191c3a \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.9.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.9.Final.jar.sha1 deleted file mode 100644 index 854be4c34454f..0000000000000 --- a/modules/transport-netty4/licenses/netty-buffer-4.1.9.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -6ec75e26374569ec0120c63c50fb930c48270e0d \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.10.Final.jar.sha1 new file mode 100644 index 0000000000000..81bdd2f885483 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-4.1.10.Final.jar.sha1 @@ -0,0 +1 @@ +1fd4a9d3a8d1ded1e1620ce16ae4afe0b3746c41 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.9.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.9.Final.jar.sha1 deleted file mode 100644 index 8a8de39f55e6d..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-4.1.9.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f41b5fdaaaf7b1e8de4e05be5f5b540f3b205959 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.10.Final.jar.sha1 new file mode 100644 index 0000000000000..e25988ddfa3f6 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-http-4.1.10.Final.jar.sha1 @@ -0,0 +1 @@ +38cbc4d4a0d40dd6f94ff722d76f6f3ea07a6ca6 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.9.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.9.Final.jar.sha1 deleted file mode 100644 index 650fdc87fcee6..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-http-4.1.9.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -efb68f8ce201d180fdbbec8ade5e25684cae12bc \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.10.Final.jar.sha1 new file mode 100644 index 0000000000000..25de3864866cd --- /dev/null +++ b/modules/transport-netty4/licenses/netty-common-4.1.10.Final.jar.sha1 @@ -0,0 +1 @@ +93eba0a663a990b8350c0949870b0db29d4d4f38 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.9.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.9.Final.jar.sha1 deleted file mode 100644 index 8caab14c0cf89..0000000000000 --- a/modules/transport-netty4/licenses/netty-common-4.1.9.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -7c6ba17f3b9e08fbbfc000fa3ce46208446a0019 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.10.Final.jar.sha1 new file mode 100644 index 0000000000000..22316debbe78b --- /dev/null +++ b/modules/transport-netty4/licenses/netty-handler-4.1.10.Final.jar.sha1 @@ -0,0 +1 @@ +c984b5d37563f15f10a0de0f4927d4a0dcb675de \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.9.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.9.Final.jar.sha1 deleted file mode 100644 index 97609dafe6b4d..0000000000000 --- a/modules/transport-netty4/licenses/netty-handler-4.1.9.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e8caa6945995ccd03b232d6b31ee698668b47a1f \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.10.Final.jar.sha1 new file mode 100644 index 0000000000000..8c8a275ce0540 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-resolver-4.1.10.Final.jar.sha1 @@ -0,0 +1 @@ +dd05aa176779768dde2562283b0df3c39b92767b \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.9.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.9.Final.jar.sha1 deleted file mode 100644 index fd603aba4c6e2..0000000000000 --- a/modules/transport-netty4/licenses/netty-resolver-4.1.9.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -9e1f26ad1988fcbe0fc824940dfe6de9d2f438da \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.10.Final.jar.sha1 new file mode 100644 index 0000000000000..fca9fad19622e --- /dev/null +++ b/modules/transport-netty4/licenses/netty-transport-4.1.10.Final.jar.sha1 @@ -0,0 +1 @@ +7a2be072e9962c751f90307379c2edb86d0b61a7 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.9.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.9.Final.jar.sha1 deleted file mode 100644 index 38dca13ff9964..0000000000000 --- a/modules/transport-netty4/licenses/netty-transport-4.1.9.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -aaeed88d6a5c0bfc8d7a78225f4f093544470f5a \ No newline at end of file diff --git a/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy b/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy index 8853d2ac69d98..490e6254cd25b 100644 --- a/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy +++ b/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy @@ -17,7 +17,7 @@ * under the License. */ -grant codeBase "${codebase.netty-common-4.1.9.Final.jar}" { +grant codeBase "${codebase.netty-common-4.1.10.Final.jar}" { // for reading the system-wide configuration for the backlog of established sockets permission java.io.FilePermission "/proc/sys/net/core/somaxconn", "read"; @@ -25,7 +25,7 @@ grant codeBase "${codebase.netty-common-4.1.9.Final.jar}" { permission java.net.SocketPermission "*", "accept,connect"; }; -grant codeBase "${codebase.netty-transport-4.1.9.Final.jar}" { +grant codeBase "${codebase.netty-transport-4.1.10.Final.jar}" { // Netty NioEventLoop wants to change this, because of https://bugs.openjdk.java.net/browse/JDK-6427854 // the bug says it only happened rarely, and that its fixed, but apparently it still happens rarely! permission java.util.PropertyPermission "sun.nio.ch.bugLevel", "write"; From 62712bf653f0a71e4e0b4468a55c774e91702f2a Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Mon, 1 May 2017 11:45:43 -0400 Subject: [PATCH 138/619] Test: extra debugging for refresh listeners stats This test failed in CI but didn't give us enough information to debug it: https://elasticsearch-ci.elastic.co/job/elastic+elasticsearch+master+nfs/223/consoleFull This turns on more debugging and make the test fail more quickly if something is obviously wrong. --- .../indices/stats/IndicesStatsTests.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java b/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java index 859e9f6e25d1d..58bf897bd395b 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.action.ShardOperationFailedException; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.support.WriteRequest.RefreshPolicy; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; @@ -30,8 +31,10 @@ import org.elasticsearch.index.engine.SegmentsStats; import org.elasticsearch.index.translog.Translog; import org.elasticsearch.test.ESSingleNodeTestCase; +import org.elasticsearch.test.junit.annotations.TestLogging; import java.util.List; +import java.util.concurrent.TimeUnit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.hamcrest.Matchers.greaterThan; @@ -115,6 +118,7 @@ public void testCommitStats() throws Exception { } } + @TestLogging("_root:debug") public void testRefreshListeners() throws Exception { // Create an index without automatic refreshes createIndex("test", Settings.builder().put("refresh_interval", -1).build()); @@ -124,11 +128,19 @@ public void testRefreshListeners() throws Exception { .setRefreshPolicy(RefreshPolicy.WAIT_UNTIL).execute(); // Wait for the refresh listener to appear in the stats - assertBusy(() -> { - IndicesStatsResponse stats = client().admin().indices().prepareStats("test").clear().setRefresh(true).get(); + long end = System.nanoTime() + TimeUnit.SECONDS.toNanos(10); + while (true) { + IndicesStatsResponse stats = client().admin().indices().prepareStats("test").clear().setRefresh(true).setDocs(true).get(); CommonStats common = stats.getIndices().get("test").getTotal(); - assertEquals(1, common.refresh.getListeners()); - }); + // There shouldn't be a doc. If there is then we did *something* weird. + assertEquals(0, common.docs.getCount()); + if (1 == common.refresh.getListeners()) { + break; + } + if (end - System.nanoTime() < 0) { + fail("didn't get a refresh listener in time: " + Strings.toString(common)); + } + } // Refresh the index and wait for the request to come back client().admin().indices().prepareRefresh("test").get(); From 130f1a56f14ad83c9fa9cdd59ce6f4201e6c26fd Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Mon, 1 May 2017 13:30:51 -0400 Subject: [PATCH 139/619] Re-enable doc testing for Pipeline Aggregations (#24374) * Re-enable doc testing for Pipeline Aggregations Also adds a response + test for movavg pipeline --- docs/build.gradle | 14 - docs/reference/aggregations/pipeline.asciidoc | 1 + .../pipeline/avg-bucket-aggregation.asciidoc | 1 + .../bucket-script-aggregation.asciidoc | 1 + .../bucket-selector-aggregation.asciidoc | 1 + .../cumulative-sum-aggregation.asciidoc | 1 + .../pipeline/derivative-aggregation.asciidoc | 1 + ...extended-stats-bucket-aggregation.asciidoc | 1 + .../pipeline/max-bucket-aggregation.asciidoc | 1 + .../pipeline/min-bucket-aggregation.asciidoc | 1 + .../pipeline/movavg-aggregation.asciidoc | 328 ++++++++++++++---- .../percentiles-bucket-aggregation.asciidoc | 1 + .../pipeline/serial-diff-aggregation.asciidoc | 1 + .../stats-bucket-aggregation.asciidoc | 1 + .../pipeline/sum-bucket-aggregation.asciidoc | 1 + 15 files changed, 275 insertions(+), 80 deletions(-) diff --git a/docs/build.gradle b/docs/build.gradle index 2648fad3cc002..e0504132d6181 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -39,20 +39,6 @@ buildRestTests.expectedUnconvertedCandidates = [ 'reference/aggregations/metrics/scripted-metric-aggregation.asciidoc', 'reference/aggregations/metrics/stats-aggregation.asciidoc', 'reference/aggregations/metrics/tophits-aggregation.asciidoc', - 'reference/aggregations/pipeline.asciidoc', - 'reference/aggregations/pipeline/avg-bucket-aggregation.asciidoc', - 'reference/aggregations/pipeline/bucket-script-aggregation.asciidoc', - 'reference/aggregations/pipeline/bucket-selector-aggregation.asciidoc', - 'reference/aggregations/pipeline/cumulative-sum-aggregation.asciidoc', - 'reference/aggregations/pipeline/derivative-aggregation.asciidoc', - 'reference/aggregations/pipeline/extended-stats-bucket-aggregation.asciidoc', - 'reference/aggregations/pipeline/max-bucket-aggregation.asciidoc', - 'reference/aggregations/pipeline/min-bucket-aggregation.asciidoc', - 'reference/aggregations/pipeline/movavg-aggregation.asciidoc', - 'reference/aggregations/pipeline/percentiles-bucket-aggregation.asciidoc', - 'reference/aggregations/pipeline/serial-diff-aggregation.asciidoc', - 'reference/aggregations/pipeline/stats-bucket-aggregation.asciidoc', - 'reference/aggregations/pipeline/sum-bucket-aggregation.asciidoc', 'reference/cat/snapshots.asciidoc', 'reference/cat/templates.asciidoc', 'reference/cat/thread_pool.asciidoc', diff --git a/docs/reference/aggregations/pipeline.asciidoc b/docs/reference/aggregations/pipeline.asciidoc index c9cd14d62039f..0f3573eaa9ad2 100644 --- a/docs/reference/aggregations/pipeline.asciidoc +++ b/docs/reference/aggregations/pipeline.asciidoc @@ -194,6 +194,7 @@ may be referred to as: --------------- "buckets_path": "my_percentile[99.9]" --------------- +// NOTCONSOLE [[gap-policy]] [float] diff --git a/docs/reference/aggregations/pipeline/avg-bucket-aggregation.asciidoc b/docs/reference/aggregations/pipeline/avg-bucket-aggregation.asciidoc index fbbee906f446e..b1b618ee2b7d1 100644 --- a/docs/reference/aggregations/pipeline/avg-bucket-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/avg-bucket-aggregation.asciidoc @@ -18,6 +18,7 @@ An `avg_bucket` aggregation looks like this in isolation: } } -------------------------------------------------- +// NOTCONSOLE .`avg_bucket` Parameters |=== diff --git a/docs/reference/aggregations/pipeline/bucket-script-aggregation.asciidoc b/docs/reference/aggregations/pipeline/bucket-script-aggregation.asciidoc index 737942bc6e2a4..aff1f8e6f5425 100644 --- a/docs/reference/aggregations/pipeline/bucket-script-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/bucket-script-aggregation.asciidoc @@ -22,6 +22,7 @@ A `bucket_script` aggregation looks like this in isolation: } } -------------------------------------------------- +// NOTCONSOLE <1> Here, `my_var1` is the name of the variable for this buckets path to use in the script, `the_sum` is the path to the metrics to use for that variable. diff --git a/docs/reference/aggregations/pipeline/bucket-selector-aggregation.asciidoc b/docs/reference/aggregations/pipeline/bucket-selector-aggregation.asciidoc index 34018fc9556e3..c8b1d1c85978e 100644 --- a/docs/reference/aggregations/pipeline/bucket-selector-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/bucket-selector-aggregation.asciidoc @@ -27,6 +27,7 @@ A `bucket_selector` aggregation looks like this in isolation: } } -------------------------------------------------- +// NOTCONSOLE <1> Here, `my_var1` is the name of the variable for this buckets path to use in the script, `the_sum` is the path to the metrics to use for that variable. diff --git a/docs/reference/aggregations/pipeline/cumulative-sum-aggregation.asciidoc b/docs/reference/aggregations/pipeline/cumulative-sum-aggregation.asciidoc index 47cbb1b3f3c75..816d4551d9d62 100644 --- a/docs/reference/aggregations/pipeline/cumulative-sum-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/cumulative-sum-aggregation.asciidoc @@ -19,6 +19,7 @@ A `cumulative_sum` aggregation looks like this in isolation: } } -------------------------------------------------- +// NOTCONSOLE .`cumulative_sum` Parameters |=== diff --git a/docs/reference/aggregations/pipeline/derivative-aggregation.asciidoc b/docs/reference/aggregations/pipeline/derivative-aggregation.asciidoc index 7db32caac929d..0e50465829d04 100644 --- a/docs/reference/aggregations/pipeline/derivative-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/derivative-aggregation.asciidoc @@ -17,6 +17,7 @@ A `derivative` aggregation looks like this in isolation: "buckets_path": "the_sum" } -------------------------------------------------- +// NOTCONSOLE .`derivative` Parameters |=== diff --git a/docs/reference/aggregations/pipeline/extended-stats-bucket-aggregation.asciidoc b/docs/reference/aggregations/pipeline/extended-stats-bucket-aggregation.asciidoc index 11d9c2906b90e..a7c1ab6b3dc0b 100644 --- a/docs/reference/aggregations/pipeline/extended-stats-bucket-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/extended-stats-bucket-aggregation.asciidoc @@ -20,6 +20,7 @@ A `extended_stats_bucket` aggregation looks like this in isolation: } } -------------------------------------------------- +// NOTCONSOLE .`extended_stats_bucket` Parameters |=== diff --git a/docs/reference/aggregations/pipeline/max-bucket-aggregation.asciidoc b/docs/reference/aggregations/pipeline/max-bucket-aggregation.asciidoc index 96c2d7e0ee613..3330cfccb87d7 100644 --- a/docs/reference/aggregations/pipeline/max-bucket-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/max-bucket-aggregation.asciidoc @@ -19,6 +19,7 @@ A `max_bucket` aggregation looks like this in isolation: } } -------------------------------------------------- +// NOTCONSOLE .`max_bucket` Parameters |=== diff --git a/docs/reference/aggregations/pipeline/min-bucket-aggregation.asciidoc b/docs/reference/aggregations/pipeline/min-bucket-aggregation.asciidoc index 031354fadb4c6..b170442c0f0e9 100644 --- a/docs/reference/aggregations/pipeline/min-bucket-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/min-bucket-aggregation.asciidoc @@ -19,6 +19,7 @@ A `min_bucket` aggregation looks like this in isolation: } } -------------------------------------------------- +// NOTCONSOLE .`min_bucket` Parameters |=== diff --git a/docs/reference/aggregations/pipeline/movavg-aggregation.asciidoc b/docs/reference/aggregations/pipeline/movavg-aggregation.asciidoc index 9025b7d9c8bb0..58f42d07c2f91 100644 --- a/docs/reference/aggregations/pipeline/movavg-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/movavg-aggregation.asciidoc @@ -34,6 +34,7 @@ A `moving_avg` aggregation looks like this in isolation: } } -------------------------------------------------- +// NOTCONSOLE .`moving_avg` Parameters |=== @@ -58,12 +59,12 @@ POST /_search "aggs": { "my_date_histo":{ <1> "date_histogram":{ - "field":"timestamp", - "interval":"day" + "field":"date", + "interval":"1M" }, "aggs":{ "the_sum":{ - "sum":{ "field": "lemmings" } <2> + "sum":{ "field": "price" } <2> }, "the_movavg":{ "moving_avg":{ "buckets_path": "the_sum" } <3> @@ -74,6 +75,7 @@ POST /_search } -------------------------------------------------- // CONSOLE +// TEST[setup:sales] <1> A `date_histogram` named "my_date_histo" is constructed on the "timestamp" field, with one-day intervals <2> A `sum` metric is used to calculate the sum of a field. This could be any metric (sum, min, max, etc) @@ -84,6 +86,57 @@ add normal metrics, such as a `sum`, inside of that histogram. Finally, the `mo The `buckets_path` parameter is then used to "point" at one of the sibling metrics inside of the histogram (see <> for a description of the syntax for `buckets_path`. +An example response from the above aggregation may look like: + +[source,js] +-------------------------------------------------- +{ + "took": 11, + "timed_out": false, + "_shards": ..., + "hits": ..., + "aggregations": { + "my_date_histo": { + "buckets": [ + { + "key_as_string": "2015/01/01 00:00:00", + "key": 1420070400000, + "doc_count": 3, + "the_sum": { + "value": 550.0 + } + }, + { + "key_as_string": "2015/02/01 00:00:00", + "key": 1422748800000, + "doc_count": 2, + "the_sum": { + "value": 60.0 + }, + "the_movavg": { + "value": 550.0 + } + }, + { + "key_as_string": "2015/03/01 00:00:00", + "key": 1425168000000, + "doc_count": 2, + "the_sum": { + "value": 375.0 + }, + "the_movavg": { + "value": 305.0 + } + } + ] + } + } +} +-------------------------------------------------- +// TESTRESPONSE[s/"took": 11/"took": $body.took/] +// TESTRESPONSE[s/"_shards": \.\.\./"_shards": $body._shards/] +// TESTRESPONSE[s/"hits": \.\.\./"hits": $body.hits/] + ==== Models @@ -102,16 +155,33 @@ the values from a `simple` moving average tend to "lag" behind the real data. [source,js] -------------------------------------------------- +POST /_search { - "the_movavg":{ - "moving_avg":{ - "buckets_path": "the_sum", - "window" : 30, - "model" : "simple" + "size": 0, + "aggs": { + "my_date_histo":{ + "date_histogram":{ + "field":"date", + "interval":"1M" + }, + "aggs":{ + "the_sum":{ + "sum":{ "field": "price" } + }, + "the_movavg":{ + "moving_avg":{ + "buckets_path": "the_sum", + "window" : 30, + "model" : "simple" + } + } + } } } } -------------------------------------------------- +// CONSOLE +// TEST[setup:sales] A `simple` model has no special settings to configure @@ -138,15 +208,33 @@ the "lag" behind the data's mean, since older points have less influence. [source,js] -------------------------------------------------- +POST /_search { - "the_movavg":{ - "moving_avg":{ - "buckets_path": "the_sum", - "window" : 30, - "model" : "linear" + "size": 0, + "aggs": { + "my_date_histo":{ + "date_histogram":{ + "field":"date", + "interval":"1M" + }, + "aggs":{ + "the_sum":{ + "sum":{ "field": "price" } + }, + "the_movavg": { + "moving_avg":{ + "buckets_path": "the_sum", + "window" : 30, + "model" : "linear" + } + } + } } + } } -------------------------------------------------- +// CONSOLE +// TEST[setup:sales] A `linear` model has no special settings to configure @@ -179,19 +267,36 @@ The EWMA model can be <> [source,js] -------------------------------------------------- +POST /_search { - "the_movavg":{ - "moving_avg":{ - "buckets_path": "the_sum", - "window" : 30, - "model" : "ewma", - "settings" : { - "alpha" : 0.5 + "size": 0, + "aggs": { + "my_date_histo":{ + "date_histogram":{ + "field":"date", + "interval":"1M" + }, + "aggs":{ + "the_sum":{ + "sum":{ "field": "price" } + }, + "the_movavg": { + "moving_avg":{ + "buckets_path": "the_sum", + "window" : 30, + "model" : "ewma", + "settings" : { + "alpha" : 0.5 + } + } + } } } + } } -------------------------------------------------- - +// CONSOLE +// TEST[setup:sales] [[single_0.2alpha]] @@ -221,19 +326,37 @@ The Holt-Linear model can be <> [source,js] -------------------------------------------------- +POST /_search { - "the_movavg":{ - "moving_avg":{ - "buckets_path": "the_sum", - "window" : 30, - "model" : "holt", - "settings" : { - "alpha" : 0.5, - "beta" : 0.5 + "size": 0, + "aggs": { + "my_date_histo":{ + "date_histogram":{ + "field":"date", + "interval":"1M" + }, + "aggs":{ + "the_sum":{ + "sum":{ "field": "price" } + }, + "the_movavg": { + "moving_avg":{ + "buckets_path": "the_sum", + "window" : 30, + "model" : "holt", + "settings" : { + "alpha" : 0.5, + "beta" : 0.5 + } + } + } } } + } } -------------------------------------------------- +// CONSOLE +// TEST[setup:sales] In practice, the `alpha` value behaves very similarly in `holt` as `ewma`: small values produce more smoothing and more lag, while larger values produce closer tracking and less lag. The value of `beta` is often difficult @@ -291,22 +414,40 @@ The additive Holt-Winters model can be <> [source,js] -------------------------------------------------- +POST /_search { - "the_movavg":{ - "moving_avg":{ - "buckets_path": "the_sum", - "window" : 30, - "model" : "holt_winters", - "settings" : { - "type" : "add", - "alpha" : 0.5, - "beta" : 0.5, - "gamma" : 0.5, - "period" : 7 + "size": 0, + "aggs": { + "my_date_histo":{ + "date_histogram":{ + "field":"date", + "interval":"1M" + }, + "aggs":{ + "the_sum":{ + "sum":{ "field": "price" } + }, + "the_movavg": { + "moving_avg":{ + "buckets_path": "the_sum", + "window" : 30, + "model" : "holt_winters", + "settings" : { + "type" : "add", + "alpha" : 0.5, + "beta" : 0.5, + "gamma" : 0.5, + "period" : 7 + } + } + } } } + } } -------------------------------------------------- +// CONSOLE +// TEST[setup:sales] [[holt_winters_add]] @@ -334,23 +475,41 @@ you can disable this behavior with `pad: false` [source,js] -------------------------------------------------- +POST /_search { - "the_movavg":{ - "moving_avg":{ - "buckets_path": "the_sum", - "window" : 30, - "model" : "holt_winters", - "settings" : { - "type" : "mult", - "alpha" : 0.5, - "beta" : 0.5, - "gamma" : 0.5, - "period" : 7, - "pad" : true + "size": 0, + "aggs": { + "my_date_histo":{ + "date_histogram":{ + "field":"date", + "interval":"1M" + }, + "aggs":{ + "the_sum":{ + "sum":{ "field": "price" } + }, + "the_movavg": { + "moving_avg":{ + "buckets_path": "the_sum", + "window" : 30, + "model" : "holt_winters", + "settings" : { + "type" : "mult", + "alpha" : 0.5, + "beta" : 0.5, + "gamma" : 0.5, + "period" : 7, + "pad" : true + } + } + } } } + } } -------------------------------------------------- +// CONSOLE +// TEST[setup:sales] ==== Prediction @@ -363,16 +522,34 @@ as your buckets: [source,js] -------------------------------------------------- +POST /_search { - "the_movavg":{ - "moving_avg":{ - "buckets_path": "the_sum", - "window" : 30, - "model" : "simple", - "predict" : 10 + "size": 0, + "aggs": { + "my_date_histo":{ + "date_histogram":{ + "field":"date", + "interval":"1M" + }, + "aggs":{ + "the_sum":{ + "sum":{ "field": "price" } + }, + "the_movavg": { + "moving_avg":{ + "buckets_path": "the_sum", + "window" : 30, + "model" : "simple", + "predict" : 10 + } + } + } } + } } -------------------------------------------------- +// CONSOLE +// TEST[setup:sales] The `simple`, `linear` and `ewma` models all produce "flat" predictions: they essentially converge on the mean of the last value in the series, producing a flat: @@ -423,19 +600,38 @@ Minimization is enabled/disabled via the `minimize` parameter: [source,js] -------------------------------------------------- +POST /_search { - "the_movavg":{ - "moving_avg":{ - "buckets_path": "the_sum", - "model" : "holt_winters", - "window" : 30, - "minimize" : true, <1> - "settings" : { - "period" : 7 + "size": 0, + "aggs": { + "my_date_histo":{ + "date_histogram":{ + "field":"date", + "interval":"1M" + }, + "aggs":{ + "the_sum":{ + "sum":{ "field": "price" } + }, + "the_movavg": { + "moving_avg":{ + "buckets_path": "the_sum", + "model" : "holt_winters", + "window" : 30, + "minimize" : true, <1> + "settings" : { + "period" : 7 + } + } + } } } + } } -------------------------------------------------- +// CONSOLE +// TEST[setup:sales] + <1> Minimization is enabled with the `minimize` parameter When enabled, minimization will find the optimal values for `alpha`, `beta` and `gamma`. The user should still provide diff --git a/docs/reference/aggregations/pipeline/percentiles-bucket-aggregation.asciidoc b/docs/reference/aggregations/pipeline/percentiles-bucket-aggregation.asciidoc index 252f535fd541b..b65f8c3be7930 100644 --- a/docs/reference/aggregations/pipeline/percentiles-bucket-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/percentiles-bucket-aggregation.asciidoc @@ -18,6 +18,7 @@ A `percentiles_bucket` aggregation looks like this in isolation: } } -------------------------------------------------- +// NOTCONSOLE .`sum_bucket` Parameters |=== diff --git a/docs/reference/aggregations/pipeline/serial-diff-aggregation.asciidoc b/docs/reference/aggregations/pipeline/serial-diff-aggregation.asciidoc index fadc07d54f4d6..d23fd4d614778 100644 --- a/docs/reference/aggregations/pipeline/serial-diff-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/serial-diff-aggregation.asciidoc @@ -46,6 +46,7 @@ A `serial_diff` aggregation looks like this in isolation: } } -------------------------------------------------- +// NOTCONSOLE .`serial_diff` Parameters |=== diff --git a/docs/reference/aggregations/pipeline/stats-bucket-aggregation.asciidoc b/docs/reference/aggregations/pipeline/stats-bucket-aggregation.asciidoc index 6c08fe5cd2fe3..5cbfe53f90eb7 100644 --- a/docs/reference/aggregations/pipeline/stats-bucket-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/stats-bucket-aggregation.asciidoc @@ -18,6 +18,7 @@ A `stats_bucket` aggregation looks like this in isolation: } } -------------------------------------------------- +// NOTCONSOLE .`stats_bucket` Parameters |=== diff --git a/docs/reference/aggregations/pipeline/sum-bucket-aggregation.asciidoc b/docs/reference/aggregations/pipeline/sum-bucket-aggregation.asciidoc index 3c249959f9dfd..b99ff6569eeda 100644 --- a/docs/reference/aggregations/pipeline/sum-bucket-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/sum-bucket-aggregation.asciidoc @@ -18,6 +18,7 @@ A `sum_bucket` aggregation looks like this in isolation: } } -------------------------------------------------- +// NOTCONSOLE .`sum_bucket` Parameters |=== From 4e49c618f2b30f017091e8778deeefbbd75d0c13 Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Mon, 1 May 2017 13:33:24 -0400 Subject: [PATCH 140/619] CONSOLEify Stats Aggregation docs (#24373) --- docs/build.gradle | 1 - .../metrics/stats-aggregation.asciidoc | 58 +++++++++++-------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/docs/build.gradle b/docs/build.gradle index e0504132d6181..98d10831707d2 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -37,7 +37,6 @@ buildRestTests.expectedUnconvertedCandidates = [ 'reference/aggregations/metrics/percentile-aggregation.asciidoc', 'reference/aggregations/metrics/percentile-rank-aggregation.asciidoc', 'reference/aggregations/metrics/scripted-metric-aggregation.asciidoc', - 'reference/aggregations/metrics/stats-aggregation.asciidoc', 'reference/aggregations/metrics/tophits-aggregation.asciidoc', 'reference/cat/snapshots.asciidoc', 'reference/cat/templates.asciidoc', diff --git a/docs/reference/aggregations/metrics/stats-aggregation.asciidoc b/docs/reference/aggregations/metrics/stats-aggregation.asciidoc index a442fb1215011..8077a81f62b58 100644 --- a/docs/reference/aggregations/metrics/stats-aggregation.asciidoc +++ b/docs/reference/aggregations/metrics/stats-aggregation.asciidoc @@ -9,12 +9,15 @@ Assuming the data consists of documents representing exams grades (between 0 and [source,js] -------------------------------------------------- +POST /exams/_search?size=0 { "aggs" : { "grades_stats" : { "stats" : { "field" : "grade" } } } } -------------------------------------------------- +// CONSOLE +// TEST[setup:exams] The above aggregation computes the grades statistics over all documents. The aggregation type is `stats` and the `field` setting defines the numeric field of the documents the stats will be computed on. The above will return the following: @@ -26,15 +29,16 @@ The above aggregation computes the grades statistics over all documents. The agg "aggregations": { "grades_stats": { - "count": 6, - "min": 60, - "max": 98, - "avg": 78.5, - "sum": 471 + "count": 2, + "min": 50.0, + "max": 100.0, + "avg": 75.0, + "sum": 150.0 } } } -------------------------------------------------- +// TESTRESPONSE[s/\.\.\./"took": $body.took,"timed_out": false,"_shards": $body._shards,"hits": $body.hits,/] The name of the aggregation (`grades_stats` above) also serves as the key by which the aggregation result can be retrieved from the returned response. @@ -44,29 +48,29 @@ Computing the grades stats based on a script: [source,js] -------------------------------------------------- +POST /exams/_search?size=0 { - ..., - "aggs" : { - "grades_stats" : { + "grades_stats" : { "stats" : { - "script" : { + "script" : { "lang": "painless", - "inline": "doc['grade'].value" + "inline": "doc['grade'].value" } - } + } } } } -------------------------------------------------- +// CONSOLE +// TEST[setup:exams] This will interpret the `script` parameter as an `inline` script with the `painless` script language and no script parameters. To use a file script use the following syntax: [source,js] -------------------------------------------------- +POST /exams/_search?size=0 { - ..., - "aggs" : { "grades_stats" : { "stats" : { @@ -81,6 +85,8 @@ This will interpret the `script` parameter as an `inline` script with the `painl } } -------------------------------------------------- +// CONSOLE +// TEST[setup:exams] TIP: for indexed scripts replace the `file` parameter with an `id` parameter. @@ -90,20 +96,17 @@ It turned out that the exam was way above the level of the students and a grade [source,js] -------------------------------------------------- +POST /exams/_search?size=0 { "aggs" : { - ... - - "aggs" : { - "grades_stats" : { - "stats" : { - "field" : "grade", - "script" : - "lang": "painless", - "inline": "_value * params.correction", - "params" : { - "correction" : 1.2 - } + "grades_stats" : { + "stats" : { + "field" : "grade", + "script" : { + "lang": "painless", + "inline": "_value * params.correction", + "params" : { + "correction" : 1.2 } } } @@ -111,6 +114,8 @@ It turned out that the exam was way above the level of the students and a grade } } -------------------------------------------------- +// CONSOLE +// TEST[setup:exams] ==== Missing value @@ -120,6 +125,7 @@ had a value. [source,js] -------------------------------------------------- +POST /exams/_search?size=0 { "aggs" : { "grades_stats" : { @@ -131,5 +137,7 @@ had a value. } } -------------------------------------------------- +// CONSOLE +// TEST[setup:exams] <1> Documents without a value in the `grade` field will fall into the same bucket as documents that have the value `0`. From 38273709b5009263c580335b978a454297e5303a Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Mon, 1 May 2017 13:56:39 -0400 Subject: [PATCH 141/619] CONSOLEify some more Indices APIs (#24375) * CONSOLEify doc testing for some more Indices APIs Related to #18160 --- docs/build.gradle | 3 -- docs/reference/indices/flush.asciidoc | 45 +++++++++++--------- docs/reference/indices/get-settings.asciidoc | 4 +- docs/reference/indices/put-mapping.asciidoc | 23 +++++++--- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/docs/build.gradle b/docs/build.gradle index 98d10831707d2..7effa7401b06e 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -54,9 +54,6 @@ buildRestTests.expectedUnconvertedCandidates = [ 'reference/index-modules/similarity.asciidoc', 'reference/index-modules/store.asciidoc', 'reference/index-modules/translog.asciidoc', - 'reference/indices/flush.asciidoc', - 'reference/indices/get-settings.asciidoc', - 'reference/indices/put-mapping.asciidoc', 'reference/indices/recovery.asciidoc', 'reference/indices/segments.asciidoc', 'reference/indices/shard-stores.asciidoc', diff --git a/docs/reference/indices/flush.asciidoc b/docs/reference/indices/flush.asciidoc index 2b9f519e17c7c..d327d0da09dc3 100644 --- a/docs/reference/indices/flush.asciidoc +++ b/docs/reference/indices/flush.asciidoc @@ -74,10 +74,11 @@ the <> API: [source,sh] -------------------------------------------------- -GET twitter/_stats?level=shards +GET twitter/_stats?filter_path=**.commit&level=shards <1> -------------------------------------------------- // CONSOLE // TEST[s/^/PUT twitter\n/] +<1> `filter_path` is used to reduce the verbosity of the response, but is entirely optional which returns something similar to: @@ -85,35 +86,40 @@ which returns something similar to: [source,js] -------------------------------------------------- { - ... "indices": { "twitter": { - "primaries": {}, - "total": {}, "shards": { "0": [ { - "routing": { - ... - }, - "commit": { - "id": "te7zF7C4UsirqvL6jp/vUg==", - "generation": 2, - "user_data": { - "sync_id": "AU2VU0meX-VX2aNbEUsD" <1>, - ... - }, - "num_docs": 0 - } + "commit" : { + "id" : "3M3zkw2GHMo2Y4h4/KFKCg==", + "generation" : 1, + "user_data" : { + "translog_uuid" : "hnOG3xFcTDeoI_kvvvOdNA", + "local_checkpoint" : "-1", + "translog_generation" : "1", + "max_seq_no" : "-1", + "max_unsafe_auto_id_timestamp" : "-1" + }, + "num_docs" : 0 + } } - ... ], - ... + "1": ..., + "2": ..., + "3": ..., + "4": ... } } } } -------------------------------------------------- +// TESTRESPONSE[s/"id" : "3M3zkw2GHMo2Y4h4\/KFKCg=="/"id": $body.indices.twitter.shards.0.0.commit.id/] +// TESTRESPONSE[s/"translog_uuid" : "hnOG3xFcTDeoI_kvvvOdNA"/"translog_uuid": $body.indices.twitter.shards.0.0.commit.user_data.translog_uuid/] +// TESTRESPONSE[s/"1": \.\.\./"1": $body.indices.twitter.shards.1/] +// TESTRESPONSE[s/"2": \.\.\./"2": $body.indices.twitter.shards.2/] +// TESTRESPONSE[s/"3": \.\.\./"3": $body.indices.twitter.shards.3/] +// TESTRESPONSE[s/"4": \.\.\./"4": $body.indices.twitter.shards.4/] <1> the `sync id` marker [float] @@ -189,6 +195,7 @@ Here is what it looks like when one shard group failed due to pending operations } } -------------------------------------------------- +// NOTCONSOLE NOTE: The above error is shown when the synced flush fails due to concurrent indexing operations. The HTTP status code in that case will be `409 CONFLICT`. @@ -225,7 +232,7 @@ fast recovery but those that succeeded still will be. This case is reported as f } } -------------------------------------------------- - +// NOTCONSOLE NOTE: When a shard copy fails to sync-flush, the HTTP status code returned will be `409 CONFLICT`. diff --git a/docs/reference/indices/get-settings.asciidoc b/docs/reference/indices/get-settings.asciidoc index 5fb488f49c3e9..fe539b5cad0b3 100644 --- a/docs/reference/indices/get-settings.asciidoc +++ b/docs/reference/indices/get-settings.asciidoc @@ -40,5 +40,7 @@ as follows: [source,js] -------------------------------------------------- -curl -XGET 'http://localhost:9200/2013-*/_settings/index.number_*' +GET /log_2013_-*/_settings/index.number_* -------------------------------------------------- +// CONSOLE +// TEST[continued] \ No newline at end of file diff --git a/docs/reference/indices/put-mapping.asciidoc b/docs/reference/indices/put-mapping.asciidoc index 0dd2ffc3d8a93..5fc4f0d9cc034 100644 --- a/docs/reference/indices/put-mapping.asciidoc +++ b/docs/reference/indices/put-mapping.asciidoc @@ -52,17 +52,26 @@ More information on how to define type mappings can be found in the === Multi-index The PUT mapping API can be applied to multiple indices with a single request. -It has the following format: +For example, we can update the `twitter-1` and `twitter-2` mappings at the same time: [source,js] -------------------------------------------------- -PUT /{index}/_mapping/{type} -{ body } --------------------------------------------------- +# Create the two indices +PUT twitter-1 +PUT twitter-2 -* `{index}` accepts <> and wildcards. -* `{type}` is the name of the type to update. -* `{body}` contains the mapping changes that should be applied. +# Update both mappings +PUT /twitter-1,twitter-2/_mapping/my_type <1> +{ + "properties": { + "user_name": { + "type": "text" + } + } +} +-------------------------------------------------- +// CONSOLE +<1> Note that the indices specified (`twitter-1,twitter-2`) follows <> and wildcard format. NOTE: When updating the `_default_` mapping with the From 91fbb0ba2868e1dc1caa1f18d0fd570de0f3d9e4 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Mon, 1 May 2017 19:59:06 +0200 Subject: [PATCH 142/619] Move IndicesAliasesRequest#concreteAliases to TransportIndicesAliasesAction (#24400) This method has to do with how the transport action may or may not resolve wildcards expressions to aliases names. It is only needed in TransportIndicesAliasesAction and for this reason it should be a private method in it rather than part of a request class which is also part of the Java API and later in the high level REST client. --- .../indices/alias/IndicesAliasesRequest.java | 35 +++++-------------- .../alias/TransportIndicesAliasesAction.java | 30 +++++++++++++--- ...0_remove_index_and_replace_with_alias.yaml | 2 +- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/alias/IndicesAliasesRequest.java b/core/src/main/java/org/elasticsearch/action/admin/indices/alias/IndicesAliasesRequest.java index e97facd748f38..e7393efd01ce7 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/alias/IndicesAliasesRequest.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/alias/IndicesAliasesRequest.java @@ -19,19 +19,15 @@ package org.elasticsearch.action.admin.indices.alias; -import com.carrotsearch.hppc.cursors.ObjectCursor; import org.elasticsearch.ElasticsearchGenerationException; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.AliasesRequest; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.master.AcknowledgedRequest; import org.elasticsearch.cluster.metadata.AliasAction; -import org.elasticsearch.cluster.metadata.AliasMetaData; -import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; @@ -92,10 +88,10 @@ public byte value() { public static Type fromValue(byte value) { switch (value) { - case 0: return ADD; - case 1: return REMOVE; - case 2: return REMOVE_INDEX; - default: throw new IllegalArgumentException("No type for action [" + value + "]"); + case 0: return ADD; + case 1: return REMOVE; + case 2: return REMOVE_INDEX; + default: throw new IllegalArgumentException("No type for action [" + value + "]"); } } } @@ -106,18 +102,21 @@ public static Type fromValue(byte value) { public static AliasActions add() { return new AliasActions(AliasActions.Type.ADD); } + /** * Build a new {@link AliasAction} to remove aliases. */ public static AliasActions remove() { return new AliasActions(AliasActions.Type.REMOVE); } + /** - * Build a new {@link AliasAction} to remove aliases. + * Build a new {@link AliasAction} to remove an index. */ public static AliasActions removeIndex() { return new AliasActions(AliasActions.Type.REMOVE_INDEX); } + private static ObjectParser parser(String name, Supplier supplier) { ObjectParser parser = new ObjectParser<>(name, supplier); parser.declareString((action, index) -> { @@ -402,24 +401,6 @@ public IndicesOptions indicesOptions() { return INDICES_OPTIONS; } - public String[] concreteAliases(MetaData metaData, String concreteIndex) { - if (expandAliasesWildcards()) { - //for DELETE we expand the aliases - String[] indexAsArray = {concreteIndex}; - ImmutableOpenMap> aliasMetaData = metaData.findAliases(aliases, indexAsArray); - List finalAliases = new ArrayList<>(); - for (ObjectCursor> curAliases : aliasMetaData.values()) { - for (AliasMetaData aliasMeta: curAliases.value) { - finalAliases.add(aliasMeta.alias()); - } - } - return finalAliases.toArray(new String[finalAliases.size()]); - } else { - //for add we just return the current aliases - return aliases; - } - } - @Override public String toString() { return "AliasActions[" diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/alias/TransportIndicesAliasesAction.java b/core/src/main/java/org/elasticsearch/action/admin/indices/alias/TransportIndicesAliasesAction.java index 44de63c028db1..9dcd361ae6421 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/alias/TransportIndicesAliasesAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/alias/TransportIndicesAliasesAction.java @@ -19,6 +19,7 @@ package org.elasticsearch.action.admin.indices.alias; +import com.carrotsearch.hppc.cursors.ObjectCursor; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; import org.elasticsearch.action.support.ActionFilters; @@ -28,9 +29,12 @@ import org.elasticsearch.cluster.block.ClusterBlockException; import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.metadata.AliasAction; +import org.elasticsearch.cluster.metadata.AliasMetaData; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.metadata.MetaDataIndexAliasesService; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.rest.action.admin.indices.AliasesNotFoundException; @@ -75,9 +79,7 @@ protected IndicesAliasesResponse newResponse() { protected ClusterBlockException checkBlock(IndicesAliasesRequest request, ClusterState state) { Set indices = new HashSet<>(); for (AliasActions aliasAction : request.aliasActions()) { - for (String index : aliasAction.indices()) { - indices.add(index); - } + Collections.addAll(indices, aliasAction.indices()); } return state.blocks().indicesBlockedException(ClusterBlockLevel.METADATA_WRITE, indices.toArray(new String[indices.size()])); } @@ -97,12 +99,12 @@ protected void masterOperation(final IndicesAliasesRequest request, final Cluste for (String index : concreteIndices) { switch (action.actionType()) { case ADD: - for (String alias : action.concreteAliases(state.metaData(), index)) { + for (String alias : concreteAliases(action, state.metaData(), index)) { finalActions.add(new AliasAction.Add(index, alias, action.filter(), action.indexRouting(), action.searchRouting())); } break; case REMOVE: - for (String alias : action.concreteAliases(state.metaData(), index)) { + for (String alias : concreteAliases(action, state.metaData(), index)) { finalActions.add(new AliasAction.Remove(index, alias)); } break; @@ -134,4 +136,22 @@ public void onFailure(Exception t) { } }); } + + private static String[] concreteAliases(AliasActions action, MetaData metaData, String concreteIndex) { + if (action.expandAliasesWildcards()) { + //for DELETE we expand the aliases + String[] indexAsArray = {concreteIndex}; + ImmutableOpenMap> aliasMetaData = metaData.findAliases(action.aliases(), indexAsArray); + List finalAliases = new ArrayList<>(); + for (ObjectCursor> curAliases : aliasMetaData.values()) { + for (AliasMetaData aliasMeta: curAliases.value) { + finalAliases.add(aliasMeta.alias()); + } + } + return finalAliases.toArray(new String[finalAliases.size()]); + } else { + //for ADD and REMOVE_INDEX we just return the current aliases + return action.aliases(); + } + } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/30_remove_index_and_replace_with_alias.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/30_remove_index_and_replace_with_alias.yaml index 66da068895f1b..ff1c77a87db8b 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/30_remove_index_and_replace_with_alias.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/30_remove_index_and_replace_with_alias.yaml @@ -1,5 +1,5 @@ --- -"Remove and index and replace it with an alias": +"Remove an index and replace it with an alias": - do: indices.create: From 8f1fe51f34191f88915ad90a8bbd9ac155449552 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Mon, 1 May 2017 14:50:13 -0400 Subject: [PATCH 143/619] Test: more logging for refresh listeners many threads test This test fails from time to time but doesn't give enough information for me to figure out what caused the assertion to fail. Relates to #24418 --- .../org/elasticsearch/index/shard/RefreshListenersTests.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java b/core/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java index aa3b9b1ee85c2..5ad430ca59e24 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java +++ b/core/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java @@ -56,6 +56,7 @@ import org.elasticsearch.test.DummyShardLock; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.IndexSettingsModule; +import org.elasticsearch.test.junit.annotations.TestLogging; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.threadpool.ThreadPool.Cancellable; @@ -272,6 +273,7 @@ public void testConcurrentRefresh() throws Exception { * Uses a bunch of threads to index, wait for refresh, and non-realtime get documents to validate that they are visible after waiting * regardless of what crazy sequence of events causes the refresh listener to fire. */ + @TestLogging("_root:debug,org.elasticsearch.index.engine.Engine.DW:trace") public void testLotsOfThreads() throws Exception { int threadCount = between(3, 10); maxListeners = between(1, threadCount * 2); From 9878aae1444e1fb7066dc97ed4935aac4c0302ec Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Mon, 1 May 2017 15:09:21 -0400 Subject: [PATCH 144/619] Ensure every repository has an incompatible-snapshots blob (#24403) In #22267, we introduced the notion of incompatible snapshots in a repository, and they were stored in a root-level blob named `incompatible-snapshots`. If there were no incompatible snapshots in the repository, then there was no `incompatible-snapshots` blob. However, this causes a problem for some cloud-based repositories, because if the blob does not exist, the cloud-based repositories may attempt to keep retrying the read of a non-existent blob with expontential backoff until giving up. This causes performance issues (and potential timeouts) on snapshot operations because getting the `incompatible-snapshots` is part of getting the repository data (see RepositoryData#getRepositoryData()). This commit fixes the issue by creating an empty `incompatible-snapshots` blob in the repository if one does not exist. --- .../blobstore/BlobStoreRepository.java | 15 +++++++++++---- .../blobstore/BlobStoreRepositoryTests.java | 12 ++++++++++++ .../DedicatedClusterSnapshotRestoreIT.java | 6 ++++-- .../snapshots/SharedClusterSnapshotRestoreIT.java | 4 ++-- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index a0d7ece8c0df5..803c58c12f4a5 100644 --- a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -635,8 +635,17 @@ public RepositoryData getRepositoryData() { repositoryData = repositoryData.incompatibleSnapshotsFromXContent(parser); } } catch (NoSuchFileException e) { - logger.debug("[{}] Incompatible snapshots blob [{}] does not exist, the likely reason is that " + - "there are no incompatible snapshots in the repository", metadata.name(), INCOMPATIBLE_SNAPSHOTS_BLOB); + if (isReadOnly()) { + logger.debug("[{}] Incompatible snapshots blob [{}] does not exist, the likely " + + "reason is that there are no incompatible snapshots in the repository", + metadata.name(), INCOMPATIBLE_SNAPSHOTS_BLOB); + } else { + // write an empty incompatible-snapshots blob - we do this so that there + // is a blob present, which helps speed up some cloud-based repositories + // (e.g. S3), which retry if a blob is missing with exponential backoff, + // delaying the read of repository data and sometimes causing a timeout + writeIncompatibleSnapshots(RepositoryData.EMPTY); + } } return repositoryData; } catch (NoSuchFileException ex) { @@ -802,8 +811,6 @@ private void writeAtomic(final String blobName, final BytesReference bytesRef) t } } - - @Override public void snapshotShard(IndexShard shard, SnapshotId snapshotId, IndexId indexId, IndexCommit snapshotIndexCommit, IndexShardSnapshotStatus snapshotStatus) { SnapshotContext snapshotContext = new SnapshotContext(shard, snapshotId, indexId, snapshotStatus); diff --git a/core/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryTests.java b/core/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryTests.java index 4b57fa43cfe32..87e8d595f4b99 100644 --- a/core/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryTests.java +++ b/core/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryTests.java @@ -189,6 +189,18 @@ public void testReadAndWriteIncompatibleSnapshots() throws Exception { assertEquals(repositoryData.getIncompatibleSnapshotIds(), readData.getIncompatibleSnapshotIds()); } + public void testIncompatibleSnapshotsBlobExists() throws Exception { + final BlobStoreRepository repository = setupRepo(); + RepositoryData emptyData = RepositoryData.EMPTY; + repository.writeIndexGen(emptyData, emptyData.getGenId()); + RepositoryData repoData = repository.getRepositoryData(); + assertEquals(emptyData, repoData); + assertTrue(repository.blobContainer().blobExists("incompatible-snapshots")); + repoData = addRandomSnapshotsToRepoData(repository.getRepositoryData(), true); + repository.writeIndexGen(repoData, repoData.getGenId()); + assertEquals(0, repository.getRepositoryData().getIncompatibleSnapshotIds().size()); + } + private BlobStoreRepository setupRepo() { final Client client = client(); final Path location = ESIntegTestCase.randomRepoPath(node().settings()); diff --git a/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java b/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java index 355b75219a0b1..2503d6e157e2d 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java +++ b/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java @@ -429,11 +429,13 @@ public void testSnapshotWithStuckNode() throws Exception { logger.info("--> making sure that snapshot no longer exists"); assertThrows(client().admin().cluster().prepareGetSnapshots("test-repo").setSnapshots("test-snap").execute(), SnapshotMissingException.class); - // Subtract three files that will remain in the repository: + // Subtract four files that will remain in the repository: // (1) index-1 // (2) index-0 (because we keep the previous version) and // (3) index-latest - assertThat("not all files were deleted during snapshot cancellation", numberOfFilesBeforeSnapshot, equalTo(numberOfFiles(repo) - 3)); + // (4) incompatible-snapshots + assertThat("not all files were deleted during snapshot cancellation", + numberOfFilesBeforeSnapshot, equalTo(numberOfFiles(repo) - 4)); logger.info("--> done"); } diff --git a/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java b/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java index 1783363e7cebc..11194a689da59 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java +++ b/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java @@ -989,8 +989,8 @@ public void testDeleteSnapshot() throws Exception { logger.info("--> delete the last snapshot"); client.admin().cluster().prepareDeleteSnapshot("test-repo", lastSnapshot).get(); logger.info("--> make sure that number of files is back to what it was when the first snapshot was made, " + - "plus one because one backup index-N file should remain"); - assertThat(numberOfFiles(repo), equalTo(numberOfFiles[0] + 1)); + "plus two because one backup index-N file should remain and incompatible-snapshots"); + assertThat(numberOfFiles(repo), equalTo(numberOfFiles[0] + 2)); } public void testDeleteSnapshotWithMissingIndexAndShardMetadata() throws Exception { From 735986c1402f78946407340afad9a08e9df30a94 Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Mon, 1 May 2017 15:14:09 -0400 Subject: [PATCH 145/619] [DOCS] Tweak doc test to sync_flush The response is attempting to illustrate the sync_id marker, but in the test the index is too "fresh" to have a sync marker. So the test needs to execute a sync flush behind the scenes so that the marker is present --- docs/reference/indices/flush.asciidoc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/reference/indices/flush.asciidoc b/docs/reference/indices/flush.asciidoc index d327d0da09dc3..e9cac91d740cc 100644 --- a/docs/reference/indices/flush.asciidoc +++ b/docs/reference/indices/flush.asciidoc @@ -77,7 +77,7 @@ the <> API: GET twitter/_stats?filter_path=**.commit&level=shards <1> -------------------------------------------------- // CONSOLE -// TEST[s/^/PUT twitter\n/] +// TEST[s/^/PUT twitter\nPOST twitter\/_flush\/synced\n/] <1> `filter_path` is used to reduce the verbosity of the response, but is entirely optional @@ -93,12 +93,13 @@ which returns something similar to: { "commit" : { "id" : "3M3zkw2GHMo2Y4h4/KFKCg==", - "generation" : 1, + "generation" : 2, "user_data" : { "translog_uuid" : "hnOG3xFcTDeoI_kvvvOdNA", "local_checkpoint" : "-1", "translog_generation" : "1", "max_seq_no" : "-1", + "sync_id" : "AVvFY-071siAOuFGEO9P", <1> "max_unsafe_auto_id_timestamp" : "-1" }, "num_docs" : 0 @@ -116,6 +117,7 @@ which returns something similar to: -------------------------------------------------- // TESTRESPONSE[s/"id" : "3M3zkw2GHMo2Y4h4\/KFKCg=="/"id": $body.indices.twitter.shards.0.0.commit.id/] // TESTRESPONSE[s/"translog_uuid" : "hnOG3xFcTDeoI_kvvvOdNA"/"translog_uuid": $body.indices.twitter.shards.0.0.commit.user_data.translog_uuid/] +// TESTRESPONSE[s/"sync_id" : "AVvFY-071siAOuFGEO9P"/"sync_id": $body.indices.twitter.shards.0.0.commit.user_data.sync_id/] // TESTRESPONSE[s/"1": \.\.\./"1": $body.indices.twitter.shards.1/] // TESTRESPONSE[s/"2": \.\.\./"2": $body.indices.twitter.shards.2/] // TESTRESPONSE[s/"3": \.\.\./"3": $body.indices.twitter.shards.3/] From ae0290bae936f0d460410fd57b33dca48b4507b4 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Mon, 1 May 2017 15:31:02 -0400 Subject: [PATCH 146/619] Doc test: use propery regex for file size The _cat/shards docs asserted that one of the columns looked like a propery byte size but used a regex like `\d+\.\d+.*` which doesn't match `0b` which is a possible value. Instead this uses `\d(\.\d+)?[kmg]?b`. --- docs/reference/cat/shards.asciidoc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/reference/cat/shards.asciidoc b/docs/reference/cat/shards.asciidoc index f7812197e43fa..8fa0b80204f4b 100644 --- a/docs/reference/cat/shards.asciidoc +++ b/docs/reference/cat/shards.asciidoc @@ -21,8 +21,7 @@ This will return twitter 0 p STARTED 3014 31.1mb 192.168.56.10 H5dfFeA --------------------------------------------------------------------------- // TESTRESPONSE[s/3014/\\d+/] -// TESTRESPONSE[s/31.1/\\d+(\.\\d+)?/] -// TESTRESPONSE[s/mb/.*/] +// TESTRESPONSE[s/31.1mb/\\d+(\.\\d+)?[kmg]?b/] // TESTRESPONSE[s/192.168.56.10/.*/] // TESTRESPONSE[s/H5dfFeA/node-0/ _cat] @@ -48,8 +47,7 @@ Which will return the following twitter 0 p STARTED 3014 31.1mb 192.168.56.10 H5dfFeA --------------------------------------------------------------------------- // TESTRESPONSE[s/3014/\\d+/] -// TESTRESPONSE[s/31.1/\\d+\.\\d+/] -// TESTRESPONSE[s/mb/.*/] +// TESTRESPONSE[s/31.1mb/\\d+(\.\\d+)?[kmg]?b/] // TESTRESPONSE[s/192.168.56.10/.*/] // TESTRESPONSE[s/H5dfFeA/node-0/ _cat] From e88d54bf0aa467d718688eafc5252c1f2021347d Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Tue, 2 May 2017 01:15:13 +0200 Subject: [PATCH 147/619] Painless: Fix method references to ctor with the new LambdaBootstrap and cleanup code (#24406) * Fix wrong delegation to constructors when compiling lambdas with method references to ctors. Also remove the get$lambda factory. * Cleanup code and remove unneeded transformations between binary and internal class names (uses ASM Type class instead) * Cleanup Exception handling * Simplification by moving the type adaption to the outside * Remove STATIC access flag from our Lambda class (not required and also officially not allowed) * Move the lambda counter to the classloader, so we have a per-script lambda ID * Change Codesource of generated lambdas to be consistent --- .../org/elasticsearch/painless/Compiler.java | 13 +- .../painless/LambdaBootstrap.java | 266 ++++++++---------- .../painless/WriterConstants.java | 1 - .../painless/FunctionRefTests.java | 7 + 4 files changed, 131 insertions(+), 156 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java index 976ef897aec2f..0d92f109eea44 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java @@ -30,6 +30,7 @@ import java.security.SecureClassLoader; import java.security.cert.Certificate; import java.util.BitSet; +import java.util.concurrent.atomic.AtomicInteger; import static org.elasticsearch.painless.WriterConstants.CLASS_NAME; @@ -66,6 +67,8 @@ final class Compiler { * A secure class loader used to define Painless scripts. */ static final class Loader extends SecureClassLoader { + private final AtomicInteger lambdaCounter = new AtomicInteger(0); + /** * @param parent The parent ClassLoader. */ @@ -90,7 +93,15 @@ Class defineScript(String name, byte[] bytes) { * @return A Class object. */ Class defineLambda(String name, byte[] bytes) { - return defineClass(name, bytes, 0, bytes.length); + return defineClass(name, bytes, 0, bytes.length, CODESOURCE); + } + + /** + * A counter used to generate a unique name for each lambda + * function/reference class in this classloader. + */ + int newLambdaIdentifier() { + return lambdaCounter.getAndIncrement(); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/LambdaBootstrap.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/LambdaBootstrap.java index 746467c454bee..2ffe9afadf38a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/LambdaBootstrap.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/LambdaBootstrap.java @@ -32,11 +32,8 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.concurrent.atomic.AtomicLong; import static java.lang.invoke.MethodHandles.Lookup; import static org.elasticsearch.painless.Compiler.Loader; @@ -69,8 +66,7 @@ * 1. member fields for any captured variables * 2. a constructor that will take in captured variables and assign them to * their respective member fields - * 3. if there are captures, a factory method that will take in captured - * variables and delegate them to the constructor + * 3. a static ctor delegation method, if the lambda function is a ctor. * 4. a method that will load the member fields representing captured variables * and take in any other necessary values based on the arguments passed into the * lambda function/reference method; it will then make a delegated call to the @@ -97,10 +93,6 @@ * this.arg$0 = arg$0; * } * - * public static $$Lambda0 get$Lambda(List arg$0) { - * return $$Lambda0(arg$0); - * } - * * public void accept(Object val$0) { * Painless$Script.lambda$0(this.arg$0, val$0); * } @@ -115,13 +107,14 @@ * } * } * - * Note if the above didn't have a captured variable then - * the factory method get$Lambda would not have been generated. * Also the accept method actually uses an invokedynamic * instruction to call the lambda$0 method so that * {@link MethodHandle#asType} can be used to do the necessary * conversions between argument types without having to hard - * code them. + * code them. For method references to a constructor, a static + * wrapper method is created, that creates a class instance and + * calls the constructor. This method is used by the + * invokedynamic call to initialize the instance. * * When the {@link CallSite} is linked the linked method depends * on whether or not there are captures. If there are no captures @@ -156,10 +149,9 @@ private Capture(int count, Class type) { } /** - * A counter used to generate a unique name - * for each lambda function/reference class. + * This method name is used to generate a static wrapper method to handle delegation of ctors. */ - private static final AtomicLong COUNTER = new AtomicLong(0); + private static final String DELEGATED_CTOR_WRAPPER_NAME = "delegate$ctor"; /** * Generates a lambda class for a lambda function/method reference @@ -173,7 +165,7 @@ private Capture(int count, Class type) { * @param factoryMethodType The type of method to be linked to this CallSite; note that * captured types are based on the parameters for this method * @param interfaceMethodType The type of method representing the functional interface method - * @param delegateClassName The name of the Painless script class + * @param delegateClassName The name of the class to delegate method call to * @param delegateInvokeType The type of method call to be made * (static, virtual, interface, or constructor) * @param delegateMethodName The name of the method to be called in the Painless script class @@ -193,34 +185,35 @@ public static CallSite lambdaBootstrap( String delegateMethodName, MethodType delegateMethodType) throws LambdaConversionException { - String factoryMethodName = "get$lambda"; - String lambdaClassName = lookup.lookupClass().getName().replace('.', '/') + - "$$Lambda" + COUNTER.getAndIncrement(); - Type lambdaClassType = Type.getType("L" + lambdaClassName + ";"); + Loader loader = (Loader)lookup.lookupClass().getClassLoader(); + String lambdaClassName = Type.getInternalName(lookup.lookupClass()) + "$$Lambda" + loader.newLambdaIdentifier(); + Type lambdaClassType = Type.getObjectType(lambdaClassName); + Type delegateClassType = Type.getObjectType(delegateClassName.replace('.', '/')); validateTypes(interfaceMethodType, delegateMethodType); - ClassWriter cw = - beginLambdaClass(lambdaClassName, factoryMethodType.returnType().getName()); + ClassWriter cw = beginLambdaClass(lambdaClassName, factoryMethodType.returnType()); Capture[] captures = generateCaptureFields(cw, factoryMethodType); - Method constructorMethod = - generateLambdaConstructor(cw, lambdaClassType, factoryMethodType, captures); - - if (captures.length > 0) { - generateFactoryMethod( - cw, factoryMethodName, factoryMethodType, lambdaClassType, constructorMethod); + generateLambdaConstructor(cw, lambdaClassType, factoryMethodType, captures); + + // Handles the special case where a method reference refers to a ctor (we need a static wrapper method): + if (delegateInvokeType == H_NEWINVOKESPECIAL) { + generateStaticCtorDelegator(cw, delegateClassType, delegateMethodName, delegateMethodType); + // replace the delegate with our static wrapper: + delegateMethodName = DELEGATED_CTOR_WRAPPER_NAME; + delegateClassType = lambdaClassType; + delegateInvokeType = H_INVOKESTATIC; } - generateInterfaceMethod(cw, factoryMethodType, lambdaClassName, lambdaClassType, - interfaceMethodName, interfaceMethodType, delegateClassName, delegateInvokeType, + generateInterfaceMethod(cw, factoryMethodType, lambdaClassType, interfaceMethodName, + interfaceMethodType, delegateClassType, delegateInvokeType, delegateMethodName, delegateMethodType, captures); + endLambdaClass(cw); - Class lambdaClass = - createLambdaClass((Loader)lookup.lookupClass().getClassLoader(), cw, lambdaClassName); - + Class lambdaClass = createLambdaClass(loader, cw, lambdaClassType); if (captures.length > 0) { - return createCaptureCallSite(lookup, factoryMethodName, factoryMethodType, lambdaClass); + return createCaptureCallSite(lookup, factoryMethodType, lambdaClass); } else { return createNoCaptureCallSite(factoryMethodType, lambdaClass); } @@ -243,14 +236,13 @@ private static void validateTypes(MethodType interfaceMethodType, MethodType del /** * Creates the {@link ClassWriter} to be used for the lambda class generation. */ - private static ClassWriter beginLambdaClass(String lambdaClassName, String lambdaInterface) { - String baseClass = Object.class.getName().replace('.', '/'); - lambdaInterface = lambdaInterface.replace('.', '/'); - int modifiers = ACC_PUBLIC | ACC_STATIC | ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC; + private static ClassWriter beginLambdaClass(String lambdaClassName, Class lambdaInterface) { + String baseClass = Type.getInternalName(Object.class); + int modifiers = ACC_PUBLIC | ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC; ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); cw.visit(CLASS_VERSION, - modifiers, lambdaClassName, null, baseClass, new String[] {lambdaInterface}); + modifiers, lambdaClassName, null, baseClass, new String[] { Type.getInternalName(lambdaInterface) }); return cw; } @@ -268,7 +260,7 @@ private static Capture[] generateCaptureFields(ClassWriter cw, MethodType factor for (int captureCount = 0; captureCount < captureTotal; ++captureCount) { captures[captureCount] = new Capture(captureCount, factoryMethodType.parameterType(captureCount)); - int modifiers = ACC_PRIVATE + ACC_FINAL; + int modifiers = ACC_PRIVATE | ACC_FINAL; FieldVisitor fv = cw.visitField( modifiers, captures[captureCount].name, captures[captureCount].desc, null, null); @@ -282,11 +274,8 @@ private static Capture[] generateCaptureFields(ClassWriter cw, MethodType factor * Generates a constructor that will take in captured * arguments if any and store them in their respective * member fields. - * @return The constructor {@link Method} used to - * call this method from a potential factory method - * if there are captured arguments */ - private static Method generateLambdaConstructor( + private static void generateLambdaConstructor( ClassWriter cw, Type lambdaClassType, MethodType factoryMethodType, @@ -315,32 +304,26 @@ private static Method generateLambdaConstructor( constructor.returnValue(); constructor.endMethod(); - - return conMeth; } /** - * Generates a factory method that can be used to create the lambda class - * if there are captured variables. + * Generates a factory method to delegate to constructors using + * {@code INVOKEDYNAMIC} using the {@link #delegateBootstrap} type converter. */ - private static void generateFactoryMethod( - ClassWriter cw, - String factoryMethodName, - MethodType factoryMethodType, - Type lambdaClassType, - Method constructorMethod) { - - String facDesc = factoryMethodType.toMethodDescriptorString(); - Method facMeth = new Method(factoryMethodName, facDesc); - int modifiers = ACC_PUBLIC | ACC_STATIC; - - GeneratorAdapter factory = new GeneratorAdapter(modifiers, facMeth, - cw.visitMethod(modifiers, factoryMethodName, facDesc, null, null)); + private static void generateStaticCtorDelegator(ClassWriter cw, Type delegateClassType, String delegateMethodName, + MethodType delegateMethodType) { + Method wrapperMethod = new Method(DELEGATED_CTOR_WRAPPER_NAME, delegateMethodType.toMethodDescriptorString()); + Method constructorMethod = + new Method(delegateMethodName, delegateMethodType.changeReturnType(void.class).toMethodDescriptorString()); + int modifiers = ACC_PRIVATE | ACC_STATIC; + + GeneratorAdapter factory = new GeneratorAdapter(modifiers, wrapperMethod, + cw.visitMethod(modifiers, DELEGATED_CTOR_WRAPPER_NAME, delegateMethodType.toMethodDescriptorString(), null, null)); factory.visitCode(); - factory.newInstance(lambdaClassType); + factory.newInstance(delegateClassType); factory.dup(); factory.loadArgs(); - factory.invokeConstructor(lambdaClassType, constructorMethod); + factory.invokeConstructor(delegateClassType, constructorMethod); factory.returnValue(); factory.endMethod(); } @@ -351,11 +334,10 @@ private static void generateFactoryMethod( private static void generateInterfaceMethod( ClassWriter cw, MethodType factoryMethodType, - String lambdaClassName, Type lambdaClassType, String interfaceMethodName, MethodType interfaceMethodType, - String delegateClassName, + Type delegateClassType, int delegateInvokeType, String delegateMethodName, MethodType delegateMethodType, @@ -363,86 +345,71 @@ private static void generateInterfaceMethod( throws LambdaConversionException { String lamDesc = interfaceMethodType.toMethodDescriptorString(); - Method lamMeth = new Method(lambdaClassName, lamDesc); + Method lamMeth = new Method(lambdaClassType.getInternalName(), lamDesc); int modifiers = ACC_PUBLIC; GeneratorAdapter iface = new GeneratorAdapter(modifiers, lamMeth, cw.visitMethod(modifiers, interfaceMethodName, lamDesc, null, null)); iface.visitCode(); + + // Loads any captured variables onto the stack. + for (int captureCount = 0; captureCount < captures.length; ++captureCount) { + iface.loadThis(); + iface.getField( + lambdaClassType, captures[captureCount].name, captures[captureCount].type); + } - // Handles the case where a reference method refers to a constructor. - // A new instance of the requested type will be created and the - // constructor with no parameters will be called. - // Example: String::new - if (delegateInvokeType == H_NEWINVOKESPECIAL) { - String conName = ""; - String conDesc = MethodType.methodType(void.class).toMethodDescriptorString(); - Method conMeth = new Method(conName, conDesc); - Type conType = Type.getType(delegateMethodType.returnType()); - - iface.newInstance(conType); - iface.dup(); - iface.invokeConstructor(conType, conMeth); - } else { - // Loads any captured variables onto the stack. - for (int captureCount = 0; captureCount < captures.length; ++captureCount) { - iface.loadThis(); - iface.getField( - lambdaClassType, captures[captureCount].name, captures[captureCount].type); - } - - // Loads any passed in arguments onto the stack. - iface.loadArgs(); - - // Handles the case for a lambda function or a static reference method. - // interfaceMethodType and delegateMethodType both have the captured types - // inserted into their type signatures. This later allows the delegate + // Loads any passed in arguments onto the stack. + iface.loadArgs(); + + // Handles the case for a lambda function or a static reference method. + // interfaceMethodType and delegateMethodType both have the captured types + // inserted into their type signatures. This later allows the delegate + // method to be invoked dynamically and have the interface method types + // appropriately converted to the delegate method types. + // Example: Integer::parseInt + // Example: something.each(x -> x + 1) + if (delegateInvokeType == H_INVOKESTATIC) { + interfaceMethodType = + interfaceMethodType.insertParameterTypes(0, factoryMethodType.parameterArray()); + delegateMethodType = + delegateMethodType.insertParameterTypes(0, factoryMethodType.parameterArray()); + } else if (delegateInvokeType == H_INVOKEVIRTUAL || + delegateInvokeType == H_INVOKEINTERFACE) { + // Handles the case for a virtual or interface reference method with no captures. + // delegateMethodType drops the 'this' parameter because it will be re-inserted + // when the method handle for the dynamically invoked delegate method is created. + // Example: Object::toString + if (captures.length == 0) { + Class clazz = delegateMethodType.parameterType(0); + delegateClassType = Type.getType(clazz); + delegateMethodType = delegateMethodType.dropParameterTypes(0, 1); + // Handles the case for a virtual or interface reference method with 'this' + // captured. interfaceMethodType inserts the 'this' type into its + // method signature. This later allows the delegate // method to be invoked dynamically and have the interface method types // appropriately converted to the delegate method types. - // Example: Integer::parseInt - // Example: something.each(x -> x + 1) - if (delegateInvokeType == H_INVOKESTATIC) { - interfaceMethodType = - interfaceMethodType.insertParameterTypes(0, factoryMethodType.parameterArray()); - delegateMethodType = - delegateMethodType.insertParameterTypes(0, factoryMethodType.parameterArray()); - } else if (delegateInvokeType == H_INVOKEVIRTUAL || - delegateInvokeType == H_INVOKEINTERFACE) { - // Handles the case for a virtual or interface reference method with no captures. - // delegateMethodType drops the 'this' parameter because it will be re-inserted - // when the method handle for the dynamically invoked delegate method is created. - // Example: Object::toString - if (captures.length == 0) { - Class clazz = delegateMethodType.parameterType(0); - delegateClassName = clazz.getName(); - delegateMethodType = delegateMethodType.dropParameterTypes(0, 1); - // Handles the case for a virtual or interface reference method with 'this' - // captured. interfaceMethodType inserts the 'this' type into its - // method signature. This later allows the delegate - // method to be invoked dynamically and have the interface method types - // appropriately converted to the delegate method types. - // Example: something::toString - } else if (captures.length == 1) { - Class clazz = factoryMethodType.parameterType(0); - delegateClassName = clazz.getName(); - interfaceMethodType = interfaceMethodType.insertParameterTypes(0, clazz); - } else { - throw new LambdaConversionException( - "unexpected number of captures [ " + captures.length + "]"); - } + // Example: something::toString + } else if (captures.length == 1) { + Class clazz = factoryMethodType.parameterType(0); + delegateClassType = Type.getType(clazz); + interfaceMethodType = interfaceMethodType.insertParameterTypes(0, clazz); } else { - throw new IllegalStateException( - "unexpected invocation type [" + delegateInvokeType + "]"); + throw new LambdaConversionException( + "unexpected number of captures [ " + captures.length + "]"); } + } else { + throw new IllegalStateException( + "unexpected invocation type [" + delegateInvokeType + "]"); + } - Handle delegateHandle = - new Handle(delegateInvokeType, delegateClassName.replace('.', '/'), - delegateMethodName, delegateMethodType.toMethodDescriptorString(), - delegateInvokeType == H_INVOKEINTERFACE); - iface.invokeDynamic(delegateMethodName, Type.getMethodType(interfaceMethodType - .toMethodDescriptorString()).getDescriptor(), DELEGATE_BOOTSTRAP_HANDLE, + Handle delegateHandle = + new Handle(delegateInvokeType, delegateClassType.getInternalName(), + delegateMethodName, delegateMethodType.toMethodDescriptorString(), + delegateInvokeType == H_INVOKEINTERFACE); + iface.invokeDynamic(delegateMethodName, Type.getMethodType(interfaceMethodType + .toMethodDescriptorString()).getDescriptor(), DELEGATE_BOOTSTRAP_HANDLE, delegateHandle); - } iface.returnValue(); iface.endMethod(); @@ -462,11 +429,13 @@ private static void endLambdaClass(ClassWriter cw) { private static Class createLambdaClass( Loader loader, ClassWriter cw, - String lambdaClassName) { + Type lambdaClassType) { byte[] classBytes = cw.toByteArray(); + // DEBUG: + // new ClassReader(classBytes).accept(new TraceClassVisitor(new PrintWriter(System.out)), ClassReader.SKIP_DEBUG); return AccessController.doPrivileged((PrivilegedAction>)() -> - loader.defineLambda(lambdaClassName.replace('/', '.'), classBytes)); + loader.defineLambda(lambdaClassType.getClassName(), classBytes)); } /** @@ -476,23 +445,12 @@ private static Class createLambdaClass( private static CallSite createNoCaptureCallSite( MethodType factoryMethodType, Class lambdaClass) { - - Constructor constructor = AccessController.doPrivileged( - (PrivilegedAction>)() -> { - try { - return lambdaClass.getConstructor(); - } catch (NoSuchMethodException nsme) { - throw new IllegalStateException("unable to create lambda class", nsme); - } - }); - + try { return new ConstantCallSite(MethodHandles.constant( - factoryMethodType.returnType(), constructor.newInstance())); - } catch (InstantiationException | - IllegalAccessException | - InvocationTargetException exception) { - throw new IllegalStateException("unable to create lambda class", exception); + factoryMethodType.returnType(), lambdaClass.getConstructor().newInstance())); + } catch (ReflectiveOperationException exception) { + throw new IllegalStateException("unable to instantiate lambda class", exception); } } @@ -501,15 +459,15 @@ private static CallSite createNoCaptureCallSite( */ private static CallSite createCaptureCallSite( Lookup lookup, - String factoryMethodName, MethodType factoryMethodType, Class lambdaClass) { try { return new ConstantCallSite( - lookup.findStatic(lambdaClass, factoryMethodName, factoryMethodType)); - } catch (NoSuchMethodException | IllegalAccessException exception) { - throw new IllegalStateException("unable to create lambda factory class", exception); + lookup.findConstructor(lambdaClass, factoryMethodType.changeReturnType(void.class)) + .asType(factoryMethodType)); + } catch (ReflectiveOperationException exception) { + throw new IllegalStateException("unable to create lambda class", exception); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java index 2772cdcf27521..6b9a71a752b3b 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java @@ -27,7 +27,6 @@ import org.objectweb.asm.commons.Method; import java.lang.invoke.CallSite; -import java.lang.invoke.LambdaMetafactory; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java index 4bd687a72058d..7c49d042108ef 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java @@ -91,6 +91,13 @@ public void testCtorMethodReferenceDef() { "return stats.getSum()")); } + public void testCtorWithParams() { + assertArrayEquals(new Object[] { "foo", "bar" }, + (Object[]) exec("List l = new ArrayList(); l.add('foo'); l.add('bar'); " + + "Stream stream = l.stream().map(StringBuilder::new);" + + "return stream.map(Object::toString).toArray()")); + } + public void testArrayCtorMethodRef() { assertEquals(1.0D, exec("List l = new ArrayList(); l.add(1.0); l.add(2.0); " + From 40ff169c544790b9c911f9962d45944d4190b122 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 1 May 2017 19:27:28 -0400 Subject: [PATCH 148/619] Set available processors for Netty Netty uses the number of processors for sizing various resources (e.g., thread pools, buffer pools, etc.). However, it uses the runtime number of available processors which might not match the configured number of processors as set in Elasticsearch to limit the number of threads (for example, in Docker containers). A new feature was added to Netty that enables configuring the number of processors Netty should see for sizing this various resources. This commit takes advantage of this feature to set this number of available processors to be equal to the configured number of processors set in Elasticsearch. Relates #24420 --- modules/transport-netty4/build.gradle | 16 ++++++++ .../netty4/Netty4HttpServerTransport.java | 1 + .../transport/netty4/Netty4Transport.java | 1 + .../transport/netty4/Netty4Utils.java | 39 +++++++++++++++++++ qa/smoke-test-http/build.gradle | 8 ++++ 5 files changed, 65 insertions(+) diff --git a/modules/transport-netty4/build.gradle b/modules/transport-netty4/build.gradle index 6bc64a178d923..c35dbe8ff293f 100644 --- a/modules/transport-netty4/build.gradle +++ b/modules/transport-netty4/build.gradle @@ -47,6 +47,22 @@ dependencyLicenses { mapping from: /netty-.*/, to: 'netty' } +test { + /* + * We have to disable setting the number of available processors as tests in the same JVM randomize processors and will step on each + * other if we allow them to set the number of available processors as it's set-once in Netty. + */ + systemProperty 'es.set.netty.runtime.available.processors', 'false' +} + +integTestRunner { + /* + * We have to disable setting the number of available processors as tests in the same JVM randomize processors and will step on each + * other if we allow them to set the number of available processors as it's set-once in Netty. + */ + systemProperty 'es.set.netty.runtime.available.processors', 'false' +} + thirdPartyAudit.excludes = [ // classes are missing diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpServerTransport.java b/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpServerTransport.java index 267a0d3dbfdc6..769ef34a78998 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpServerTransport.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpServerTransport.java @@ -209,6 +209,7 @@ public class Netty4HttpServerTransport extends AbstractLifecycleComponent implem public Netty4HttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays, ThreadPool threadPool, NamedXContentRegistry xContentRegistry, Dispatcher dispatcher) { super(settings); + Netty4Utils.setAvailableProcessors(EsExecutors.PROCESSORS_SETTING.get(settings)); this.networkService = networkService; this.bigArrays = bigArrays; this.threadPool = threadPool; diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Transport.java b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Transport.java index 4b515aab86958..8adeb665e0454 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Transport.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Transport.java @@ -134,6 +134,7 @@ public class Netty4Transport extends TcpTransport { public Netty4Transport(Settings settings, ThreadPool threadPool, NetworkService networkService, BigArrays bigArrays, NamedWriteableRegistry namedWriteableRegistry, CircuitBreakerService circuitBreakerService) { super("netty", settings, threadPool, bigArrays, circuitBreakerService, namedWriteableRegistry, networkService); + Netty4Utils.setAvailableProcessors(EsExecutors.PROCESSORS_SETTING.get(settings)); this.workerCount = WORKER_COUNT.get(settings); this.maxCumulationBufferCapacity = NETTY_MAX_CUMULATION_BUFFER_CAPACITY.get(settings); this.maxCompositeBufferComponents = NETTY_MAX_COMPOSITE_BUFFER_COMPONENTS.get(settings); diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Utils.java b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Utils.java index 4d28bf9a257e9..d71e1ee937690 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Utils.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Utils.java @@ -24,10 +24,12 @@ import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; +import io.netty.util.NettyRuntime; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefIterator; +import org.elasticsearch.common.Booleans; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.logging.ESLoggerFactory; @@ -36,6 +38,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Locale; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; public class Netty4Utils { @@ -55,6 +59,41 @@ public static void setup() { } + private static AtomicBoolean isAvailableProcessorsSet = new AtomicBoolean(); + + /** + * Set the number of available processors that Netty uses for sizing various resources (e.g., thread pools). + * + * @param availableProcessors the number of available processors + * @throws IllegalStateException if available processors was set previously and the specified value does not match the already-set value + */ + public static void setAvailableProcessors(final int availableProcessors) { + // we set this to false in tests to avoid tests that randomly set processors from stepping on each other + final boolean set = Booleans.parseBoolean(System.getProperty("es.set.netty.runtime.available.processors", "true")); + if (!set) { + return; + } + + /* + * This can be invoked twice, once from Netty4Transport and another time from Netty4HttpServerTransport; however, + * Netty4Runtime#availableProcessors forbids settings the number of processors twice so we prevent double invocation here. + */ + if (isAvailableProcessorsSet.compareAndSet(false, true)) { + NettyRuntime.setAvailableProcessors(availableProcessors); + } else if (availableProcessors != NettyRuntime.availableProcessors()) { + /* + * We have previously set the available processors yet either we are trying to set it to a different value now or there is a bug + * in Netty and our previous value did not take, bail. + */ + final String message = String.format( + Locale.ROOT, + "available processors value [%d] did not match current value [%d]", + availableProcessors, + NettyRuntime.availableProcessors()); + throw new IllegalStateException(message); + } + } + /** * Turns the given BytesReference into a ByteBuf. Note: the returned ByteBuf will reference the internal * pages of the BytesReference. Don't free the bytes of reference before the ByteBuf goes out of scope. diff --git a/qa/smoke-test-http/build.gradle b/qa/smoke-test-http/build.gradle index d15a4b5d90078..03440912d04b6 100644 --- a/qa/smoke-test-http/build.gradle +++ b/qa/smoke-test-http/build.gradle @@ -24,3 +24,11 @@ apply plugin: 'elasticsearch.test-with-dependencies' dependencies { testCompile project(path: ':modules:transport-netty4', configuration: 'runtime') // for http } + +integTestRunner { + /* + * We have to disable setting the number of available processors as tests in the same JVM randomize processors and will step on each + * other if we allow them to set the number of available processors as it's set-once in Netty. + */ + systemProperty 'es.set.netty.runtime.available.processors', 'false' +} From 186b401fd873b472303dcd6a9a83d22bad99b3c1 Mon Sep 17 00:00:00 2001 From: David Pilato Date: Tue, 2 May 2017 09:58:21 +0200 Subject: [PATCH 149/619] Upgrade to JUnit 4.12 (#23877) * Upgrade to JUnit 4.12 * Add permission to junit 4.12 and remove junit4-ant specific permission --- buildSrc/version.properties | 2 +- .../org/elasticsearch/bootstrap/test-framework.policy | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/buildSrc/version.properties b/buildSrc/version.properties index ca263b0efa93e..680c2fc89f5bf 100644 --- a/buildSrc/version.properties +++ b/buildSrc/version.properties @@ -14,7 +14,7 @@ jna = 4.4.0 # test dependencies randomizedrunner = 2.5.0 -junit = 4.11 +junit = 4.12 httpclient = 4.5.2 # When updating httpcore, please also update core/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy httpcore = 4.4.5 diff --git a/core/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy b/core/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy index 9a089ef2810ff..1e26c42a4cc10 100644 --- a/core/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy +++ b/core/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy @@ -53,12 +53,7 @@ grant codeBase "${codebase.randomizedtesting-runner-2.5.0.jar}" { permission java.lang.RuntimePermission "accessDeclaredMembers"; }; -grant codeBase "${codebase.junit4-ant-2.3.2.jar}" { - // needed for stream redirection - permission java.lang.RuntimePermission "setIO"; -}; - -grant codeBase "${codebase.junit-4.11.jar}" { +grant codeBase "${codebase.junit-4.12.jar}" { // needed for TestClass creation permission java.lang.RuntimePermission "accessDeclaredMembers"; }; From 9d8254fadf4c9ef4d27e9a7109c3bfeef6c4231a Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Tue, 2 May 2017 10:14:47 +0200 Subject: [PATCH 150/619] Fix FieldCaps documentation Fix the expected output for field_caps call. Fixes #24413 --- docs/reference/search/field-caps.asciidoc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/reference/search/field-caps.asciidoc b/docs/reference/search/field-caps.asciidoc index d327362f81c7b..42211342682d4 100644 --- a/docs/reference/search/field-caps.asciidoc +++ b/docs/reference/search/field-caps.asciidoc @@ -47,11 +47,11 @@ Supported request options: The field capabilities api returns the following information per field: [horizontal] -`is_searchable`:: +`searchable`:: Whether this field is indexed for search on all indices. -`is_aggregatable`:: +`aggregatable`:: Whether this field can be aggregated on all indices. @@ -88,22 +88,22 @@ GET _field_caps?fields=rating,title "fields": { "rating": { <1> "long": { - "is_searchable": true, - "is_aggregatable": false, + "searchable": true, + "aggregatable": false, "indices": ["index1", "index2"], "non_aggregatable_indices": ["index1"] <2> }, "keyword": { - "is_searchable": false, - "is_aggregatable": true, + "searchable": false, + "aggregatable": true, "indices": ["index3", "index4"], "non_searchable_indices": ["index4"] <3> } }, "title": { <4> "text": { - "is_searchable": true, - "is_aggregatable": false + "searchable": true, + "aggregatable": false } } From ad3c042fc40880b47686a8d73b0d3401e66e1fcc Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Tue, 2 May 2017 10:55:45 +0200 Subject: [PATCH 151/619] [Test] Add unit tests for HDR/TDigest PercentilesAggregators (#24245) Relates to #22278 --- .../hdr/HDRPercentilesAggregatorTests.java | 139 +++++++++++++++ .../TDigestPercentilesAggregatorTests.java | 160 ++++++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/HDRPercentilesAggregatorTests.java create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/TDigestPercentilesAggregatorTests.java diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/HDRPercentilesAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/HDRPercentilesAggregatorTests.java new file mode 100644 index 0000000000000..f264243044ef1 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/HDRPercentilesAggregatorTests.java @@ -0,0 +1,139 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.percentiles.hdr; + +import org.apache.lucene.document.LongPoint; +import org.apache.lucene.document.NumericDocValuesField; +import org.apache.lucene.document.SortedNumericDocValuesField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.search.FieldValueQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.store.Directory; +import org.elasticsearch.common.CheckedConsumer; +import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.NumberFieldMapper; +import org.elasticsearch.search.aggregations.AggregatorTestCase; +import org.elasticsearch.search.aggregations.metrics.percentiles.PercentilesAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.percentiles.PercentilesMethod; + +import java.io.IOException; +import java.util.function.Consumer; + +import static java.util.Arrays.asList; +import static java.util.Collections.singleton; + +public class HDRPercentilesAggregatorTests extends AggregatorTestCase { + + public void testNoDocs() throws IOException { + testCase(new MatchAllDocsQuery(), iw -> { + // Intentionally not writing any docs + }, hdr -> { + assertEquals(0L, hdr.state.getTotalCount()); + }); + } + + public void testNoMatchingField() throws IOException { + testCase(new MatchAllDocsQuery(), iw -> { + iw.addDocument(singleton(new SortedNumericDocValuesField("wrong_number", 7))); + iw.addDocument(singleton(new SortedNumericDocValuesField("wrong_number", 1))); + }, hdr -> { + assertEquals(0L, hdr.state.getTotalCount()); + }); + } + + public void testSomeMatchesSortedNumericDocValues() throws IOException { + testCase(new FieldValueQuery("number"), iw -> { + iw.addDocument(singleton(new SortedNumericDocValuesField("number", 60))); + iw.addDocument(singleton(new SortedNumericDocValuesField("number", 40))); + iw.addDocument(singleton(new SortedNumericDocValuesField("number", 20))); + iw.addDocument(singleton(new SortedNumericDocValuesField("number", 10))); + }, hdr -> { + assertEquals(4L, hdr.state.getTotalCount()); + double approximation = 0.05d; + assertEquals(10.0d, hdr.percentile(25), approximation); + assertEquals(20.0d, hdr.percentile(50), approximation); + assertEquals(40.0d, hdr.percentile(75), approximation); + assertEquals(60.0d, hdr.percentile(99), approximation); + }); + } + + public void testSomeMatchesNumericDocValues() throws IOException { + testCase(new FieldValueQuery("number"), iw -> { + iw.addDocument(singleton(new NumericDocValuesField("number", 60))); + iw.addDocument(singleton(new NumericDocValuesField("number", 40))); + iw.addDocument(singleton(new NumericDocValuesField("number", 20))); + iw.addDocument(singleton(new NumericDocValuesField("number", 10))); + }, hdr -> { + assertEquals(4L, hdr.state.getTotalCount()); + double approximation = 0.05d; + assertEquals(10.0d, hdr.percentile(25), approximation); + assertEquals(20.0d, hdr.percentile(50), approximation); + assertEquals(40.0d, hdr.percentile(75), approximation); + assertEquals(60.0d, hdr.percentile(99), approximation); + }); + } + + public void testQueryFiltering() throws IOException { + final CheckedConsumer docs = iw -> { + iw.addDocument(asList(new LongPoint("row", 4), new SortedNumericDocValuesField("number", 60))); + iw.addDocument(asList(new LongPoint("row", 3), new SortedNumericDocValuesField("number", 40))); + iw.addDocument(asList(new LongPoint("row", 2), new SortedNumericDocValuesField("number", 20))); + iw.addDocument(asList(new LongPoint("row", 1), new SortedNumericDocValuesField("number", 10))); + }; + + testCase(LongPoint.newRangeQuery("row", 0, 2), docs, hdr -> { + assertEquals(2L, hdr.state.getTotalCount()); + assertEquals(10.0d, hdr.percentile(randomDoubleBetween(1, 50, true)), 0.05d); + }); + + testCase(LongPoint.newRangeQuery("row", 5, 10), docs, hdr -> { + assertEquals(0L, hdr.state.getTotalCount()); + }); + } + + private void testCase(Query query, CheckedConsumer buildIndex, + Consumer verify) throws IOException { + try (Directory directory = newDirectory()) { + try (RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory)) { + buildIndex.accept(indexWriter); + } + + try (IndexReader indexReader = DirectoryReader.open(directory)) { + IndexSearcher indexSearcher = newSearcher(indexReader, true, true); + + PercentilesAggregationBuilder builder = + new PercentilesAggregationBuilder("test").field("number").method(PercentilesMethod.HDR); + + MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.LONG); + fieldType.setName("number"); + try (HDRPercentilesAggregator aggregator = createAggregator(builder, indexSearcher, fieldType)) { + aggregator.preCollection(); + indexSearcher.search(query, aggregator); + aggregator.postCollection(); + verify.accept((InternalHDRPercentiles) aggregator.buildAggregation(0L)); + } + } + } + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/TDigestPercentilesAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/TDigestPercentilesAggregatorTests.java new file mode 100644 index 0000000000000..90cc2464a1e7d --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/TDigestPercentilesAggregatorTests.java @@ -0,0 +1,160 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.percentiles.tdigest; + +import org.apache.lucene.document.LongPoint; +import org.apache.lucene.document.NumericDocValuesField; +import org.apache.lucene.document.SortedNumericDocValuesField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.search.FieldValueQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.store.Directory; +import org.elasticsearch.common.CheckedConsumer; +import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.NumberFieldMapper; +import org.elasticsearch.search.aggregations.AggregatorTestCase; +import org.elasticsearch.search.aggregations.metrics.percentiles.PercentilesAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.percentiles.PercentilesMethod; + +import java.io.IOException; +import java.util.function.Consumer; + +import static java.util.Arrays.asList; +import static java.util.Collections.singleton; + +public class TDigestPercentilesAggregatorTests extends AggregatorTestCase { + + public void testNoDocs() throws IOException { + testCase(new MatchAllDocsQuery(), iw -> { + // Intentionally not writing any docs + }, tdigest -> { + assertEquals(0L, tdigest.state.size()); + }); + } + + public void testNoMatchingField() throws IOException { + testCase(new MatchAllDocsQuery(), iw -> { + iw.addDocument(singleton(new SortedNumericDocValuesField("wrong_number", 7))); + iw.addDocument(singleton(new SortedNumericDocValuesField("wrong_number", 1))); + }, tdigest -> { + assertEquals(0L, tdigest.state.size()); + }); + } + + public void testSomeMatchesSortedNumericDocValues() throws IOException { + testCase(new FieldValueQuery("number"), iw -> { + iw.addDocument(singleton(new SortedNumericDocValuesField("number", 8))); + iw.addDocument(singleton(new SortedNumericDocValuesField("number", 5))); + iw.addDocument(singleton(new SortedNumericDocValuesField("number", 3))); + iw.addDocument(singleton(new SortedNumericDocValuesField("number", 2))); + iw.addDocument(singleton(new SortedNumericDocValuesField("number", 1))); + iw.addDocument(singleton(new SortedNumericDocValuesField("number", 1))); + iw.addDocument(singleton(new SortedNumericDocValuesField("number", 0))); + }, tdigest -> { + assertEquals(7L, tdigest.state.size()); + assertEquals(7L, tdigest.state.centroidCount()); + assertEquals(4.0d, tdigest.percentile(75), 0.0d); + assertEquals("4.0", tdigest.percentileAsString(75)); + assertEquals(2.0d, tdigest.percentile(50), 0.0d); + assertEquals("2.0", tdigest.percentileAsString(50)); + assertEquals(1.0d, tdigest.percentile(20), 0.0d); + assertEquals("1.0", tdigest.percentileAsString(20)); + }); + } + + public void testSomeMatchesNumericDocValues() throws IOException { + testCase(new FieldValueQuery("number"), iw -> { + iw.addDocument(singleton(new NumericDocValuesField("number", 8))); + iw.addDocument(singleton(new NumericDocValuesField("number", 5))); + iw.addDocument(singleton(new NumericDocValuesField("number", 3))); + iw.addDocument(singleton(new NumericDocValuesField("number", 2))); + iw.addDocument(singleton(new NumericDocValuesField("number", 1))); + iw.addDocument(singleton(new NumericDocValuesField("number", 1))); + iw.addDocument(singleton(new NumericDocValuesField("number", 0))); + }, tdigest -> { + assertEquals(tdigest.state.size(), 7L); + assertEquals(tdigest.state.centroidCount(), 7L); + assertEquals(8.0d, tdigest.percentile(100), 0.0d); + assertEquals("8.0", tdigest.percentileAsString(100)); + assertEquals(5.48d, tdigest.percentile(86), 0.0d); + assertEquals("5.48", tdigest.percentileAsString(86)); + assertEquals(1.0d, tdigest.percentile(33), 0.0d); + assertEquals("1.0", tdigest.percentileAsString(33)); + assertEquals(1.0d, tdigest.percentile(25), 0.0d); + assertEquals("1.0", tdigest.percentileAsString(25)); + assertEquals(0.06d, tdigest.percentile(1), 0.0d); + assertEquals("0.06", tdigest.percentileAsString(1)); + }); + } + + public void testQueryFiltering() throws IOException { + final CheckedConsumer docs = iw -> { + iw.addDocument(asList(new LongPoint("row", 7), new SortedNumericDocValuesField("number", 8))); + iw.addDocument(asList(new LongPoint("row", 6), new SortedNumericDocValuesField("number", 5))); + iw.addDocument(asList(new LongPoint("row", 5), new SortedNumericDocValuesField("number", 3))); + iw.addDocument(asList(new LongPoint("row", 4), new SortedNumericDocValuesField("number", 2))); + iw.addDocument(asList(new LongPoint("row", 3), new SortedNumericDocValuesField("number", 1))); + iw.addDocument(asList(new LongPoint("row", 2), new SortedNumericDocValuesField("number", 1))); + iw.addDocument(asList(new LongPoint("row", 1), new SortedNumericDocValuesField("number", 0))); + }; + + testCase(LongPoint.newRangeQuery("row", 1, 4), docs, tdigest -> { + assertEquals(4L, tdigest.state.size()); + assertEquals(4L, tdigest.state.centroidCount()); + assertEquals(2.0d, tdigest.percentile(100), 0.0d); + assertEquals(1.0d, tdigest.percentile(50), 0.0d); + assertEquals(0.75d, tdigest.percentile(25), 0.0d); + }); + + testCase(LongPoint.newRangeQuery("row", 100, 110), docs, tdigest -> { + assertEquals(0L, tdigest.state.size()); + assertEquals(0L, tdigest.state.centroidCount()); + }); + } + + private void testCase(Query query, CheckedConsumer buildIndex, + Consumer verify) throws IOException { + try (Directory directory = newDirectory()) { + try (RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory)) { + buildIndex.accept(indexWriter); + } + + try (IndexReader indexReader = DirectoryReader.open(directory)) { + IndexSearcher indexSearcher = newSearcher(indexReader, true, true); + + PercentilesAggregationBuilder builder = + new PercentilesAggregationBuilder("test").field("number").method(PercentilesMethod.TDIGEST); + + MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.LONG); + fieldType.setName("number"); + try (TDigestPercentilesAggregator aggregator = createAggregator(builder, indexSearcher, fieldType)) { + aggregator.preCollection(); + indexSearcher.search(query, aggregator); + aggregator.postCollection(); + verify.accept((InternalTDigestPercentiles) aggregator.buildAggregation(0L)); + } + } + } + } +} From c1ba4fdcb45fd7e2b50f0e5d236522b3e71cd7bc Mon Sep 17 00:00:00 2001 From: Winston Ewert Date: Tue, 2 May 2017 13:24:23 +0300 Subject: [PATCH 152/619] Allow scripted metric agg to access `_score` (#24295) * Fixes #24259 Corrects the ScriptedMetricAggregator so that the script can have access to scores during the map stage. * Restored original tests. Added seperate test. As requested, I've restored the non-score dependant tests, and added the score dependent metric as a seperate test. --- .../scripted/ScriptedMetricAggregator.java | 2 +- .../ScriptedMetricAggregatorTests.java | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregator.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregator.java index 3bfc057682a2a..cee7b3402f3e4 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregator.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregator.java @@ -63,7 +63,7 @@ public boolean needsScores() { public LeafBucketCollector getLeafCollector(LeafReaderContext ctx, final LeafBucketCollector sub) throws IOException { final LeafSearchScript leafMapScript = mapScript.getLeafSearchScript(ctx); - return new LeafBucketCollectorBase(sub, mapScript) { + return new LeafBucketCollectorBase(sub, leafMapScript) { @Override public void collect(int doc, long bucket) throws IOException { assert bucket == 0 : bucket; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregatorTests.java index d9eb76310d241..5dcae47da43b1 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregatorTests.java @@ -33,6 +33,7 @@ import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.script.MockScriptEngine; +import org.elasticsearch.script.ScoreAccessor; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptContextRegistry; import org.elasticsearch.script.ScriptEngineRegistry; @@ -59,6 +60,11 @@ public class ScriptedMetricAggregatorTests extends AggregatorTestCase { private static final Script MAP_SCRIPT = new Script(ScriptType.INLINE, MockScriptEngine.NAME, "mapScript", Collections.emptyMap()); private static final Script COMBINE_SCRIPT = new Script(ScriptType.INLINE, MockScriptEngine.NAME, "combineScript", Collections.emptyMap()); + + private static final Script INIT_SCRIPT_SCORE = new Script(ScriptType.INLINE, MockScriptEngine.NAME, "initScriptScore", Collections.emptyMap()); + private static final Script MAP_SCRIPT_SCORE = new Script(ScriptType.INLINE, MockScriptEngine.NAME, "mapScriptScore", Collections.emptyMap()); + private static final Script COMBINE_SCRIPT_SCORE = new Script(ScriptType.INLINE, MockScriptEngine.NAME, "combineScriptScore", + Collections.emptyMap()); private static final Map, Object>> SCRIPTS = new HashMap<>(); @@ -79,6 +85,21 @@ public static void initMockScripts() { Map agg = (Map) params.get("_agg"); return ((List) agg.get("collector")).stream().mapToInt(Integer::intValue).sum(); }); + + SCRIPTS.put("initScriptScore", params -> { + Map agg = (Map) params.get("_agg"); + agg.put("collector", new ArrayList()); + return agg; + }); + SCRIPTS.put("mapScriptScore", params -> { + Map agg = (Map) params.get("_agg"); + ((List) agg.get("collector")).add(((ScoreAccessor) params.get("_score")).doubleValue()); + return agg; + }); + SCRIPTS.put("combineScriptScore", params -> { + Map agg = (Map) params.get("_agg"); + return ((List) agg.get("collector")).stream().mapToDouble(Double::doubleValue).sum(); + }); } @SuppressWarnings("unchecked") @@ -144,6 +165,29 @@ public void testScriptedMetricWithCombine() throws IOException { } } + /** + * test that uses the score of the documents + */ + public void testScriptedMetricWithCombineAccessesScores() throws IOException { + try (Directory directory = newDirectory()) { + Integer numDocs = randomInt(100); + try (RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory)) { + for (int i = 0; i < numDocs; i++) { + indexWriter.addDocument(singleton(new SortedNumericDocValuesField("number", i))); + } + } + try (IndexReader indexReader = DirectoryReader.open(directory)) { + ScriptedMetricAggregationBuilder aggregationBuilder = new ScriptedMetricAggregationBuilder(AGG_NAME); + aggregationBuilder.initScript(INIT_SCRIPT_SCORE).mapScript(MAP_SCRIPT_SCORE).combineScript(COMBINE_SCRIPT_SCORE); + ScriptedMetric scriptedMetric = search(newSearcher(indexReader, true, true), new MatchAllDocsQuery(), aggregationBuilder); + assertEquals(AGG_NAME, scriptedMetric.getName()); + assertNotNull(scriptedMetric.aggregation()); + // all documents have score of 1.0 + assertEquals((double) numDocs, scriptedMetric.aggregation()); + } + } + } + /** * We cannot use Mockito for mocking QueryShardContext in this case because * script-related methods (e.g. QueryShardContext#getLazyExecutableScript) From 7b7cc488da436104f7212dd4f95d74e83952dde2 Mon Sep 17 00:00:00 2001 From: Colin Goodheart-Smithe Date: Tue, 2 May 2017 11:37:36 +0100 Subject: [PATCH 153/619] Fixes checkstyle errors --- .../metrics/scripted/ScriptedMetricAggregatorTests.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregatorTests.java index 5dcae47da43b1..aa48c64c3d788 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregatorTests.java @@ -61,8 +61,10 @@ public class ScriptedMetricAggregatorTests extends AggregatorTestCase { private static final Script COMBINE_SCRIPT = new Script(ScriptType.INLINE, MockScriptEngine.NAME, "combineScript", Collections.emptyMap()); - private static final Script INIT_SCRIPT_SCORE = new Script(ScriptType.INLINE, MockScriptEngine.NAME, "initScriptScore", Collections.emptyMap()); - private static final Script MAP_SCRIPT_SCORE = new Script(ScriptType.INLINE, MockScriptEngine.NAME, "mapScriptScore", Collections.emptyMap()); + private static final Script INIT_SCRIPT_SCORE = new Script(ScriptType.INLINE, MockScriptEngine.NAME, "initScriptScore", + Collections.emptyMap()); + private static final Script MAP_SCRIPT_SCORE = new Script(ScriptType.INLINE, MockScriptEngine.NAME, "mapScriptScore", + Collections.emptyMap()); private static final Script COMBINE_SCRIPT_SCORE = new Script(ScriptType.INLINE, MockScriptEngine.NAME, "combineScriptScore", Collections.emptyMap()); private static final Map, Object>> SCRIPTS = new HashMap<>(); From 582b3c06b6dab6f9be32ea63eba405b855a939c8 Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Tue, 2 May 2017 14:25:03 +0200 Subject: [PATCH 154/619] Added docs for batched_reduce_size Relates to #23288 --- docs/reference/search/request-body.asciidoc | 8 ++++++++ docs/reference/search/uri-request.asciidoc | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/docs/reference/search/request-body.asciidoc b/docs/reference/search/request-body.asciidoc index 13df8d471eb0b..8e90711df952e 100644 --- a/docs/reference/search/request-body.asciidoc +++ b/docs/reference/search/request-body.asciidoc @@ -93,6 +93,14 @@ And here is a sample response: the query execution has actually terminated_early. Defaults to no terminate_after. +`batched_reduce_size`:: + + The number of shard results that should be reduced at once on the + coordinating node. This value should be used as a protection mechanism to + reduce the memory overhead per search request if the potential number of + shards in the request can be large. + + Out of the above, the `search_type` and the `request_cache` must be passed as query-string parameters. The rest of the search request should be passed diff --git a/docs/reference/search/uri-request.asciidoc b/docs/reference/search/uri-request.asciidoc index 6670b9f31d574..9010db8e0a1df 100644 --- a/docs/reference/search/uri-request.asciidoc +++ b/docs/reference/search/uri-request.asciidoc @@ -67,6 +67,11 @@ query. |`analyze_wildcard` |Should wildcard and prefix queries be analyzed or not. Defaults to `false`. +|`batched_reduce_size` | The number of shard results that should be reduced +at once on the coordinating node. This value should be used as a protection +mechanism to reduce the memory overhead per search request if the potential +number of shards in the request can be large. + |`default_operator` |The default operator to be used, can be `AND` or `OR`. Defaults to `OR`. From 1435c23df25f95d6e1e97ea7dce84aea5572fd58 Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Tue, 2 May 2017 09:56:05 -0400 Subject: [PATCH 155/619] Adds check to snapshot repository incompatible-snapshots blob to delete a pre-existing one before attempting to overwrite it. --- .../repositories/blobstore/BlobStoreRepository.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index 803c58c12f4a5..805f902100cf9 100644 --- a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -728,6 +728,9 @@ void writeIncompatibleSnapshots(RepositoryData repositoryData) throws IOExceptio } bytes = bStream.bytes(); } + if (snapshotsBlobContainer.blobExists(INCOMPATIBLE_SNAPSHOTS_BLOB)) { + snapshotsBlobContainer.deleteBlob(INCOMPATIBLE_SNAPSHOTS_BLOB); + } // write the incompatible snapshots blob writeAtomic(INCOMPATIBLE_SNAPSHOTS_BLOB, bytes); } From 691ec68a3c1ed87889b50fae16ded298f3e5050a Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Tue, 2 May 2017 16:02:36 +0200 Subject: [PATCH 156/619] Extract a common base class to allow services to listen to remote cluster config updates (#24367) RemoteClusterService is an internal service that should not necessarily be exposed to plugins or other parts of the system. Yet, for cluster name parsing for instance it is crucial to reuse some code that is used for the RemoteClusterService. This change extracts a base class that allows to share the settings related code as well as cluster settings updates to `search.remote.*` to be observed by other services. --- .../action/search/RemoteClusterAware.java | 162 ++++++++++++++++++ .../action/search/RemoteClusterService.java | 110 +----------- .../action/search/SearchTransportService.java | 4 +- .../action/search/TransportSearchAction.java | 2 +- .../common/settings/ClusterSettings.java | 3 +- .../search/RemoteClusterServiceTests.java | 12 +- .../action/search/SearchAsyncActionTests.java | 2 +- 7 files changed, 181 insertions(+), 114 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/action/search/RemoteClusterAware.java diff --git a/core/src/main/java/org/elasticsearch/action/search/RemoteClusterAware.java b/core/src/main/java/org/elasticsearch/action/search/RemoteClusterAware.java new file mode 100644 index 0000000000000..2785a8efdb6f0 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/action/search/RemoteClusterAware.java @@ -0,0 +1,162 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.action.search; + +import org.elasticsearch.Version; +import org.elasticsearch.cluster.metadata.ClusterNameExpressionResolver; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.common.component.AbstractComponent; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.transport.TransportAddress; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Base class for all services and components that need up-to-date information about the registered remote clusters + */ +public abstract class RemoteClusterAware extends AbstractComponent { + + /** + * A list of initial seed nodes to discover eligible nodes from the remote cluster + */ + public static final Setting.AffixSetting> REMOTE_CLUSTERS_SEEDS = Setting.affixKeySetting("search.remote.", + "seeds", (key) -> Setting.listSetting(key, Collections.emptyList(), RemoteClusterAware::parseSeedAddress, + Setting.Property.NodeScope, Setting.Property.Dynamic)); + protected static final char REMOTE_CLUSTER_INDEX_SEPARATOR = ':'; + protected static final String LOCAL_CLUSTER_GROUP_KEY = ""; + protected final ClusterNameExpressionResolver clusterNameResolver; + + /** + * Creates a new {@link RemoteClusterAware} instance + * @param settings the nodes level settings + */ + protected RemoteClusterAware(Settings settings) { + super(settings); + this.clusterNameResolver = new ClusterNameExpressionResolver(settings); + } + + protected static Map> buildRemoteClustersSeeds(Settings settings) { + Stream>> allConcreteSettings = REMOTE_CLUSTERS_SEEDS.getAllConcreteSettings(settings); + return allConcreteSettings.collect( + Collectors.toMap(REMOTE_CLUSTERS_SEEDS::getNamespace, concreteSetting -> { + String clusterName = REMOTE_CLUSTERS_SEEDS.getNamespace(concreteSetting); + List nodes = new ArrayList<>(); + for (InetSocketAddress address : concreteSetting.get(settings)) { + TransportAddress transportAddress = new TransportAddress(address); + DiscoveryNode node = new DiscoveryNode(clusterName + "#" + transportAddress.toString(), + transportAddress, + Version.CURRENT.minimumCompatibilityVersion()); + nodes.add(node); + } + return nodes; + })); + } + + /** + * Groups indices per cluster by splitting remote cluster-alias, index-name pairs on {@link #REMOTE_CLUSTER_INDEX_SEPARATOR}. All + * indices per cluster are collected as a list in the returned map keyed by the cluster alias. Local indices are grouped under + * {@link #LOCAL_CLUSTER_GROUP_KEY}. The returned map is mutable. + * + * @param requestIndices the indices in the search request to filter + * @param indexExists a predicate that can test if a certain index or alias exists in the local cluster + * + * @return a map of grouped remote and local indices + */ + protected Map> groupClusterIndices(String[] requestIndices, Predicate indexExists) { + Map> perClusterIndices = new HashMap<>(); + Set remoteClusterNames = getRemoteClusterNames(); + for (String index : requestIndices) { + int i = index.indexOf(RemoteClusterService.REMOTE_CLUSTER_INDEX_SEPARATOR); + if (i >= 0) { + String remoteClusterName = index.substring(0, i); + List clusters = clusterNameResolver.resolveClusterNames(remoteClusterNames, remoteClusterName); + if (clusters.isEmpty() == false) { + if (indexExists.test(index)) { + // we use : as a separator for remote clusters. might conflict if there is an index that is actually named + // remote_cluster_alias:index_name - for this case we fail the request. the user can easily change the cluster alias + // if that happens + throw new IllegalArgumentException("Can not filter indices; index " + index + + " exists but there is also a remote cluster named: " + remoteClusterName); + } + String indexName = index.substring(i + 1); + for (String clusterName : clusters) { + perClusterIndices.computeIfAbsent(clusterName, k -> new ArrayList<>()).add(indexName); + } + } else { + perClusterIndices.computeIfAbsent(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, k -> new ArrayList<>()).add(index); + } + } else { + perClusterIndices.computeIfAbsent(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, k -> new ArrayList<>()).add(index); + } + } + return perClusterIndices; + } + + protected abstract Set getRemoteClusterNames(); + + /** + * Subclasses must implement this to receive information about updated cluster aliases. If the given address list is + * empty the cluster alias is unregistered and should be removed. + */ + protected abstract void updateRemoteCluster(String clusterAlias, List addresses); + + /** + * Registers this instance to listen to updates on the cluster settings. + */ + public void listenForUpdates(ClusterSettings clusterSettings) { + clusterSettings.addAffixUpdateConsumer(RemoteClusterAware.REMOTE_CLUSTERS_SEEDS, this::updateRemoteCluster, + (namespace, value) -> {}); + } + + private static InetSocketAddress parseSeedAddress(String remoteHost) { + int portSeparator = remoteHost.lastIndexOf(':'); // in case we have a IPv6 address ie. [::1]:9300 + if (portSeparator == -1 || portSeparator == remoteHost.length()) { + throw new IllegalArgumentException("remote hosts need to be configured as [host:port], found [" + remoteHost + "] instead"); + } + String host = remoteHost.substring(0, portSeparator); + InetAddress hostAddress; + try { + hostAddress = InetAddress.getByName(host); + } catch (UnknownHostException e) { + throw new IllegalArgumentException("unknown host [" + host + "]", e); + } + try { + int port = Integer.valueOf(remoteHost.substring(portSeparator + 1)); + if (port <= 0) { + throw new IllegalArgumentException("port number must be > 0 but was: [" + port + "]"); + } + return new InetSocketAddress(hostAddress, port); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("port must be a number", e); + } + } +} diff --git a/core/src/main/java/org/elasticsearch/action/search/RemoteClusterService.java b/core/src/main/java/org/elasticsearch/action/search/RemoteClusterService.java index 40fed0299b3f9..cf2be61ed0526 100644 --- a/core/src/main/java/org/elasticsearch/action/search/RemoteClusterService.java +++ b/core/src/main/java/org/elasticsearch/action/search/RemoteClusterService.java @@ -27,11 +27,9 @@ import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsResponse; import org.elasticsearch.action.support.GroupedActionListener; import org.elasticsearch.action.support.PlainActionFuture; -import org.elasticsearch.cluster.metadata.ClusterNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.Booleans; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; @@ -46,10 +44,7 @@ import java.io.Closeable; import java.io.IOException; -import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -64,21 +59,12 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * Basic service for accessing remote clusters via gateway nodes */ -public final class RemoteClusterService extends AbstractComponent implements Closeable { +public final class RemoteClusterService extends RemoteClusterAware implements Closeable { - static final String LOCAL_CLUSTER_GROUP_KEY = ""; - - /** - * A list of initial seed nodes to discover eligible nodes from the remote cluster - */ - public static final Setting.AffixSetting> REMOTE_CLUSTERS_SEEDS = Setting.affixKeySetting("search.remote.", - "seeds", (key) -> Setting.listSetting(key, Collections.emptyList(), RemoteClusterService::parseSeedAddress, - Setting.Property.NodeScope, Setting.Property.Dynamic)); /** * The maximum number of connections that will be established to a remote cluster. For instance if there is only a single * seed node, other nodes will be discovered up to the given number of nodes in this setting. The default is 3. @@ -109,17 +95,13 @@ public final class RemoteClusterService extends AbstractComponent implements Clo public static final Setting ENABLE_REMOTE_CLUSTERS = Setting.boolSetting("search.remote.connect", true, Setting.Property.NodeScope); - private static final char REMOTE_CLUSTER_INDEX_SEPARATOR = ':'; - private final TransportService transportService; private final int numRemoteConnections; - private final ClusterNameExpressionResolver clusterNameResolver; private volatile Map remoteClusters = Collections.emptyMap(); RemoteClusterService(Settings settings, TransportService transportService) { super(settings); this.transportService = transportService; - this.clusterNameResolver = new ClusterNameExpressionResolver(settings); numRemoteConnections = REMOTE_CONNECTIONS_PER_CLUSTER.get(settings); } @@ -195,46 +177,6 @@ boolean isRemoteNodeConnected(final String remoteCluster, final DiscoveryNode no return remoteClusters.get(remoteCluster).isNodeConnected(node); } - /** - * Groups indices per cluster by splitting remote cluster-alias, index-name pairs on {@link #REMOTE_CLUSTER_INDEX_SEPARATOR}. All - * indices per cluster are collected as a list in the returned map keyed by the cluster alias. Local indices are grouped under - * {@link #LOCAL_CLUSTER_GROUP_KEY}. The returned map is mutable. - * - * @param requestIndices the indices in the search request to filter - * @param indexExists a predicate that can test if a certain index or alias exists - * - * @return a map of grouped remote and local indices - */ - Map> groupClusterIndices(String[] requestIndices, Predicate indexExists) { - Map> perClusterIndices = new HashMap<>(); - Set remoteClusterNames = this.remoteClusters.keySet(); - for (String index : requestIndices) { - int i = index.indexOf(REMOTE_CLUSTER_INDEX_SEPARATOR); - if (i >= 0) { - String remoteClusterName = index.substring(0, i); - List clusters = clusterNameResolver.resolveClusterNames(remoteClusterNames, remoteClusterName); - if (clusters.isEmpty() == false) { - if (indexExists.test(index)) { - // we use : as a separator for remote clusters. might conflict if there is an index that is actually named - // remote_cluster_alias:index_name - for this case we fail the request. the user can easily change the cluster alias - // if that happens - throw new IllegalArgumentException("Can not filter indices; index " + index + - " exists but there is also a remote cluster named: " + remoteClusterName); - } - String indexName = index.substring(i + 1); - for (String clusterName : clusters) { - perClusterIndices.computeIfAbsent(clusterName, k -> new ArrayList<>()).add(indexName); - } - } else { - perClusterIndices.computeIfAbsent(LOCAL_CLUSTER_GROUP_KEY, k -> new ArrayList<>()).add(index); - } - } else { - perClusterIndices.computeIfAbsent(LOCAL_CLUSTER_GROUP_KEY, k -> new ArrayList<>()).add(index); - } - } - return perClusterIndices; -} - /** * Returns true iff the given cluster is configured as a remote cluster. Otherwise false */ @@ -342,7 +284,12 @@ private Transport.Connection getConnection(DiscoveryNode node, String cluster) { return connection.getConnection(node); } - void updateRemoteCluster(String clusterAlias, List addresses) { + @Override + protected Set getRemoteClusterNames() { + return this.remoteClusters.keySet(); + } + + protected void updateRemoteCluster(String clusterAlias, List addresses) { updateRemoteCluster(clusterAlias, addresses, ActionListener.wrap((x) -> {}, (x) -> {})); } @@ -359,47 +306,6 @@ void updateRemoteCluster( updateRemoteClusters(Collections.singletonMap(clusterAlias, nodes), connectionListener); } - static Map> buildRemoteClustersSeeds(Settings settings) { - Stream>> allConcreteSettings = REMOTE_CLUSTERS_SEEDS.getAllConcreteSettings(settings); - return allConcreteSettings.collect( - Collectors.toMap(REMOTE_CLUSTERS_SEEDS::getNamespace, concreteSetting -> { - String clusterName = REMOTE_CLUSTERS_SEEDS.getNamespace(concreteSetting); - List nodes = new ArrayList<>(); - for (InetSocketAddress address : concreteSetting.get(settings)) { - TransportAddress transportAddress = new TransportAddress(address); - DiscoveryNode node = new DiscoveryNode(clusterName + "#" + transportAddress.toString(), - transportAddress, - Version.CURRENT.minimumCompatibilityVersion()); - nodes.add(node); - } - return nodes; - })); - } - - private static InetSocketAddress parseSeedAddress(String remoteHost) { - int portSeparator = remoteHost.lastIndexOf(':'); // in case we have a IPv6 address ie. [::1]:9300 - if (portSeparator == -1 || portSeparator == remoteHost.length()) { - throw new IllegalArgumentException("remote hosts need to be configured as [host:port], found [" + remoteHost + "] instead"); - } - String host = remoteHost.substring(0, portSeparator); - InetAddress hostAddress; - try { - hostAddress = InetAddress.getByName(host); - } catch (UnknownHostException e) { - throw new IllegalArgumentException("unknown host [" + host + "]", e); - } - try { - int port = Integer.valueOf(remoteHost.substring(portSeparator + 1)); - if (port <= 0) { - throw new IllegalArgumentException("port number must be > 0 but was: [" + port + "]"); - } - return new InetSocketAddress(hostAddress, port); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("port must be a number", e); - } - - } - /** * Connects to all remote clusters in a blocking fashion. This should be called on node startup to establish an initial connection * to all configured seed nodes. @@ -407,7 +313,7 @@ private static InetSocketAddress parseSeedAddress(String remoteHost) { void initializeRemoteClusters() { final TimeValue timeValue = REMOTE_INITIAL_CONNECTION_TIMEOUT_SETTING.get(settings); final PlainActionFuture future = new PlainActionFuture<>(); - Map> seeds = buildRemoteClustersSeeds(settings); + Map> seeds = RemoteClusterAware.buildRemoteClustersSeeds(settings); updateRemoteClusters(seeds, future); try { future.get(timeValue.millis(), TimeUnit.MILLISECONDS); diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java index 436d8da95eba1..a221c6001a58a 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java @@ -86,9 +86,7 @@ public SearchTransportService(Settings settings, ClusterSettings clusterSettings this.transportService = transportService; this.remoteClusterService = new RemoteClusterService(settings, transportService); if (connectToRemote) { - clusterSettings.addAffixUpdateConsumer(RemoteClusterService.REMOTE_CLUSTERS_SEEDS, remoteClusterService::updateRemoteCluster, - (namespace, value) -> { - }); + remoteClusterService.listenForUpdates(clusterSettings); } } diff --git a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 6f7cc26e59e58..94803d771ebaf 100644 --- a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -179,7 +179,7 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< final Map> groupedIndices = remoteClusterService.groupClusterIndices(searchRequest.indices(), // empty string is not allowed idx -> indexNameExpressionResolver.hasIndexOrAlias(idx, clusterState)); - List remove = groupedIndices.remove(RemoteClusterService.LOCAL_CLUSTER_GROUP_KEY); + List remove = groupedIndices.remove(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY); String[] indices = remove == null ? Strings.EMPTY_ARRAY : remove.toArray(new String[remove.size()]); localIndices = new OriginalIndices(indices, searchRequest.indicesOptions()); Map originalIndicesMap = new HashMap<>(); diff --git a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java index 0e92c43844c55..18ca8ef7ad624 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java +++ b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java @@ -19,6 +19,7 @@ package org.elasticsearch.common.settings; import org.elasticsearch.action.admin.indices.close.TransportCloseIndexAction; +import org.elasticsearch.action.search.RemoteClusterAware; import org.elasticsearch.action.search.RemoteClusterService; import org.elasticsearch.action.search.TransportSearchAction; import org.elasticsearch.action.support.AutoCreateIndex; @@ -254,7 +255,7 @@ public void apply(Settings value, Settings current, Settings previous) { SearchService.DEFAULT_SEARCH_TIMEOUT_SETTING, ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING, TransportSearchAction.SHARD_COUNT_LIMIT_SETTING, - RemoteClusterService.REMOTE_CLUSTERS_SEEDS, + RemoteClusterAware.REMOTE_CLUSTERS_SEEDS, RemoteClusterService.REMOTE_CONNECTIONS_PER_CLUSTER, RemoteClusterService.REMOTE_INITIAL_CONNECTION_TIMEOUT_SETTING, RemoteClusterService.REMOTE_NODE_ATTRIBUTE, diff --git a/core/src/test/java/org/elasticsearch/action/search/RemoteClusterServiceTests.java b/core/src/test/java/org/elasticsearch/action/search/RemoteClusterServiceTests.java index 63f6e8aa5a63e..909c85a5b60eb 100644 --- a/core/src/test/java/org/elasticsearch/action/search/RemoteClusterServiceTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/RemoteClusterServiceTests.java @@ -78,7 +78,7 @@ private MockTransportService startTransport( } public void testSettingsAreRegistered() { - assertTrue(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS.contains(RemoteClusterService.REMOTE_CLUSTERS_SEEDS)); + assertTrue(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS.contains(RemoteClusterAware.REMOTE_CLUSTERS_SEEDS)); assertTrue(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS.contains(RemoteClusterService.REMOTE_CONNECTIONS_PER_CLUSTER)); assertTrue(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS.contains(RemoteClusterService.REMOTE_INITIAL_CONNECTION_TIMEOUT_SETTING)); assertTrue(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS.contains(RemoteClusterService.REMOTE_NODE_ATTRIBUTE)); @@ -89,12 +89,12 @@ public void testRemoteClusterSeedSetting() { Settings settings = Settings.builder() .put("search.remote.foo.seeds", "192.168.0.1:8080") .put("search.remote.bar.seed", "[::1]:9090").build(); - RemoteClusterService.REMOTE_CLUSTERS_SEEDS.getAllConcreteSettings(settings).forEach(setting -> setting.get(settings)); + RemoteClusterAware.REMOTE_CLUSTERS_SEEDS.getAllConcreteSettings(settings).forEach(setting -> setting.get(settings)); Settings brokenSettings = Settings.builder() .put("search.remote.foo.seeds", "192.168.0.1").build(); expectThrows(IllegalArgumentException.class, () -> - RemoteClusterService.REMOTE_CLUSTERS_SEEDS.getAllConcreteSettings(brokenSettings).forEach(setting -> setting.get(brokenSettings))); + RemoteClusterAware.REMOTE_CLUSTERS_SEEDS.getAllConcreteSettings(brokenSettings).forEach(setting -> setting.get(brokenSettings))); } public void testBuiltRemoteClustersSeeds() throws Exception { @@ -145,9 +145,9 @@ public void testGroupClusterIndices() throws IOException { assertFalse(service.isRemoteClusterRegistered("foo")); Map> perClusterIndices = service.groupClusterIndices(new String[]{"foo:bar", "cluster_1:bar", "cluster_2:foo:bar", "cluster_1:test", "cluster_2:foo*", "foo", "cluster*:baz", "*:boo", "no*match:boo"}, i -> false); - String[] localIndices = perClusterIndices.computeIfAbsent(RemoteClusterService.LOCAL_CLUSTER_GROUP_KEY, + String[] localIndices = perClusterIndices.computeIfAbsent(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, k -> Collections.emptyList()).toArray(new String[0]); - assertNotNull(perClusterIndices.remove(RemoteClusterService.LOCAL_CLUSTER_GROUP_KEY)); + assertNotNull(perClusterIndices.remove(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)); assertArrayEquals(new String[]{"foo:bar", "foo", "no*match:boo"}, localIndices); assertEquals(2, perClusterIndices.size()); assertEquals(Arrays.asList("bar", "test", "baz", "boo"), perClusterIndices.get("cluster_1")); @@ -195,7 +195,7 @@ public void testIncrementallyAddClusters() throws IOException { service.updateRemoteCluster("cluster_2", Collections.emptyList()); assertFalse(service.isRemoteClusterRegistered("cluster_2")); IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, - () -> service.updateRemoteCluster(RemoteClusterService.LOCAL_CLUSTER_GROUP_KEY, Collections.emptyList())); + () -> service.updateRemoteCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, Collections.emptyList())); assertEquals("remote clusters must not have the empty string as its key", iae.getMessage()); } } diff --git a/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java b/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java index 2d94fe2edd0db..1039e8e959d3d 100644 --- a/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java @@ -81,7 +81,7 @@ public void onFailure(Exception e) { randomIntBetween(1, 10), randomBoolean(), primaryNode, replicaNode); AtomicInteger numFreedContext = new AtomicInteger(); SearchTransportService transportService = new SearchTransportService(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, - Collections.singleton(RemoteClusterService.REMOTE_CLUSTERS_SEEDS)), null) { + Collections.singleton(RemoteClusterAware.REMOTE_CLUSTERS_SEEDS)), null) { @Override public void sendFreeContext(Transport.Connection connection, long contextId, OriginalIndices originalIndices) { numFreedContext.incrementAndGet(); From 62fa7081b0a886e9856734c5cc02d967a62055b0 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Tue, 2 May 2017 17:08:51 +0200 Subject: [PATCH 157/619] Painless: Add tests to check for existence and correct detection of the special Java 9 optimizations: Indified String concat and MethodHandles#ArrayLengthHelper() (#24405) --- .../src/main/java/org/elasticsearch/painless/Def.java | 4 ++-- .../test/java/org/elasticsearch/painless/ArrayTests.java | 2 ++ .../java/org/elasticsearch/painless/StringTests.java | 9 +++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index 6d9fad7d79e17..dd1b51a5151bb 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java @@ -112,8 +112,8 @@ private ArrayLengthHelper() {} private static final MethodHandle MAP_INDEX_NORMALIZE; /** pointer to {@link Def#listIndexNormalize}. */ private static final MethodHandle LIST_INDEX_NORMALIZE; - /** factory for arraylength MethodHandle (intrinsic) from Java 9 */ - private static final MethodHandle JAVA9_ARRAY_LENGTH_MH_FACTORY; + /** factory for arraylength MethodHandle (intrinsic) from Java 9 (pkg-private for tests) */ + static final MethodHandle JAVA9_ARRAY_LENGTH_MH_FACTORY; static { final Lookup lookup = MethodHandles.publicLookup(); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ArrayTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ArrayTests.java index fe2ee1683bb77..5c1141a49e557 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ArrayTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ArrayTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.painless; +import org.apache.lucene.util.Constants; import org.hamcrest.Matcher; import java.lang.invoke.MethodHandle; @@ -44,6 +45,7 @@ protected Matcher outOfBoundsExceptionMessageMatcher(int index, int size } public void testArrayLengthHelper() throws Throwable { + assertEquals(Constants.JRE_IS_MINIMUM_JAVA9, Def.JAVA9_ARRAY_LENGTH_MH_FACTORY != null); assertArrayLength(2, new int[2]); assertArrayLength(3, new long[3]); assertArrayLength(4, new byte[4]); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/StringTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/StringTests.java index 2888eca3db4fa..55cce62b8193a 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/StringTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/StringTests.java @@ -19,6 +19,8 @@ package org.elasticsearch.painless; +import org.apache.lucene.util.Constants; + import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -249,4 +251,11 @@ public void testBase64Augmentations() { String rando = randomRealisticUnicodeOfLength(between(5, 1000)); assertEquals(rando, exec("params.rando.encodeBase64().decodeBase64()", singletonMap("rando", rando), true)); } + + public void testJava9StringConcatBytecode() { + assumeTrue("Needs Java 9 to test indified String concat", Constants.JRE_IS_MINIMUM_JAVA9); + assertNotNull(WriterConstants.INDY_STRING_CONCAT_BOOTSTRAP_HANDLE); + assertBytecodeExists("String s = \"cat\"; return s + true + 'abc' + null;", + "INVOKEDYNAMIC concat(Ljava/lang/String;ZLjava/lang/String;Ljava/lang/Object;)Ljava/lang/String;"); + } } From d0a10cf140671d114ac7cba142d2112bef6c14ec Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Tue, 2 May 2017 08:28:43 -0700 Subject: [PATCH 158/619] [DOCS] Update XPack Reference URL for 5.4 (#24425) --- docs/Versions.asciidoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/Versions.asciidoc b/docs/Versions.asciidoc index dc2164dc3b18f..12e606ba78a11 100644 --- a/docs/Versions.asciidoc +++ b/docs/Versions.asciidoc @@ -15,7 +15,7 @@ release-state can be: released | prerelease | unreleased :defguide: https://www.elastic.co/guide/en/elasticsearch/guide/master :plugins: https://www.elastic.co/guide/en/elasticsearch/plugins/{branch} :javaclient: https://www.elastic.co/guide/en/elasticsearch/client/java-api/{branch} -:xpack: https://www.elastic.co/guide/en/x-pack/5.0 +:xpack: https://www.elastic.co/guide/en/x-pack/5.4 :logstash: https://www.elastic.co/guide/en/logstash/{branch} :kibana: https://www.elastic.co/guide/en/kibana/{branch} :issue: https://github.com/elastic/elasticsearch/issues/ @@ -42,4 +42,3 @@ ifeval::["{release-state}"!="unreleased"] :elasticsearch-javadoc: https://artifacts.elastic.co/javadoc/org/elasticsearch/elasticsearch/{version} :painless-javadoc: https://artifacts.elastic.co/javadoc/org/elasticsearch/painless/lang-painless/{version} endif::[] - From 3b47355e5691928219494b3c4f40e51fd01f53e9 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Tue, 2 May 2017 11:34:12 -0400 Subject: [PATCH 159/619] Try not to lose stacktraces (#24426) This adds `-XX:-OmitStackTraceInFastThrow` to the JVM arguments which *should* prevent the JVM from omitting stack traces on common exception sites. Even though these sites are common, we'd still like the exceptions to debug them. This also adds the flag when running tests and adapts some tests that had workarounds for the absense of the flag. Closes #24376 --- .../org/elasticsearch/gradle/BuildPlugin.groovy | 1 + distribution/src/main/resources/config/jvm.options | 4 ++++ .../painless/ArrayLikeObjectTestCase.java | 7 ++++++- .../java/org/elasticsearch/painless/ListTests.java | 7 +------ .../org/elasticsearch/painless/ScriptTestCase.java | 13 +++++++------ 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy index b3c2f4faef8e8..19f3846ca5d5b 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy @@ -469,6 +469,7 @@ class BuildPlugin implements Plugin { heapdumpDir.mkdirs() jvmArg '-XX:HeapDumpPath=' + heapdumpDir argLine System.getProperty('tests.jvm.argline') + argLine '-XX:-OmitStackTraceInFastThrow' // we use './temp' since this is per JVM and tests are forbidden from writing to CWD systemProperty 'java.io.tmpdir', './temp' diff --git a/distribution/src/main/resources/config/jvm.options b/distribution/src/main/resources/config/jvm.options index e08ffcabbb016..293e4c092f60b 100644 --- a/distribution/src/main/resources/config/jvm.options +++ b/distribution/src/main/resources/config/jvm.options @@ -62,6 +62,10 @@ # use our provided JNA always versus the system one -Djna.nosys=true +# turn off a JDK optimization that throws away stack traces for common +# exceptions because stack traces are important for debugging +-XX:-OmitStackTraceInFastThrow + # use old-style file permissions on JDK9 -Djdk.io.permissionsUseCanonicalPath=true diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ArrayLikeObjectTestCase.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ArrayLikeObjectTestCase.java index 5fc41c8c63038..2f7c37e2f6939 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ArrayLikeObjectTestCase.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ArrayLikeObjectTestCase.java @@ -80,9 +80,14 @@ private void expectOutOfBounds(int index, String script, Object val) { IndexOutOfBoundsException e = expectScriptThrows(IndexOutOfBoundsException.class, () -> exec(script, singletonMap("val", val), true)); try { + /* If this fails you *might* be missing -XX:-OmitStackTraceInFastThrow in the test jvm + * In Eclipse you can add this by default by going to Preference->Java->Installed JREs, + * clicking on the default JRE, clicking edit, and adding the flag to the + * "Default VM Arguments". + */ assertThat(e.getMessage(), outOfBoundsExceptionMessageMatcher(index, 5)); } catch (AssertionError ae) { - ae.addSuppressed(e); // Mark the exception we are testing as suppressed so we get its stack trace. If it has one :( + ae.addSuppressed(e); // Mark the exception we are testing as suppressed so we get its stack trace. throw ae; } } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ListTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ListTests.java index 1ae7ca0bc4fb3..88e1f7db817f5 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ListTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ListTests.java @@ -25,9 +25,7 @@ import java.util.Arrays; import java.util.List; -import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.nullValue; /** Tests for working with lists. */ public class ListTests extends ArrayLikeObjectTestCase { @@ -61,10 +59,7 @@ protected Matcher outOfBoundsExceptionMessageMatcher(int index, int size if (index > size) { return equalTo("Index: " + index + ", Size: " + size); } - Matcher matcher = equalTo(Integer.toString(index)); - // If we set -XX:-OmitStackTraceInFastThrow we wouldn't need this - matcher = anyOf(matcher, nullValue()); - return matcher; + return equalTo(Integer.toString(index)); } else { // This exception is locale dependent so we attempt to reproduce it List list = new ArrayList<>(); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java index 1ab5aa14508ce..334d311c49cb5 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java @@ -128,12 +128,13 @@ public static T expectScriptThrows(Class expectedType, if (e instanceof ScriptException) { boolean hasEmptyScriptStack = ((ScriptException) e).getScriptStack().isEmpty(); if (shouldHaveScriptStack && hasEmptyScriptStack) { - if (0 != e.getCause().getStackTrace().length) { - // Without -XX:-OmitStackTraceInFastThrow the jvm can eat the stack trace which causes us to ignore script_stack - AssertionFailedError assertion = new AssertionFailedError("ScriptException should have a scriptStack"); - assertion.initCause(e); - throw assertion; - } + /* If this fails you *might* be missing -XX:-OmitStackTraceInFastThrow in the test jvm + * In Eclipse you can add this by default by going to Preference->Java->Installed JREs, + * clicking on the default JRE, clicking edit, and adding the flag to the + * "Default VM Arguments". */ + AssertionFailedError assertion = new AssertionFailedError("ScriptException should have a scriptStack"); + assertion.initCause(e); + throw assertion; } else if (false == shouldHaveScriptStack && false == hasEmptyScriptStack) { AssertionFailedError assertion = new AssertionFailedError("ScriptException shouldn't have a scriptStack"); assertion.initCause(e); From 5de2bcc624bec37a929ef9a20855db77dcc46beb Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 2 May 2017 11:55:43 -0400 Subject: [PATCH 160/619] Fix license header in WildflyIT.java The license header in this file was after and on the same line as the package statement. This commit moves the package statement to be after the license header. --- .../src/test/java/org/elasticsearch/wildfly/WildflyIT.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qa/wildfly/src/test/java/org/elasticsearch/wildfly/WildflyIT.java b/qa/wildfly/src/test/java/org/elasticsearch/wildfly/WildflyIT.java index 90090c35fa9ee..baf44ed777d1a 100644 --- a/qa/wildfly/src/test/java/org/elasticsearch/wildfly/WildflyIT.java +++ b/qa/wildfly/src/test/java/org/elasticsearch/wildfly/WildflyIT.java @@ -1,4 +1,4 @@ -package org.elasticsearch.wildfly;/* +/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright @@ -17,6 +17,8 @@ * under the License. */ +package org.elasticsearch.wildfly; + import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPut; From 2f9e9460d4c0c656e4f0b41376c803cddecf28bf Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Tue, 2 May 2017 18:09:32 +0200 Subject: [PATCH 161/619] Move RemoteClusterService into TransportService (#24424) TransportService and RemoteClusterService are closely coupled already today and to simplify remote cluster integration down the road it can be a direct dependency of TransportService. This change moves RemoteClusterService into TransportService with the goal to make it a hidden implementation detail of TransportService in followup changes. --- .../resources/checkstyle_suppressions.xml | 2 - .../cluster/remote/RemoteInfoResponse.java | 2 +- .../remote/TransportRemoteInfoAction.java | 4 +- .../action/search/SearchTransportService.java | 30 +---- .../action/search/TransportSearchAction.java | 72 +++++++++-- .../common/settings/ClusterSettings.java | 4 +- .../java/org/elasticsearch/node/Node.java | 7 -- .../RemoteClusterAware.java | 8 +- .../RemoteClusterConnection.java | 14 +-- .../RemoteClusterService.java | 56 +-------- .../RemoteConnectionInfo.java | 2 +- .../transport/TransportService.java | 21 +++- .../action/search/SearchAsyncActionTests.java | 1 + .../search/TransportSearchActionTests.java | 116 ++++++++++++++++++ .../RemoteClusterConnectionTests.java | 5 +- .../RemoteClusterServiceTests.java | 103 +--------------- .../test/transport/MockTransportService.java | 2 +- 17 files changed, 228 insertions(+), 221 deletions(-) rename core/src/main/java/org/elasticsearch/{action/search => transport}/RemoteClusterAware.java (96%) rename core/src/main/java/org/elasticsearch/{action/search => transport}/RemoteClusterConnection.java (97%) rename core/src/main/java/org/elasticsearch/{action/search => transport}/RemoteClusterService.java (81%) rename core/src/main/java/org/elasticsearch/{action/search => transport}/RemoteConnectionInfo.java (99%) rename core/src/test/java/org/elasticsearch/{action/search => transport}/RemoteClusterConnectionTests.java (99%) rename core/src/test/java/org/elasticsearch/{action/search => transport}/RemoteClusterServiceTests.java (68%) diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 5deb52e53fdba..82c0ce3b77c86 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -147,7 +147,6 @@ - @@ -454,7 +453,6 @@ - diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/remote/RemoteInfoResponse.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/remote/RemoteInfoResponse.java index 6d79e23092291..8e9360bdb1238 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/remote/RemoteInfoResponse.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/remote/RemoteInfoResponse.java @@ -20,7 +20,7 @@ package org.elasticsearch.action.admin.cluster.remote; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.action.search.RemoteConnectionInfo; +import org.elasticsearch.transport.RemoteConnectionInfo; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ToXContentObject; diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/remote/TransportRemoteInfoAction.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/remote/TransportRemoteInfoAction.java index cdb79a825834b..33254a9aed9ab 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/remote/TransportRemoteInfoAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/remote/TransportRemoteInfoAction.java @@ -20,7 +20,7 @@ package org.elasticsearch.action.admin.cluster.remote; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.search.RemoteClusterService; +import org.elasticsearch.transport.RemoteClusterService; import org.elasticsearch.action.search.SearchTransportService; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; @@ -30,8 +30,6 @@ import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; -import java.util.ArrayList; - public final class TransportRemoteInfoAction extends HandledTransportAction { private final RemoteClusterService remoteClusterService; diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java index a221c6001a58a..9e858a4ccafdd 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java @@ -26,7 +26,7 @@ import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.common.component.AbstractLifecycleComponent; +import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.ClusterSettings; @@ -46,6 +46,7 @@ import org.elasticsearch.search.query.ScrollQuerySearchResult; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.RemoteClusterService; import org.elasticsearch.transport.Transport; import org.elasticsearch.transport.TransportActionProxy; import org.elasticsearch.transport.TaskAwareTransportRequestHandler; @@ -62,7 +63,7 @@ * An encapsulation of {@link org.elasticsearch.search.SearchService} operations exposed through * transport. */ -public class SearchTransportService extends AbstractLifecycleComponent { +public class SearchTransportService extends AbstractComponent { public static final String FREE_CONTEXT_SCROLL_ACTION_NAME = "indices:data/read/search[free_context/scroll]"; public static final String FREE_CONTEXT_ACTION_NAME = "indices:data/read/search[free_context]"; @@ -77,17 +78,10 @@ public class SearchTransportService extends AbstractLifecycleComponent { public static final String FETCH_ID_ACTION_NAME = "indices:data/read/search[phase/fetch/id]"; private final TransportService transportService; - private final RemoteClusterService remoteClusterService; - private final boolean connectToRemote; public SearchTransportService(Settings settings, ClusterSettings clusterSettings, TransportService transportService) { super(settings); - this.connectToRemote = RemoteClusterService.ENABLE_REMOTE_CLUSTERS.get(settings); this.transportService = transportService; - this.remoteClusterService = new RemoteClusterService(settings, transportService); - if (connectToRemote) { - remoteClusterService.listenForUpdates(clusterSettings); - } } public void sendFreeContext(Transport.Connection connection, final long contextId, OriginalIndices originalIndices) { @@ -181,7 +175,7 @@ void sendExecuteMultiSearch(final MultiSearchRequest request, SearchTask task, } public RemoteClusterService getRemoteClusterService() { - return remoteClusterService; + return transportService.getRemoteClusterService(); } static class ScrollFreeContextRequest extends TransportRequest { @@ -399,20 +393,4 @@ public void messageReceived(ShardFetchSearchRequest request, TransportChannel ch Transport.Connection getConnection(DiscoveryNode node) { return transportService.getConnection(node); } - - @Override - protected void doStart() { - if (connectToRemote) { - // here we start to connect to the remote clusters - remoteClusterService.initializeRemoteClusters(); - } - } - - @Override - protected void doStop() {} - - @Override - protected void doClose() throws IOException { - remoteClusterService.close(); - } } diff --git a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 94803d771ebaf..ae18caa50f0ba 100644 --- a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -19,8 +19,11 @@ package org.elasticsearch.action.search; +import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.OriginalIndices; +import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsGroup; +import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsResponse; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.cluster.ClusterState; @@ -37,15 +40,19 @@ import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.Index; +import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.SearchService; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.internal.AliasFilter; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.RemoteClusterAware; +import org.elasticsearch.transport.RemoteClusterService; import org.elasticsearch.transport.Transport; import org.elasticsearch.transport.TransportService; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -203,7 +210,7 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< ActionListener.wrap((searchShardsResponses) -> { List remoteShardIterators = new ArrayList<>(); Map remoteAliasFilters = new HashMap<>(); - Function connectionFunction = remoteClusterService.processRemoteShards( + Function connectionFunction = processRemoteShards(remoteClusterService, searchShardsResponses, remoteClusterIndices, remoteShardIterators, remoteAliasFilters); executeSearch((SearchTask)task, timeProvider, searchRequest, localIndices, remoteShardIterators, connectionFunction, clusterState, remoteAliasFilters, listener); @@ -211,6 +218,51 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< } } + static Function processRemoteShards(RemoteClusterService remoteClusterService, + Map searchShardsResponses, + Map remoteIndicesByCluster, + List remoteShardIterators, + Map aliasFilterMap) { + Map> nodeToCluster = new HashMap<>(); + for (Map.Entry entry : searchShardsResponses.entrySet()) { + String clusterAlias = entry.getKey(); + ClusterSearchShardsResponse searchShardsResponse = entry.getValue(); + for (DiscoveryNode remoteNode : searchShardsResponse.getNodes()) { + nodeToCluster.put(remoteNode.getId(), () -> remoteClusterService.getConnection(remoteNode, clusterAlias)); + } + Map indicesAndFilters = searchShardsResponse.getIndicesAndFilters(); + for (ClusterSearchShardsGroup clusterSearchShardsGroup : searchShardsResponse.getGroups()) { + //add the cluster name to the remote index names for indices disambiguation + //this ends up in the hits returned with the search response + ShardId shardId = clusterSearchShardsGroup.getShardId(); + Index remoteIndex = shardId.getIndex(); + Index index = new Index(clusterAlias + RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR + remoteIndex.getName(), + remoteIndex.getUUID()); + OriginalIndices originalIndices = remoteIndicesByCluster.get(clusterAlias); + assert originalIndices != null; + SearchShardIterator shardIterator = new SearchShardIterator(new ShardId(index, shardId.getId()), + Arrays.asList(clusterSearchShardsGroup.getShards()), originalIndices); + remoteShardIterators.add(shardIterator); + AliasFilter aliasFilter; + if (indicesAndFilters == null) { + aliasFilter = new AliasFilter(null, Strings.EMPTY_ARRAY); + } else { + aliasFilter = indicesAndFilters.get(shardId.getIndexName()); + assert aliasFilter != null; + } + // here we have to map the filters to the UUID since from now on we use the uuid for the lookup + aliasFilterMap.put(remoteIndex.getUUID(), aliasFilter); + } + } + return (nodeId) -> { + Supplier supplier = nodeToCluster.get(nodeId); + if (supplier == null) { + throw new IllegalArgumentException("unknown remote node: " + nodeId); + } + return supplier.get(); + }; + } + private void executeSearch(SearchTask task, SearchTimeProvider timeProvider, SearchRequest searchRequest, OriginalIndices localIndices, List remoteShardIterators, Function remoteConnections, ClusterState clusterState, Map remoteAliasMap, @@ -234,9 +286,10 @@ private void executeSearch(SearchTask task, SearchTimeProvider timeProvider, Sea for (int i = 0; i < indices.length; i++) { concreteIndices[i] = indices[i].getName(); } - GroupShardsIterator localShardsIterator = clusterService.operationRouting().searchShards(clusterState, concreteIndices, routingMap, - searchRequest.preference()); - GroupShardsIterator shardIterators = mergeShardsIterators(localShardsIterator, localIndices, remoteShardIterators); + GroupShardsIterator localShardsIterator = clusterService.operationRouting().searchShards(clusterState, + concreteIndices, routingMap, searchRequest.preference()); + GroupShardsIterator shardIterators = mergeShardsIterators(localShardsIterator, localIndices, + remoteShardIterators); failIfOverShardCountLimit(clusterService, shardIterators.size()); @@ -297,7 +350,8 @@ protected final void doExecute(SearchRequest searchRequest, ActionListener shardIterators, - SearchTimeProvider timeProvider, Function connectionLookup, + SearchTimeProvider timeProvider, + Function connectionLookup, long clusterStateVersion, Map aliasFilter, Map concreteIndexBoosts, ActionListener listener) { @@ -306,13 +360,13 @@ private AbstractSearchAsyncAction searchAsyncAction(SearchTask task, SearchReque switch(searchRequest.searchType()) { case DFS_QUERY_THEN_FETCH: searchAsyncAction = new SearchDfsQueryThenFetchAsyncAction(logger, searchTransportService, connectionLookup, - aliasFilter, concreteIndexBoosts, searchPhaseController, executor, searchRequest, listener, shardIterators, timeProvider, - clusterStateVersion, task); + aliasFilter, concreteIndexBoosts, searchPhaseController, executor, searchRequest, listener, shardIterators, + timeProvider, clusterStateVersion, task); break; case QUERY_THEN_FETCH: searchAsyncAction = new SearchQueryThenFetchAsyncAction(logger, searchTransportService, connectionLookup, - aliasFilter, concreteIndexBoosts, searchPhaseController, executor, searchRequest, listener, shardIterators, timeProvider, - clusterStateVersion, task); + aliasFilter, concreteIndexBoosts, searchPhaseController, executor, searchRequest, listener, shardIterators, + timeProvider, clusterStateVersion, task); break; default: throw new IllegalStateException("Unknown search type: [" + searchRequest.searchType() + "]"); diff --git a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java index 18ca8ef7ad624..778b02dd7915a 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java +++ b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java @@ -19,8 +19,8 @@ package org.elasticsearch.common.settings; import org.elasticsearch.action.admin.indices.close.TransportCloseIndexAction; -import org.elasticsearch.action.search.RemoteClusterAware; -import org.elasticsearch.action.search.RemoteClusterService; +import org.elasticsearch.transport.RemoteClusterService; +import org.elasticsearch.transport.RemoteClusterAware; import org.elasticsearch.action.search.TransportSearchAction; import org.elasticsearch.action.support.AutoCreateIndex; import org.elasticsearch.action.support.DestructiveOperations; diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index 0b10b16863b07..c89f31fbe749c 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -736,10 +736,6 @@ public void onTimeout(TimeValue timeout) { // start nodes now, after the http server, because it may take some time tribeService.startNodes(); - // starts connecting to remote clusters if any cluster is configured - SearchTransportService searchTransportService = injector.getInstance(SearchTransportService.class); - searchTransportService.start(); - logger.info("started"); return this; @@ -773,7 +769,6 @@ private Node stop() { injector.getInstance(GatewayService.class).stop(); injector.getInstance(SearchService.class).stop(); injector.getInstance(TransportService.class).stop(); - injector.getInstance(SearchTransportService.class).stop(); pluginLifecycleComponents.forEach(LifecycleComponent::stop); // we should stop this last since it waits for resources to get released @@ -835,8 +830,6 @@ public synchronized void close() throws IOException { toClose.add(injector.getInstance(SearchService.class)); toClose.add(() -> stopWatch.stop().start("transport")); toClose.add(injector.getInstance(TransportService.class)); - toClose.add(() -> stopWatch.stop().start("search_transport_service")); - toClose.add(injector.getInstance(SearchTransportService.class)); for (LifecycleComponent plugin : pluginLifecycleComponents) { toClose.add(() -> stopWatch.stop().start("plugin(" + plugin.getClass().getName() + ")")); diff --git a/core/src/main/java/org/elasticsearch/action/search/RemoteClusterAware.java b/core/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java similarity index 96% rename from core/src/main/java/org/elasticsearch/action/search/RemoteClusterAware.java rename to core/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java index 2785a8efdb6f0..42ab7315234bc 100644 --- a/core/src/main/java/org/elasticsearch/action/search/RemoteClusterAware.java +++ b/core/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.action.search; +package org.elasticsearch.transport; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.ClusterNameExpressionResolver; @@ -51,8 +51,8 @@ public abstract class RemoteClusterAware extends AbstractComponent { public static final Setting.AffixSetting> REMOTE_CLUSTERS_SEEDS = Setting.affixKeySetting("search.remote.", "seeds", (key) -> Setting.listSetting(key, Collections.emptyList(), RemoteClusterAware::parseSeedAddress, Setting.Property.NodeScope, Setting.Property.Dynamic)); - protected static final char REMOTE_CLUSTER_INDEX_SEPARATOR = ':'; - protected static final String LOCAL_CLUSTER_GROUP_KEY = ""; + public static final char REMOTE_CLUSTER_INDEX_SEPARATOR = ':'; + public static final String LOCAL_CLUSTER_GROUP_KEY = ""; protected final ClusterNameExpressionResolver clusterNameResolver; /** @@ -91,7 +91,7 @@ protected static Map> buildRemoteClustersSeeds(Setti * * @return a map of grouped remote and local indices */ - protected Map> groupClusterIndices(String[] requestIndices, Predicate indexExists) { + public Map> groupClusterIndices(String[] requestIndices, Predicate indexExists) { Map> perClusterIndices = new HashMap<>(); Set remoteClusterNames = getRemoteClusterNames(); for (String index : requestIndices) { diff --git a/core/src/main/java/org/elasticsearch/action/search/RemoteClusterConnection.java b/core/src/main/java/org/elasticsearch/transport/RemoteClusterConnection.java similarity index 97% rename from core/src/main/java/org/elasticsearch/action/search/RemoteClusterConnection.java rename to core/src/main/java/org/elasticsearch/transport/RemoteClusterConnection.java index a3f3f3a9612b5..5c7e072f65063 100644 --- a/core/src/main/java/org/elasticsearch/action/search/RemoteClusterConnection.java +++ b/core/src/main/java/org/elasticsearch/transport/RemoteClusterConnection.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.action.search; +package org.elasticsearch.transport; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; @@ -33,6 +33,7 @@ import org.elasticsearch.action.admin.cluster.state.ClusterStateAction; import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; +import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.common.component.AbstractComponent; @@ -42,17 +43,6 @@ import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.transport.ConnectTransportException; -import org.elasticsearch.transport.ConnectionProfile; -import org.elasticsearch.transport.TcpTransport; -import org.elasticsearch.transport.Transport; -import org.elasticsearch.transport.TransportActionProxy; -import org.elasticsearch.transport.TransportConnectionListener; -import org.elasticsearch.transport.TransportException; -import org.elasticsearch.transport.TransportRequest; -import org.elasticsearch.transport.TransportRequestOptions; -import org.elasticsearch.transport.TransportResponseHandler; -import org.elasticsearch.transport.TransportService; import java.io.Closeable; import java.io.IOException; diff --git a/core/src/main/java/org/elasticsearch/action/search/RemoteClusterService.java b/core/src/main/java/org/elasticsearch/transport/RemoteClusterService.java similarity index 81% rename from core/src/main/java/org/elasticsearch/action/search/RemoteClusterService.java rename to core/src/main/java/org/elasticsearch/transport/RemoteClusterService.java index cf2be61ed0526..92dce9d53f13a 100644 --- a/core/src/main/java/org/elasticsearch/action/search/RemoteClusterService.java +++ b/core/src/main/java/org/elasticsearch/transport/RemoteClusterService.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.action.search; +package org.elasticsearch.transport; import org.apache.logging.log4j.util.Supplier; import org.apache.lucene.util.IOUtils; @@ -25,6 +25,8 @@ import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsGroup; import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsResponse; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchShardIterator; import org.elasticsearch.action.support.GroupedActionListener; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -38,9 +40,6 @@ import org.elasticsearch.index.Index; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.internal.AliasFilter; -import org.elasticsearch.transport.Transport; -import org.elasticsearch.transport.TransportException; -import org.elasticsearch.transport.TransportService; import java.io.Closeable; import java.io.IOException; @@ -169,7 +168,7 @@ private synchronized void updateRemoteClusters(Map> /** * Returns true if at least one remote cluster is configured */ - boolean isCrossClusterSearchEnabled() { + public boolean isCrossClusterSearchEnabled() { return remoteClusters.isEmpty() == false; } @@ -184,7 +183,7 @@ boolean isRemoteClusterRegistered(String clusterName) { return remoteClusters.containsKey(clusterName); } - void collectSearchShards(SearchRequest searchRequest, Map remoteIndicesByCluster, + public void collectSearchShards(SearchRequest searchRequest, Map remoteIndicesByCluster, ActionListener> listener) { final CountDown responsesCountDown = new CountDown(remoteIndicesByCluster.size()); final Map searchShardsResponses = new ConcurrentHashMap<>(); @@ -229,54 +228,11 @@ public void onFailure(Exception e) { } } - Function processRemoteShards(Map searchShardsResponses, - Map remoteIndicesByCluster, - List remoteShardIterators, - Map aliasFilterMap) { - Map> nodeToCluster = new HashMap<>(); - for (Map.Entry entry : searchShardsResponses.entrySet()) { - String clusterAlias = entry.getKey(); - ClusterSearchShardsResponse searchShardsResponse = entry.getValue(); - for (DiscoveryNode remoteNode : searchShardsResponse.getNodes()) { - nodeToCluster.put(remoteNode.getId(), () -> getConnection(remoteNode, clusterAlias)); - } - Map indicesAndFilters = searchShardsResponse.getIndicesAndFilters(); - for (ClusterSearchShardsGroup clusterSearchShardsGroup : searchShardsResponse.getGroups()) { - //add the cluster name to the remote index names for indices disambiguation - //this ends up in the hits returned with the search response - ShardId shardId = clusterSearchShardsGroup.getShardId(); - Index remoteIndex = shardId.getIndex(); - Index index = new Index(clusterAlias + REMOTE_CLUSTER_INDEX_SEPARATOR + remoteIndex.getName(), remoteIndex.getUUID()); - OriginalIndices originalIndices = remoteIndicesByCluster.get(clusterAlias); - assert originalIndices != null; - SearchShardIterator shardIterator = new SearchShardIterator(new ShardId(index, shardId.getId()), - Arrays.asList(clusterSearchShardsGroup.getShards()), originalIndices); - remoteShardIterators.add(shardIterator); - AliasFilter aliasFilter; - if (indicesAndFilters == null) { - aliasFilter = new AliasFilter(null, Strings.EMPTY_ARRAY); - } else { - aliasFilter = indicesAndFilters.get(shardId.getIndexName()); - assert aliasFilter != null; - } - // here we have to map the filters to the UUID since from now on we use the uuid for the lookup - aliasFilterMap.put(remoteIndex.getUUID(), aliasFilter); - } - } - return (nodeId) -> { - Supplier supplier = nodeToCluster.get(nodeId); - if (supplier == null) { - throw new IllegalArgumentException("unknown remote node: " + nodeId); - } - return supplier.get(); - }; - } - /** * Returns a connection to the given node on the given remote cluster * @throws IllegalArgumentException if the remote cluster is unknown */ - private Transport.Connection getConnection(DiscoveryNode node, String cluster) { + public Transport.Connection getConnection(DiscoveryNode node, String cluster) { RemoteClusterConnection connection = remoteClusters.get(cluster); if (connection == null) { throw new IllegalArgumentException("no such remote cluster: " + cluster); diff --git a/core/src/main/java/org/elasticsearch/action/search/RemoteConnectionInfo.java b/core/src/main/java/org/elasticsearch/transport/RemoteConnectionInfo.java similarity index 99% rename from core/src/main/java/org/elasticsearch/action/search/RemoteConnectionInfo.java rename to core/src/main/java/org/elasticsearch/transport/RemoteConnectionInfo.java index ff3548d215b59..53e6d220da1a5 100644 --- a/core/src/main/java/org/elasticsearch/action/search/RemoteConnectionInfo.java +++ b/core/src/main/java/org/elasticsearch/transport/RemoteConnectionInfo.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.action.search; +package org.elasticsearch.transport; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; diff --git a/core/src/main/java/org/elasticsearch/transport/TransportService.java b/core/src/main/java/org/elasticsearch/transport/TransportService.java index d956149b0df7c..7de9606361555 100644 --- a/core/src/main/java/org/elasticsearch/transport/TransportService.java +++ b/core/src/main/java/org/elasticsearch/transport/TransportService.java @@ -21,6 +21,7 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.lucene.util.IOUtils; import org.elasticsearch.Version; import org.elasticsearch.action.admin.cluster.node.liveness.TransportLivenessAction; import org.elasticsearch.cluster.ClusterName; @@ -82,6 +83,7 @@ public class TransportService extends AbstractLifecycleComponent { protected final TaskManager taskManager; private final TransportInterceptor.AsyncSender asyncSender; private final Function localNodeFactory; + private final boolean connectToRemoteCluster; volatile Map requestHandlers = Collections.emptyMap(); final Object requestHandlerMutex = new Object(); @@ -119,6 +121,8 @@ protected boolean removeEldestEntry(Map.Entry eldest) { volatile String[] tracerLogInclude; volatile String[] tracerLogExclude; + private final RemoteClusterService remoteClusterService; + /** if set will call requests sent to this id to shortcut and executed locally */ volatile DiscoveryNode localNode = null; private final Transport.Connection localNodeConnection = new Transport.Connection() { @@ -158,12 +162,21 @@ public TransportService(Settings settings, Transport transport, ThreadPool threa taskManager = createTaskManager(); this.interceptor = transportInterceptor; this.asyncSender = interceptor.interceptSender(this::sendRequestInternal); + this.connectToRemoteCluster = RemoteClusterService.ENABLE_REMOTE_CLUSTERS.get(settings); + remoteClusterService = new RemoteClusterService(settings, this); if (clusterSettings != null) { clusterSettings.addSettingsUpdateConsumer(TRACE_LOG_INCLUDE_SETTING, this::setTracerLogInclude); clusterSettings.addSettingsUpdateConsumer(TRACE_LOG_EXCLUDE_SETTING, this::setTracerLogExclude); + if (connectToRemoteCluster) { + remoteClusterService.listenForUpdates(clusterSettings); + } } } + public RemoteClusterService getRemoteClusterService() { + return remoteClusterService; + } + public DiscoveryNode getLocalNode() { return localNode; } @@ -209,6 +222,10 @@ protected void doStart() { false, false, (request, channel) -> channel.sendResponse( new HandshakeResponse(localNode, clusterName, localNode.getVersion()))); + if (connectToRemoteCluster) { + // here we start to connect to the remote clusters + remoteClusterService.initializeRemoteClusters(); + } } @Override @@ -253,8 +270,8 @@ public void doRun() { } @Override - protected void doClose() { - transport.close(); + protected void doClose() throws IOException { + IOUtils.close(remoteClusterService, transport); } /** diff --git a/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java b/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java index 1039e8e959d3d..b6d79b6ead962 100644 --- a/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java @@ -37,6 +37,7 @@ import org.elasticsearch.search.SearchPhaseResult; import org.elasticsearch.search.internal.AliasFilter; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.transport.RemoteClusterAware; import org.elasticsearch.transport.Transport; import org.elasticsearch.transport.TransportException; import org.elasticsearch.transport.TransportRequest; diff --git a/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java b/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java index 696e25de75e9b..fbd622f878d82 100644 --- a/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java @@ -19,25 +19,52 @@ package org.elasticsearch.action.search; +import org.elasticsearch.Version; import org.elasticsearch.action.OriginalIndices; +import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsGroup; +import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsResponse; import org.elasticsearch.action.support.IndicesOptions; +import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.GroupShardsIterator; import org.elasticsearch.cluster.routing.PlainShardIterator; import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardRouting; +import org.elasticsearch.cluster.routing.ShardRoutingState; import org.elasticsearch.cluster.routing.TestShardRouting; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.query.MatchAllQueryBuilder; +import org.elasticsearch.index.query.TermsQueryBuilder; import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.search.internal.AliasFilter; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.transport.MockTransportService; +import org.elasticsearch.threadpool.TestThreadPool; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.RemoteClusterService; +import org.elasticsearch.transport.TransportService; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; import static org.elasticsearch.cluster.routing.ShardRoutingState.STARTED; public class TransportSearchActionTests extends ESTestCase { + private final ThreadPool threadPool = new TestThreadPool(getClass().getName()); + + @Override + public void tearDown() throws Exception { + super.tearDown(); + ThreadPool.terminate(threadPool, 10, TimeUnit.SECONDS); + } + + public void testMergeShardsIterators() throws IOException { List localShardIterators = new ArrayList<>(); { @@ -119,4 +146,93 @@ public void testMergeShardsIterators() throws IOException { } } } + + public void testProcessRemoteShards() throws IOException { + try (TransportService transportService = MockTransportService.createNewService(Settings.EMPTY, Version.CURRENT, threadPool, + null)) { + RemoteClusterService service = transportService.getRemoteClusterService(); + assertFalse(service.isCrossClusterSearchEnabled()); + List iteratorList = new ArrayList<>(); + Map searchShardsResponseMap = new HashMap<>(); + DiscoveryNode[] nodes = new DiscoveryNode[] { + new DiscoveryNode("node1", buildNewFakeTransportAddress(), Version.CURRENT), + new DiscoveryNode("node2", buildNewFakeTransportAddress(), Version.CURRENT) + }; + Map indicesAndAliases = new HashMap<>(); + indicesAndAliases.put("foo", new AliasFilter(new TermsQueryBuilder("foo", "bar"), Strings.EMPTY_ARRAY)); + indicesAndAliases.put("bar", new AliasFilter(new MatchAllQueryBuilder(), Strings.EMPTY_ARRAY)); + ClusterSearchShardsGroup[] groups = new ClusterSearchShardsGroup[] { + new ClusterSearchShardsGroup(new ShardId("foo", "foo_id", 0), + new ShardRouting[] {TestShardRouting.newShardRouting("foo", 0, "node1", true, ShardRoutingState.STARTED), + TestShardRouting.newShardRouting("foo", 0, "node2", false, ShardRoutingState.STARTED)}), + new ClusterSearchShardsGroup(new ShardId("foo", "foo_id", 1), + new ShardRouting[] {TestShardRouting.newShardRouting("foo", 0, "node1", true, ShardRoutingState.STARTED), + TestShardRouting.newShardRouting("foo", 1, "node2", false, ShardRoutingState.STARTED)}), + new ClusterSearchShardsGroup(new ShardId("bar", "bar_id", 0), + new ShardRouting[] {TestShardRouting.newShardRouting("bar", 0, "node2", true, ShardRoutingState.STARTED), + TestShardRouting.newShardRouting("bar", 0, "node1", false, ShardRoutingState.STARTED)}) + }; + searchShardsResponseMap.put("test_cluster_1", new ClusterSearchShardsResponse(groups, nodes, indicesAndAliases)); + DiscoveryNode[] nodes2 = new DiscoveryNode[] { + new DiscoveryNode("node3", buildNewFakeTransportAddress(), Version.CURRENT) + }; + ClusterSearchShardsGroup[] groups2 = new ClusterSearchShardsGroup[] { + new ClusterSearchShardsGroup(new ShardId("xyz", "xyz_id", 0), + new ShardRouting[] {TestShardRouting.newShardRouting("xyz", 0, "node3", true, ShardRoutingState.STARTED)}) + }; + searchShardsResponseMap.put("test_cluster_2", new ClusterSearchShardsResponse(groups2, nodes2, null)); + + Map remoteIndicesByCluster = new HashMap<>(); + remoteIndicesByCluster.put("test_cluster_1", + new OriginalIndices(new String[]{"fo*", "ba*"}, IndicesOptions.strictExpandOpenAndForbidClosed())); + remoteIndicesByCluster.put("test_cluster_2", + new OriginalIndices(new String[]{"x*"}, IndicesOptions.strictExpandOpenAndForbidClosed())); + Map remoteAliases = new HashMap<>(); + TransportSearchAction.processRemoteShards(service, searchShardsResponseMap, remoteIndicesByCluster, iteratorList, + remoteAliases); + assertEquals(4, iteratorList.size()); + for (SearchShardIterator iterator : iteratorList) { + if (iterator.shardId().getIndexName().endsWith("foo")) { + assertArrayEquals(new String[]{"fo*", "ba*"}, iterator.getOriginalIndices().indices()); + assertTrue(iterator.shardId().getId() == 0 || iterator.shardId().getId() == 1); + assertEquals("test_cluster_1:foo", iterator.shardId().getIndexName()); + ShardRouting shardRouting = iterator.nextOrNull(); + assertNotNull(shardRouting); + assertEquals(shardRouting.getIndexName(), "foo"); + shardRouting = iterator.nextOrNull(); + assertNotNull(shardRouting); + assertEquals(shardRouting.getIndexName(), "foo"); + assertNull(iterator.nextOrNull()); + } else if (iterator.shardId().getIndexName().endsWith("bar")) { + assertArrayEquals(new String[]{"fo*", "ba*"}, iterator.getOriginalIndices().indices()); + assertEquals(0, iterator.shardId().getId()); + assertEquals("test_cluster_1:bar", iterator.shardId().getIndexName()); + ShardRouting shardRouting = iterator.nextOrNull(); + assertNotNull(shardRouting); + assertEquals(shardRouting.getIndexName(), "bar"); + shardRouting = iterator.nextOrNull(); + assertNotNull(shardRouting); + assertEquals(shardRouting.getIndexName(), "bar"); + assertNull(iterator.nextOrNull()); + } else if (iterator.shardId().getIndexName().endsWith("xyz")) { + assertArrayEquals(new String[]{"x*"}, iterator.getOriginalIndices().indices()); + assertEquals(0, iterator.shardId().getId()); + assertEquals("test_cluster_2:xyz", iterator.shardId().getIndexName()); + ShardRouting shardRouting = iterator.nextOrNull(); + assertNotNull(shardRouting); + assertEquals(shardRouting.getIndexName(), "xyz"); + assertNull(iterator.nextOrNull()); + } + } + assertEquals(3, remoteAliases.size()); + assertTrue(remoteAliases.toString(), remoteAliases.containsKey("foo_id")); + assertTrue(remoteAliases.toString(), remoteAliases.containsKey("bar_id")); + assertTrue(remoteAliases.toString(), remoteAliases.containsKey("xyz_id")); + assertEquals(new TermsQueryBuilder("foo", "bar"), remoteAliases.get("foo_id").getQueryBuilder()); + assertEquals(new MatchAllQueryBuilder(), remoteAliases.get("bar_id").getQueryBuilder()); + assertNull(remoteAliases.get("xyz_id").getQueryBuilder()); + } + + } + } diff --git a/core/src/test/java/org/elasticsearch/action/search/RemoteClusterConnectionTests.java b/core/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/action/search/RemoteClusterConnectionTests.java rename to core/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java index 8cf6d7d48c77e..1ce73aee905d0 100644 --- a/core/src/test/java/org/elasticsearch/action/search/RemoteClusterConnectionTests.java +++ b/core/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.action.search; +package org.elasticsearch.transport; import org.apache.lucene.store.AlreadyClosedException; import org.elasticsearch.Build; @@ -33,6 +33,7 @@ import org.elasticsearch.action.admin.cluster.state.ClusterStateAction; import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; +import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -53,6 +54,8 @@ import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.RemoteClusterConnection; +import org.elasticsearch.transport.RemoteConnectionInfo; import org.elasticsearch.transport.RemoteTransportException; import org.elasticsearch.transport.TransportConnectionListener; import org.elasticsearch.transport.TransportService; diff --git a/core/src/test/java/org/elasticsearch/action/search/RemoteClusterServiceTests.java b/core/src/test/java/org/elasticsearch/transport/RemoteClusterServiceTests.java similarity index 68% rename from core/src/test/java/org/elasticsearch/action/search/RemoteClusterServiceTests.java rename to core/src/test/java/org/elasticsearch/transport/RemoteClusterServiceTests.java index 909c85a5b60eb..32a672e1bbc9a 100644 --- a/core/src/test/java/org/elasticsearch/action/search/RemoteClusterServiceTests.java +++ b/core/src/test/java/org/elasticsearch/transport/RemoteClusterServiceTests.java @@ -16,26 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.action.search; +package org.elasticsearch.transport; import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.OriginalIndices; -import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsGroup; -import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsResponse; -import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.routing.ShardRouting; -import org.elasticsearch.cluster.routing.ShardRoutingState; -import org.elasticsearch.cluster.routing.TestShardRouting; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; -import org.elasticsearch.index.query.MatchAllQueryBuilder; -import org.elasticsearch.index.query.TermsQueryBuilder; -import org.elasticsearch.index.shard.ShardId; -import org.elasticsearch.search.internal.AliasFilter; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.threadpool.TestThreadPool; @@ -44,10 +32,8 @@ import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; @@ -144,7 +130,8 @@ public void testGroupClusterIndices() throws IOException { assertTrue(service.isRemoteClusterRegistered("cluster_2")); assertFalse(service.isRemoteClusterRegistered("foo")); Map> perClusterIndices = service.groupClusterIndices(new String[]{"foo:bar", "cluster_1:bar", - "cluster_2:foo:bar", "cluster_1:test", "cluster_2:foo*", "foo", "cluster*:baz", "*:boo", "no*match:boo"}, i -> false); + "cluster_2:foo:bar", "cluster_1:test", "cluster_2:foo*", "foo", "cluster*:baz", "*:boo", "no*match:boo"}, + i -> false); String[] localIndices = perClusterIndices.computeIfAbsent(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, k -> Collections.emptyList()).toArray(new String[0]); assertNotNull(perClusterIndices.remove(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)); @@ -202,90 +189,6 @@ public void testIncrementallyAddClusters() throws IOException { } } - public void testProcessRemoteShards() throws IOException { - try (RemoteClusterService service = new RemoteClusterService(Settings.EMPTY, null)) { - assertFalse(service.isCrossClusterSearchEnabled()); - List iteratorList = new ArrayList<>(); - Map searchShardsResponseMap = new HashMap<>(); - DiscoveryNode[] nodes = new DiscoveryNode[] { - new DiscoveryNode("node1", buildNewFakeTransportAddress(), Version.CURRENT), - new DiscoveryNode("node2", buildNewFakeTransportAddress(), Version.CURRENT) - }; - Map indicesAndAliases = new HashMap<>(); - indicesAndAliases.put("foo", new AliasFilter(new TermsQueryBuilder("foo", "bar"), Strings.EMPTY_ARRAY)); - indicesAndAliases.put("bar", new AliasFilter(new MatchAllQueryBuilder(), Strings.EMPTY_ARRAY)); - ClusterSearchShardsGroup[] groups = new ClusterSearchShardsGroup[] { - new ClusterSearchShardsGroup(new ShardId("foo", "foo_id", 0), - new ShardRouting[] {TestShardRouting.newShardRouting("foo", 0, "node1", true, ShardRoutingState.STARTED), - TestShardRouting.newShardRouting("foo", 0, "node2", false, ShardRoutingState.STARTED)}), - new ClusterSearchShardsGroup(new ShardId("foo", "foo_id", 1), - new ShardRouting[] {TestShardRouting.newShardRouting("foo", 0, "node1", true, ShardRoutingState.STARTED), - TestShardRouting.newShardRouting("foo", 1, "node2", false, ShardRoutingState.STARTED)}), - new ClusterSearchShardsGroup(new ShardId("bar", "bar_id", 0), - new ShardRouting[] {TestShardRouting.newShardRouting("bar", 0, "node2", true, ShardRoutingState.STARTED), - TestShardRouting.newShardRouting("bar", 0, "node1", false, ShardRoutingState.STARTED)}) - }; - searchShardsResponseMap.put("test_cluster_1", new ClusterSearchShardsResponse(groups, nodes, indicesAndAliases)); - DiscoveryNode[] nodes2 = new DiscoveryNode[] { - new DiscoveryNode("node3", buildNewFakeTransportAddress(), Version.CURRENT) - }; - ClusterSearchShardsGroup[] groups2 = new ClusterSearchShardsGroup[] { - new ClusterSearchShardsGroup(new ShardId("xyz", "xyz_id", 0), - new ShardRouting[] {TestShardRouting.newShardRouting("xyz", 0, "node3", true, ShardRoutingState.STARTED)}) - }; - searchShardsResponseMap.put("test_cluster_2", new ClusterSearchShardsResponse(groups2, nodes2, null)); - - Map remoteIndicesByCluster = new HashMap<>(); - remoteIndicesByCluster.put("test_cluster_1", - new OriginalIndices(new String[]{"fo*", "ba*"}, IndicesOptions.strictExpandOpenAndForbidClosed())); - remoteIndicesByCluster.put("test_cluster_2", - new OriginalIndices(new String[]{"x*"}, IndicesOptions.strictExpandOpenAndForbidClosed())); - Map remoteAliases = new HashMap<>(); - service.processRemoteShards(searchShardsResponseMap, remoteIndicesByCluster, iteratorList, remoteAliases); - assertEquals(4, iteratorList.size()); - for (SearchShardIterator iterator : iteratorList) { - if (iterator.shardId().getIndexName().endsWith("foo")) { - assertArrayEquals(new String[]{"fo*", "ba*"}, iterator.getOriginalIndices().indices()); - assertTrue(iterator.shardId().getId() == 0 || iterator.shardId().getId() == 1); - assertEquals("test_cluster_1:foo", iterator.shardId().getIndexName()); - ShardRouting shardRouting = iterator.nextOrNull(); - assertNotNull(shardRouting); - assertEquals(shardRouting.getIndexName(), "foo"); - shardRouting = iterator.nextOrNull(); - assertNotNull(shardRouting); - assertEquals(shardRouting.getIndexName(), "foo"); - assertNull(iterator.nextOrNull()); - } else if (iterator.shardId().getIndexName().endsWith("bar")) { - assertArrayEquals(new String[]{"fo*", "ba*"}, iterator.getOriginalIndices().indices()); - assertEquals(0, iterator.shardId().getId()); - assertEquals("test_cluster_1:bar", iterator.shardId().getIndexName()); - ShardRouting shardRouting = iterator.nextOrNull(); - assertNotNull(shardRouting); - assertEquals(shardRouting.getIndexName(), "bar"); - shardRouting = iterator.nextOrNull(); - assertNotNull(shardRouting); - assertEquals(shardRouting.getIndexName(), "bar"); - assertNull(iterator.nextOrNull()); - } else if (iterator.shardId().getIndexName().endsWith("xyz")) { - assertArrayEquals(new String[]{"x*"}, iterator.getOriginalIndices().indices()); - assertEquals(0, iterator.shardId().getId()); - assertEquals("test_cluster_2:xyz", iterator.shardId().getIndexName()); - ShardRouting shardRouting = iterator.nextOrNull(); - assertNotNull(shardRouting); - assertEquals(shardRouting.getIndexName(), "xyz"); - assertNull(iterator.nextOrNull()); - } - } - assertEquals(3, remoteAliases.size()); - assertTrue(remoteAliases.toString(), remoteAliases.containsKey("foo_id")); - assertTrue(remoteAliases.toString(), remoteAliases.containsKey("bar_id")); - assertTrue(remoteAliases.toString(), remoteAliases.containsKey("xyz_id")); - assertEquals(new TermsQueryBuilder("foo", "bar"), remoteAliases.get("foo_id").getQueryBuilder()); - assertEquals(new MatchAllQueryBuilder(), remoteAliases.get("bar_id").getQueryBuilder()); - assertNull(remoteAliases.get("xyz_id").getQueryBuilder()); - } - } - public void testRemoteNodeAttribute() throws IOException, InterruptedException { final Settings settings = Settings.builder().put("search.remote.node.attr", "gateway").build(); diff --git a/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransportService.java b/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransportService.java index 117c168bc4177..a25f435af2c96 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransportService.java +++ b/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransportService.java @@ -819,7 +819,7 @@ public void close() throws IOException { } @Override - protected void doClose() { + protected void doClose() throws IOException { super.doClose(); synchronized (openConnections) { assert openConnections.size() == 0 : "still open connections: " + openConnections; From 732741dd8dab99b30e0f61b01aeae1bdac7eea6d Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Tue, 2 May 2017 13:00:56 -0400 Subject: [PATCH 162/619] Build that java api docs from a test (#24354) We've had `QueryDSLDocumentationTests` for a while but it had a very hopeful comment at the top about how we want to make sure that the example in the query-dsl docs match up with the test but we never had anything that made *sure* that they did. This changes that! Now the examples from the query-dsl docs are all built from the `QueryDSLDocumentationTests`. All except for the percolator example because that is hard to do as it stands now. To make this easier this change moves `QueryDSLDocumentationTests` from core and into the high level rest client. This is useful for two reasons: 1. We expect the high level rest client to be able to use the builders. 2. The code that builds that docs doesn't check out all of Elasticsearch. It only checks out certain directories. Since we're already including snippets from that directory we don't have to make any changes to that process. Closes #24320 --- .../QueryDSLDocumentationTests.java | 453 ++++++++++++++++++ .../query/QueryDSLDocumentationTests.java | 341 ------------- docs/java-api/query-dsl.asciidoc | 4 +- docs/java-api/query-dsl/bool-query.asciidoc | 10 +- .../query-dsl/boosting-query.asciidoc | 8 +- .../query-dsl/common-terms-query.asciidoc | 5 +- .../query-dsl/constant-score-query.asciidoc | 7 +- .../java-api/query-dsl/dis-max-query.asciidoc | 8 +- docs/java-api/query-dsl/exists-query.asciidoc | 5 +- .../query-dsl/function-score-query.asciidoc | 12 +- docs/java-api/query-dsl/fuzzy-query.asciidoc | 8 +- .../query-dsl/geo-bounding-box-query.asciidoc | 8 +- .../query-dsl/geo-distance-query.asciidoc | 7 +- .../query-dsl/geo-polygon-query.asciidoc | 11 +- .../query-dsl/geo-shape-query.asciidoc | 25 +- .../query-dsl/has-child-query.asciidoc | 9 +- .../query-dsl/has-parent-query.asciidoc | 10 +- docs/java-api/query-dsl/ids-query.asciidoc | 9 +- .../query-dsl/match-all-query.asciidoc | 4 +- docs/java-api/query-dsl/match-query.asciidoc | 8 +- docs/java-api/query-dsl/mlt-query.asciidoc | 13 +- .../query-dsl/multi-match-query.asciidoc | 7 +- docs/java-api/query-dsl/nested-query.asciidoc | 10 +- docs/java-api/query-dsl/prefix-query.asciidoc | 9 +- .../query-dsl/query-string-query.asciidoc | 5 +- docs/java-api/query-dsl/range-query.asciidoc | 15 +- docs/java-api/query-dsl/regexp-query.asciidoc | 6 +- docs/java-api/query-dsl/script-query.asciidoc | 18 +- .../simple-query-string-query.asciidoc | 5 +- .../query-dsl/span-containing-query.asciidoc | 9 +- .../query-dsl/span-first-query.asciidoc | 8 +- .../query-dsl/span-multi-term-query.asciidoc | 7 +- .../query-dsl/span-near-query.asciidoc | 10 +- .../query-dsl/span-not-query.asciidoc | 7 +- .../java-api/query-dsl/span-or-query.asciidoc | 8 +- .../query-dsl/span-term-query.asciidoc | 8 +- .../query-dsl/span-within-query.asciidoc | 8 +- docs/java-api/query-dsl/term-query.asciidoc | 8 +- docs/java-api/query-dsl/terms-query.asciidoc | 5 +- docs/java-api/query-dsl/type-query.asciidoc | 4 +- .../query-dsl/wildcard-query.asciidoc | 7 +- 41 files changed, 543 insertions(+), 586 deletions(-) create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java delete mode 100644 core/src/test/java/org/elasticsearch/index/query/QueryDSLDocumentationTests.java diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java new file mode 100644 index 0000000000000..1c20e2253a239 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java @@ -0,0 +1,453 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.client.documentation; + +import org.apache.lucene.search.join.ScoreMode; +import org.elasticsearch.common.geo.GeoPoint; +import org.elasticsearch.common.geo.ShapeRelation; +import org.elasticsearch.common.geo.builders.CoordinatesBuilder; +import org.elasticsearch.common.geo.builders.ShapeBuilders; +import org.elasticsearch.common.unit.DistanceUnit; +import org.elasticsearch.index.query.GeoShapeQueryBuilder; +import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; +import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder; +import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptType; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.singletonMap; +import static org.elasticsearch.index.query.QueryBuilders.boolQuery; +import static org.elasticsearch.index.query.QueryBuilders.boostingQuery; +import static org.elasticsearch.index.query.QueryBuilders.commonTermsQuery; +import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; +import static org.elasticsearch.index.query.QueryBuilders.disMaxQuery; +import static org.elasticsearch.index.query.QueryBuilders.existsQuery; +import static org.elasticsearch.index.query.QueryBuilders.functionScoreQuery; +import static org.elasticsearch.index.query.QueryBuilders.fuzzyQuery; +import static org.elasticsearch.index.query.QueryBuilders.geoBoundingBoxQuery; +import static org.elasticsearch.index.query.QueryBuilders.geoDistanceQuery; +import static org.elasticsearch.index.query.QueryBuilders.geoPolygonQuery; +import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery; +import static org.elasticsearch.index.query.QueryBuilders.hasChildQuery; +import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; +import static org.elasticsearch.index.query.QueryBuilders.idsQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchQuery; +import static org.elasticsearch.index.query.QueryBuilders.moreLikeThisQuery; +import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery; +import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; +import static org.elasticsearch.index.query.QueryBuilders.prefixQuery; +import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; +import static org.elasticsearch.index.query.QueryBuilders.rangeQuery; +import static org.elasticsearch.index.query.QueryBuilders.regexpQuery; +import static org.elasticsearch.index.query.QueryBuilders.scriptQuery; +import static org.elasticsearch.index.query.QueryBuilders.simpleQueryStringQuery; +import static org.elasticsearch.index.query.QueryBuilders.spanContainingQuery; +import static org.elasticsearch.index.query.QueryBuilders.spanFirstQuery; +import static org.elasticsearch.index.query.QueryBuilders.spanMultiTermQueryBuilder; +import static org.elasticsearch.index.query.QueryBuilders.spanNearQuery; +import static org.elasticsearch.index.query.QueryBuilders.spanNotQuery; +import static org.elasticsearch.index.query.QueryBuilders.spanOrQuery; +import static org.elasticsearch.index.query.QueryBuilders.spanTermQuery; +import static org.elasticsearch.index.query.QueryBuilders.spanWithinQuery; +import static org.elasticsearch.index.query.QueryBuilders.termQuery; +import static org.elasticsearch.index.query.QueryBuilders.termsQuery; +import static org.elasticsearch.index.query.QueryBuilders.typeQuery; +import static org.elasticsearch.index.query.QueryBuilders.wildcardQuery; +import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.exponentialDecayFunction; +import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.randomFunction; + +/** + * Examples of using the transport client that are imported into the transport client documentation. + * There are no assertions here because we're mostly concerned with making sure that the examples + * compile and don't throw weird runtime exceptions. Assertions and example data would be nice, but + * that is secondary. + */ +public class QueryDSLDocumentationTests extends ESTestCase { + public void testBool() { + // tag::bool + boolQuery() + .must(termQuery("content", "test1")) // <1> + .must(termQuery("content", "test4")) // <1> + .mustNot(termQuery("content", "test2")) // <2> + .should(termQuery("content", "test3")) // <3> + .filter(termQuery("content", "test5")); // <4> + // end::bool + } + + public void testBoosting() { + // tag::boosting + boostingQuery( + termQuery("name","kimchy"), // <1> + termQuery("name","dadoonet")) // <2> + .negativeBoost(0.2f); // <3> + // end::boosting + } + + public void testCommonTerms() { + // tag::common_terms + commonTermsQuery("name", // <1> + "kimchy"); // <2> + // end::common_terms + } + + public void testConstantScore() { + // tag::constant_score + constantScoreQuery( + termQuery("name","kimchy")) // <1> + .boost(2.0f); // <2> + // end::constant_score + } + + public void testDisMax() { + // tag::dis_max + disMaxQuery() + .add(termQuery("name", "kimchy")) // <1> + .add(termQuery("name", "elasticsearch")) // <2> + .boost(1.2f) // <3> + .tieBreaker(0.7f); // <4> + // end::dis_max + } + + public void testExists() { + // tag::exists + existsQuery("name"); // <1> + // end::exists + } + + public void testFunctionScore() { + // tag::function_score + FilterFunctionBuilder[] functions = { + new FunctionScoreQueryBuilder.FilterFunctionBuilder( + matchQuery("name", "kimchy"), // <1> + randomFunction("ABCDEF")), // <2> + new FunctionScoreQueryBuilder.FilterFunctionBuilder( + exponentialDecayFunction("age", 0L, 1L)) // <3> + }; + functionScoreQuery(functions); + // end::function_score + } + + public void testFuzzy() { + // tag::fuzzy + fuzzyQuery( + "name", // <1> + "kimchy"); // <2> + // end::fuzzy + } + + public void testGeoBoundingBox() { + // tag::geo_bounding_box + geoBoundingBoxQuery("pin.location") // <1> + .setCorners(40.73, -74.1, // <2> + 40.717, -73.99); // <3> + // end::geo_bounding_box + } + + public void testGeoDistance() { + // tag::geo_distance + geoDistanceQuery("pin.location") // <1> + .point(40, -70) // <2> + .distance(200, DistanceUnit.KILOMETERS); // <3> + // end::geo_distance + } + + public void testGeoPolygon() { + // tag::geo_polygon + List points = new ArrayList(); // <1> + points.add(new GeoPoint(40, -70)); + points.add(new GeoPoint(30, -80)); + points.add(new GeoPoint(20, -90)); + geoPolygonQuery("pin.location", points); // <2> + // end::geo_polygon + } + + public void testGeoShape() throws IOException { + { + // tag::geo_shape + GeoShapeQueryBuilder qb = geoShapeQuery( + "pin.location", // <1> + ShapeBuilders.newMultiPoint( // <2> + new CoordinatesBuilder() + .coordinate(0, 0) + .coordinate(0, 10) + .coordinate(10, 10) + .coordinate(10, 0) + .coordinate(0, 0) + .build())); + qb.relation(ShapeRelation.WITHIN); // <3> + // end::geo_shape + } + + { + // tag::indexed_geo_shape + // Using pre-indexed shapes + GeoShapeQueryBuilder qb = geoShapeQuery( + "pin.location", // <1> + "DEU", // <2> + "countries"); // <3> + qb.relation(ShapeRelation.WITHIN) // <4> + .indexedShapeIndex("shapes") // <5> + .indexedShapePath("location"); // <6> + // end::indexed_geo_shape + } + } + + public void testHasChild() { + // tag::has_child + hasChildQuery( + "blog_tag", // <1> + termQuery("tag","something"), // <2> + ScoreMode.None); // <3> + // end::has_child + } + + public void testHasParent() { + // tag::has_parent + hasParentQuery( + "blog", // <1> + termQuery("tag","something"), // <2> + false); // <3> + // end::has_parent + } + + public void testIds() { + // tag::ids + idsQuery("my_type", "type2") + .addIds("1", "4", "100"); + + idsQuery() // <1> + .addIds("1", "4", "100"); + // end::ids + } + + public void testMatchAll() { + // tag::match_all + matchAllQuery(); + // end::match_all + } + + public void testMatch() { + // tag::match + matchQuery( + "name", // <1> + "kimchy elasticsearch"); // <2> + // end::match + } + + public void testMoreLikeThis() { + // tag::more_like_this + String[] fields = {"name.first", "name.last"}; // <1> + String[] texts = {"text like this one"}; // <2> + + moreLikeThisQuery(fields, texts, null) + .minTermFreq(1) // <3> + .maxQueryTerms(12); // <4> + // end::more_like_this + } + + public void testMultiMatch() { + // tag::multi_match + multiMatchQuery( + "kimchy elasticsearch", // <1> + "user", "message"); // <2> + // end::multi_match + } + + public void testNested() { + // tag::nested + nestedQuery( + "obj1", // <1> + boolQuery() // <2> + .must(matchQuery("obj1.name", "blue")) + .must(rangeQuery("obj1.count").gt(5)), + ScoreMode.Avg); // <3> + // end::nested + } + + public void testPrefix() { + // tag::prefix + prefixQuery( + "brand", // <1> + "heine"); // <2> + // end::prefix + } + + public void testQueryString() { + // tag::query_string + queryStringQuery("+kimchy -elasticsearch"); + // end::query_string + } + + public void testRange() { + // tag::range + rangeQuery("price") // <1> + .from(5) // <2> + .to(10) // <3> + .includeLower(true) // <4> + .includeUpper(false); // <5> + // end::range + + // tag::range_simplified + // A simplified form using gte, gt, lt or lte + rangeQuery("age") // <1> + .gte("10") // <2> + .lt("20"); // <3> + // end::range_simplified + } + + public void testRegExp() { + // tag::regexp + regexpQuery( + "name.first", // <1> + "s.*y"); // <2> + // end::regexp + } + + public void testScript() { + // tag::script_inline + scriptQuery( + new Script("doc['num1'].value > 1") // <1> + ); + // end::script_inline + + // tag::script_file + Map parameters = new HashMap<>(); + parameters.put("param1", 5); + scriptQuery(new Script( + ScriptType.FILE, // <1> + "painless", // <2> + "myscript", // <3> + singletonMap("param1", 5))); // <4> + // end::script_file + } + + public void testSimpleQueryString() { + // tag::simple_query_string + simpleQueryStringQuery("+kimchy -elasticsearch"); + // end::simple_query_string + } + + public void testSpanContaining() { + // tag::span_containing + spanContainingQuery( + spanNearQuery(spanTermQuery("field1","bar"), 5) // <1> + .addClause(spanTermQuery("field1","baz")) + .inOrder(true), + spanTermQuery("field1","foo")); // <2> + // end::span_containing + } + + public void testSpanFirst() { + // tag::span_first + spanFirstQuery( + spanTermQuery("user", "kimchy"), // <1> + 3 // <2> + ); + // end::span_first + } + + public void testSpanMultiTerm() { + // tag::span_multi + spanMultiTermQueryBuilder( + prefixQuery("user", "ki")); // <1> + // end::span_multi + } + + public void testSpanNear() { + // tag::span_near + spanNearQuery( + spanTermQuery("field","value1"), // <1> + 12) // <2> + .addClause(spanTermQuery("field","value2")) // <1> + .addClause(spanTermQuery("field","value3")) // <1> + .inOrder(false); // <3> + // end::span_near + } + + public void testSpanNot() { + // tag::span_not + spanNotQuery( + spanTermQuery("field","value1"), // <1> + spanTermQuery("field","value2")); // <2> + // end::span_not + } + + public void testSpanOr() { + // tag::span_or + spanOrQuery(spanTermQuery("field","value1")) // <1> + .addClause(spanTermQuery("field","value2")) // <1> + .addClause(spanTermQuery("field","value3")); // <1> + // end::span_or + } + + public void testSpanTerm() { + // tag::span_term + spanTermQuery( + "user", // <1> + "kimchy"); // <2> + // end::span_term + } + + public void testSpanWithin() { + // tag::span_within + spanWithinQuery( + spanNearQuery(spanTermQuery("field1", "bar"), 5) // <1> + .addClause(spanTermQuery("field1", "baz")) + .inOrder(true), + spanTermQuery("field1", "foo")); // <2> + // end::span_within + } + + public void testTerm() { + // tag::term + termQuery( + "name", // <1> + "kimchy"); // <2> + // end::term + } + + public void testTerms() { + // tag::terms + termsQuery("tags", // <1> + "blue", "pill"); // <2> + // end::terms + } + + public void testType() { + // tag::type + typeQuery("my_type"); // <1> + // end::type + } + + public void testWildcard() { + // tag::wildcard + wildcardQuery( + "user", // <1> + "k?mch*"); // <2> + // end::wildcard + } +} diff --git a/core/src/test/java/org/elasticsearch/index/query/QueryDSLDocumentationTests.java b/core/src/test/java/org/elasticsearch/index/query/QueryDSLDocumentationTests.java deleted file mode 100644 index 9ee12d0f0ed56..0000000000000 --- a/core/src/test/java/org/elasticsearch/index/query/QueryDSLDocumentationTests.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.index.query; - -import org.apache.lucene.search.join.ScoreMode; -import org.elasticsearch.common.geo.GeoDistance; -import org.elasticsearch.common.geo.GeoPoint; -import org.elasticsearch.common.geo.ShapeRelation; -import org.elasticsearch.common.geo.builders.CoordinatesBuilder; -import org.elasticsearch.common.geo.builders.ShapeBuilders; -import org.elasticsearch.common.unit.DistanceUnit; -import org.elasticsearch.index.query.MoreLikeThisQueryBuilder.Item; -import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; -import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder; -import org.elasticsearch.script.Script; -import org.elasticsearch.script.ScriptType; -import org.elasticsearch.test.ESTestCase; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.elasticsearch.index.query.QueryBuilders.boolQuery; -import static org.elasticsearch.index.query.QueryBuilders.boostingQuery; -import static org.elasticsearch.index.query.QueryBuilders.commonTermsQuery; -import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; -import static org.elasticsearch.index.query.QueryBuilders.disMaxQuery; -import static org.elasticsearch.index.query.QueryBuilders.existsQuery; -import static org.elasticsearch.index.query.QueryBuilders.functionScoreQuery; -import static org.elasticsearch.index.query.QueryBuilders.fuzzyQuery; -import static org.elasticsearch.index.query.QueryBuilders.geoBoundingBoxQuery; -import static org.elasticsearch.index.query.QueryBuilders.geoDistanceQuery; -import static org.elasticsearch.index.query.QueryBuilders.geoPolygonQuery; -import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasChildQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; -import static org.elasticsearch.index.query.QueryBuilders.idsQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchQuery; -import static org.elasticsearch.index.query.QueryBuilders.moreLikeThisQuery; -import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery; -import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; -import static org.elasticsearch.index.query.QueryBuilders.prefixQuery; -import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; -import static org.elasticsearch.index.query.QueryBuilders.rangeQuery; -import static org.elasticsearch.index.query.QueryBuilders.regexpQuery; -import static org.elasticsearch.index.query.QueryBuilders.scriptQuery; -import static org.elasticsearch.index.query.QueryBuilders.simpleQueryStringQuery; -import static org.elasticsearch.index.query.QueryBuilders.spanContainingQuery; -import static org.elasticsearch.index.query.QueryBuilders.spanFirstQuery; -import static org.elasticsearch.index.query.QueryBuilders.spanMultiTermQueryBuilder; -import static org.elasticsearch.index.query.QueryBuilders.spanNearQuery; -import static org.elasticsearch.index.query.QueryBuilders.spanNotQuery; -import static org.elasticsearch.index.query.QueryBuilders.spanOrQuery; -import static org.elasticsearch.index.query.QueryBuilders.spanTermQuery; -import static org.elasticsearch.index.query.QueryBuilders.spanWithinQuery; -import static org.elasticsearch.index.query.QueryBuilders.termQuery; -import static org.elasticsearch.index.query.QueryBuilders.termsQuery; -import static org.elasticsearch.index.query.QueryBuilders.typeQuery; -import static org.elasticsearch.index.query.QueryBuilders.wildcardQuery; -import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.exponentialDecayFunction; -import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.randomFunction; - -/** - * If one of the following tests doesn't compile make sure to not only fix the compilation error here - * but also the documentation under ./docs/java-api/query-dsl/bool-query.asciidoc - * - * There are no assertions here on purpose - all of these tests ((ideally) should) equal to what is - * documented in the java api query dsl part of our reference guide. - * */ -public class QueryDSLDocumentationTests extends ESTestCase { - public void testBool() { - boolQuery() - .must(termQuery("content", "test1")) - .must(termQuery("content", "test4")) - .mustNot(termQuery("content", "test2")) - .should(termQuery("content", "test3")) - .filter(termQuery("content", "test5")); - } - - public void testBoosting() { - boostingQuery(termQuery("name","kimchy"), termQuery("name","dadoonet")) - .negativeBoost(0.2f); - } - - public void testCommonTerms() { - commonTermsQuery("name", "kimchy"); - } - - public void testConstantScore() { - constantScoreQuery(termQuery("name","kimchy")) - .boost(2.0f); - } - - public void testDisMax() { - disMaxQuery() - .add(termQuery("name", "kimchy")) - .add(termQuery("name", "elasticsearch")) - .boost(1.2f) - .tieBreaker(0.7f); - } - - public void testExists() { - existsQuery("name"); - } - - public void testFunctionScore() { - FilterFunctionBuilder[] functions = { - new FunctionScoreQueryBuilder.FilterFunctionBuilder( - matchQuery("name", "kimchy"), - randomFunction("ABCDEF")), - new FunctionScoreQueryBuilder.FilterFunctionBuilder( - exponentialDecayFunction("age", 0L, 1L)) - }; - functionScoreQuery(functions); - } - - public void testFuzzy() { - fuzzyQuery("name", "kimchy"); - } - - public void testGeoBoundingBox() { - geoBoundingBoxQuery("pin.location").setCorners(40.73, -74.1, 40.717, -73.99); - } - - public void testGeoDistance() { - geoDistanceQuery("pin.location") - .point(40, -70) - .distance(200, DistanceUnit.KILOMETERS) - .geoDistance(GeoDistance.ARC); - } - - public void testGeoPolygon() { - List points = new ArrayList(); - points.add(new GeoPoint(40, -70)); - points.add(new GeoPoint(30, -80)); - points.add(new GeoPoint(20, -90)); - geoPolygonQuery("pin.location", points); - } - - public void testGeoShape() throws IOException { - GeoShapeQueryBuilder qb = geoShapeQuery( - "pin.location", - ShapeBuilders.newMultiPoint( - new CoordinatesBuilder() - .coordinate(0, 0) - .coordinate(0, 10) - .coordinate(10, 10) - .coordinate(10, 0) - .coordinate(0, 0) - .build())); - qb.relation(ShapeRelation.WITHIN); - - qb = geoShapeQuery( - "pin.location", - "DEU", - "countries"); - qb.relation(ShapeRelation.WITHIN) - .indexedShapeIndex("shapes") - .indexedShapePath("location"); - } - - public void testHasChild() { - hasChildQuery( - "blog_tag", - termQuery("tag","something"), - ScoreMode.None); - } - - public void testHasParent() { - hasParentQuery( - "blog", - termQuery("tag","something"), - false); - } - - public void testIds() { - idsQuery("my_type", "type2") - .addIds("1", "4", "100"); - - idsQuery().addIds("1", "4", "100"); - } - - public void testMatchAll() { - matchAllQuery(); - } - - public void testMatch() { - matchQuery("name", "kimchy elasticsearch"); - } - - public void testMLT() { - String[] fields = {"name.first", "name.last"}; - String[] texts = {"text like this one"}; - Item[] items = null; - - moreLikeThisQuery(fields, texts, items) - .minTermFreq(1) - .maxQueryTerms(12); - } - - public void testMultiMatch() { - multiMatchQuery("kimchy elasticsearch", "user", "message"); - } - - public void testNested() { - nestedQuery( - "obj1", - boolQuery() - .must(matchQuery("obj1.name", "blue")) - .must(rangeQuery("obj1.count").gt(5)), - ScoreMode.Avg); - } - - public void testPrefix() { - prefixQuery("brand", "heine"); - } - - public void testQueryString() { - queryStringQuery("+kimchy -elasticsearch"); - } - - public void testRange() { - rangeQuery("price") - .from(5) - .to(10) - .includeLower(true) - .includeUpper(false); - - rangeQuery("age") - .gte("10") - .lt("20"); - } - - public void testRegExp() { - regexpQuery("name.first", "s.*y"); - } - - public void testScript() { - scriptQuery( - new Script("doc['num1'].value > 1") - ); - - Map parameters = new HashMap<>(); - parameters.put("param1", 5); - scriptQuery( - new Script( - ScriptType.FILE, "coollang", "myscript", - parameters) - ); - - } - - public void testSimpleQueryString() { - simpleQueryStringQuery("+kimchy -elasticsearch"); - } - - public void testSpanContaining() { - spanContainingQuery( - spanNearQuery(spanTermQuery("field1","bar"), 5) - .addClause(spanTermQuery("field1","baz")) - .inOrder(true), - spanTermQuery("field1","foo")); - } - - public void testSpanFirst() { - spanFirstQuery( - spanTermQuery("user", "kimchy"), - 3 - ); - } - - public void testSpanMultiTerm() { - spanMultiTermQueryBuilder(prefixQuery("user", "ki")); - } - - public void testSpanNear() { - spanNearQuery(spanTermQuery("field","value1"), 12) - .addClause(spanTermQuery("field","value2")) - .addClause(spanTermQuery("field","value3")) - .inOrder(false); - } - - public void testSpanNot() { - spanNotQuery(spanTermQuery("field","value1"), - spanTermQuery("field","value2")); - } - - public void testSpanOr() { - spanOrQuery(spanTermQuery("field","value1")) - .addClause(spanTermQuery("field","value2")) - .addClause(spanTermQuery("field","value3")); - } - - public void testSpanTerm() { - spanTermQuery("user", "kimchy"); - } - - public void testSpanWithin() { - spanWithinQuery( - spanNearQuery(spanTermQuery("field1", "bar"), 5) - .addClause(spanTermQuery("field1", "baz")) - .inOrder(true), - spanTermQuery("field1", "foo")); - } - - public void testTerm() { - termQuery("name", "kimchy"); - } - - public void testTerms() { - termsQuery("tags", "blue", "pill"); - } - - public void testType() { - typeQuery("my_type"); - } - - public void testWildcard() { - wildcardQuery("user", "k?mch*"); - } -} diff --git a/docs/java-api/query-dsl.asciidoc b/docs/java-api/query-dsl.asciidoc index b01972b7296dc..f4823fa08ab51 100644 --- a/docs/java-api/query-dsl.asciidoc +++ b/docs/java-api/query-dsl.asciidoc @@ -19,6 +19,8 @@ Note that you can easily print (aka debug) JSON generated queries using The `QueryBuilder` can then be used with any API that accepts a query, such as `count` and `search`. +:query-dsl-test: {docdir}/../../client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java + include::query-dsl/match-all-query.asciidoc[] include::query-dsl/full-text-queries.asciidoc[] @@ -35,4 +37,4 @@ include::query-dsl/special-queries.asciidoc[] include::query-dsl/span-queries.asciidoc[] - +:query-dsl-test!: diff --git a/docs/java-api/query-dsl/bool-query.asciidoc b/docs/java-api/query-dsl/bool-query.asciidoc index cc55ada32a514..da9ca0ad0cc8c 100644 --- a/docs/java-api/query-dsl/bool-query.asciidoc +++ b/docs/java-api/query-dsl/bool-query.asciidoc @@ -3,17 +3,11 @@ See {ref}/query-dsl-bool-query.html[Bool Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = boolQuery() - .must(termQuery("content", "test1")) <1> - .must(termQuery("content", "test4")) <1> - .mustNot(termQuery("content", "test2")) <2> - .should(termQuery("content", "test3")) <3> - .filter(termQuery("content", "test5")); <4> +include-tagged::{query-dsl-test}[bool] -------------------------------------------------- <1> must query <2> must not query <3> should query <4> a query that must appear in the matching documents but doesn't contribute to scoring. - diff --git a/docs/java-api/query-dsl/boosting-query.asciidoc b/docs/java-api/query-dsl/boosting-query.asciidoc index c07c1a51630bc..2a3c4437d1f89 100644 --- a/docs/java-api/query-dsl/boosting-query.asciidoc +++ b/docs/java-api/query-dsl/boosting-query.asciidoc @@ -3,14 +3,10 @@ See {ref}/query-dsl-boosting-query.html[Boosting Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = boostingQuery( - termQuery("name","kimchy"), <1> - termQuery("name","dadoonet")) <2> - .negativeBoost(0.2f); <3> +include-tagged::{query-dsl-test}[boosting] -------------------------------------------------- <1> query that will promote documents <2> query that will demote documents <3> negative boost - diff --git a/docs/java-api/query-dsl/common-terms-query.asciidoc b/docs/java-api/query-dsl/common-terms-query.asciidoc index 3a4c41bb4ade8..2c8dfc7a88cfe 100644 --- a/docs/java-api/query-dsl/common-terms-query.asciidoc +++ b/docs/java-api/query-dsl/common-terms-query.asciidoc @@ -3,10 +3,9 @@ See {ref}/query-dsl-common-terms-query.html[Common Terms Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = commonTermsQuery("name", <1> - "kimchy"); <2> +include-tagged::{query-dsl-test}[common_terms] -------------------------------------------------- <1> field <2> value diff --git a/docs/java-api/query-dsl/constant-score-query.asciidoc b/docs/java-api/query-dsl/constant-score-query.asciidoc index 40c1d0d97915f..49c5adbee6a73 100644 --- a/docs/java-api/query-dsl/constant-score-query.asciidoc +++ b/docs/java-api/query-dsl/constant-score-query.asciidoc @@ -3,12 +3,9 @@ See {ref}/query-dsl-constant-score-query.html[Constant Score Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = constantScoreQuery( - termQuery("name","kimchy") <1> - ) - .boost(2.0f); <2> +include-tagged::{query-dsl-test}[constant_score] -------------------------------------------------- <1> your query <2> query score diff --git a/docs/java-api/query-dsl/dis-max-query.asciidoc b/docs/java-api/query-dsl/dis-max-query.asciidoc index 86745ed949058..8c91bcb99011a 100644 --- a/docs/java-api/query-dsl/dis-max-query.asciidoc +++ b/docs/java-api/query-dsl/dis-max-query.asciidoc @@ -3,13 +3,9 @@ See {ref}/query-dsl-dis-max-query.html[Dis Max Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = disMaxQuery() - .add(termQuery("name", "kimchy")) <1> - .add(termQuery("name", "elasticsearch")) <2> - .boost(1.2f) <3> - .tieBreaker(0.7f); <4> +include-tagged::{query-dsl-test}[dis_max] -------------------------------------------------- <1> add your queries <2> add your queries diff --git a/docs/java-api/query-dsl/exists-query.asciidoc b/docs/java-api/query-dsl/exists-query.asciidoc index c924f20278ff7..6fa5ba6a6f257 100644 --- a/docs/java-api/query-dsl/exists-query.asciidoc +++ b/docs/java-api/query-dsl/exists-query.asciidoc @@ -3,9 +3,8 @@ See {ref}/query-dsl-exists-query.html[Exists Query]. -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = existsQuery("name"); <1> +include-tagged::{query-dsl-test}[exists] -------------------------------------------------- <1> field - diff --git a/docs/java-api/query-dsl/function-score-query.asciidoc b/docs/java-api/query-dsl/function-score-query.asciidoc index 9e731edb8ba7b..fcd5f2dc473f5 100644 --- a/docs/java-api/query-dsl/function-score-query.asciidoc +++ b/docs/java-api/query-dsl/function-score-query.asciidoc @@ -10,18 +10,10 @@ To use `ScoreFunctionBuilders` just import them in your class: import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.*; -------------------------------------------------- -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -FilterFunctionBuilder[] functions = { - new FunctionScoreQueryBuilder.FilterFunctionBuilder( - matchQuery("name", "kimchy"), <1> - randomFunction("ABCDEF")), <2> - new FunctionScoreQueryBuilder.FilterFunctionBuilder( - exponentialDecayFunction("age", 0L, 1L)) <3> -}; -QueryBuilder qb = QueryBuilders.functionScoreQuery(functions); +include-tagged::{query-dsl-test}[function_score] -------------------------------------------------- <1> Add a first function based on a query <2> And randomize the score based on a given seed <3> Add another function based on the age field - diff --git a/docs/java-api/query-dsl/fuzzy-query.asciidoc b/docs/java-api/query-dsl/fuzzy-query.asciidoc index e871bc9d0b4d3..4a7bde82cdfb7 100644 --- a/docs/java-api/query-dsl/fuzzy-query.asciidoc +++ b/docs/java-api/query-dsl/fuzzy-query.asciidoc @@ -3,13 +3,9 @@ See {ref}/query-dsl-fuzzy-query.html[Fuzzy Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = fuzzyQuery( - "name", <1> - "kimzhy" <2> -); +include-tagged::{query-dsl-test}[fuzzy] -------------------------------------------------- <1> field <2> text - diff --git a/docs/java-api/query-dsl/geo-bounding-box-query.asciidoc b/docs/java-api/query-dsl/geo-bounding-box-query.asciidoc index f877b11923afe..4983a21213376 100644 --- a/docs/java-api/query-dsl/geo-bounding-box-query.asciidoc +++ b/docs/java-api/query-dsl/geo-bounding-box-query.asciidoc @@ -3,14 +3,10 @@ See {ref}/query-dsl-geo-bounding-box-query.html[Geo Bounding Box Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = geoBoundingBoxQuery("pin.location") <1> - .setCorners(40.73, -74.1, <2> - 40.717, -73.99); <3> +include-tagged::{query-dsl-test}[geo_bounding_box] -------------------------------------------------- <1> field <2> bounding box top left point <3> bounding box bottom right point - - diff --git a/docs/java-api/query-dsl/geo-distance-query.asciidoc b/docs/java-api/query-dsl/geo-distance-query.asciidoc index d03ba2017f594..7927dff440be6 100644 --- a/docs/java-api/query-dsl/geo-distance-query.asciidoc +++ b/docs/java-api/query-dsl/geo-distance-query.asciidoc @@ -3,13 +3,10 @@ See {ref}/query-dsl-geo-distance-query.html[Geo Distance Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = geoDistanceQuery("pin.location") <1> - .point(40, -70) <2> - .distance(200, DistanceUnit.KILOMETERS); <3> +include-tagged::{query-dsl-test}[geo_bounding_box] -------------------------------------------------- <1> field <2> center point <3> distance from center point - diff --git a/docs/java-api/query-dsl/geo-polygon-query.asciidoc b/docs/java-api/query-dsl/geo-polygon-query.asciidoc index 362e1751bb935..7dbf49b8d1afd 100644 --- a/docs/java-api/query-dsl/geo-polygon-query.asciidoc +++ b/docs/java-api/query-dsl/geo-polygon-query.asciidoc @@ -3,16 +3,9 @@ See {ref}/query-dsl-geo-polygon-query.html[Geo Polygon Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -List points = new ArrayList<>(); <1> -points.add(new GeoPoint(40, -70)); -points.add(new GeoPoint(30, -80)); -points.add(new GeoPoint(20, -90)); - -QueryBuilder qb = - geoPolygonQuery("pin.location", points); <2> +include-tagged::{query-dsl-test}[geo_polygon] -------------------------------------------------- <1> add your polygon of points a document should fall within <2> initialise the query with field and points - diff --git a/docs/java-api/query-dsl/geo-shape-query.asciidoc b/docs/java-api/query-dsl/geo-shape-query.asciidoc index 62e8ee199c9e1..c8084c5ea9fd6 100644 --- a/docs/java-api/query-dsl/geo-shape-query.asciidoc +++ b/docs/java-api/query-dsl/geo-shape-query.asciidoc @@ -37,34 +37,17 @@ import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.geo.builders.ShapeBuilder; -------------------------------------------------- -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -List points = new ArrayList<>(); -points.add(new Coordinate(0, 0)); -points.add(new Coordinate(0, 10)); -points.add(new Coordinate(10, 10)); -points.add(new Coordinate(10, 0)); -points.add(new Coordinate(0, 0)); - -QueryBuilder qb = geoShapeQuery( - "pin.location", <1> - ShapeBuilders.newMultiPoint(points) <2> - .relation(ShapeRelation.WITHIN); <3> +include-tagged::{query-dsl-test}[geo_shape] -------------------------------------------------- <1> field <2> shape <3> relation can be `ShapeRelation.CONTAINS`, `ShapeRelation.WITHIN`, `ShapeRelation.INTERSECTS` or `ShapeRelation.DISJOINT` -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -// Using pre-indexed shapes -QueryBuilder qb = geoShapeQuery( - "pin.location", <1> - "DEU", <2> - "countries") <3> - .relation(ShapeRelation.WITHIN)) <4> - .indexedShapeIndex("shapes") <5> - .indexedShapePath("location"); <6> +include-tagged::{query-dsl-test}[indexed_geo_shape] -------------------------------------------------- <1> field <2> The ID of the document that containing the pre-indexed shape. diff --git a/docs/java-api/query-dsl/has-child-query.asciidoc b/docs/java-api/query-dsl/has-child-query.asciidoc index f6995227e9814..fc29e8a90288e 100644 --- a/docs/java-api/query-dsl/has-child-query.asciidoc +++ b/docs/java-api/query-dsl/has-child-query.asciidoc @@ -3,15 +3,10 @@ See {ref}/query-dsl-has-child-query.html[Has Child Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = hasChildQuery( - "blog_tag", <1> - termQuery("tag","something"), <2> - ScoreMode.Avg <3> -); +include-tagged::{query-dsl-test}[has_child] -------------------------------------------------- <1> child type to query against <2> query <3> score mode can be `ScoreMode.Avg`, `ScoreMode.Max`, `ScoreMode.Min`, `ScoreMode.None` or `ScoreMode.Total` - diff --git a/docs/java-api/query-dsl/has-parent-query.asciidoc b/docs/java-api/query-dsl/has-parent-query.asciidoc index df60861383353..6ebf01be8c33f 100644 --- a/docs/java-api/query-dsl/has-parent-query.asciidoc +++ b/docs/java-api/query-dsl/has-parent-query.asciidoc @@ -3,14 +3,10 @@ See {ref}/query-dsl-has-parent-query.html[Has Parent] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = hasParentQuery( - "blog", <1> - termQuery("tag","something"), <2> - false <3> -); +include-tagged::{query-dsl-test}[has_parent] -------------------------------------------------- <1> parent type to query against <2> query -<3> whether the score from the parent hit should propogate to the child hit +<3> whether the score from the parent hit should propagate to the child hit diff --git a/docs/java-api/query-dsl/ids-query.asciidoc b/docs/java-api/query-dsl/ids-query.asciidoc index c5abfb14dedd4..9abc8ed9fed7c 100644 --- a/docs/java-api/query-dsl/ids-query.asciidoc +++ b/docs/java-api/query-dsl/ids-query.asciidoc @@ -4,13 +4,8 @@ See {ref}/query-dsl-ids-query.html[Ids Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = idsQuery("my_type", "type2") - .addIds("1", "4", "100"); - -QueryBuilder qb = idsQuery() <1> - .addIds("1", "4", "100"); +include-tagged::{query-dsl-test}[ids] -------------------------------------------------- <1> type is optional - diff --git a/docs/java-api/query-dsl/match-all-query.asciidoc b/docs/java-api/query-dsl/match-all-query.asciidoc index 1c367f96453f3..85d847528f5b8 100644 --- a/docs/java-api/query-dsl/match-all-query.asciidoc +++ b/docs/java-api/query-dsl/match-all-query.asciidoc @@ -3,7 +3,7 @@ See {ref}/query-dsl-match-all-query.html[Match All Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = matchAllQuery(); +include-tagged::{query-dsl-test}[match_all] -------------------------------------------------- diff --git a/docs/java-api/query-dsl/match-query.asciidoc b/docs/java-api/query-dsl/match-query.asciidoc index 40acbb278850e..6884deb5f1f24 100644 --- a/docs/java-api/query-dsl/match-query.asciidoc +++ b/docs/java-api/query-dsl/match-query.asciidoc @@ -3,13 +3,9 @@ See {ref}/query-dsl-match-query.html[Match Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = matchQuery( - "name", <1> - "kimchy elasticsearch" <2> -); +include-tagged::{query-dsl-test}[match] -------------------------------------------------- <1> field <2> text - diff --git a/docs/java-api/query-dsl/mlt-query.asciidoc b/docs/java-api/query-dsl/mlt-query.asciidoc index 7104fe5efa0fa..11e5c7ef40482 100644 --- a/docs/java-api/query-dsl/mlt-query.asciidoc +++ b/docs/java-api/query-dsl/mlt-query.asciidoc @@ -1,18 +1,11 @@ [[java-query-dsl-mlt-query]] ==== More Like This Query -See: - * {ref}/query-dsl-mlt-query.html[More Like This Query] +See {ref}/query-dsl-mlt-query.html[More Like This Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -String[] fields = {"name.first", "name.last"}; <1> -String[] texts = {"text like this one"}; <2> -Item[] items = null; - -QueryBuilder qb = moreLikeThisQuery(fields, texts, items) - .minTermFreq(1) <3> - .maxQueryTerms(12); <4> +include-tagged::{query-dsl-test}[more_like_this] -------------------------------------------------- <1> fields <2> text diff --git a/docs/java-api/query-dsl/multi-match-query.asciidoc b/docs/java-api/query-dsl/multi-match-query.asciidoc index db1c5fd973028..86b384d44d3c0 100644 --- a/docs/java-api/query-dsl/multi-match-query.asciidoc +++ b/docs/java-api/query-dsl/multi-match-query.asciidoc @@ -3,12 +3,9 @@ See {ref}/query-dsl-multi-match-query.html[Multi Match Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = multiMatchQuery( - "kimchy elasticsearch", <1> - "user", "message" <2> -); +include-tagged::{query-dsl-test}[multi_match] -------------------------------------------------- <1> text <2> fields diff --git a/docs/java-api/query-dsl/nested-query.asciidoc b/docs/java-api/query-dsl/nested-query.asciidoc index d6da597eecdd2..9b675ea72acfd 100644 --- a/docs/java-api/query-dsl/nested-query.asciidoc +++ b/docs/java-api/query-dsl/nested-query.asciidoc @@ -3,15 +3,9 @@ See {ref}/query-dsl-nested-query.html[Nested Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = nestedQuery( - "obj1", <1> - boolQuery() <2> - .must(matchQuery("obj1.name", "blue")) - .must(rangeQuery("obj1.count").gt(5)), - ScoreMode.Avg <3> - ); +include-tagged::{query-dsl-test}[nested] -------------------------------------------------- <1> path to nested document <2> your query. Any fields referenced inside the query must use the complete path (fully qualified). diff --git a/docs/java-api/query-dsl/prefix-query.asciidoc b/docs/java-api/query-dsl/prefix-query.asciidoc index f91f82c6d82d0..eb15c4426f633 100644 --- a/docs/java-api/query-dsl/prefix-query.asciidoc +++ b/docs/java-api/query-dsl/prefix-query.asciidoc @@ -3,14 +3,9 @@ See {ref}/query-dsl-prefix-query.html[Prefix Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = prefixQuery( - "brand", <1> - "heine" <2> -); +include-tagged::{query-dsl-test}[prefix] -------------------------------------------------- <1> field <2> prefix - - diff --git a/docs/java-api/query-dsl/query-string-query.asciidoc b/docs/java-api/query-dsl/query-string-query.asciidoc index 2a8c178306904..7d8bead2e340a 100644 --- a/docs/java-api/query-dsl/query-string-query.asciidoc +++ b/docs/java-api/query-dsl/query-string-query.asciidoc @@ -3,8 +3,7 @@ See {ref}/query-dsl-query-string-query.html[Query String Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = queryStringQuery("+kimchy -elasticsearch"); <1> +include-tagged::{query-dsl-test}[query_string] -------------------------------------------------- -<1> text diff --git a/docs/java-api/query-dsl/range-query.asciidoc b/docs/java-api/query-dsl/range-query.asciidoc index e3883c16d9f44..2d58fbd3a34ef 100644 --- a/docs/java-api/query-dsl/range-query.asciidoc +++ b/docs/java-api/query-dsl/range-query.asciidoc @@ -3,13 +3,9 @@ See {ref}/query-dsl-range-query.html[Range Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = rangeQuery("price") <1> - .from(5) <2> - .to(10) <3> - .includeLower(true) <4> - .includeUpper(false); <5> +include-tagged::{query-dsl-test}[range] -------------------------------------------------- <1> field <2> from @@ -17,12 +13,9 @@ QueryBuilder qb = rangeQuery("price") <1> <4> include lower value means that `from` is `gt` when `false` or `gte` when `true` <5> include upper value means that `to` is `lt` when `false` or `lte` when `true` -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -// A simplified form using gte, gt, lt or lte -QueryBuilder qb = rangeQuery("age") <1> - .gte("10") <2> - .lt("20"); <3> +include-tagged::{query-dsl-test}[range_simplified] -------------------------------------------------- <1> field <2> set `from` to 10 and `includeLower` to `true` diff --git a/docs/java-api/query-dsl/regexp-query.asciidoc b/docs/java-api/query-dsl/regexp-query.asciidoc index 617f589e39c3c..f9cd8cd72d9d5 100644 --- a/docs/java-api/query-dsl/regexp-query.asciidoc +++ b/docs/java-api/query-dsl/regexp-query.asciidoc @@ -3,11 +3,9 @@ See {ref}/query-dsl-regexp-query.html[Regexp Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = regexpQuery( - "name.first", <1> - "s.*y"); <2> +include-tagged::{query-dsl-test}[regexp] -------------------------------------------------- <1> field <2> regexp diff --git a/docs/java-api/query-dsl/script-query.asciidoc b/docs/java-api/query-dsl/script-query.asciidoc index a79af0f2cef05..a8c60f1d8eb0d 100644 --- a/docs/java-api/query-dsl/script-query.asciidoc +++ b/docs/java-api/query-dsl/script-query.asciidoc @@ -3,11 +3,9 @@ See {ref}/query-dsl-script-query.html[Script Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = scriptQuery( - new Script("doc['num1'].value > 1") <1> -); +include-tagged::{query-dsl-test}[script_inline] -------------------------------------------------- <1> inlined script @@ -21,17 +19,11 @@ doc['num1'].value > params.param1 You can use it then with: -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = scriptQuery( - new Script( - ScriptType.FILE, <1> - "painless", <2> - "myscript", <3> - Collections.singletonMap("param1", 5)) <4> -); +include-tagged::{query-dsl-test}[script_file] -------------------------------------------------- <1> Script type: either `ScriptType.FILE`, `ScriptType.INLINE` or `ScriptType.INDEXED` <2> Scripting engine <3> Script name -<4> Parameters as a `Map` of `` +<4> Parameters as a `Map` diff --git a/docs/java-api/query-dsl/simple-query-string-query.asciidoc b/docs/java-api/query-dsl/simple-query-string-query.asciidoc index bd5f3bdbddb20..c3b32ecd1cbb2 100644 --- a/docs/java-api/query-dsl/simple-query-string-query.asciidoc +++ b/docs/java-api/query-dsl/simple-query-string-query.asciidoc @@ -3,8 +3,7 @@ See {ref}/query-dsl-simple-query-string-query.html[Simple Query String Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = simpleQueryStringQuery("+kimchy -elasticsearch"); <1> +include-tagged::{query-dsl-test}[simple_query_string] -------------------------------------------------- -<1> text diff --git a/docs/java-api/query-dsl/span-containing-query.asciidoc b/docs/java-api/query-dsl/span-containing-query.asciidoc index a9f762f929d69..173e26952c265 100644 --- a/docs/java-api/query-dsl/span-containing-query.asciidoc +++ b/docs/java-api/query-dsl/span-containing-query.asciidoc @@ -3,14 +3,9 @@ See {ref}/query-dsl-span-containing-query.html[Span Containing Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = spanContainingQuery( - spanNearQuery(spanTermQuery("field1","bar"), 5) <1> - .addClause(spanTermQuery("field1","baz")) - .inOrder(true), - spanTermQuery("field1","foo")); <2> +include-tagged::{query-dsl-test}[span_containing] -------------------------------------------------- <1> `big` part <2> `little` part - diff --git a/docs/java-api/query-dsl/span-first-query.asciidoc b/docs/java-api/query-dsl/span-first-query.asciidoc index a9c6b47efb15f..d02c164754c53 100644 --- a/docs/java-api/query-dsl/span-first-query.asciidoc +++ b/docs/java-api/query-dsl/span-first-query.asciidoc @@ -3,13 +3,9 @@ See {ref}/query-dsl-span-first-query.html[Span First Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = spanFirstQuery( - spanTermQuery("user", "kimchy"), <1> - 3 <2> -); +include-tagged::{query-dsl-test}[span_first] -------------------------------------------------- <1> query <2> max end position - diff --git a/docs/java-api/query-dsl/span-multi-term-query.asciidoc b/docs/java-api/query-dsl/span-multi-term-query.asciidoc index 841c34dd25035..eea00f61fe7e1 100644 --- a/docs/java-api/query-dsl/span-multi-term-query.asciidoc +++ b/docs/java-api/query-dsl/span-multi-term-query.asciidoc @@ -3,12 +3,9 @@ See {ref}/query-dsl-span-multi-term-query.html[Span Multi Term Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = spanMultiTermQueryBuilder( - prefixQuery("user", "ki") <1> -); +include-tagged::{query-dsl-test}[span_multi] -------------------------------------------------- <1> Can be any builder extending the `MultiTermQueryBuilder` class. For example: `FuzzyQueryBuilder`, `PrefixQueryBuilder`, `RangeQueryBuilder`, `RegexpQueryBuilder` or `WildcardQueryBuilder`. - diff --git a/docs/java-api/query-dsl/span-near-query.asciidoc b/docs/java-api/query-dsl/span-near-query.asciidoc index 6e811d595811d..6f4661e34c9d1 100644 --- a/docs/java-api/query-dsl/span-near-query.asciidoc +++ b/docs/java-api/query-dsl/span-near-query.asciidoc @@ -3,16 +3,10 @@ See {ref}/query-dsl-span-near-query.html[Span Near Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = spanNearQuery( - spanTermQuery("field","value1"), <1> - 12) <2> - .addClause(spanTermQuery("field","value2")) <1> - .addClause(spanTermQuery("field","value3")) <1> - .inOrder(false); <3> +include-tagged::{query-dsl-test}[span_near] -------------------------------------------------- <1> span term queries <2> slop factor: the maximum number of intervening unmatched positions <3> whether matches are required to be in-order - diff --git a/docs/java-api/query-dsl/span-not-query.asciidoc b/docs/java-api/query-dsl/span-not-query.asciidoc index 31026b7d8ea21..001c2ca025e6d 100644 --- a/docs/java-api/query-dsl/span-not-query.asciidoc +++ b/docs/java-api/query-dsl/span-not-query.asciidoc @@ -3,12 +3,9 @@ See {ref}/query-dsl-span-not-query.html[Span Not Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = spanNotQuery( - spanTermQuery("field","value1"), <1> - spanTermQuery("field","value2")); <2> +include-tagged::{query-dsl-test}[span_not] -------------------------------------------------- <1> span query whose matches are filtered <2> span query whose matches must not overlap those returned - diff --git a/docs/java-api/query-dsl/span-or-query.asciidoc b/docs/java-api/query-dsl/span-or-query.asciidoc index 54f566c47701f..787628b59342f 100644 --- a/docs/java-api/query-dsl/span-or-query.asciidoc +++ b/docs/java-api/query-dsl/span-or-query.asciidoc @@ -3,12 +3,8 @@ See {ref}/query-dsl-span-or-query.html[Span Or Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = spanOrQuery( - spanTermQuery("field","value1")) <1> - .addClause(spanTermQuery("field","value2")) <1> - .addClause(spanTermQuery("field","value3")); <1> +include-tagged::{query-dsl-test}[span_or] -------------------------------------------------- <1> span term queries - diff --git a/docs/java-api/query-dsl/span-term-query.asciidoc b/docs/java-api/query-dsl/span-term-query.asciidoc index b8b74e45219e9..2bdf9276515dc 100644 --- a/docs/java-api/query-dsl/span-term-query.asciidoc +++ b/docs/java-api/query-dsl/span-term-query.asciidoc @@ -3,13 +3,9 @@ See {ref}/query-dsl-span-term-query.html[Span Term Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = spanTermQuery( - "user", <1> - "kimchy" <2> -); +include-tagged::{query-dsl-test}[span_term] -------------------------------------------------- <1> field <2> value - diff --git a/docs/java-api/query-dsl/span-within-query.asciidoc b/docs/java-api/query-dsl/span-within-query.asciidoc index 0e8752785fb5d..afa527c0b67fb 100644 --- a/docs/java-api/query-dsl/span-within-query.asciidoc +++ b/docs/java-api/query-dsl/span-within-query.asciidoc @@ -3,13 +3,9 @@ See {ref}/query-dsl-span-within-query.html[Span Within Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = spanWithinQuery( - spanNearQuery(spanTermQuery("field1", "bar"), 5) <1> - .addClause(spanTermQuery("field1", "baz")) - .inOrder(true), - spanTermQuery("field1", "foo")); <2> +include-tagged::{query-dsl-test}[span_within] -------------------------------------------------- <1> `big` part <2> `little` part diff --git a/docs/java-api/query-dsl/term-query.asciidoc b/docs/java-api/query-dsl/term-query.asciidoc index 47fcc522130e4..7c8549dbed403 100644 --- a/docs/java-api/query-dsl/term-query.asciidoc +++ b/docs/java-api/query-dsl/term-query.asciidoc @@ -3,13 +3,9 @@ See {ref}/query-dsl-term-query.html[Term Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = termQuery( - "name", <1> - "kimchy" <2> -); +include-tagged::{query-dsl-test}[term] -------------------------------------------------- <1> field <2> text - diff --git a/docs/java-api/query-dsl/terms-query.asciidoc b/docs/java-api/query-dsl/terms-query.asciidoc index 525961cee6106..587968ba18e77 100644 --- a/docs/java-api/query-dsl/terms-query.asciidoc +++ b/docs/java-api/query-dsl/terms-query.asciidoc @@ -3,10 +3,9 @@ See {ref}/query-dsl-terms-query.html[Terms Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = termsQuery("tags", <1> - "blue", "pill"); <2> +include-tagged::{query-dsl-test}[terms] -------------------------------------------------- <1> field <2> values diff --git a/docs/java-api/query-dsl/type-query.asciidoc b/docs/java-api/query-dsl/type-query.asciidoc index f0cfdcebc2299..cbbc666607758 100644 --- a/docs/java-api/query-dsl/type-query.asciidoc +++ b/docs/java-api/query-dsl/type-query.asciidoc @@ -3,8 +3,8 @@ See {ref}/query-dsl-type-query.html[Type Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = typeQuery("my_type"); <1> +include-tagged::{query-dsl-test}[type] -------------------------------------------------- <1> type diff --git a/docs/java-api/query-dsl/wildcard-query.asciidoc b/docs/java-api/query-dsl/wildcard-query.asciidoc index 756eb7be61dff..f9ace822aac9d 100644 --- a/docs/java-api/query-dsl/wildcard-query.asciidoc +++ b/docs/java-api/query-dsl/wildcard-query.asciidoc @@ -3,8 +3,9 @@ See {ref}/query-dsl-wildcard-query.html[Wildcard Query] -[source,java] +["source","java",subs="attributes,callouts,macros"] -------------------------------------------------- -QueryBuilder qb = wildcardQuery("user", "k?mc*"); +include-tagged::{query-dsl-test}[wildcard] -------------------------------------------------- - +<1> field +<2> wildcard expression From db160380613d451e4d48fcf952c520d9593e37a9 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Tue, 2 May 2017 13:01:30 -0400 Subject: [PATCH 163/619] Docs: correct indentation on callout I've aligned them all to 70 characters except this one.... Relates to #24354 --- .../client/documentation/QueryDSLDocumentationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java index 1c20e2253a239..f01e4824b3fa3 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java @@ -134,7 +134,7 @@ public void testDisMax() { public void testExists() { // tag::exists - existsQuery("name"); // <1> + existsQuery("name"); // <1> // end::exists } From 6fcd24d264dafc5f3e00ade2c7cb4fde51449edd Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Tue, 2 May 2017 20:25:09 +0200 Subject: [PATCH 164/619] Check index sorting with no replica since we cannot ensure that the replica index is ready when forceMerge is called. Closes #24416 --- .../test/indices.sort/10_basic.yaml | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.sort/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.sort/10_basic.yaml index 679b4f4e535c7..8119e837d69df 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.sort/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.sort/10_basic.yaml @@ -11,7 +11,7 @@ body: settings: number_of_shards: 1 - number_of_replicas: 1 + number_of_replicas: 0 index.sort.field: rank mappings: test: @@ -19,6 +19,10 @@ rank: type: integer + - do: + cluster.health: + wait_for_status: green + - do: index: index: test @@ -60,6 +64,14 @@ indices.refresh: index: test + - do: + indices.segments: + index: test + + - match: { _shards.total: 1} + - length: { indices.test.shards.0: 1} + - length: { indices.test.shards.0.0.segments: 1} + - do: search: index: test @@ -67,9 +79,9 @@ body: sort: _doc - - match: {hits.total: 4 } + - match: {hits.total: 4 } - length: {hits.hits: 4 } - - match: {hits.hits.0._id: "2" } - - match: {hits.hits.1._id: "4" } - - match: {hits.hits.2._id: "3" } - - match: {hits.hits.3._id: "1" } + - match: {hits.hits.0._id: "2" } + - match: {hits.hits.1._id: "4" } + - match: {hits.hits.2._id: "3" } + - match: {hits.hits.3._id: "1" } From 23801153c7f2985b7bd6e2ed55461429c84d7ea5 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 2 May 2017 19:59:07 -0400 Subject: [PATCH 165/619] Fix JVM test argline (#24448) * Fix JVM test argline The argline was being overridden by '-XX:-OmitStackTraceInFastThrow' which led to test failures that were expecting the JVM to be in a certain state based on the value of tests.jvm.argline but they were not since these arguments were never passed to the JVM. Additionally, we need to respect the provided JVM argline if it is already provided with a flag for OmitStackTraceInFastThrow. This commit fixes this by only setting OmitStackTraceInFastThrow if it is not already set. * Add comment * Fix comment * More elaborate comment * Sigh --- .../org/elasticsearch/gradle/BuildPlugin.groovy | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy index 19f3846ca5d5b..3acf865f7bc47 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy @@ -468,8 +468,18 @@ class BuildPlugin implements Plugin { File heapdumpDir = new File(project.buildDir, 'heapdump') heapdumpDir.mkdirs() jvmArg '-XX:HeapDumpPath=' + heapdumpDir - argLine System.getProperty('tests.jvm.argline') - argLine '-XX:-OmitStackTraceInFastThrow' + /* + * We only want to append -XX:-OmitStackTraceInFastThrow if a flag for OmitStackTraceInFastThrow is not already included in + * tests.jvm.argline. + */ + final String testsJvmArgline = System.getProperty('tests.jvm.argline') + if (testsJvmArgline == null) { + argLine '-XX:-OmitStackTraceInFastThrow' + } else if (testsJvmArgline.indexOf("OmitStackTraceInFastThrow") < 0) { + argLine testsJvmArgline.trim() + ' ' + '-XX:-OmitStackTraceInFastThrow' + } else { + argLine testsJvmArgline.trim() + } // we use './temp' since this is per JVM and tests are forbidden from writing to CWD systemProperty 'java.io.tmpdir', './temp' From 4289e206ae7895123067bfa86600324462e056ab Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 2 May 2017 21:33:24 -0400 Subject: [PATCH 166/619] Log JVM arguments on startup These can be very useful to have, let's have them at our fingertips in the logs (in case the JVM dies and we can no longer retrieve them, at least they are here). Relates #24451 --- core/src/main/java/org/elasticsearch/node/Node.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index c89f31fbe749c..54ce2bf7a205b 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -291,6 +291,7 @@ protected Node(final Environment environment, Collection Constants.JVM_NAME, Constants.JAVA_VERSION, Constants.JVM_VERSION); + logger.info("JVM arguments {}", Arrays.toString(jvmInfo.getInputArguments())); warnIfPreRelease(Version.CURRENT, Build.CURRENT.isSnapshot(), logger); if (logger.isDebugEnabled()) { From 4a578e9c710a5bf7d7f328440d5d2d54f48ba11f Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 2 May 2017 22:25:52 -0400 Subject: [PATCH 167/619] Show JVM arguments We often want the JVM arguments used for a running instance of Elasticsearch. It sure would be nice if these came as part of the nodes API, or any API that includes JVM info. This commit causes these arguments to be displayed. Relates #24450 --- core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java b/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java index e277faafd5ffa..c18a1c5e3fdd9 100644 --- a/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java +++ b/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java @@ -461,6 +461,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field(Fields.USING_COMPRESSED_OOPS, useCompressedOops); + builder.field(Fields.INPUT_ARGUMENTS, inputArguments); + builder.endObject(); return builder; } @@ -489,6 +491,7 @@ static final class Fields { static final String GC_COLLECTORS = "gc_collectors"; static final String MEMORY_POOLS = "memory_pools"; static final String USING_COMPRESSED_OOPS = "using_compressed_ordinary_object_pointers"; + static final String INPUT_ARGUMENTS = "input_arguments"; } public static class Mem implements Writeable { From be19ccef573b4e9a839159157d7e6c2645261897 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Wed, 3 May 2017 09:47:21 +0200 Subject: [PATCH 168/619] Discard stale node responses from async shard fetching (#24434) Async shard fetching only uses the node id to correlate responses to requests. This can lead to a situation where a response from an earlier request is mistaken as response from a new request when a node is restarted. This commit adds unique round ids to correlate responses to requests. --- .../TransportIndicesShardStoresAction.java | 3 +- .../gateway/AsyncShardFetch.java | 91 ++++++++++++------- .../gateway/AsyncShardFetchTests.java | 58 +++++++++++- .../gateway/PrimaryShardAllocatorTests.java | 2 +- .../gateway/ReplicaShardAllocatorTests.java | 2 +- .../test/gateway/TestGatewayAllocator.java | 5 +- 6 files changed, 114 insertions(+), 47 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/shards/TransportIndicesShardStoresAction.java b/core/src/main/java/org/elasticsearch/action/admin/indices/shards/TransportIndicesShardStoresAction.java index ef19fd2eae0df..c11a2ded83d4b 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/shards/TransportIndicesShardStoresAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/shards/TransportIndicesShardStoresAction.java @@ -29,7 +29,6 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.cluster.health.ClusterShardHealth; -import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; @@ -155,7 +154,7 @@ private class InternalAsyncFetch extends AsyncShardFetch responses, List failures) { + protected synchronized void processAsyncFetch(List responses, List failures, long fetchingRound) { fetchResponses.add(new Response(shardId, responses, failures)); if (expectedOps.countDown()) { finish(); diff --git a/core/src/main/java/org/elasticsearch/gateway/AsyncShardFetch.java b/core/src/main/java/org/elasticsearch/gateway/AsyncShardFetch.java index 5a38a4b2b9e34..e2bbae775e5d7 100644 --- a/core/src/main/java/org/elasticsearch/gateway/AsyncShardFetch.java +++ b/core/src/main/java/org/elasticsearch/gateway/AsyncShardFetch.java @@ -44,6 +44,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; @@ -67,10 +68,11 @@ public interface Lister, N protected final Logger logger; protected final String type; - private final ShardId shardId; + protected final ShardId shardId; private final Lister, T> action; private final Map> cache = new HashMap<>(); private final Set nodesToIgnore = new HashSet<>(); + private final AtomicLong round = new AtomicLong(); private boolean closed; @SuppressWarnings("unchecked") @@ -112,20 +114,22 @@ public synchronized FetchResult fetchData(DiscoveryNodes nodes, Set i } nodesToIgnore.addAll(ignoreNodes); fillShardCacheWithDataNodes(cache, nodes); - Set> nodesToFetch = findNodesToFetch(cache); + List> nodesToFetch = findNodesToFetch(cache); if (nodesToFetch.isEmpty() == false) { // mark all node as fetching and go ahead and async fetch them + // use a unique round id to detect stale responses in processAsyncFetch + final long fetchingRound = round.incrementAndGet(); for (NodeEntry nodeEntry : nodesToFetch) { - nodeEntry.markAsFetching(); + nodeEntry.markAsFetching(fetchingRound); } DiscoveryNode[] discoNodesToFetch = nodesToFetch.stream().map(NodeEntry::getNodeId).map(nodes::get) .toArray(DiscoveryNode[]::new); - asyncFetch(shardId, discoNodesToFetch); + asyncFetch(discoNodesToFetch, fetchingRound); } // if we are still fetching, return null to indicate it if (hasAnyNodeFetching(cache)) { - return new FetchResult<>(shardId, null, emptySet(), emptySet()); + return new FetchResult<>(shardId, null, emptySet()); } else { // nothing to fetch, yay, build the return value Map fetchData = new HashMap<>(); @@ -158,7 +162,7 @@ public synchronized FetchResult fetchData(DiscoveryNodes nodes, Set i if (failedNodes.isEmpty() == false || allIgnoreNodes.isEmpty() == false) { reroute(shardId, "nodes failed [" + failedNodes.size() + "], ignored [" + allIgnoreNodes.size() + "]"); } - return new FetchResult<>(shardId, fetchData, failedNodes, allIgnoreNodes); + return new FetchResult<>(shardId, fetchData, allIgnoreNodes); } } @@ -168,7 +172,7 @@ public synchronized FetchResult fetchData(DiscoveryNodes nodes, Set i * the shard (response + failures), issuing a reroute at the end of it to make sure there will be another round * of allocations taking this new data into account. */ - protected synchronized void processAsyncFetch(ShardId shardId, List responses, List failures) { + protected synchronized void processAsyncFetch(List responses, List failures, long fetchingRound) { if (closed) { // we are closed, no need to process this async fetch at all logger.trace("{} ignoring fetched [{}] results, already closed", shardId, type); @@ -179,15 +183,19 @@ protected synchronized void processAsyncFetch(ShardId shardId, List responses if (responses != null) { for (T response : responses) { NodeEntry nodeEntry = cache.get(response.getNode().getId()); - // if the entry is there, and not marked as failed already, process it - if (nodeEntry == null) { - continue; - } - if (nodeEntry.isFailed()) { - logger.trace("{} node {} has failed for [{}] (failure [{}])", shardId, nodeEntry.getNodeId(), type, nodeEntry.getFailure()); - } else { - logger.trace("{} marking {} as done for [{}], result is [{}]", shardId, nodeEntry.getNodeId(), type, response); - nodeEntry.doneFetching(response); + if (nodeEntry != null) { + if (nodeEntry.getFetchingRound() != fetchingRound) { + assert nodeEntry.getFetchingRound() > fetchingRound : "node entries only replaced by newer rounds"; + logger.trace("{} received response for [{}] from node {} for an older fetching round (expected: {} but was: {})", + shardId, nodeEntry.getNodeId(), type, nodeEntry.getFetchingRound(), fetchingRound); + } else if (nodeEntry.isFailed()) { + logger.trace("{} node {} has failed for [{}] (failure [{}])", shardId, nodeEntry.getNodeId(), type, + nodeEntry.getFailure()); + } else { + // if the entry is there, for the right fetching round and not marked as failed already, process it + logger.trace("{} marking {} as done for [{}], result is [{}]", shardId, nodeEntry.getNodeId(), type, response); + nodeEntry.doneFetching(response); + } } } } @@ -195,15 +203,24 @@ protected synchronized void processAsyncFetch(ShardId shardId, List responses for (FailedNodeException failure : failures) { logger.trace("{} processing failure {} for [{}]", shardId, failure, type); NodeEntry nodeEntry = cache.get(failure.nodeId()); - // if the entry is there, and not marked as failed already, process it - if (nodeEntry != null && nodeEntry.isFailed() == false) { - Throwable unwrappedCause = ExceptionsHelper.unwrapCause(failure.getCause()); - // if the request got rejected or timed out, we need to try it again next time... - if (unwrappedCause instanceof EsRejectedExecutionException || unwrappedCause instanceof ReceiveTimeoutTransportException || unwrappedCause instanceof ElasticsearchTimeoutException) { - nodeEntry.restartFetching(); - } else { - logger.warn((Supplier) () -> new ParameterizedMessage("{}: failed to list shard for {} on node [{}]", shardId, type, failure.nodeId()), failure); - nodeEntry.doneFetching(failure.getCause()); + if (nodeEntry != null) { + if (nodeEntry.getFetchingRound() != fetchingRound) { + assert nodeEntry.getFetchingRound() > fetchingRound : "node entries only replaced by newer rounds"; + logger.trace("{} received failure for [{}] from node {} for an older fetching round (expected: {} but was: {})", + shardId, nodeEntry.getNodeId(), type, nodeEntry.getFetchingRound(), fetchingRound); + } else if (nodeEntry.isFailed() == false) { + // if the entry is there, for the right fetching round and not marked as failed already, process it + Throwable unwrappedCause = ExceptionsHelper.unwrapCause(failure.getCause()); + // if the request got rejected or timed out, we need to try it again next time... + if (unwrappedCause instanceof EsRejectedExecutionException || + unwrappedCause instanceof ReceiveTimeoutTransportException || + unwrappedCause instanceof ElasticsearchTimeoutException) { + nodeEntry.restartFetching(); + } else { + logger.warn((Supplier) () -> new ParameterizedMessage("{}: failed to list shard for {} on node [{}]", + shardId, type, failure.nodeId()), failure); + nodeEntry.doneFetching(failure.getCause()); + } } } } @@ -241,8 +258,8 @@ private void fillShardCacheWithDataNodes(Map> shardCache, D * Finds all the nodes that need to be fetched. Those are nodes that have no * data, and are not in fetch mode. */ - private Set> findNodesToFetch(Map> shardCache) { - Set> nodesToFetch = new HashSet<>(); + private List> findNodesToFetch(Map> shardCache) { + List> nodesToFetch = new ArrayList<>(); for (NodeEntry nodeEntry : shardCache.values()) { if (nodeEntry.hasData() == false && nodeEntry.isFetching() == false) { nodesToFetch.add(nodeEntry); @@ -267,12 +284,12 @@ private boolean hasAnyNodeFetching(Map> shardCache) { * Async fetches data for the provided shard with the set of nodes that need to be fetched from. */ // visible for testing - void asyncFetch(final ShardId shardId, final DiscoveryNode[] nodes) { + void asyncFetch(final DiscoveryNode[] nodes, long fetchingRound) { logger.trace("{} fetching [{}] from {}", shardId, type, nodes); action.list(shardId, nodes, new ActionListener>() { @Override public void onResponse(BaseNodesResponse response) { - processAsyncFetch(shardId, response.getNodes(), response.failures()); + processAsyncFetch(response.getNodes(), response.failures(), fetchingRound); } @Override @@ -281,7 +298,7 @@ public void onFailure(Exception e) { for (final DiscoveryNode node: nodes) { failures.add(new FailedNodeException(node.getId(), "total failure in fetching", e)); } - processAsyncFetch(shardId, null, failures); + processAsyncFetch(null, failures, fetchingRound); } }); } @@ -294,13 +311,11 @@ public static class FetchResult { private final ShardId shardId; private final Map data; - private final Set failedNodes; private final Set ignoreNodes; - public FetchResult(ShardId shardId, Map data, Set failedNodes, Set ignoreNodes) { + public FetchResult(ShardId shardId, Map data, Set ignoreNodes) { this.shardId = shardId; this.data = data; - this.failedNodes = failedNodes; this.ignoreNodes = ignoreNodes; } @@ -342,6 +357,7 @@ static class NodeEntry { private T value; private boolean valueSet; private Throwable failure; + private long fetchingRound; NodeEntry(String nodeId) { this.nodeId = nodeId; @@ -355,9 +371,10 @@ boolean isFetching() { return fetching; } - void markAsFetching() { + void markAsFetching(long fetchingRound) { assert fetching == false : "double marking a node as fetching"; - fetching = true; + this.fetching = true; + this.fetchingRound = fetchingRound; } void doneFetching(T value) { @@ -402,5 +419,9 @@ T getValue() { assert valueSet : "value is not set, hasn't been fetched yet"; return value; } + + long getFetchingRound() { + return fetchingRound; + } } } diff --git a/core/src/test/java/org/elasticsearch/gateway/AsyncShardFetchTests.java b/core/src/test/java/org/elasticsearch/gateway/AsyncShardFetchTests.java index 1cdaa27815ae7..2b58831a956ca 100644 --- a/core/src/test/java/org/elasticsearch/gateway/AsyncShardFetchTests.java +++ b/core/src/test/java/org/elasticsearch/gateway/AsyncShardFetchTests.java @@ -140,6 +140,55 @@ public void testFullCircleSingleNodeFailure() throws Exception { assertThat(fetchData.getData().get(node1), sameInstance(response1)); } + public void testIgnoreResponseFromDifferentRound() throws Exception { + DiscoveryNodes nodes = DiscoveryNodes.builder().add(node1).build(); + test.addSimulation(node1.getId(), response1); + + // first fetch, no data, still on going + AsyncShardFetch.FetchResult fetchData = test.fetchData(nodes, emptySet()); + assertThat(fetchData.hasData(), equalTo(false)); + assertThat(test.reroute.get(), equalTo(0)); + + // handle a response with incorrect round id, wait on reroute incrementing + test.processAsyncFetch(Collections.singletonList(response1), Collections.emptyList(), 0); + assertThat(fetchData.hasData(), equalTo(false)); + assertThat(test.reroute.get(), equalTo(1)); + + // fire a response (with correct round id), wait on reroute incrementing + test.fireSimulationAndWait(node1.getId()); + // verify we get back the data node + assertThat(test.reroute.get(), equalTo(2)); + fetchData = test.fetchData(nodes, emptySet()); + assertThat(fetchData.hasData(), equalTo(true)); + assertThat(fetchData.getData().size(), equalTo(1)); + assertThat(fetchData.getData().get(node1), sameInstance(response1)); + } + + public void testIgnoreFailureFromDifferentRound() throws Exception { + DiscoveryNodes nodes = DiscoveryNodes.builder().add(node1).build(); + // add a failed response for node1 + test.addSimulation(node1.getId(), failure1); + + // first fetch, no data, still on going + AsyncShardFetch.FetchResult fetchData = test.fetchData(nodes, emptySet()); + assertThat(fetchData.hasData(), equalTo(false)); + assertThat(test.reroute.get(), equalTo(0)); + + // handle a failure with incorrect round id, wait on reroute incrementing + test.processAsyncFetch(Collections.emptyList(), Collections.singletonList( + new FailedNodeException(node1.getId(), "dummy failure", failure1)), 0); + assertThat(fetchData.hasData(), equalTo(false)); + assertThat(test.reroute.get(), equalTo(1)); + + // fire a response, wait on reroute incrementing + test.fireSimulationAndWait(node1.getId()); + // failure, fetched data exists, but has no data + assertThat(test.reroute.get(), equalTo(2)); + fetchData = test.fetchData(nodes, emptySet()); + assertThat(fetchData.hasData(), equalTo(true)); + assertThat(fetchData.getData().size(), equalTo(0)); + } + public void testTwoNodesOnSetup() throws Exception { DiscoveryNodes nodes = DiscoveryNodes.builder().add(node1).add(node2).build(); test.addSimulation(node1.getId(), response1); @@ -267,7 +316,7 @@ protected void reroute(ShardId shardId, String reason) { } @Override - protected void asyncFetch(final ShardId shardId, DiscoveryNode[] nodes) { + protected void asyncFetch(DiscoveryNode[] nodes, long fetchingRound) { for (final DiscoveryNode node : nodes) { final String nodeId = node.getId(); threadPool.generic().execute(new Runnable() { @@ -283,11 +332,10 @@ public void run() { assert entry != null; entry.executeLatch.await(); if (entry.failure != null) { - processAsyncFetch(shardId, null, Collections.singletonList(new FailedNodeException(nodeId, - "unexpected", - entry.failure))); + processAsyncFetch(null, + Collections.singletonList(new FailedNodeException(nodeId, "unexpected", entry.failure)), fetchingRound); } else { - processAsyncFetch(shardId, Collections.singletonList(entry.response), null); + processAsyncFetch(Collections.singletonList(entry.response), null, fetchingRound); } } catch (Exception e) { logger.error("unexpected failure", e); diff --git a/core/src/test/java/org/elasticsearch/gateway/PrimaryShardAllocatorTests.java b/core/src/test/java/org/elasticsearch/gateway/PrimaryShardAllocatorTests.java index c09c92a70414e..e91017ecdf913 100644 --- a/core/src/test/java/org/elasticsearch/gateway/PrimaryShardAllocatorTests.java +++ b/core/src/test/java/org/elasticsearch/gateway/PrimaryShardAllocatorTests.java @@ -481,7 +481,7 @@ public TestAllocator addData(DiscoveryNode node, String allocationId, boolean pr @Override protected AsyncShardFetch.FetchResult fetchData(ShardRouting shard, RoutingAllocation allocation) { - return new AsyncShardFetch.FetchResult<>(shardId, data, Collections.emptySet(), Collections.emptySet()); + return new AsyncShardFetch.FetchResult<>(shardId, data, Collections.emptySet()); } } } diff --git a/core/src/test/java/org/elasticsearch/gateway/ReplicaShardAllocatorTests.java b/core/src/test/java/org/elasticsearch/gateway/ReplicaShardAllocatorTests.java index 775f7e8f1b590..133c8e3381605 100644 --- a/core/src/test/java/org/elasticsearch/gateway/ReplicaShardAllocatorTests.java +++ b/core/src/test/java/org/elasticsearch/gateway/ReplicaShardAllocatorTests.java @@ -389,7 +389,7 @@ protected AsyncShardFetch.FetchResult(shardId, tData, Collections.emptySet(), Collections.emptySet()); + return new AsyncShardFetch.FetchResult<>(shardId, tData, Collections.emptySet()); } @Override diff --git a/test/framework/src/main/java/org/elasticsearch/test/gateway/TestGatewayAllocator.java b/test/framework/src/main/java/org/elasticsearch/test/gateway/TestGatewayAllocator.java index 9706f91f68b8d..f8c8c4694e59d 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/gateway/TestGatewayAllocator.java +++ b/test/framework/src/main/java/org/elasticsearch/test/gateway/TestGatewayAllocator.java @@ -77,7 +77,7 @@ protected AsyncShardFetch.FetchResult fetchData(ShardR new NodeGatewayStartedShards( currentNodes.get(routing.currentNodeId()), routing.allocationId().getId(), routing.primary()))); - return new AsyncShardFetch.FetchResult<>(shardId, foundShards, Collections.emptySet(), ignoreNodes); + return new AsyncShardFetch.FetchResult<>(shardId, foundShards, ignoreNodes); } }; @@ -86,8 +86,7 @@ protected AsyncShardFetch.FetchResult fetchData(ShardR protected AsyncShardFetch.FetchResult fetchData(ShardRouting shard, RoutingAllocation allocation) { // for now, just pretend no node has data final ShardId shardId = shard.shardId(); - return new AsyncShardFetch.FetchResult<>(shardId, Collections.emptyMap(), Collections.emptySet(), - allocation.getIgnoreNodes(shardId)); + return new AsyncShardFetch.FetchResult<>(shardId, Collections.emptyMap(), allocation.getIgnoreNodes(shardId)); } @Override From fbd793d9e677f989296549f3545f7e0b1e5f4f26 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 3 May 2017 10:45:31 +0200 Subject: [PATCH 169/619] Introduce SearchResponseSections base class (#24442) SearchResponseSections is the common part extracted from InternalSearchResponse that can be shared between high level REST and elasticsearch. The only bits left out are around serialization which is not supported. This way it can accept Aggregations as a constructor argument, without requiring InternalAggregations, as the high level REST client uses its own objects for aggs parsing rather than internal ones. This change also makes Aggregations implement ToXContent, and Aggregation extend ToXContent. Especially the latter is suboptimal but the best solution that allows to share as much code as possible between core and the client, that doesn't require messing with generics and making the api complicated. Also it doesn't have downsides as all of the current implementations of Aggregation do implement ToXContent already. --- .../action/search/SearchResponse.java | 7 +- .../action/search/SearchResponseSections.java | 122 ++++++++++++++++ .../search/aggregations/Aggregation.java | 5 +- .../search/aggregations/Aggregations.java | 43 +++++- .../aggregations/InternalAggregations.java | 23 +-- .../internal/InternalSearchResponse.java | 136 +++--------------- 6 files changed, 190 insertions(+), 146 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/action/search/SearchResponseSections.java diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchResponse.java b/core/src/main/java/org/elasticsearch/action/search/SearchResponse.java index 54d8eab99e727..3e83996c7c630 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchResponse.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchResponse.java @@ -39,14 +39,13 @@ import java.util.Map; import static org.elasticsearch.action.search.ShardSearchFailure.readShardSearchFailure; -import static org.elasticsearch.search.internal.InternalSearchResponse.readInternalSearchResponse; /** * A response of a search request. */ public class SearchResponse extends ActionResponse implements StatusToXContentObject { - private InternalSearchResponse internalResponse; + private SearchResponseSections internalResponse; private String scrollId; @@ -61,7 +60,7 @@ public class SearchResponse extends ActionResponse implements StatusToXContentOb public SearchResponse() { } - public SearchResponse(InternalSearchResponse internalResponse, String scrollId, int totalShards, int successfulShards, + public SearchResponse(SearchResponseSections internalResponse, String scrollId, int totalShards, int successfulShards, long tookInMillis, ShardSearchFailure[] shardFailures) { this.internalResponse = internalResponse; this.scrollId = scrollId; @@ -209,7 +208,7 @@ public XContentBuilder innerToXContent(XContentBuilder builder, Params params) t @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); - internalResponse = readInternalSearchResponse(in); + internalResponse = new InternalSearchResponse(in); totalShards = in.readVInt(); successfulShards = in.readVInt(); int size = in.readVInt(); diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchResponseSections.java b/core/src/main/java/org/elasticsearch/action/search/SearchResponseSections.java new file mode 100644 index 0000000000000..1757acbfd6d93 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/action/search/SearchResponseSections.java @@ -0,0 +1,122 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.action.search; + +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.aggregations.Aggregations; +import org.elasticsearch.search.profile.ProfileShardResult; +import org.elasticsearch.search.profile.SearchProfileShardResults; +import org.elasticsearch.search.suggest.Suggest; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; + +/** + * Base class that holds the various sections which a search response is + * composed of (hits, aggs, suggestions etc.) and allows to retrieve them. + * + * The reason why this class exists is that the high level REST client uses its own classes + * to parse aggregations into, which are not serializable. This is the common part that can be + * shared between core and client. + */ +public class SearchResponseSections implements ToXContent { + + protected final SearchHits hits; + protected final Aggregations aggregations; + protected final Suggest suggest; + protected final SearchProfileShardResults profileResults; + protected final boolean timedOut; + protected final Boolean terminatedEarly; + protected final int numReducePhases; + + public SearchResponseSections(SearchHits hits, Aggregations aggregations, Suggest suggest, boolean timedOut, Boolean terminatedEarly, + SearchProfileShardResults profileResults, int numReducePhases) { + this.hits = hits; + this.aggregations = aggregations; + this.suggest = suggest; + this.profileResults = profileResults; + this.timedOut = timedOut; + this.terminatedEarly = terminatedEarly; + this.numReducePhases = numReducePhases; + } + + public final boolean timedOut() { + return this.timedOut; + } + + public final Boolean terminatedEarly() { + return this.terminatedEarly; + } + + public final SearchHits hits() { + return hits; + } + + public final Aggregations aggregations() { + return aggregations; + } + + public final Suggest suggest() { + return suggest; + } + + /** + * Returns the number of reduce phases applied to obtain this search response + */ + public final int getNumReducePhases() { + return numReducePhases; + } + + /** + * Returns the profile results for this search response (including all shards). + * An empty map is returned if profiling was not enabled + * + * @return Profile results + */ + public final Map profile() { + if (profileResults == null) { + return Collections.emptyMap(); + } + return profileResults.getShardResults(); + } + + @Override + public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + hits.toXContent(builder, params); + if (aggregations != null) { + aggregations.toXContent(builder, params); + } + if (suggest != null) { + suggest.toXContent(builder, params); + } + if (profileResults != null) { + profileResults.toXContent(builder, params); + } + return builder; + } + + protected void writeTo(StreamOutput out) throws IOException { + throw new UnsupportedOperationException(); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/Aggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/Aggregation.java index 991243ce0d480..eb55f1e7abcd1 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/Aggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/Aggregation.java @@ -19,13 +19,14 @@ package org.elasticsearch.search.aggregations; import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ToXContent; import java.util.Map; /** - * An aggregation + * An aggregation. Extends {@link ToXContent} as it makes it easier to print out its content. */ -public interface Aggregation { +public interface Aggregation extends ToXContent { /** * Delimiter used when prefixing aggregation names with their type diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/Aggregations.java b/core/src/main/java/org/elasticsearch/search/aggregations/Aggregations.java index ef84edfb72957..c7f84fe65772e 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/Aggregations.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/Aggregations.java @@ -18,6 +18,13 @@ */ package org.elasticsearch.search.aggregations; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParserUtils; + +import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -30,7 +37,9 @@ /** * Represents a set of {@link Aggregation}s */ -public abstract class Aggregations implements Iterable { +public class Aggregations implements Iterable, ToXContent { + + public static final String AGGREGATIONS_FIELD = "aggregations"; protected List aggregations = Collections.emptyList(); protected Map aggregationsAsMap; @@ -98,4 +107,36 @@ public final boolean equals(Object obj) { public final int hashCode() { return Objects.hash(getClass(), aggregations); } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + if (aggregations.isEmpty()) { + return builder; + } + builder.startObject(AGGREGATIONS_FIELD); + toXContentInternal(builder, params); + return builder.endObject(); + } + + /** + * Directly write all the aggregations without their bounding object. Used by sub-aggregations (non top level aggs) + */ + public XContentBuilder toXContentInternal(XContentBuilder builder, Params params) throws IOException { + for (Aggregation aggregation : aggregations) { + aggregation.toXContent(builder, params); + } + return builder; + } + + //TODO add tests for this method + public static Aggregations fromXContent(XContentParser parser) throws IOException { + final List aggregations = new ArrayList<>(); + XContentParser.Token token; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.START_OBJECT) { + aggregations.add(XContentParserUtils.parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Aggregation.class)); + } + } + return new Aggregations(aggregations); + } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/InternalAggregations.java b/core/src/main/java/org/elasticsearch/search/aggregations/InternalAggregations.java index 64c9e4794c338..846dc1bb3b0dc 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/InternalAggregations.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/InternalAggregations.java @@ -22,7 +22,6 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Streamable; import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.search.aggregations.InternalAggregation.ReduceContext; import java.io.IOException; @@ -32,6 +31,7 @@ import java.util.Map; import static java.util.Collections.emptyMap; + /** * An internal implementation of {@link Aggregations}. */ @@ -80,27 +80,6 @@ public static InternalAggregations reduce(List aggregation return new InternalAggregations(reducedAggregations); } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - if (aggregations.isEmpty()) { - return builder; - } - builder.startObject("aggregations"); - toXContentInternal(builder, params); - return builder.endObject(); - } - - /** - * Directly write all the aggregations without their bounding object. Used by sub-aggregations (non top level aggs) - */ - public XContentBuilder toXContentInternal(XContentBuilder builder, Params params) throws IOException { - for (Aggregation aggregation : aggregations) { - ((InternalAggregation)aggregation).toXContent(builder, params); - } - return builder; - } - - public static InternalAggregations readAggregations(StreamInput in) throws IOException { InternalAggregations result = new InternalAggregations(); result.readFrom(in); diff --git a/core/src/main/java/org/elasticsearch/search/internal/InternalSearchResponse.java b/core/src/main/java/org/elasticsearch/search/internal/InternalSearchResponse.java index 391f6efe18bfe..baa153e347093 100644 --- a/core/src/main/java/org/elasticsearch/search/internal/InternalSearchResponse.java +++ b/core/src/main/java/org/elasticsearch/search/internal/InternalSearchResponse.java @@ -19,148 +19,50 @@ package org.elasticsearch.search.internal; +import org.elasticsearch.action.search.SearchResponseSections; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.io.stream.Streamable; +import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.search.SearchHits; -import org.elasticsearch.search.aggregations.Aggregations; import org.elasticsearch.search.aggregations.InternalAggregations; -import org.elasticsearch.search.profile.ProfileShardResult; import org.elasticsearch.search.profile.SearchProfileShardResults; import org.elasticsearch.search.suggest.Suggest; import java.io.IOException; -import java.util.Collections; -import java.util.Map; -public class InternalSearchResponse implements Streamable, ToXContent { +/** + * {@link SearchResponseSections} subclass that can be serialized over the wire. + */ +public class InternalSearchResponse extends SearchResponseSections implements Writeable, ToXContent { public static InternalSearchResponse empty() { return new InternalSearchResponse(SearchHits.empty(), null, null, null, false, null, 1); } - private SearchHits hits; - - private InternalAggregations aggregations; - - private Suggest suggest; - - private SearchProfileShardResults profileResults; - - private boolean timedOut; - - private Boolean terminatedEarly = null; - - private int numReducePhases = 1; - - private InternalSearchResponse() { - } - public InternalSearchResponse(SearchHits hits, InternalAggregations aggregations, Suggest suggest, SearchProfileShardResults profileResults, boolean timedOut, Boolean terminatedEarly, int numReducePhases) { - this.hits = hits; - this.aggregations = aggregations; - this.suggest = suggest; - this.profileResults = profileResults; - this.timedOut = timedOut; - this.terminatedEarly = terminatedEarly; - this.numReducePhases = numReducePhases; - } - - public boolean timedOut() { - return this.timedOut; - } - - public Boolean terminatedEarly() { - return this.terminatedEarly; - } - - public SearchHits hits() { - return hits; - } - - public Aggregations aggregations() { - return aggregations; - } - - public Suggest suggest() { - return suggest; - } - - /** - * Returns the number of reduce phases applied to obtain this search response - */ - public int getNumReducePhases() { - return numReducePhases; + super(hits, aggregations, suggest, timedOut, terminatedEarly, profileResults, numReducePhases); } - /** - * Returns the profile results for this search response (including all shards). - * An empty map is returned if profiling was not enabled - * - * @return Profile results - */ - public Map profile() { - if (profileResults == null) { - return Collections.emptyMap(); - } - return profileResults.getShardResults(); - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - hits.toXContent(builder, params); - if (aggregations != null) { - aggregations.toXContent(builder, params); - } - if (suggest != null) { - suggest.toXContent(builder, params); - } - if (profileResults != null) { - profileResults.toXContent(builder, params); - } - return builder; - } - - public static InternalSearchResponse readInternalSearchResponse(StreamInput in) throws IOException { - InternalSearchResponse response = new InternalSearchResponse(); - response.readFrom(in); - return response; - } - - @Override - public void readFrom(StreamInput in) throws IOException { - hits = SearchHits.readSearchHits(in); - if (in.readBoolean()) { - aggregations = InternalAggregations.readAggregations(in); - } - if (in.readBoolean()) { - suggest = Suggest.readSuggest(in); - } - timedOut = in.readBoolean(); - terminatedEarly = in.readOptionalBoolean(); - profileResults = in.readOptionalWriteable(SearchProfileShardResults::new); - numReducePhases = in.readVInt(); + public InternalSearchResponse(StreamInput in) throws IOException { + super( + SearchHits.readSearchHits(in), + in.readBoolean() ? InternalAggregations.readAggregations(in) : null, + in.readBoolean() ? Suggest.readSuggest(in) : null, + in.readBoolean(), + in.readOptionalBoolean(), + in.readOptionalWriteable(SearchProfileShardResults::new), + in.readVInt() + ); } @Override public void writeTo(StreamOutput out) throws IOException { hits.writeTo(out); - if (aggregations == null) { - out.writeBoolean(false); - } else { - out.writeBoolean(true); - aggregations.writeTo(out); - } - if (suggest == null) { - out.writeBoolean(false); - } else { - out.writeBoolean(true); - suggest.writeTo(out); - } + out.writeOptionalStreamable((InternalAggregations)aggregations); + out.writeOptionalStreamable(suggest); out.writeBoolean(timedOut); out.writeOptionalBoolean(terminatedEarly); out.writeOptionalWriteable(profileResults); From 92bfd16c5824885ca673c5bb99acac9148c18693 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 3 May 2017 11:20:53 +0200 Subject: [PATCH 170/619] Java api: ActionRequestBuilder#execute to return a PlainActionFuture (#24415) This change makes the request builder code-path same as `Client#execute`. The request builder used to return a `ListenableActionFuture` when calling execute, which allows to associate listeners with the returned future. For async execution though it is recommended to use the `execute` method that accepts an `ActionListener`, like users would do when using `Client#execute`. Relates to #24412 Relates to #9201 --- .../action/ActionRequestBuilder.java | 23 ++--- .../admin/cluster/node/tasks/TasksIT.java | 87 +++++++++---------- .../indices/stats/IndicesStatsTests.java | 4 +- .../support/ActiveShardsObserverIT.java | 4 +- .../client/AbstractClientHeadersTestCase.java | 34 ++++---- .../index/WaitUntilRefreshIT.java | 4 +- .../search/SearchCancellationIT.java | 12 +-- .../DedicatedClusterSnapshotRestoreIT.java | 4 +- .../MinThreadsSnapshotRestoreIT.java | 10 +-- .../SharedClusterSnapshotRestoreIT.java | 15 ++-- .../migration/migrate_6_0/java.asciidoc | 12 ++- .../index/reindex/CancelTests.java | 4 +- .../index/reindex/RethrottleTests.java | 9 +- .../index/reindex/RetryTests.java | 6 +- 14 files changed, 108 insertions(+), 120 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/ActionRequestBuilder.java b/core/src/main/java/org/elasticsearch/action/ActionRequestBuilder.java index 076d4ae67f6c3..964568fc472fd 100644 --- a/core/src/main/java/org/elasticsearch/action/ActionRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/action/ActionRequestBuilder.java @@ -19,18 +19,16 @@ package org.elasticsearch.action; -import org.elasticsearch.action.support.PlainListenableActionFuture; import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.threadpool.ThreadPool; import java.util.Objects; -public abstract class ActionRequestBuilder> { +public abstract class ActionRequestBuilder> { protected final Action action; protected final Request request; - private final ThreadPool threadPool; protected final ElasticsearchClient client; protected ActionRequestBuilder(ElasticsearchClient client, Action action, Request request) { @@ -38,18 +36,14 @@ protected ActionRequestBuilder(ElasticsearchClient client, Action execute() { - PlainListenableActionFuture future = new PlainListenableActionFuture<>(threadPool); - execute(future); - return future; + public ActionFuture execute() { + return client.execute(action, request); } /** @@ -74,13 +68,6 @@ public Response get(String timeout) { } public void execute(ActionListener listener) { - client.execute(action, beforeExecute(request), listener); - } - - /** - * A callback to additionally process the request before its executed - */ - protected Request beforeExecute(Request request) { - return request; + client.execute(action, request, listener); } } diff --git a/core/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java b/core/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java index 48df23ea3aea8..a2cab6b85abde 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java +++ b/core/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java @@ -21,9 +21,9 @@ import org.elasticsearch.ElasticsearchTimeoutException; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.ResourceNotFoundException; +import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.FailedNodeException; -import org.elasticsearch.action.ListenableActionFuture; import org.elasticsearch.action.TaskOperationFailure; import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction; import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksResponse; @@ -90,7 +90,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertThrows; import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.emptyCollectionOf; @@ -466,8 +465,7 @@ public void waitForTaskCompletion(Task task) { public void testTasksCancellation() throws Exception { // Start blocking test task // Get real client (the plugin is not registered on transport nodes) - ListenableActionFuture future = TestTaskPlugin.TestTaskAction.INSTANCE.newRequestBuilder(client()) - .execute(); + ActionFuture future = TestTaskPlugin.TestTaskAction.INSTANCE.newRequestBuilder(client()).execute(); logger.info("--> started test tasks"); // Wait for the task to start on all nodes @@ -488,8 +486,7 @@ public void testTasksCancellation() throws Exception { public void testTasksUnblocking() throws Exception { // Start blocking test task - ListenableActionFuture future = TestTaskPlugin.TestTaskAction.INSTANCE.newRequestBuilder(client()) - .execute(); + ActionFuture future = TestTaskPlugin.TestTaskAction.INSTANCE.newRequestBuilder(client()).execute(); // Wait for the task to start on all nodes assertBusy(() -> assertEquals(internalCluster().size(), client().admin().cluster().prepareListTasks().setActions(TestTaskPlugin.TestTaskAction.NAME + "[n]").get().getTasks().size())); @@ -502,42 +499,45 @@ public void testTasksUnblocking() throws Exception { } public void testListTasksWaitForCompletion() throws Exception { - waitForCompletionTestCase(randomBoolean(), id -> { - return client().admin().cluster().prepareListTasks().setActions(TestTaskPlugin.TestTaskAction.NAME) - .setWaitForCompletion(true).execute(); - }, response -> { - assertThat(response.getNodeFailures(), empty()); - assertThat(response.getTaskFailures(), empty()); - assertThat(response.getTasks(), hasSize(1)); - TaskInfo task = response.getTasks().get(0); - assertEquals(TestTaskPlugin.TestTaskAction.NAME, task.getAction()); - }); + waitForCompletionTestCase(randomBoolean(), + id -> client().admin().cluster().prepareListTasks().setActions(TestTaskPlugin.TestTaskAction.NAME) + .setWaitForCompletion(true).execute(), + response -> { + assertThat(response.getNodeFailures(), empty()); + assertThat(response.getTaskFailures(), empty()); + assertThat(response.getTasks(), hasSize(1)); + TaskInfo task = response.getTasks().get(0); + assertEquals(TestTaskPlugin.TestTaskAction.NAME, task.getAction()); + } + ); } public void testGetTaskWaitForCompletionWithoutStoringResult() throws Exception { - waitForCompletionTestCase(false, id -> { - return client().admin().cluster().prepareGetTask(id).setWaitForCompletion(true).execute(); - }, response -> { - assertTrue(response.getTask().isCompleted()); - // We didn't store the result so it won't come back when we wait - assertNull(response.getTask().getResponse()); - // But the task's details should still be there because we grabbed a reference to the task before waiting for it to complete. - assertNotNull(response.getTask().getTask()); - assertEquals(TestTaskPlugin.TestTaskAction.NAME, response.getTask().getTask().getAction()); - }); + waitForCompletionTestCase(false, + id -> client().admin().cluster().prepareGetTask(id).setWaitForCompletion(true).execute(), + response -> { + assertTrue(response.getTask().isCompleted()); + //We didn't store the result so it won't come back when we wait + assertNull(response.getTask().getResponse()); + //But the task's details should still be there because we grabbed a reference to the task before waiting for it to complete + assertNotNull(response.getTask().getTask()); + assertEquals(TestTaskPlugin.TestTaskAction.NAME, response.getTask().getTask().getAction()); + } + ); } public void testGetTaskWaitForCompletionWithStoringResult() throws Exception { - waitForCompletionTestCase(true, id -> { - return client().admin().cluster().prepareGetTask(id).setWaitForCompletion(true).execute(); - }, response -> { - assertTrue(response.getTask().isCompleted()); - // We stored the task so we should get its results - assertEquals(0, response.getTask().getResponseAsMap().get("failure_count")); - // The task's details should also be there - assertNotNull(response.getTask().getTask()); - assertEquals(TestTaskPlugin.TestTaskAction.NAME, response.getTask().getTask().getAction()); - }); + waitForCompletionTestCase(true, + id -> client().admin().cluster().prepareGetTask(id).setWaitForCompletion(true).execute(), + response -> { + assertTrue(response.getTask().isCompleted()); + // We stored the task so we should get its results + assertEquals(0, response.getTask().getResponseAsMap().get("failure_count")); + // The task's details should also be there + assertNotNull(response.getTask().getTask()); + assertEquals(TestTaskPlugin.TestTaskAction.NAME, response.getTask().getTask().getAction()); + } + ); } /** @@ -546,13 +546,13 @@ public void testGetTaskWaitForCompletionWithStoringResult() throws Exception { * @param wait start waiting for a task. Accepts that id of the task to wait for and returns a future waiting for it. * @param validator validate the response and return the task ids that were found */ - private void waitForCompletionTestCase(boolean storeResult, Function> wait, Consumer validator) + private void waitForCompletionTestCase(boolean storeResult, Function> wait, Consumer validator) throws Exception { // Start blocking test task - ListenableActionFuture future = TestTaskPlugin.TestTaskAction.INSTANCE.newRequestBuilder(client()) + ActionFuture future = TestTaskPlugin.TestTaskAction.INSTANCE.newRequestBuilder(client()) .setShouldStoreResult(storeResult).execute(); - ListenableActionFuture waitResponseFuture; + ActionFuture waitResponseFuture; TaskId taskId; try { taskId = waitForTestTaskStartOnAllNodes(); @@ -623,8 +623,7 @@ public void testGetTaskWaitForTimeout() throws Exception { */ private void waitForTimeoutTestCase(Function> wait) throws Exception { // Start blocking test task - ListenableActionFuture future = TestTaskPlugin.TestTaskAction.INSTANCE.newRequestBuilder(client()) - .execute(); + ActionFuture future = TestTaskPlugin.TestTaskAction.INSTANCE.newRequestBuilder(client()).execute(); try { TaskId taskId = waitForTestTaskStartOnAllNodes(); @@ -662,7 +661,7 @@ private TaskId waitForTestTaskStartOnAllNodes() throws Exception { public void testTasksListWaitForNoTask() throws Exception { // Spin up a request to wait for no matching tasks - ListenableActionFuture waitResponseFuture = client().admin().cluster().prepareListTasks() + ActionFuture waitResponseFuture = client().admin().cluster().prepareListTasks() .setActions(TestTaskPlugin.TestTaskAction.NAME + "[n]").setWaitForCompletion(true).setTimeout(timeValueMillis(10)) .execute(); @@ -672,12 +671,12 @@ public void testTasksListWaitForNoTask() throws Exception { public void testTasksGetWaitForNoTask() throws Exception { // Spin up a request to wait for no matching tasks - ListenableActionFuture waitResponseFuture = client().admin().cluster().prepareGetTask("notfound:1") + ActionFuture waitResponseFuture = client().admin().cluster().prepareGetTask("notfound:1") .setWaitForCompletion(true).setTimeout(timeValueMillis(10)) .execute(); // It should finish quickly and without complaint - expectNotFound(() -> waitResponseFuture.get()); + expectNotFound(waitResponseFuture::get); } public void testTasksWaitForAllTask() throws Exception { diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java b/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java index 58bf897bd395b..6c1f44ea69a14 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java @@ -19,7 +19,7 @@ package org.elasticsearch.action.admin.indices.stats; -import org.elasticsearch.action.ListenableActionFuture; +import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.ShardOperationFailedException; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.support.WriteRequest.RefreshPolicy; @@ -124,7 +124,7 @@ public void testRefreshListeners() throws Exception { createIndex("test", Settings.builder().put("refresh_interval", -1).build()); // Index a document asynchronously so the request will only return when document is refreshed - ListenableActionFuture index = client().prepareIndex("test", "test", "test").setSource("test", "test") + ActionFuture index = client().prepareIndex("test", "test", "test").setSource("test", "test") .setRefreshPolicy(RefreshPolicy.WAIT_UNTIL).execute(); // Wait for the refresh listener to appear in the stats diff --git a/core/src/test/java/org/elasticsearch/action/support/ActiveShardsObserverIT.java b/core/src/test/java/org/elasticsearch/action/support/ActiveShardsObserverIT.java index 5f52293d7eef6..f3611663b426a 100644 --- a/core/src/test/java/org/elasticsearch/action/support/ActiveShardsObserverIT.java +++ b/core/src/test/java/org/elasticsearch/action/support/ActiveShardsObserverIT.java @@ -19,7 +19,7 @@ package org.elasticsearch.action.support; -import org.elasticsearch.action.ListenableActionFuture; +import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.common.Priority; import org.elasticsearch.common.settings.Settings; @@ -140,7 +140,7 @@ public void testCreateIndexStopsWaitingWhenIndexDeleted() throws Exception { .build(); logger.info("--> start the index creation process"); - ListenableActionFuture responseListener = + ActionFuture responseListener = prepareCreate(indexName) .setSettings(settings) .setWaitForActiveShards(ActiveShardCount.ALL) diff --git a/core/src/test/java/org/elasticsearch/client/AbstractClientHeadersTestCase.java b/core/src/test/java/org/elasticsearch/client/AbstractClientHeadersTestCase.java index 43d866a47b797..67318b7b21f80 100644 --- a/core/src/test/java/org/elasticsearch/client/AbstractClientHeadersTestCase.java +++ b/core/src/test/java/org/elasticsearch/client/AbstractClientHeadersTestCase.java @@ -25,6 +25,7 @@ import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction; import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotAction; import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction; +import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptAction; import org.elasticsearch.action.admin.indices.cache.clear.ClearIndicesCacheAction; import org.elasticsearch.action.admin.indices.create.CreateIndexAction; import org.elasticsearch.action.admin.indices.flush.FlushAction; @@ -32,7 +33,6 @@ import org.elasticsearch.action.delete.DeleteAction; import org.elasticsearch.action.get.GetAction; import org.elasticsearch.action.index.IndexAction; -import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptAction; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; @@ -101,22 +101,22 @@ public void testActions() { // validation in the settings??? - ugly and conceptually wrong) // choosing arbitrary top level actions to test - client.prepareGet("idx", "type", "id").execute().addListener(new AssertingActionListener<>(GetAction.NAME, client.threadPool())); - client.prepareSearch().execute().addListener(new AssertingActionListener<>(SearchAction.NAME, client.threadPool())); - client.prepareDelete("idx", "type", "id").execute().addListener(new AssertingActionListener<>(DeleteAction.NAME, client.threadPool())); - client.admin().cluster().prepareDeleteStoredScript("lang", "id").execute().addListener(new AssertingActionListener<>(DeleteStoredScriptAction.NAME, client.threadPool())); - client.prepareIndex("idx", "type", "id").setSource("source", XContentType.JSON).execute().addListener(new AssertingActionListener<>(IndexAction.NAME, client.threadPool())); + client.prepareGet("idx", "type", "id").execute(new AssertingActionListener<>(GetAction.NAME, client.threadPool())); + client.prepareSearch().execute(new AssertingActionListener<>(SearchAction.NAME, client.threadPool())); + client.prepareDelete("idx", "type", "id").execute(new AssertingActionListener<>(DeleteAction.NAME, client.threadPool())); + client.admin().cluster().prepareDeleteStoredScript("lang", "id").execute(new AssertingActionListener<>(DeleteStoredScriptAction.NAME, client.threadPool())); + client.prepareIndex("idx", "type", "id").setSource("source", XContentType.JSON).execute(new AssertingActionListener<>(IndexAction.NAME, client.threadPool())); // choosing arbitrary cluster admin actions to test - client.admin().cluster().prepareClusterStats().execute().addListener(new AssertingActionListener<>(ClusterStatsAction.NAME, client.threadPool())); - client.admin().cluster().prepareCreateSnapshot("repo", "bck").execute().addListener(new AssertingActionListener<>(CreateSnapshotAction.NAME, client.threadPool())); - client.admin().cluster().prepareReroute().execute().addListener(new AssertingActionListener<>(ClusterRerouteAction.NAME, client.threadPool())); + client.admin().cluster().prepareClusterStats().execute(new AssertingActionListener<>(ClusterStatsAction.NAME, client.threadPool())); + client.admin().cluster().prepareCreateSnapshot("repo", "bck").execute(new AssertingActionListener<>(CreateSnapshotAction.NAME, client.threadPool())); + client.admin().cluster().prepareReroute().execute(new AssertingActionListener<>(ClusterRerouteAction.NAME, client.threadPool())); // choosing arbitrary indices admin actions to test - client.admin().indices().prepareCreate("idx").execute().addListener(new AssertingActionListener<>(CreateIndexAction.NAME, client.threadPool())); - client.admin().indices().prepareStats().execute().addListener(new AssertingActionListener<>(IndicesStatsAction.NAME, client.threadPool())); - client.admin().indices().prepareClearCache("idx1", "idx2").execute().addListener(new AssertingActionListener<>(ClearIndicesCacheAction.NAME, client.threadPool())); - client.admin().indices().prepareFlush().execute().addListener(new AssertingActionListener<>(FlushAction.NAME, client.threadPool())); + client.admin().indices().prepareCreate("idx").execute(new AssertingActionListener<>(CreateIndexAction.NAME, client.threadPool())); + client.admin().indices().prepareStats().execute(new AssertingActionListener<>(IndicesStatsAction.NAME, client.threadPool())); + client.admin().indices().prepareClearCache("idx1", "idx2").execute(new AssertingActionListener<>(ClearIndicesCacheAction.NAME, client.threadPool())); + client.admin().indices().prepareFlush().execute(new AssertingActionListener<>(FlushAction.NAME, client.threadPool())); } public void testOverrideHeader() throws Exception { @@ -126,13 +126,13 @@ public void testOverrideHeader() throws Exception { expected.put("key2", "val 2"); client.threadPool().getThreadContext().putHeader("key1", key1Val); client.prepareGet("idx", "type", "id") - .execute().addListener(new AssertingActionListener<>(GetAction.NAME, expected, client.threadPool())); + .execute(new AssertingActionListener<>(GetAction.NAME, expected, client.threadPool())); client.admin().cluster().prepareClusterStats() - .execute().addListener(new AssertingActionListener<>(ClusterStatsAction.NAME, expected, client.threadPool())); + .execute(new AssertingActionListener<>(ClusterStatsAction.NAME, expected, client.threadPool())); client.admin().indices().prepareCreate("idx") - .execute().addListener(new AssertingActionListener<>(CreateIndexAction.NAME, expected, client.threadPool())); + .execute(new AssertingActionListener<>(CreateIndexAction.NAME, expected, client.threadPool())); } protected static void assertHeaders(Map headers, Map expected) { @@ -205,7 +205,5 @@ public Throwable unwrap(Throwable t, Class exceptionType) { } return result; } - } - } diff --git a/core/src/test/java/org/elasticsearch/index/WaitUntilRefreshIT.java b/core/src/test/java/org/elasticsearch/index/WaitUntilRefreshIT.java index ca089e6eb83ba..cad590b3c8db8 100644 --- a/core/src/test/java/org/elasticsearch/index/WaitUntilRefreshIT.java +++ b/core/src/test/java/org/elasticsearch/index/WaitUntilRefreshIT.java @@ -19,8 +19,8 @@ package org.elasticsearch.index; +import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.DocWriteResponse; -import org.elasticsearch.action.ListenableActionFuture; import org.elasticsearch.action.bulk.BulkItemResponse; import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.bulk.BulkResponse; @@ -149,7 +149,7 @@ public void testBulk() { */ public void testNoRefreshInterval() throws InterruptedException, ExecutionException { client().admin().indices().prepareUpdateSettings("test").setSettings(singletonMap("index.refresh_interval", -1)).get(); - ListenableActionFuture index = client().prepareIndex("test", "index", "1").setSource("foo", "bar") + ActionFuture index = client().prepareIndex("test", "index", "1").setSource("foo", "bar") .setRefreshPolicy(RefreshPolicy.WAIT_UNTIL).execute(); while (false == index.isDone()) { client().admin().indices().prepareRefresh("test").get(); diff --git a/core/src/test/java/org/elasticsearch/search/SearchCancellationIT.java b/core/src/test/java/org/elasticsearch/search/SearchCancellationIT.java index 7366834391bb3..02607f0c1fd06 100644 --- a/core/src/test/java/org/elasticsearch/search/SearchCancellationIT.java +++ b/core/src/test/java/org/elasticsearch/search/SearchCancellationIT.java @@ -19,7 +19,7 @@ package org.elasticsearch.search; -import org.elasticsearch.action.ListenableActionFuture; +import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksResponse; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.elasticsearch.action.bulk.BulkRequestBuilder; @@ -128,7 +128,7 @@ private void cancelSearch(String action) { assertThat(cancelTasksResponse.getTasks().get(0).getTaskId(), equalTo(searchTask.getTaskId())); } - private SearchResponse ensureSearchWasCancelled(ListenableActionFuture searchResponse) { + private SearchResponse ensureSearchWasCancelled(ActionFuture searchResponse) { try { SearchResponse response = searchResponse.actionGet(); logger.info("Search response {}", response); @@ -146,7 +146,7 @@ public void testCancellationDuringQueryPhase() throws Exception { indexTestData(); logger.info("Executing search"); - ListenableActionFuture searchResponse = client().prepareSearch("test").setQuery( + ActionFuture searchResponse = client().prepareSearch("test").setQuery( scriptQuery(new Script( ScriptType.INLINE, "native", NativeTestScriptedBlockFactory.TEST_NATIVE_BLOCK_SCRIPT, Collections.emptyMap()))) .execute(); @@ -164,7 +164,7 @@ public void testCancellationDuringFetchPhase() throws Exception { indexTestData(); logger.info("Executing search"); - ListenableActionFuture searchResponse = client().prepareSearch("test") + ActionFuture searchResponse = client().prepareSearch("test") .addScriptField("test_field", new Script(ScriptType.INLINE, "native", NativeTestScriptedBlockFactory.TEST_NATIVE_BLOCK_SCRIPT, Collections.emptyMap()) ).execute(); @@ -182,7 +182,7 @@ public void testCancellationOfScrollSearches() throws Exception { indexTestData(); logger.info("Executing search"); - ListenableActionFuture searchResponse = client().prepareSearch("test") + ActionFuture searchResponse = client().prepareSearch("test") .setScroll(TimeValue.timeValueSeconds(10)) .setSize(5) .setQuery( @@ -230,7 +230,7 @@ public void testCancellationOfScrollSearchesOnFollowupRequests() throws Exceptio String scrollId = searchResponse.getScrollId(); logger.info("Executing scroll with id {}", scrollId); - ListenableActionFuture scrollResponse = client().prepareSearchScroll(searchResponse.getScrollId()) + ActionFuture scrollResponse = client().prepareSearchScroll(searchResponse.getScrollId()) .setScroll(keepAlive).execute(); awaitForBlock(plugins); diff --git a/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java b/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java index 2503d6e157e2d..6c34cdf2e4503 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java +++ b/core/src/test/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java @@ -21,7 +21,7 @@ import com.carrotsearch.hppc.IntHashSet; import com.carrotsearch.hppc.IntSet; -import org.elasticsearch.action.ListenableActionFuture; +import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse; import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotResponse; @@ -412,7 +412,7 @@ public void testSnapshotWithStuckNode() throws Exception { logger.info("--> execution was blocked on node [{}], aborting snapshot", blockedNode); - ListenableActionFuture deleteSnapshotResponseFuture = internalCluster().client(nodes.get(0)).admin().cluster().prepareDeleteSnapshot("test-repo", "test-snap").execute(); + ActionFuture deleteSnapshotResponseFuture = internalCluster().client(nodes.get(0)).admin().cluster().prepareDeleteSnapshot("test-repo", "test-snap").execute(); // Make sure that abort makes some progress Thread.sleep(100); unblockNode("test-repo", blockedNode); diff --git a/core/src/test/java/org/elasticsearch/snapshots/MinThreadsSnapshotRestoreIT.java b/core/src/test/java/org/elasticsearch/snapshots/MinThreadsSnapshotRestoreIT.java index 680bfea6b3b3c..29657c5fb8b93 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/MinThreadsSnapshotRestoreIT.java +++ b/core/src/test/java/org/elasticsearch/snapshots/MinThreadsSnapshotRestoreIT.java @@ -19,7 +19,7 @@ package org.elasticsearch.snapshots; -import org.elasticsearch.action.ListenableActionFuture; +import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; @@ -83,7 +83,7 @@ public void testConcurrentSnapshotDeletionsNotAllowed() throws Exception { String blockedNode = internalCluster().getMasterName(); ((MockRepository)internalCluster().getInstance(RepositoriesService.class, blockedNode).repository(repo)).blockOnDataFiles(true); logger.info("--> start deletion of first snapshot"); - ListenableActionFuture future = + ActionFuture future = client().admin().cluster().prepareDeleteSnapshot(repo, snapshot2).execute(); logger.info("--> waiting for block to kick in on node [{}]", blockedNode); waitForBlock(blockedNode, repo, TimeValue.timeValueSeconds(10)); @@ -129,8 +129,7 @@ public void testSnapshottingWithInProgressDeletionNotAllowed() throws Exception String blockedNode = internalCluster().getMasterName(); ((MockRepository)internalCluster().getInstance(RepositoriesService.class, blockedNode).repository(repo)).blockOnDataFiles(true); logger.info("--> start deletion of snapshot"); - ListenableActionFuture future = - client().admin().cluster().prepareDeleteSnapshot(repo, snapshot1).execute(); + ActionFuture future = client().admin().cluster().prepareDeleteSnapshot(repo, snapshot1).execute(); logger.info("--> waiting for block to kick in on node [{}]", blockedNode); waitForBlock(blockedNode, repo, TimeValue.timeValueSeconds(10)); @@ -185,8 +184,7 @@ public void testRestoreWithInProgressDeletionsNotAllowed() throws Exception { String blockedNode = internalCluster().getMasterName(); ((MockRepository)internalCluster().getInstance(RepositoriesService.class, blockedNode).repository(repo)).blockOnDataFiles(true); logger.info("--> start deletion of snapshot"); - ListenableActionFuture future = - client().admin().cluster().prepareDeleteSnapshot(repo, snapshot2).execute(); + ActionFuture future = client().admin().cluster().prepareDeleteSnapshot(repo, snapshot2).execute(); logger.info("--> waiting for block to kick in on node [{}]", blockedNode); waitForBlock(blockedNode, repo, TimeValue.timeValueSeconds(10)); diff --git a/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java b/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java index 11194a689da59..67911f3e5b03d 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java +++ b/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java @@ -22,7 +22,7 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.Version; -import org.elasticsearch.action.ListenableActionFuture; +import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse; import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse; @@ -56,7 +56,6 @@ import org.elasticsearch.cluster.routing.IndexRoutingTable; import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.Priority; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; @@ -155,7 +154,7 @@ public void testBasicWorkFlow() throws Exception { assertHitCount(client.prepareSearch("test-idx-2").setSize(0).get(), 100L); assertHitCount(client.prepareSearch("test-idx-3").setSize(0).get(), 100L); - ListenableActionFuture flushResponseFuture = null; + ActionFuture flushResponseFuture = null; if (randomBoolean()) { ArrayList indicesToFlush = new ArrayList<>(); for (int i = 1; i < 4; i++) { @@ -888,7 +887,7 @@ public void testDeletionOfFailingToRecoverIndexShouldStopRestore() throws Except logger.info("--> delete index"); cluster().wipeIndices("test-idx"); logger.info("--> restore index after deletion"); - ListenableActionFuture restoreSnapshotResponseFuture = + ActionFuture restoreSnapshotResponseFuture = client.admin().cluster().prepareRestoreSnapshot("test-repo", "test-snap").setWaitForCompletion(true).execute(); logger.info("--> wait for the index to appear"); @@ -2014,7 +2013,7 @@ public void testCloseOrDeleteIndexDuringSnapshot() throws Exception { assertThat(client.prepareSearch("test-idx-3").setSize(0).get().getHits().getTotalHits(), equalTo(100L)); logger.info("--> snapshot allow partial {}", allowPartial); - ListenableActionFuture future = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap") + ActionFuture future = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap") .setIndices("test-idx-*").setWaitForCompletion(true).setPartial(allowPartial).execute(); logger.info("--> wait for block to kick in"); if (initBlocking) { @@ -2109,7 +2108,7 @@ public void testCloseIndexDuringRestore() throws Exception { blockAllDataNodes("test-repo"); logger.info("--> execution will be blocked on all data nodes"); - final ListenableActionFuture restoreFut; + final ActionFuture restoreFut; try { logger.info("--> start restore"); restoreFut = client.admin().cluster().prepareRestoreSnapshot("test-repo", "test-snap") @@ -2174,7 +2173,7 @@ public void testDeleteSnapshotWhileRestoringFails() throws Exception { logger.info("--> execution will be blocked on all data nodes"); blockAllDataNodes(repoName); - final ListenableActionFuture restoreFut; + final ActionFuture restoreFut; try { logger.info("--> start restore"); restoreFut = client.admin().cluster().prepareRestoreSnapshot(repoName, snapshotName) @@ -2461,7 +2460,7 @@ public void testGetSnapshotsRequest() throws Exception { // take initial snapshot with a block, making sure we only get 1 in-progress snapshot returned // block a node so the create snapshot operation can remain in progress final String initialBlockedNode = blockNodeWithIndex(repositoryName, indexName); - ListenableActionFuture responseListener = + ActionFuture responseListener = client.admin().cluster().prepareCreateSnapshot(repositoryName, "snap-on-empty-repo") .setWaitForCompletion(false) .setIndices(indexName) diff --git a/docs/reference/migration/migrate_6_0/java.asciidoc b/docs/reference/migration/migrate_6_0/java.asciidoc index a8954732657a3..b82a46501e2c7 100644 --- a/docs/reference/migration/migrate_6_0/java.asciidoc +++ b/docs/reference/migration/migrate_6_0/java.asciidoc @@ -8,14 +8,22 @@ an object source did not require the XContentType to be specified. The auto-dete type is no longer used, so these methods now require the XContentType as an additional argument when providing the source in bytes or as a string. -=== `DeleteByQueryRequest` requires an explicitly set query +==== `DeleteByQueryRequest` requires an explicitly set query In previous versions of Elasticsearch, delete by query requests without an explicit query were accepted, match_all was used as the default query and all documents were deleted as a result. From version 6.0.0, a `DeleteByQueryRequest` requires an explicit query be set. -=== `InternalStats` and `Stats` getCountAsString() method removed +==== `InternalStats` and `Stats` getCountAsString() method removed The `count` value in the stats aggregation represents a doc count that shouldnn't require a formatted version. This method was deprecated in 5.4 in favour of just using `String.valueOf(getCount())` if needed + +==== `ActionRequestBuilder#execute` returns `ActionFuture` rather than `ListenableActionFuture` + +When sending a request through the request builders e.g. client.prepareSearch().execute(), it used to +be possible to call `addListener` against the returned `ListenableActionFuture`. With this change an +`ActionFuture` is returned instead, which is consistent with what the `Client` methods return, hence +it is not possible to associate the future with listeners. The `execute` method that accept a listener +as an argument can be used instead. \ No newline at end of file diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java index 436ebec439b51..3aa35fb6e19de 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java @@ -19,7 +19,7 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.ListenableActionFuture; +import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; @@ -111,7 +111,7 @@ private void testCancel(String action, AbstractBulkByScrollRequestBuilder ALLOWED_OPERATIONS.release(numModifiedDocs - builder.request().getSlices()); // Now execute the reindex action... - ListenableActionFuture future = builder.execute(); + ActionFuture future = builder.execute(); /* ... and waits for the indexing operation listeners to block. It is important to realize that some of the workers might have * exhausted their slice while others might have quite a bit left to work on. We can't control that. */ diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java index 91c184e16a6fd..f40afce9a4e39 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java @@ -19,7 +19,7 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.ListenableActionFuture; +import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.elasticsearch.action.admin.cluster.node.tasks.list.TaskGroup; import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; @@ -31,6 +31,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.allOf; @@ -87,7 +88,7 @@ private void testCase(AbstractBulkByScrollRequestBuilder request, String a // Start a request that will never finish unless we rethrottle it request.setRequestsPerSecond(.000001f); // Throttle "forever" request.source().setSize(1); // Make sure we use multiple batches - ListenableActionFuture responseListener = request.execute(); + ActionFuture responseListener = request.execute(); TaskGroup taskGroupToRethrottle = findTaskToRethrottle(actionName, request.request().getSlices()); TaskId taskToRethrottle = taskGroupToRethrottle.getTaskInfo().getTaskId(); @@ -102,7 +103,7 @@ private void testCase(AbstractBulkByScrollRequestBuilder request, String a assertBusy(() -> { BulkByScrollTask.Status parent = (BulkByScrollTask.Status) client().admin().cluster().prepareGetTask(taskToRethrottle).get() .getTask().getTask().getStatus(); - long finishedSubTasks = parent.getSliceStatuses().stream().filter(s -> s != null).count(); + long finishedSubTasks = parent.getSliceStatuses().stream().filter(Objects::nonNull).count(); ListTasksResponse list = client().admin().cluster().prepareListTasks().setParentTaskId(taskToRethrottle).get(); list.rethrowFailures("subtasks"); assertThat(finishedSubTasks + list.getTasks().size(), greaterThanOrEqualTo((long) request.request().getSlices())); @@ -124,7 +125,7 @@ private void testCase(AbstractBulkByScrollRequestBuilder request, String a /* Check that at least one slice was rethrottled. We won't always rethrottle all of them because they might have completed. * With multiple slices these numbers might not add up perfectly, thus the 1.01F. */ long unfinished = status.getSliceStatuses().stream() - .filter(slice -> slice != null) + .filter(Objects::nonNull) .filter(slice -> slice.getStatus().getTotal() > slice.getStatus().getSuccessfullyProcessed()) .count(); float maxExpectedSliceRequestsPerSecond = newRequestsPerSecond == Float.POSITIVE_INFINITY ? diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java index be22d28daa916..86fbd47fd9665 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java @@ -19,7 +19,7 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.ListenableActionFuture; +import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.elasticsearch.action.bulk.BackoffPolicy; @@ -30,13 +30,11 @@ import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.action.bulk.byscroll.BulkIndexByScrollResponseMatcher; -import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; -import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.plugins.Plugin; @@ -151,7 +149,7 @@ private void testCase(String action, AbstractBulkByScrollRequestBuilder re request.source().setSize(DOC_COUNT / randomIntBetween(2, 10)); logger.info("Starting request"); - ListenableActionFuture responseListener = request.execute(); + ActionFuture responseListener = request.execute(); try { logger.info("Waiting for search rejections on the initial search"); From 194742b3f4ec41365da813df2cdbeddb6688bd66 Mon Sep 17 00:00:00 2001 From: javanna Date: Wed, 3 May 2017 11:40:25 +0200 Subject: [PATCH 171/619] [TEST] Remove unnecessary usages of ListenableActionFuture --- .../TransportActionFilterChainTests.java | 17 +++-------------- .../transport/TransportClientRetryIT.java | 4 ++-- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/action/support/TransportActionFilterChainTests.java b/core/src/test/java/org/elasticsearch/action/support/TransportActionFilterChainTests.java index aa61c2569d818..d5aab07f24734 100644 --- a/core/src/test/java/org/elasticsearch/action/support/TransportActionFilterChainTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/TransportActionFilterChainTests.java @@ -31,7 +31,6 @@ import org.junit.Before; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; @@ -77,12 +76,7 @@ protected void doExecute(TestRequest request, ActionListener liste }; ArrayList actionFiltersByOrder = new ArrayList<>(filters); - Collections.sort(actionFiltersByOrder, new Comparator() { - @Override - public int compare(ActionFilter o1, ActionFilter o2) { - return Integer.compare(o1.order(), o2.order()); - } - }); + actionFiltersByOrder.sort(Comparator.comparingInt(ActionFilter::order)); List expectedActionFilters = new ArrayList<>(); boolean errorExpected = false; @@ -97,7 +91,7 @@ public int compare(ActionFilter o1, ActionFilter o2) { } } - PlainListenableActionFuture future = new PlainListenableActionFuture<>(null); + PlainActionFuture future = new PlainActionFuture<>(); transportAction.execute(new TestRequest(), future); try { assertThat(future.get(), notNullValue()); @@ -110,12 +104,7 @@ public int compare(ActionFilter o1, ActionFilter o2) { for (ActionFilter actionFilter : actionFilters.filters()) { testFiltersByLastExecution.add((RequestTestFilter) actionFilter); } - Collections.sort(testFiltersByLastExecution, new Comparator() { - @Override - public int compare(RequestTestFilter o1, RequestTestFilter o2) { - return Integer.compare(o1.executionToken, o2.executionToken); - } - }); + testFiltersByLastExecution.sort(Comparator.comparingInt(o -> o.executionToken)); ArrayList finalTestFilters = new ArrayList<>(); for (ActionFilter filter : testFiltersByLastExecution) { diff --git a/core/src/test/java/org/elasticsearch/client/transport/TransportClientRetryIT.java b/core/src/test/java/org/elasticsearch/client/transport/TransportClientRetryIT.java index ab34b38f59801..899d1b0ce4a5a 100644 --- a/core/src/test/java/org/elasticsearch/client/transport/TransportClientRetryIT.java +++ b/core/src/test/java/org/elasticsearch/client/transport/TransportClientRetryIT.java @@ -21,7 +21,7 @@ import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; -import org.elasticsearch.action.support.PlainListenableActionFuture; +import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.client.Requests; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; @@ -69,7 +69,7 @@ public void testRetry() throws IOException, ExecutionException, InterruptedExcep if (randomBoolean()) { clusterState = client.admin().cluster().state(clusterStateRequest).get().getState(); } else { - PlainListenableActionFuture future = new PlainListenableActionFuture<>(client.threadPool()); + PlainActionFuture future = new PlainActionFuture<>(); client.admin().cluster().state(clusterStateRequest, future); clusterState = future.get().getState(); } From c99cc8a8962e1fbd2494e4b4dff52c70bc1144aa Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Wed, 3 May 2017 12:03:30 +0200 Subject: [PATCH 172/619] Preserve cluster alias throughout search execution to lookup nodes by cluster and ID (#24438) today we only lookup nodes by their ID but never by the (clusterAlias, nodeId) tuple. This could in theory lead to lookups on the wrong cluster if there are 2 clusters with a node that has the same ID. It's very unlikely to happen but we now can clearly disambiguate between clusters and their nodes. --- .../search/AbstractSearchAsyncAction.java | 19 +++--- .../action/search/DfsQueryPhase.java | 2 +- .../action/search/FetchSearchPhase.java | 5 +- .../action/search/InitialSearchPhase.java | 5 +- .../SearchDfsQueryThenFetchAsyncAction.java | 61 +++++-------------- .../action/search/SearchPhaseContext.java | 5 +- .../SearchQueryThenFetchAsyncAction.java | 56 +++++------------ .../action/search/SearchShardIterator.java | 8 ++- .../action/search/SearchTransportService.java | 18 ++++-- .../action/search/ShardSearchFailure.java | 2 +- .../action/search/TransportSearchAction.java | 54 ++++++++-------- .../common/io/stream/StreamInput.java | 1 - .../java/org/elasticsearch/node/Node.java | 2 +- .../org/elasticsearch/search/SearchHit.java | 2 +- .../elasticsearch/search/SearchService.java | 2 +- .../search/SearchShardTarget.java | 15 +++-- .../ElasticsearchExceptionTests.java | 8 +-- ...va => AbstractSearchAsyncActionTests.java} | 53 ++++++++-------- .../action/search/DfsQueryPhaseTests.java | 7 +-- .../action/search/ExpandSearchPhaseTests.java | 6 +- .../action/search/FetchSearchPhaseTests.java | 10 +-- .../action/search/MockSearchPhaseContext.java | 5 +- .../action/search/SearchAsyncActionTests.java | 13 ++-- .../search/ShardSearchFailureTests.java | 4 +- .../search/TransportSearchActionTests.java | 8 +-- .../elasticsearch/search/SearchHitTests.java | 2 +- 26 files changed, 166 insertions(+), 207 deletions(-) rename core/src/test/java/org/elasticsearch/action/search/{AbstractSearchAsyncActionTookTests.java => AbstractSearchAsyncActionTests.java} (60%) diff --git a/core/src/main/java/org/elasticsearch/action/search/AbstractSearchAsyncAction.java b/core/src/main/java/org/elasticsearch/action/search/AbstractSearchAsyncAction.java index 0abebebdb1833..d5ee044782bcc 100644 --- a/core/src/main/java/org/elasticsearch/action/search/AbstractSearchAsyncAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/AbstractSearchAsyncAction.java @@ -29,7 +29,6 @@ import org.elasticsearch.action.ShardOperationFailedException; import org.elasticsearch.action.support.TransportActions; import org.elasticsearch.cluster.routing.GroupShardsIterator; -import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.util.concurrent.AtomicArray; import org.elasticsearch.search.SearchPhaseResult; @@ -44,7 +43,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Function; +import java.util.function.BiFunction; import java.util.stream.Collectors; abstract class AbstractSearchAsyncAction extends InitialSearchPhase @@ -58,7 +57,7 @@ abstract class AbstractSearchAsyncAction exten /** * Used by subclasses to resolve node ids to DiscoveryNodes. **/ - private final Function nodeIdToConnection; + private final BiFunction nodeIdToConnection; private final SearchTask task; private final SearchPhaseResults results; private final long clusterStateVersion; @@ -71,7 +70,7 @@ abstract class AbstractSearchAsyncAction exten protected AbstractSearchAsyncAction(String name, Logger logger, SearchTransportService searchTransportService, - Function nodeIdToConnection, + BiFunction nodeIdToConnection, Map aliasFilter, Map concreteIndexBoosts, Executor executor, SearchRequest request, ActionListener listener, GroupShardsIterator shardsIts, @@ -210,7 +209,7 @@ private void raisePhaseFailure(SearchPhaseExecutionException exception) { results.getSuccessfulResults().forEach((entry) -> { try { SearchShardTarget searchShardTarget = entry.getSearchShardTarget(); - Transport.Connection connection = nodeIdToConnection.apply(searchShardTarget.getNodeId()); + Transport.Connection connection = getConnection(null, searchShardTarget.getNodeId()); sendReleaseSearchContext(entry.getRequestId(), connection, searchShardTarget.getOriginalIndices()); } catch (Exception inner) { inner.addSuppressed(exception); @@ -273,8 +272,8 @@ public final void onPhaseFailure(SearchPhase phase, String msg, Throwable cause) } @Override - public final Transport.Connection getConnection(String nodeId) { - return nodeIdToConnection.apply(nodeId); + public final Transport.Connection getConnection(String clusterAlias, String nodeId) { + return nodeIdToConnection.apply(clusterAlias, nodeId); } @Override @@ -297,10 +296,10 @@ public final void onFailure(Exception e) { listener.onFailure(e); } - public final ShardSearchTransportRequest buildShardSearchRequest(SearchShardIterator shardIt, ShardRouting shard) { - AliasFilter filter = aliasFilter.get(shard.index().getUUID()); + public final ShardSearchTransportRequest buildShardSearchRequest(SearchShardIterator shardIt) { + AliasFilter filter = aliasFilter.get(shardIt.shardId().getIndex().getUUID()); assert filter != null; - float indexBoost = concreteIndexBoosts.getOrDefault(shard.index().getUUID(), DEFAULT_INDEX_BOOST); + float indexBoost = concreteIndexBoosts.getOrDefault(shardIt.shardId().getIndex().getUUID(), DEFAULT_INDEX_BOOST); return new ShardSearchTransportRequest(shardIt.getOriginalIndices(), request, shardIt.shardId(), getNumShards(), filter, indexBoost, timeProvider.getAbsoluteStartMillis()); } diff --git a/core/src/main/java/org/elasticsearch/action/search/DfsQueryPhase.java b/core/src/main/java/org/elasticsearch/action/search/DfsQueryPhase.java index 66a88ce2fee65..a72dcac4f241a 100644 --- a/core/src/main/java/org/elasticsearch/action/search/DfsQueryPhase.java +++ b/core/src/main/java/org/elasticsearch/action/search/DfsQueryPhase.java @@ -72,7 +72,7 @@ public void run() throws IOException { () -> context.executeNextPhase(this, nextPhaseFactory.apply(queryResult)), context); for (final DfsSearchResult dfsResult : resultList) { final SearchShardTarget searchShardTarget = dfsResult.getSearchShardTarget(); - Transport.Connection connection = context.getConnection(searchShardTarget.getNodeId()); + Transport.Connection connection = context.getConnection(searchShardTarget.getClusterAlias(), searchShardTarget.getNodeId()); QuerySearchRequest querySearchRequest = new QuerySearchRequest(searchShardTarget.getOriginalIndices(), dfsResult.getRequestId(), dfs); final int shardIndex = dfsResult.getShardIndex(); diff --git a/core/src/main/java/org/elasticsearch/action/search/FetchSearchPhase.java b/core/src/main/java/org/elasticsearch/action/search/FetchSearchPhase.java index 25231efe49b98..b824a46c50f03 100644 --- a/core/src/main/java/org/elasticsearch/action/search/FetchSearchPhase.java +++ b/core/src/main/java/org/elasticsearch/action/search/FetchSearchPhase.java @@ -136,7 +136,8 @@ private void innerRun() throws IOException { counter.countDown(); } else { SearchShardTarget searchShardTarget = queryResult.getSearchShardTarget(); - Transport.Connection connection = context.getConnection(searchShardTarget.getNodeId()); + Transport.Connection connection = context.getConnection(searchShardTarget.getClusterAlias(), + searchShardTarget.getNodeId()); ShardFetchSearchRequest fetchSearchRequest = createFetchRequest(queryResult.queryResult().getRequestId(), i, entry, lastEmittedDocPerShard, searchShardTarget.getOriginalIndices()); executeFetch(i, searchShardTarget, counter, fetchSearchRequest, queryResult.queryResult(), @@ -191,7 +192,7 @@ private void releaseIrrelevantSearchContext(QuerySearchResult queryResult) { if (context.getRequest().scroll() == null && queryResult.hasSearchContext()) { try { SearchShardTarget searchShardTarget = queryResult.getSearchShardTarget(); - Transport.Connection connection = context.getConnection(searchShardTarget.getNodeId()); + Transport.Connection connection = context.getConnection(searchShardTarget.getClusterAlias(), searchShardTarget.getNodeId()); context.sendReleaseSearchContext(queryResult.getRequestId(), connection, searchShardTarget.getOriginalIndices()); } catch (Exception e) { context.getLogger().trace("failed to release context", e); diff --git a/core/src/main/java/org/elasticsearch/action/search/InitialSearchPhase.java b/core/src/main/java/org/elasticsearch/action/search/InitialSearchPhase.java index 2453e2b80b5a1..de58b1906427f 100644 --- a/core/src/main/java/org/elasticsearch/action/search/InitialSearchPhase.java +++ b/core/src/main/java/org/elasticsearch/action/search/InitialSearchPhase.java @@ -67,7 +67,8 @@ private void onShardFailure(final int shardIndex, @Nullable ShardRouting shard, final SearchShardIterator shardIt, Exception e) { // we always add the shard failure for a specific shard instance // we do make sure to clean it on a successful response from a shard - SearchShardTarget shardTarget = new SearchShardTarget(nodeId, shardIt.shardId(), shardIt.getOriginalIndices()); + SearchShardTarget shardTarget = new SearchShardTarget(nodeId, shardIt.shardId(), shardIt.getClusterAlias(), + shardIt.getOriginalIndices()); onShardFailure(shardIndex, shardTarget, e); if (totalOps.incrementAndGet() == expectedTotalOps) { @@ -144,7 +145,7 @@ private void performPhaseOnShard(final int shardIndex, final SearchShardIterator } else { try { executePhaseOnShard(shardIt, shard, new SearchActionListener(new SearchShardTarget(shard.currentNodeId(), - shardIt.shardId(), shardIt.getOriginalIndices()), shardIndex) { + shardIt.shardId(), shardIt.getClusterAlias(), shardIt.getOriginalIndices()), shardIndex) { @Override public void innerOnResponse(FirstResult result) { onShardResult(result, shardIt); diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchDfsQueryThenFetchAsyncAction.java b/core/src/main/java/org/elasticsearch/action/search/SearchDfsQueryThenFetchAsyncAction.java index be8cb0cff0193..a87b58c4e67b1 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchDfsQueryThenFetchAsyncAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchDfsQueryThenFetchAsyncAction.java @@ -29,62 +29,33 @@ import java.util.Map; import java.util.concurrent.Executor; -import java.util.function.Function; +import java.util.function.BiFunction; final class SearchDfsQueryThenFetchAsyncAction extends AbstractSearchAsyncAction { private final SearchPhaseController searchPhaseController; - SearchDfsQueryThenFetchAsyncAction( - final Logger logger, - final SearchTransportService searchTransportService, - final Function nodeIdToConnection, - final Map aliasFilter, - final Map concreteIndexBoosts, - final SearchPhaseController searchPhaseController, - final Executor executor, - final SearchRequest request, - final ActionListener listener, - final GroupShardsIterator shardsIts, - final TransportSearchAction.SearchTimeProvider timeProvider, - final long clusterStateVersion, - final SearchTask task) { - super( - "dfs", - logger, - searchTransportService, - nodeIdToConnection, - aliasFilter, - concreteIndexBoosts, - executor, - request, - listener, - shardsIts, - timeProvider, - clusterStateVersion, - task, - new SearchPhaseResults<>(shardsIts.size())); + SearchDfsQueryThenFetchAsyncAction(final Logger logger, final SearchTransportService searchTransportService, + final BiFunction nodeIdToConnection, final Map aliasFilter, + final Map concreteIndexBoosts, final SearchPhaseController searchPhaseController, final Executor executor, + final SearchRequest request, final ActionListener listener, + final GroupShardsIterator shardsIts, final TransportSearchAction.SearchTimeProvider timeProvider, + final long clusterStateVersion, final SearchTask task) { + super("dfs", logger, searchTransportService, nodeIdToConnection, aliasFilter, concreteIndexBoosts, executor, request, listener, + shardsIts, timeProvider, clusterStateVersion, task, new SearchPhaseResults<>(shardsIts.size())); this.searchPhaseController = searchPhaseController; } @Override - protected void executePhaseOnShard( - final SearchShardIterator shardIt, - final ShardRouting shard, - final SearchActionListener listener) { - getSearchTransport().sendExecuteDfs(getConnection(shard.currentNodeId()), - buildShardSearchRequest(shardIt, shard) , getTask(), listener); + protected void executePhaseOnShard(final SearchShardIterator shardIt, final ShardRouting shard, + final SearchActionListener listener) { + getSearchTransport().sendExecuteDfs(getConnection(shardIt.getClusterAlias(), shard.currentNodeId()), + buildShardSearchRequest(shardIt) , getTask(), listener); } @Override - protected SearchPhase getNextPhase( - final SearchPhaseResults results, final SearchPhaseContext context) { - return new DfsQueryPhase( - results.results, - searchPhaseController, - (queryResults) -> - new FetchSearchPhase(queryResults, searchPhaseController, context), - context); + protected SearchPhase getNextPhase(final SearchPhaseResults results, final SearchPhaseContext context) { + return new DfsQueryPhase(results.results, searchPhaseController, (queryResults) -> + new FetchSearchPhase(queryResults, searchPhaseController, context), context); } - } diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchPhaseContext.java b/core/src/main/java/org/elasticsearch/action/search/SearchPhaseContext.java index a109ab9639716..9829ff6a98337 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchPhaseContext.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchPhaseContext.java @@ -21,7 +21,6 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.OriginalIndices; -import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.common.Nullable; import org.elasticsearch.search.SearchShardTarget; import org.elasticsearch.search.internal.InternalSearchResponse; @@ -84,7 +83,7 @@ interface SearchPhaseContext extends ActionListener, Executor { * Returns a connection to the node if connected otherwise and {@link org.elasticsearch.transport.ConnectTransportException} will be * thrown. */ - Transport.Connection getConnection(String nodeId); + Transport.Connection getConnection(String clusterAlias, String nodeId); /** * Returns the {@link SearchTransportService} to send shard request to other nodes @@ -106,7 +105,7 @@ default void sendReleaseSearchContext(long contextId, Transport.Connection conne /** * Builds an request for the initial search phase. */ - ShardSearchTransportRequest buildShardSearchRequest(SearchShardIterator shardIt, ShardRouting shard); + ShardSearchTransportRequest buildShardSearchRequest(SearchShardIterator shardIt); /** * Processes the phase transition from on phase to another. This method handles all errors that happen during the initial run execution diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchQueryThenFetchAsyncAction.java b/core/src/main/java/org/elasticsearch/action/search/SearchQueryThenFetchAsyncAction.java index 855e02162849c..de8109aadd8fe 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchQueryThenFetchAsyncAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchQueryThenFetchAsyncAction.java @@ -29,59 +29,31 @@ import java.util.Map; import java.util.concurrent.Executor; -import java.util.function.Function; +import java.util.function.BiFunction; final class SearchQueryThenFetchAsyncAction extends AbstractSearchAsyncAction { private final SearchPhaseController searchPhaseController; - SearchQueryThenFetchAsyncAction( - final Logger logger, - final SearchTransportService searchTransportService, - final Function nodeIdToConnection, - final Map aliasFilter, - final Map concreteIndexBoosts, - final SearchPhaseController searchPhaseController, - final Executor executor, - final SearchRequest request, - final ActionListener listener, - final GroupShardsIterator shardsIts, - final TransportSearchAction.SearchTimeProvider timeProvider, - long clusterStateVersion, - SearchTask task) { - super( - "query", - logger, - searchTransportService, - nodeIdToConnection, - aliasFilter, - concreteIndexBoosts, - executor, - request, - listener, - shardsIts, - timeProvider, - clusterStateVersion, - task, - searchPhaseController.newSearchPhaseResults(request, shardsIts.size())); + SearchQueryThenFetchAsyncAction(final Logger logger, final SearchTransportService searchTransportService, + final BiFunction nodeIdToConnection, final Map aliasFilter, + final Map concreteIndexBoosts, final SearchPhaseController searchPhaseController, final Executor executor, + final SearchRequest request, final ActionListener listener, + final GroupShardsIterator shardsIts, final TransportSearchAction.SearchTimeProvider timeProvider, + long clusterStateVersion, SearchTask task) { + super("query", logger, searchTransportService, nodeIdToConnection, aliasFilter, concreteIndexBoosts, executor, request, listener, + shardsIts, timeProvider, clusterStateVersion, task, searchPhaseController.newSearchPhaseResults(request, shardsIts.size())); this.searchPhaseController = searchPhaseController; } - protected void executePhaseOnShard( - final SearchShardIterator shardIt, - final ShardRouting shard, - final SearchActionListener listener) { - getSearchTransport().sendExecuteQuery( - getConnection(shard.currentNodeId()), - buildShardSearchRequest(shardIt, shard), - getTask(), - listener); + protected void executePhaseOnShard(final SearchShardIterator shardIt, final ShardRouting shard, + final SearchActionListener listener) { + getSearchTransport().sendExecuteQuery(getConnection(shardIt.getClusterAlias(), shard.currentNodeId()), + buildShardSearchRequest(shardIt), getTask(), listener); } @Override - protected SearchPhase getNextPhase( - final SearchPhaseResults results, - final SearchPhaseContext context) { + protected SearchPhase getNextPhase(final SearchPhaseResults results, final SearchPhaseContext context) { return new FetchSearchPhase(results, searchPhaseController, context); } } diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchShardIterator.java b/core/src/main/java/org/elasticsearch/action/search/SearchShardIterator.java index ca78945a29929..d3d707771b8db 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchShardIterator.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchShardIterator.java @@ -33,6 +33,7 @@ public final class SearchShardIterator extends PlainShardIterator { private final OriginalIndices originalIndices; + private String clusterAlias; /** * Creates a {@link PlainShardIterator} instance that iterates over a subset of the given shards @@ -41,9 +42,10 @@ public final class SearchShardIterator extends PlainShardIterator { * @param shardId shard id of the group * @param shards shards to iterate */ - public SearchShardIterator(ShardId shardId, List shards, OriginalIndices originalIndices) { + public SearchShardIterator(String clusterAlias, ShardId shardId, List shards, OriginalIndices originalIndices) { super(shardId, shards); this.originalIndices = originalIndices; + this.clusterAlias = clusterAlias; } /** @@ -52,4 +54,8 @@ public SearchShardIterator(ShardId shardId, List shards, OriginalI public OriginalIndices getOriginalIndices() { return originalIndices; } + + public String getClusterAlias() { + return clusterAlias; + } } diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java index 9e858a4ccafdd..221641097d362 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java @@ -29,7 +29,6 @@ import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.search.SearchPhaseResult; import org.elasticsearch.search.SearchService; @@ -79,7 +78,7 @@ public class SearchTransportService extends AbstractComponent { private final TransportService transportService; - public SearchTransportService(Settings settings, ClusterSettings clusterSettings, TransportService transportService) { + public SearchTransportService(Settings settings, TransportService transportService) { super(settings); this.transportService = transportService; } @@ -390,7 +389,18 @@ public void messageReceived(ShardFetchSearchRequest request, TransportChannel ch TransportActionProxy.registerProxyAction(transportService, FETCH_ID_ACTION_NAME, FetchSearchResult::new); } - Transport.Connection getConnection(DiscoveryNode node) { - return transportService.getConnection(node); + /** + * Returns a connection to the given node on the provided cluster. If the cluster alias is null the node will be resolved + * against the local cluster. + * @param clusterAlias the cluster alias the node should be resolve against + * @param node the node to resolve + * @return a connection to the given node belonging to the cluster with the provided alias. + */ + Transport.Connection getConnection(String clusterAlias, DiscoveryNode node) { + if (clusterAlias == null) { + return transportService.getConnection(node); + } else { + return transportService.getRemoteClusterService().getConnection(node, clusterAlias); + } } } diff --git a/core/src/main/java/org/elasticsearch/action/search/ShardSearchFailure.java b/core/src/main/java/org/elasticsearch/action/search/ShardSearchFailure.java index 6d5b30fd9bd6a..8fed61af29412 100644 --- a/core/src/main/java/org/elasticsearch/action/search/ShardSearchFailure.java +++ b/core/src/main/java/org/elasticsearch/action/search/ShardSearchFailure.java @@ -214,7 +214,7 @@ public static ShardSearchFailure fromXContent(XContentParser parser) throws IOEx } return new ShardSearchFailure(exception, new SearchShardTarget(nodeId, - new ShardId(new Index(indexName, IndexMetaData.INDEX_UUID_NA_VALUE), shardId), OriginalIndices.NONE)); + new ShardId(new Index(indexName, IndexMetaData.INDEX_UUID_NA_VALUE), shardId), null, OriginalIndices.NONE)); } @Override diff --git a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index ae18caa50f0ba..91a23bf6a6ff2 100644 --- a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -19,7 +19,6 @@ package org.elasticsearch.action.search; -import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsGroup; @@ -59,7 +58,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; -import java.util.function.Function; +import java.util.function.BiFunction; import java.util.function.LongSupplier; import static org.elasticsearch.action.search.SearchType.QUERY_THEN_FETCH; @@ -204,31 +203,32 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< if (remoteClusterIndices.isEmpty()) { executeSearch((SearchTask)task, timeProvider, searchRequest, localIndices, Collections.emptyList(), - (nodeId) -> null, clusterState, Collections.emptyMap(), listener); + (clusterName, nodeId) -> null, clusterState, Collections.emptyMap(), listener); } else { remoteClusterService.collectSearchShards(searchRequest, remoteClusterIndices, ActionListener.wrap((searchShardsResponses) -> { List remoteShardIterators = new ArrayList<>(); Map remoteAliasFilters = new HashMap<>(); - Function connectionFunction = processRemoteShards(remoteClusterService, - searchShardsResponses, remoteClusterIndices, remoteShardIterators, remoteAliasFilters); + BiFunction clusterNodeLookup = processRemoteShards(searchShardsResponses, + remoteClusterIndices, remoteShardIterators, remoteAliasFilters); executeSearch((SearchTask)task, timeProvider, searchRequest, localIndices, remoteShardIterators, - connectionFunction, clusterState, remoteAliasFilters, listener); + clusterNodeLookup, clusterState, remoteAliasFilters, listener); }, listener::onFailure)); } } - static Function processRemoteShards(RemoteClusterService remoteClusterService, - Map searchShardsResponses, + static BiFunction processRemoteShards(Map searchShardsResponses, Map remoteIndicesByCluster, List remoteShardIterators, Map aliasFilterMap) { - Map> nodeToCluster = new HashMap<>(); + Map> clusterToNode = new HashMap<>(); for (Map.Entry entry : searchShardsResponses.entrySet()) { String clusterAlias = entry.getKey(); ClusterSearchShardsResponse searchShardsResponse = entry.getValue(); + HashMap idToDiscoveryNode = new HashMap<>(); + clusterToNode.put(clusterAlias, idToDiscoveryNode); for (DiscoveryNode remoteNode : searchShardsResponse.getNodes()) { - nodeToCluster.put(remoteNode.getId(), () -> remoteClusterService.getConnection(remoteNode, clusterAlias)); + idToDiscoveryNode.put(remoteNode.getId(), remoteNode); } Map indicesAndFilters = searchShardsResponse.getIndicesAndFilters(); for (ClusterSearchShardsGroup clusterSearchShardsGroup : searchShardsResponse.getGroups()) { @@ -240,7 +240,7 @@ static Function processRemoteShards(RemoteClusterS remoteIndex.getUUID()); OriginalIndices originalIndices = remoteIndicesByCluster.get(clusterAlias); assert originalIndices != null; - SearchShardIterator shardIterator = new SearchShardIterator(new ShardId(index, shardId.getId()), + SearchShardIterator shardIterator = new SearchShardIterator(clusterAlias, new ShardId(index, shardId.getId()), Arrays.asList(clusterSearchShardsGroup.getShards()), originalIndices); remoteShardIterators.add(shardIterator); AliasFilter aliasFilter; @@ -254,17 +254,17 @@ static Function processRemoteShards(RemoteClusterS aliasFilterMap.put(remoteIndex.getUUID(), aliasFilter); } } - return (nodeId) -> { - Supplier supplier = nodeToCluster.get(nodeId); - if (supplier == null) { - throw new IllegalArgumentException("unknown remote node: " + nodeId); - } - return supplier.get(); + return (clusterAlias, nodeId) -> { + Map clusterNodes = clusterToNode.get(clusterAlias); + if (clusterNodes == null) { + throw new IllegalArgumentException("unknown remote cluster: " + clusterAlias); + } + return clusterNodes.get(nodeId); }; } private void executeSearch(SearchTask task, SearchTimeProvider timeProvider, SearchRequest searchRequest, OriginalIndices localIndices, - List remoteShardIterators, Function remoteConnections, + List remoteShardIterators, BiFunction remoteConnections, ClusterState clusterState, Map remoteAliasMap, ActionListener listener) { @@ -312,18 +312,12 @@ private void executeSearch(SearchTask task, SearchTimeProvider timeProvider, Sea } final DiscoveryNodes nodes = clusterState.nodes(); - Function connectionLookup = (nodeId) -> { - final DiscoveryNode discoveryNode = nodes.get(nodeId); - final Transport.Connection connection; - if (discoveryNode != null) { - connection = searchTransportService.getConnection(discoveryNode); - } else { - connection = remoteConnections.apply(nodeId); - } - if (connection == null) { + BiFunction connectionLookup = (clusterName, nodeId) -> { + final DiscoveryNode discoveryNode = clusterName == null ? nodes.get(nodeId) : remoteConnections.apply(clusterName, nodeId); + if (discoveryNode == null) { throw new IllegalStateException("no node found for id: " + nodeId); } - return connection; + return searchTransportService.getConnection(clusterName, discoveryNode); }; searchAsyncAction(task, searchRequest, shardIterators, timeProvider, connectionLookup, clusterState.version(), @@ -338,7 +332,7 @@ static GroupShardsIterator mergeShardsIterators(GroupShards shards.add(shardIterator); } for (ShardIterator shardIterator : localShardsIterator) { - shards.add(new SearchShardIterator(shardIterator.shardId(), shardIterator.getShardRoutings(), localIndices)); + shards.add(new SearchShardIterator(null, shardIterator.shardId(), shardIterator.getShardRoutings(), localIndices)); } return new GroupShardsIterator<>(shards); } @@ -351,7 +345,7 @@ protected final void doExecute(SearchRequest searchRequest, ActionListener shardIterators, SearchTimeProvider timeProvider, - Function connectionLookup, + BiFunction connectionLookup, long clusterStateVersion, Map aliasFilter, Map concreteIndexBoosts, ActionListener listener) { diff --git a/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java b/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java index 7d175916c8e88..0efb99a1fabb4 100644 --- a/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java +++ b/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java @@ -933,5 +933,4 @@ private int readArraySize() throws IOException { * be a no-op depending on the underlying implementation if the information of the remaining bytes is not present. */ protected abstract void ensureCanReadBytes(int length) throws EOFException; - } diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index 54ce2bf7a205b..c2d72efec6872 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -412,7 +412,7 @@ protected Node(final Environment environment, Collection final TransportService transportService = newTransportService(settings, transport, threadPool, networkModule.getTransportInterceptor(), localNodeFactory, settingsModule.getClusterSettings()); final SearchTransportService searchTransportService = new SearchTransportService(settings, - settingsModule.getClusterSettings(), transportService); + transportService); final Consumer httpBind; final HttpServerTransport httpServerTransport; if (networkModule.isHttpEnabled()) { diff --git a/core/src/main/java/org/elasticsearch/search/SearchHit.java b/core/src/main/java/org/elasticsearch/search/SearchHit.java index d0d5047863fec..6172f974b149f 100644 --- a/core/src/main/java/org/elasticsearch/search/SearchHit.java +++ b/core/src/main/java/org/elasticsearch/search/SearchHit.java @@ -545,7 +545,7 @@ public static SearchHit createFromMap(Map values) { ShardId shardId = get(Fields._SHARD, values, null); String nodeId = get(Fields._NODE, values, null); if (shardId != null && nodeId != null) { - searchHit.shard(new SearchShardTarget(nodeId, shardId, OriginalIndices.NONE)); + searchHit.shard(new SearchShardTarget(nodeId, shardId, null, OriginalIndices.NONE)); } searchHit.fields(fields); return searchHit; diff --git a/core/src/main/java/org/elasticsearch/search/SearchService.java b/core/src/main/java/org/elasticsearch/search/SearchService.java index b1192c59e4cc7..f9d0b3dc338e6 100644 --- a/core/src/main/java/org/elasticsearch/search/SearchService.java +++ b/core/src/main/java/org/elasticsearch/search/SearchService.java @@ -500,7 +500,7 @@ public DefaultSearchContext createSearchContext(ShardSearchRequest request, Time IndexService indexService = indicesService.indexServiceSafe(request.shardId().getIndex()); IndexShard indexShard = indexService.getShard(request.shardId().getId()); SearchShardTarget shardTarget = new SearchShardTarget(clusterService.localNode().getId(), - indexShard.shardId(), OriginalIndices.NONE); + indexShard.shardId(), null, OriginalIndices.NONE); Engine.Searcher engineSearcher = searcher == null ? indexShard.acquireSearcher("search") : searcher; final DefaultSearchContext searchContext = new DefaultSearchContext(idGenerator.incrementAndGet(), request, shardTarget, diff --git a/core/src/main/java/org/elasticsearch/search/SearchShardTarget.java b/core/src/main/java/org/elasticsearch/search/SearchShardTarget.java index 88045cd361825..ec528340fa7d3 100644 --- a/core/src/main/java/org/elasticsearch/search/SearchShardTarget.java +++ b/core/src/main/java/org/elasticsearch/search/SearchShardTarget.java @@ -33,13 +33,14 @@ /** * The target that the search request was executed on. */ -public class SearchShardTarget implements Writeable, Comparable { +public final class SearchShardTarget implements Writeable, Comparable { private final Text nodeId; private final ShardId shardId; - //original indices are only needed in the coordinating node throughout the search request execution. + //original indices and cluster alias are only needed in the coordinating node throughout the search request execution. //no need to serialize them as part of SearchShardTarget. private final transient OriginalIndices originalIndices; + private final transient String clusterAlias; public SearchShardTarget(StreamInput in) throws IOException { if (in.readBoolean()) { @@ -49,17 +50,19 @@ public SearchShardTarget(StreamInput in) throws IOException { } shardId = ShardId.readShardId(in); this.originalIndices = null; + this.clusterAlias = null; } - public SearchShardTarget(String nodeId, ShardId shardId, OriginalIndices originalIndices) { + public SearchShardTarget(String nodeId, ShardId shardId, String clusterAlias, OriginalIndices originalIndices) { this.nodeId = nodeId == null ? null : new Text(nodeId); this.shardId = shardId; this.originalIndices = originalIndices; + this.clusterAlias = clusterAlias; } //this constructor is only used in tests public SearchShardTarget(String nodeId, Index index, int shardId) { - this(nodeId, new ShardId(index, shardId), OriginalIndices.NONE); + this(nodeId, new ShardId(index, shardId), null, OriginalIndices.NONE); } @Nullable @@ -83,6 +86,10 @@ public OriginalIndices getOriginalIndices() { return originalIndices; } + public String getClusterAlias() { + return clusterAlias; + } + @Override public int compareTo(SearchShardTarget o) { int i = shardId.getIndexName().compareTo(o.getIndex()); diff --git a/core/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java b/core/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java index 1c02a9d3090a5..6d5f906da1fef 100644 --- a/core/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java +++ b/core/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java @@ -759,10 +759,10 @@ public void testFailureToAndFromXContentWithDetails() throws IOException { failureCause = new NoShardAvailableActionException(new ShardId("_index_g", "_uuid_g", 6), "node_g", failureCause); ShardSearchFailure[] shardFailures = new ShardSearchFailure[]{ new ShardSearchFailure(new ParsingException(0, 0, "Parsing g", null), - new SearchShardTarget("node_g", new ShardId(new Index("_index_g", "_uuid_g"), 61), OriginalIndices.NONE)), - new ShardSearchFailure(new RepositoryException("repository_g", "Repo"), - new SearchShardTarget("node_g", new ShardId(new Index("_index_g", "_uuid_g"), 62), OriginalIndices.NONE)), - new ShardSearchFailure(new SearchContextMissingException(0L), null) + new SearchShardTarget("node_g", new ShardId(new Index("_index_g", "_uuid_g"), 61), null, + OriginalIndices.NONE)), new ShardSearchFailure(new RepositoryException("repository_g", "Repo"), + new SearchShardTarget("node_g", new ShardId(new Index("_index_g", "_uuid_g"), 62), null, + OriginalIndices.NONE)), new ShardSearchFailure(new SearchContextMissingException(0L), null) }; failure = new SearchPhaseExecutionException("phase_g", "G", failureCause, shardFailures); diff --git a/core/src/test/java/org/elasticsearch/action/search/AbstractSearchAsyncActionTookTests.java b/core/src/test/java/org/elasticsearch/action/search/AbstractSearchAsyncActionTests.java similarity index 60% rename from core/src/test/java/org/elasticsearch/action/search/AbstractSearchAsyncActionTookTests.java rename to core/src/test/java/org/elasticsearch/action/search/AbstractSearchAsyncActionTests.java index 508fcc69d64f9..839a061f852aa 100644 --- a/core/src/test/java/org/elasticsearch/action/search/AbstractSearchAsyncActionTookTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/AbstractSearchAsyncActionTests.java @@ -19,9 +19,16 @@ package org.elasticsearch.action.search; +import org.elasticsearch.action.OriginalIndices; +import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.cluster.routing.GroupShardsIterator; import org.elasticsearch.cluster.routing.ShardRouting; +import org.elasticsearch.index.Index; +import org.elasticsearch.index.query.MatchAllQueryBuilder; +import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.SearchPhaseResult; +import org.elasticsearch.search.internal.AliasFilter; +import org.elasticsearch.search.internal.ShardSearchTransportRequest; import org.elasticsearch.test.ESTestCase; import java.util.Collections; @@ -31,7 +38,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; -public class AbstractSearchAsyncActionTookTests extends ESTestCase { +public class AbstractSearchAsyncActionTests extends ESTestCase { private AbstractSearchAsyncAction createAction( final boolean controlled, @@ -53,35 +60,19 @@ private AbstractSearchAsyncAction createAction( System::nanoTime); } - return new AbstractSearchAsyncAction( - "test", - null, - null, - null, - null, - null, - null, - null, - null, - new GroupShardsIterator<>(Collections.singletonList(new SearchShardIterator(null, Collections.emptyList(), null))), - timeProvider, - 0, - null, - null - ) { + return new AbstractSearchAsyncAction("test", null, null, null, + Collections.singletonMap("foo", new AliasFilter(new MatchAllQueryBuilder())), Collections.singletonMap("foo", 2.0f), null, + new SearchRequest(), null, new GroupShardsIterator<>(Collections.singletonList( + new SearchShardIterator(null, null, Collections.emptyList(), null))), timeProvider, 0, null, + new InitialSearchPhase.SearchPhaseResults<>(10)) { @Override - protected SearchPhase getNextPhase( - final SearchPhaseResults results, - final SearchPhaseContext context) { + protected SearchPhase getNextPhase(final SearchPhaseResults results, final SearchPhaseContext context) { return null; } @Override - protected void executePhaseOnShard( - final SearchShardIterator shardIt, - final ShardRouting shard, - final SearchActionListener listener) { - + protected void executePhaseOnShard(final SearchShardIterator shardIt, final ShardRouting shard, + final SearchActionListener listener) { } @Override @@ -112,4 +103,16 @@ private void runTestTook(final boolean controlled) { assertThat(actual, greaterThanOrEqualTo(TimeUnit.NANOSECONDS.toMillis(expected.get()))); } } + + public void testBuildShardSearchTransportRequest() { + final AtomicLong expected = new AtomicLong(); + AbstractSearchAsyncAction action = createAction(false, expected); + SearchShardIterator iterator = new SearchShardIterator("test-cluster", new ShardId(new Index("name", "foo"), 1), + Collections.emptyList(), new OriginalIndices(new String[] {"name", "name1"}, IndicesOptions.strictExpand())); + ShardSearchTransportRequest shardSearchTransportRequest = action.buildShardSearchRequest(iterator); + assertEquals(IndicesOptions.strictExpand(), shardSearchTransportRequest.indicesOptions()); + assertArrayEquals(new String[] {"name", "name1"}, shardSearchTransportRequest.indices()); + assertEquals(new MatchAllQueryBuilder(), shardSearchTransportRequest.filteringAliases()); + assertEquals(2.0f, shardSearchTransportRequest.indexBoost(), 0.0f); + } } diff --git a/core/src/test/java/org/elasticsearch/action/search/DfsQueryPhaseTests.java b/core/src/test/java/org/elasticsearch/action/search/DfsQueryPhaseTests.java index c2f21a7cc2ca2..76ba4f6dcc80e 100644 --- a/core/src/test/java/org/elasticsearch/action/search/DfsQueryPhaseTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/DfsQueryPhaseTests.java @@ -23,7 +23,6 @@ import org.apache.lucene.search.TermStatistics; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.MockDirectoryWrapper; -import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.concurrent.AtomicArray; @@ -59,7 +58,7 @@ public void testDfsWith2Shards() throws IOException { SearchPhaseController controller = new SearchPhaseController(Settings.EMPTY, BigArrays.NON_RECYCLING_INSTANCE, null); SearchTransportService searchTransportService = new SearchTransportService( - Settings.builder().put("search.remote.connect", false).build(), null, null) { + Settings.builder().put("search.remote.connect", false).build(), null) { @Override public void sendExecuteQuery(Transport.Connection connection, QuerySearchRequest request, SearchTask task, @@ -114,7 +113,7 @@ public void testDfsWith1ShardFailed() throws IOException { SearchPhaseController controller = new SearchPhaseController(Settings.EMPTY, BigArrays.NON_RECYCLING_INSTANCE, null); SearchTransportService searchTransportService = new SearchTransportService( - Settings.builder().put("search.remote.connect", false).build(), null, null) { + Settings.builder().put("search.remote.connect", false).build(), null) { @Override public void sendExecuteQuery(Transport.Connection connection, QuerySearchRequest request, SearchTask task, @@ -169,7 +168,7 @@ public void testFailPhaseOnException() throws IOException { SearchPhaseController controller = new SearchPhaseController(Settings.EMPTY, BigArrays.NON_RECYCLING_INSTANCE, null); SearchTransportService searchTransportService = new SearchTransportService( - Settings.builder().put("search.remote.connect", false).build(), null, null) { + Settings.builder().put("search.remote.connect", false).build(), null) { @Override public void sendExecuteQuery(Transport.Connection connection, QuerySearchRequest request, SearchTask task, diff --git a/core/src/test/java/org/elasticsearch/action/search/ExpandSearchPhaseTests.java b/core/src/test/java/org/elasticsearch/action/search/ExpandSearchPhaseTests.java index 20e295561bbcd..b7f0e0785f982 100644 --- a/core/src/test/java/org/elasticsearch/action/search/ExpandSearchPhaseTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/ExpandSearchPhaseTests.java @@ -57,7 +57,7 @@ public void testCollapseSingleHit() throws IOException { .collapse(new CollapseBuilder("someField").setInnerHits(new InnerHitBuilder().setName("foobarbaz")))); mockSearchPhaseContext.getRequest().source().query(originalQuery); mockSearchPhaseContext.searchTransport = new SearchTransportService( - Settings.builder().put("search.remote.connect", false).build(), null, null) { + Settings.builder().put("search.remote.connect", false).build(), null) { @Override void sendExecuteMultiSearch(MultiSearchRequest request, SearchTask task, ActionListener listener) { @@ -126,7 +126,7 @@ public void testFailOneItemFailsEntirePhase() throws IOException { mockSearchPhaseContext.getRequest().source(new SearchSourceBuilder() .collapse(new CollapseBuilder("someField").setInnerHits(new InnerHitBuilder().setName("foobarbaz")))); mockSearchPhaseContext.searchTransport = new SearchTransportService( - Settings.builder().put("search.remote.connect", false).build(), null, null) { + Settings.builder().put("search.remote.connect", false).build(), null) { @Override void sendExecuteMultiSearch(MultiSearchRequest request, SearchTask task, ActionListener listener) { @@ -168,7 +168,7 @@ public void run() throws IOException { public void testSkipPhase() throws IOException { MockSearchPhaseContext mockSearchPhaseContext = new MockSearchPhaseContext(1); mockSearchPhaseContext.searchTransport = new SearchTransportService( - Settings.builder().put("search.remote.connect", false).build(), null, null) { + Settings.builder().put("search.remote.connect", false).build(), null) { @Override void sendExecuteMultiSearch(MultiSearchRequest request, SearchTask task, ActionListener listener) { diff --git a/core/src/test/java/org/elasticsearch/action/search/FetchSearchPhaseTests.java b/core/src/test/java/org/elasticsearch/action/search/FetchSearchPhaseTests.java index 239f8f10a413a..be42455a80a02 100644 --- a/core/src/test/java/org/elasticsearch/action/search/FetchSearchPhaseTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/FetchSearchPhaseTests.java @@ -103,7 +103,7 @@ public void testFetchTwoDocument() throws IOException { results.consumeResult(queryResult); SearchTransportService searchTransportService = new SearchTransportService( - Settings.builder().put("search.remote.connect", false).build(), null, null) { + Settings.builder().put("search.remote.connect", false).build(), null) { @Override public void sendExecuteFetch(Transport.Connection connection, ShardFetchSearchRequest request, SearchTask task, SearchActionListener listener) { @@ -157,7 +157,7 @@ public void testFailFetchOneDoc() throws IOException { results.consumeResult(queryResult); SearchTransportService searchTransportService = new SearchTransportService( - Settings.builder().put("search.remote.connect", false).build(), null, null) { + Settings.builder().put("search.remote.connect", false).build(), null) { @Override public void sendExecuteFetch(Transport.Connection connection, ShardFetchSearchRequest request, SearchTask task, SearchActionListener listener) { @@ -210,7 +210,7 @@ public void testFetchDocsConcurrently() throws IOException, InterruptedException results.consumeResult(queryResult); } SearchTransportService searchTransportService = new SearchTransportService( - Settings.builder().put("search.remote.connect", false).build(), null, null) { + Settings.builder().put("search.remote.connect", false).build(), null) { @Override public void sendExecuteFetch(Transport.Connection connection, ShardFetchSearchRequest request, SearchTask task, SearchActionListener listener) { @@ -271,7 +271,7 @@ public void testExceptionFailsPhase() throws IOException { results.consumeResult(queryResult); AtomicInteger numFetches = new AtomicInteger(0); SearchTransportService searchTransportService = new SearchTransportService( - Settings.builder().put("search.remote.connect", false).build(), null, null) { + Settings.builder().put("search.remote.connect", false).build(), null) { @Override public void sendExecuteFetch(Transport.Connection connection, ShardFetchSearchRequest request, SearchTask task, SearchActionListener listener) { @@ -324,7 +324,7 @@ public void testCleanupIrrelevantContexts() throws IOException { // contexts tha results.consumeResult(queryResult); SearchTransportService searchTransportService = new SearchTransportService( - Settings.builder().put("search.remote.connect", false).build(), null, null) { + Settings.builder().put("search.remote.connect", false).build(), null) { @Override public void sendExecuteFetch(Transport.Connection connection, ShardFetchSearchRequest request, SearchTask task, SearchActionListener listener) { diff --git a/core/src/test/java/org/elasticsearch/action/search/MockSearchPhaseContext.java b/core/src/test/java/org/elasticsearch/action/search/MockSearchPhaseContext.java index 98b6d2e7527bd..9135778479e2e 100644 --- a/core/src/test/java/org/elasticsearch/action/search/MockSearchPhaseContext.java +++ b/core/src/test/java/org/elasticsearch/action/search/MockSearchPhaseContext.java @@ -20,7 +20,6 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.action.OriginalIndices; -import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.search.SearchShardTarget; @@ -100,7 +99,7 @@ public void onShardFailure(int shardIndex, @Nullable SearchShardTarget shardTarg } @Override - public Transport.Connection getConnection(String nodeId) { + public Transport.Connection getConnection(String clusterAlias, String nodeId) { return null; // null is ok here for this test } @@ -111,7 +110,7 @@ public SearchTransportService getSearchTransport() { } @Override - public ShardSearchTransportRequest buildShardSearchRequest(SearchShardIterator shardIt, ShardRouting shard) { + public ShardSearchTransportRequest buildShardSearchRequest(SearchShardIterator shardIt) { Assert.fail("should not be called"); return null; } diff --git a/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java b/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java index b6d79b6ead962..39890038f2a2a 100644 --- a/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/SearchAsyncActionTests.java @@ -30,14 +30,12 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.Index; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.SearchPhaseResult; import org.elasticsearch.search.internal.AliasFilter; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.transport.RemoteClusterAware; import org.elasticsearch.transport.Transport; import org.elasticsearch.transport.TransportException; import org.elasticsearch.transport.TransportRequest; @@ -81,8 +79,7 @@ public void onFailure(Exception e) { new OriginalIndices(new String[]{"idx"}, IndicesOptions.strictExpandOpenAndForbidClosed()), randomIntBetween(1, 10), randomBoolean(), primaryNode, replicaNode); AtomicInteger numFreedContext = new AtomicInteger(); - SearchTransportService transportService = new SearchTransportService(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, - Collections.singleton(RemoteClusterAware.REMOTE_CLUSTERS_SEEDS)), null) { + SearchTransportService transportService = new SearchTransportService(Settings.EMPTY, null) { @Override public void sendFreeContext(Transport.Connection connection, long contextId, OriginalIndices originalIndices) { numFreedContext.incrementAndGet(); @@ -99,7 +96,9 @@ public void sendFreeContext(Transport.Connection connection, long contextId, Ori "test", logger, transportService, - lookup::get, + (cluster, node) -> { + assert cluster == null : "cluster was not null: " + cluster; + return lookup.get(node); }, aliasFilters, Collections.emptyMap(), null, @@ -116,7 +115,7 @@ public void sendFreeContext(Transport.Connection connection, long contextId, Ori protected void executePhaseOnShard(SearchShardIterator shardIt, ShardRouting shard, SearchActionListener listener) { assertTrue("shard: " + shard.shardId() + " has been queried twice", response.queried.add(shard.shardId())); - Transport.Connection connection = getConnection(shard.currentNodeId()); + Transport.Connection connection = getConnection(null, shard.currentNodeId()); TestSearchPhaseResult testSearchPhaseResult = new TestSearchPhaseResult(contextIdGenerator.incrementAndGet(), connection.getNode()); Set ids = nodeToContextMap.computeIfAbsent(connection.getNode(), (n) -> new HashSet<>()); @@ -187,7 +186,7 @@ private static GroupShardsIterator getShardsIter(String ind } Collections.shuffle(started, random()); started.addAll(initializing); - list.add(new SearchShardIterator(new ShardId(new Index(index, "_na_"), i), started, originalIndices)); + list.add(new SearchShardIterator(null, new ShardId(new Index(index, "_na_"), i), started, originalIndices)); } return new GroupShardsIterator<>(list); } diff --git a/core/src/test/java/org/elasticsearch/action/search/ShardSearchFailureTests.java b/core/src/test/java/org/elasticsearch/action/search/ShardSearchFailureTests.java index eac949c7753a2..81a6e7a70c64a 100644 --- a/core/src/test/java/org/elasticsearch/action/search/ShardSearchFailureTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/ShardSearchFailureTests.java @@ -43,7 +43,7 @@ public static ShardSearchFailure createTestItem() { String indexUuid = randomAlphaOfLengthBetween(5, 10); int shardId = randomInt(); return new ShardSearchFailure(ex, - new SearchShardTarget(nodeId, new ShardId(new Index(indexName, indexUuid), shardId), null)); + new SearchShardTarget(nodeId, new ShardId(new Index(indexName, indexUuid), shardId), null, null)); } public void testFromXContent() throws IOException { @@ -74,7 +74,7 @@ public void testFromXContent() throws IOException { public void testToXContent() throws IOException { ShardSearchFailure failure = new ShardSearchFailure(new ParsingException(0, 0, "some message", null), - new SearchShardTarget("nodeId", new ShardId(new Index("indexName", "indexUuid"), 123), OriginalIndices.NONE)); + new SearchShardTarget("nodeId", new ShardId(new Index("indexName", "indexUuid"), 123), null, OriginalIndices.NONE)); BytesReference xContent = toXContent(failure, XContentType.JSON, randomBoolean()); assertEquals( "{\"shard\":123," diff --git a/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java b/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java index fbd622f878d82..f34f4313fd6df 100644 --- a/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java @@ -90,14 +90,14 @@ public void testMergeShardsIterators() throws IOException { { ShardId remoteShardId = new ShardId("remote_index", "remote_index_uuid", 2); ShardRouting remoteShardRouting = TestShardRouting.newShardRouting(remoteShardId, "remote_node", true, STARTED); - SearchShardIterator remoteShardIterator = new SearchShardIterator(remoteShardId, + SearchShardIterator remoteShardIterator = new SearchShardIterator("remote", remoteShardId, Collections.singletonList(remoteShardRouting), remoteIndices); remoteShardIterators.add(remoteShardIterator); } { ShardId remoteShardId2 = new ShardId("remote_index_2", "remote_index_2_uuid", 3); ShardRouting remoteShardRouting2 = TestShardRouting.newShardRouting(remoteShardId2, "remote_node", true, STARTED); - SearchShardIterator remoteShardIterator2 = new SearchShardIterator(remoteShardId2, + SearchShardIterator remoteShardIterator2 = new SearchShardIterator("remote", remoteShardId2, Collections.singletonList(remoteShardRouting2), remoteIndices); remoteShardIterators.add(remoteShardIterator2); } @@ -106,7 +106,7 @@ public void testMergeShardsIterators() throws IOException { { ShardId remoteShardId3 = new ShardId("remote_index_3", "remote_index_3_uuid", 4); ShardRouting remoteShardRouting3 = TestShardRouting.newShardRouting(remoteShardId3, "remote_node", true, STARTED); - SearchShardIterator remoteShardIterator3 = new SearchShardIterator(remoteShardId3, + SearchShardIterator remoteShardIterator3 = new SearchShardIterator("remote", remoteShardId3, Collections.singletonList(remoteShardRouting3), remoteIndices2); remoteShardIterators.add(remoteShardIterator3); } @@ -188,7 +188,7 @@ public void testProcessRemoteShards() throws IOException { remoteIndicesByCluster.put("test_cluster_2", new OriginalIndices(new String[]{"x*"}, IndicesOptions.strictExpandOpenAndForbidClosed())); Map remoteAliases = new HashMap<>(); - TransportSearchAction.processRemoteShards(service, searchShardsResponseMap, remoteIndicesByCluster, iteratorList, + TransportSearchAction.processRemoteShards(searchShardsResponseMap, remoteIndicesByCluster, iteratorList, remoteAliases); assertEquals(4, iteratorList.size()); for (SearchShardIterator iterator : iteratorList) { diff --git a/core/src/test/java/org/elasticsearch/search/SearchHitTests.java b/core/src/test/java/org/elasticsearch/search/SearchHitTests.java index a2c11e8a64157..4f34d427c8745 100644 --- a/core/src/test/java/org/elasticsearch/search/SearchHitTests.java +++ b/core/src/test/java/org/elasticsearch/search/SearchHitTests.java @@ -129,7 +129,7 @@ public static SearchHit createTestItem(boolean withOptionalInnerHits) { } if (randomBoolean()) { hit.shard(new SearchShardTarget(randomAlphaOfLengthBetween(5, 10), - new ShardId(new Index(randomAlphaOfLengthBetween(5, 10), randomAlphaOfLengthBetween(5, 10)), randomInt()), + new ShardId(new Index(randomAlphaOfLengthBetween(5, 10), randomAlphaOfLengthBetween(5, 10)), randomInt()), null, OriginalIndices.NONE)); } return hit; From 070963658b392e9b367776e3d682a928cf9f9cac Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Wed, 3 May 2017 06:48:09 -0400 Subject: [PATCH 173/619] Block global checkpoint advances when recovering After a replica shard finishes recovery, it will be marked as active and its local checkpoint will be considered in the calculation of the global checkpoint on the primary. If there were operations in flight during recovery, when the replica is activated its local checkpoint could be lagging behind the global checkpoint on the primary. This means that when the replica shard is activated, we can end up in a situtaion where a global checkpoint update would want to move the global checkpoint backwards, violating an invariant of the system. This only arises if a background global checkpoint sync executes, which today is only a scheduled operation and might be delayed until the in-flight operations complete and the replica catches up to the primary. Yet, we are going to move to inlining global checkpoints which will cause this issue to be more likely to manifest. Additionally, the global checkpoint on the replica, which is the local knowledge on the replica updated under the mandate of the primary, could be higher than the local checkpoint on the replica, again violating an invariant of the system. This commit addresses these issues by blocking global checkpoint on the primary when a replica shard is finalizing recovery. While we have blocked global checkpoint advancement, recovery on the replica shard will not be considered complete until its local checkpoint advances to the blocked global checkpoint. Relates #24404 --- .../index/seqno/GlobalCheckpointTracker.java | 226 +++++--- .../index/seqno/SequenceNumbersService.java | 15 +- .../elasticsearch/index/shard/IndexShard.java | 30 +- .../index/translog/ChannelFactory.java | 2 +- .../recovery/PeerRecoveryTargetService.java | 20 +- .../recovery/RecoverySourceHandler.java | 84 +-- .../indices/recovery/RecoveryTarget.java | 11 +- .../recovery/RecoveryTargetHandler.java | 4 +- .../RecoveryTranslogOperationsResponse.java | 71 +++ .../recovery/RemoteRecoveryTargetHandler.java | 18 +- .../ESIndexLevelReplicationTestCase.java | 6 +- .../RecoveryDuringReplicationTests.java | 238 +++++++-- .../index/seqno/GlobalCheckpointTests.java | 247 --------- .../seqno/GlobalCheckpointTrackerTests.java | 490 ++++++++++++++++++ .../index/shard/IndexShardTests.java | 10 +- .../PeerRecoveryTargetServiceTests.java | 57 +- .../recovery/RecoverySourceHandlerTests.java | 12 +- 17 files changed, 1106 insertions(+), 435 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTranslogOperationsResponse.java delete mode 100644 core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTests.java create mode 100644 core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java diff --git a/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointTracker.java b/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointTracker.java index fd9b2f4687fd6..9c9f9a3ca4927 100644 --- a/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointTracker.java +++ b/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointTracker.java @@ -22,7 +22,7 @@ import com.carrotsearch.hppc.ObjectLongHashMap; import com.carrotsearch.hppc.ObjectLongMap; import com.carrotsearch.hppc.cursors.ObjectLongCursor; -import org.apache.logging.log4j.Logger; +import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.shard.AbstractIndexShardComponent; import org.elasticsearch.index.shard.ShardId; @@ -31,8 +31,6 @@ import java.util.Locale; import java.util.Set; -import static org.elasticsearch.index.seqno.SequenceNumbersService.UNASSIGNED_SEQ_NO; - /** * This class is responsible of tracking the global checkpoint. The global checkpoint is the highest sequence number for which all lower (or * equal) sequence number have been processed on all shards that are currently active. Since shards count as "active" when the master starts @@ -49,14 +47,20 @@ public class GlobalCheckpointTracker extends AbstractIndexShardComponent { * through recovery. These shards are treated as valid copies and participate in determining the global checkpoint. This map is keyed by * allocation IDs. All accesses to this set are guarded by a lock on this. */ - private final ObjectLongMap inSyncLocalCheckpoints; + final ObjectLongMap inSyncLocalCheckpoints; + + /* + * This map holds the last known local checkpoint for initializing shards that are undergoing recovery. Such shards do not participate + * in determining the global checkpoint. We must track these local checkpoints so that when a shard is activated we use the highest + * known checkpoint. + */ + final ObjectLongMap trackingLocalCheckpoints; /* - * This set holds the last set of known valid allocation ids as received by the master. This is important to make sure shard that are - * failed or relocated are cleaned up from {@link #inSyncLocalCheckpoints} and do not hold the global checkpoint back. All accesses to - * this set are guarded by a lock on this. + * This set contains allocation IDs for which there is a thread actively waiting for the local checkpoint to advance to at least the + * current global checkpoint. */ - private final Set assignedAllocationIds; + final Set pendingInSync; /* * The current global checkpoint for this shard. Note that this field is guarded by a lock on this and thus this field does not need to @@ -74,10 +78,11 @@ public class GlobalCheckpointTracker extends AbstractIndexShardComponent { */ GlobalCheckpointTracker(final ShardId shardId, final IndexSettings indexSettings, final long globalCheckpoint) { super(shardId, indexSettings); - assert globalCheckpoint >= UNASSIGNED_SEQ_NO : "illegal initial global checkpoint: " + globalCheckpoint; - inSyncLocalCheckpoints = new ObjectLongHashMap<>(1 + indexSettings.getNumberOfReplicas()); - assignedAllocationIds = new HashSet<>(1 + indexSettings.getNumberOfReplicas()); + assert globalCheckpoint >= SequenceNumbersService.UNASSIGNED_SEQ_NO : "illegal initial global checkpoint: " + globalCheckpoint; + this.inSyncLocalCheckpoints = new ObjectLongHashMap<>(1 + indexSettings.getNumberOfReplicas()); + this.trackingLocalCheckpoints = new ObjectLongHashMap<>(indexSettings.getNumberOfReplicas()); this.globalCheckpoint = globalCheckpoint; + this.pendingInSync = new HashSet<>(); } /** @@ -86,59 +91,85 @@ public class GlobalCheckpointTracker extends AbstractIndexShardComponent { * shards that are removed to be re-added. * * @param allocationId the allocation ID of the shard to update the local checkpoint for - * @param checkpoint the local checkpoint for the shard + * @param localCheckpoint the local checkpoint for the shard */ - public synchronized void updateLocalCheckpoint(final String allocationId, final long checkpoint) { - final int indexOfKey = inSyncLocalCheckpoints.indexOf(allocationId); - if (indexOfKey >= 0) { - final long current = inSyncLocalCheckpoints.indexGet(indexOfKey); - if (current < checkpoint) { - inSyncLocalCheckpoints.indexReplace(indexOfKey, checkpoint); - if (logger.isTraceEnabled()) { - logger.trace("updated local checkpoint of [{}] to [{}] (was [{}])", allocationId, checkpoint, current); - } + public synchronized void updateLocalCheckpoint(final String allocationId, final long localCheckpoint) { + final boolean updated; + if (updateLocalCheckpoint(allocationId, localCheckpoint, inSyncLocalCheckpoints, "in-sync")) { + updated = true; + } else if (updateLocalCheckpoint(allocationId, localCheckpoint, trackingLocalCheckpoints, "tracking")) { + updated = true; + } else { + logger.trace("ignored local checkpoint [{}] of [{}], allocation ID is not tracked", localCheckpoint, allocationId); + updated = false; + } + if (updated) { + notifyAllWaiters(); + } + } + + @SuppressForbidden(reason = "Object#notifyAll waiters for local checkpoint advancement") + private synchronized void notifyAllWaiters() { + this.notifyAll(); + } + + private boolean updateLocalCheckpoint( + final String allocationId, final long localCheckpoint, ObjectLongMap map, final String reason) { + final int index = map.indexOf(allocationId); + if (index >= 0) { + final long current = map.indexGet(index); + if (current < localCheckpoint) { + map.indexReplace(index, localCheckpoint); + logger.trace("updated local checkpoint of [{}] in [{}] from [{}] to [{}]", allocationId, reason, current, localCheckpoint); } else { logger.trace( - "skipping update of local checkpoint [{}], current checkpoint is higher (current [{}], incoming [{}], type [{}])", - allocationId, - current, - checkpoint, - allocationId); + "skipped updating local checkpoint of [{}] in [{}] from [{}] to [{}], current checkpoint is higher", + allocationId, + reason, + current, + localCheckpoint); } + return true; } else { - logger.trace("[{}] isn't marked as in sync. ignoring local checkpoint of [{}].", allocationId, checkpoint); + return false; } } /** * Scans through the currently known local checkpoint and updates the global checkpoint accordingly. * - * @return {@code true} if the checkpoint has been updated or if it can not be updated since one of the local checkpoints of one of the - * active allocations is not known. + * @return {@code true} if the checkpoint has been updated or if it can not be updated since the local checkpoints of one of the active + * allocations is not known. */ synchronized boolean updateCheckpointOnPrimary() { - long minCheckpoint = Long.MAX_VALUE; - if (inSyncLocalCheckpoints.isEmpty()) { + long minLocalCheckpoint = Long.MAX_VALUE; + if (inSyncLocalCheckpoints.isEmpty() || !pendingInSync.isEmpty()) { return false; } - for (final ObjectLongCursor cp : inSyncLocalCheckpoints) { - if (cp.value == UNASSIGNED_SEQ_NO) { - logger.trace("unknown local checkpoint for active allocationId [{}], requesting a sync", cp.key); + for (final ObjectLongCursor localCheckpoint : inSyncLocalCheckpoints) { + if (localCheckpoint.value == SequenceNumbersService.UNASSIGNED_SEQ_NO) { + logger.trace("unknown local checkpoint for active allocation ID [{}], requesting a sync", localCheckpoint.key); return true; } - minCheckpoint = Math.min(cp.value, minCheckpoint); + minLocalCheckpoint = Math.min(localCheckpoint.value, minLocalCheckpoint); } - if (minCheckpoint < globalCheckpoint) { + assert minLocalCheckpoint != SequenceNumbersService.UNASSIGNED_SEQ_NO : "new global checkpoint must be assigned"; + if (minLocalCheckpoint < globalCheckpoint) { final String message = - String.format(Locale.ROOT, "new global checkpoint [%d] is lower than previous one [%d]", minCheckpoint, globalCheckpoint); + String.format( + Locale.ROOT, + "new global checkpoint [%d] is lower than previous one [%d]", + minLocalCheckpoint, + globalCheckpoint); throw new IllegalStateException(message); } - if (globalCheckpoint != minCheckpoint) { - logger.trace("global checkpoint updated to [{}]", minCheckpoint); - globalCheckpoint = minCheckpoint; + if (globalCheckpoint != minLocalCheckpoint) { + logger.trace("global checkpoint updated to [{}]", minLocalCheckpoint); + globalCheckpoint = minLocalCheckpoint; return true; + } else { + return false; } - return false; } /** @@ -153,17 +184,17 @@ public synchronized long getCheckpoint() { /** * Updates the global checkpoint on a replica shard after it has been updated by the primary. * - * @param checkpoint the global checkpoint + * @param globalCheckpoint the global checkpoint */ - synchronized void updateCheckpointOnReplica(final long checkpoint) { + synchronized void updateGlobalCheckpointOnReplica(final long globalCheckpoint) { /* * The global checkpoint here is a local knowledge which is updated under the mandate of the primary. It can happen that the primary * information is lagging compared to a replica (e.g., if a replica is promoted to primary but has stale info relative to other * replica shards). In these cases, the local knowledge of the global checkpoint could be higher than sync from the lagging primary. */ - if (this.globalCheckpoint <= checkpoint) { - this.globalCheckpoint = checkpoint; - logger.trace("global checkpoint updated from primary to [{}]", checkpoint); + if (this.globalCheckpoint <= globalCheckpoint) { + this.globalCheckpoint = globalCheckpoint; + logger.trace("global checkpoint updated from primary to [{}]", globalCheckpoint); } } @@ -173,33 +204,98 @@ synchronized void updateCheckpointOnReplica(final long checkpoint) { * @param activeAllocationIds the allocation IDs of the currently active shard copies * @param initializingAllocationIds the allocation IDs of the currently initializing shard copies */ - public synchronized void updateAllocationIdsFromMaster(final Set activeAllocationIds, - final Set initializingAllocationIds) { - assignedAllocationIds.removeIf( - aId -> activeAllocationIds.contains(aId) == false && initializingAllocationIds.contains(aId) == false); - assignedAllocationIds.addAll(activeAllocationIds); - assignedAllocationIds.addAll(initializingAllocationIds); - for (String activeId : activeAllocationIds) { - if (inSyncLocalCheckpoints.containsKey(activeId) == false) { - inSyncLocalCheckpoints.put(activeId, UNASSIGNED_SEQ_NO); + public synchronized void updateAllocationIdsFromMaster( + final Set activeAllocationIds, final Set initializingAllocationIds) { + // remove shards whose allocation ID no longer exists + inSyncLocalCheckpoints.removeAll(a -> !activeAllocationIds.contains(a) && !initializingAllocationIds.contains(a)); + + // add any new active allocation IDs + for (final String a : activeAllocationIds) { + if (!inSyncLocalCheckpoints.containsKey(a)) { + final long localCheckpoint = trackingLocalCheckpoints.getOrDefault(a, SequenceNumbersService.UNASSIGNED_SEQ_NO); + inSyncLocalCheckpoints.put(a, localCheckpoint); + logger.trace("marked [{}] as in-sync with local checkpoint [{}] via cluster state update from master", a, localCheckpoint); + } + } + + trackingLocalCheckpoints.removeAll(a -> !initializingAllocationIds.contains(a)); + for (final String a : initializingAllocationIds) { + if (inSyncLocalCheckpoints.containsKey(a)) { + /* + * This can happen if we mark the allocation ID as in sync at the end of recovery before seeing a cluster state update from + * marking the shard as active. + */ + continue; } + if (trackingLocalCheckpoints.containsKey(a)) { + // we are already tracking this allocation ID + continue; + } + // this is a new allocation ID + trackingLocalCheckpoints.put(a, SequenceNumbersService.UNASSIGNED_SEQ_NO); + logger.trace("tracking [{}] via cluster state update from master", a); } - inSyncLocalCheckpoints.removeAll(key -> assignedAllocationIds.contains(key) == false); } /** - * Marks the shard with the provided allocation ID as in-sync with the primary shard. This should be called at the end of recovery where - * the primary knows all operations below the global checkpoint have been completed on this shard. + * Marks the shard with the provided allocation ID as in-sync with the primary shard. This method will block until the local checkpoint + * on the specified shard advances above the current global checkpoint. + * + * @param allocationId the allocation ID of the shard to mark as in-sync + * @param localCheckpoint the current local checkpoint on the shard * - * @param allocationId the allocation ID of the shard to mark as in-sync + * @throws InterruptedException if the thread is interrupted waiting for the local checkpoint on the shard to advance */ - public synchronized void markAllocationIdAsInSync(final String allocationId) { - if (assignedAllocationIds.contains(allocationId) == false) { - // master has removed this allocation, ignore + public synchronized void markAllocationIdAsInSync(final String allocationId, final long localCheckpoint) throws InterruptedException { + if (!trackingLocalCheckpoints.containsKey(allocationId)) { + /* + * This can happen if the recovery target has been failed and the cluster state update from the master has triggered removing + * this allocation ID from the tracking map but this recovery thread has not yet been made aware that the recovery is + * cancelled. + */ return; } - logger.trace("marked [{}] as in sync", allocationId); - inSyncLocalCheckpoints.put(allocationId, UNASSIGNED_SEQ_NO); + + updateLocalCheckpoint(allocationId, localCheckpoint, trackingLocalCheckpoints, "tracking"); + waitForAllocationIdToBeInSync(allocationId); + } + + private synchronized void waitForAllocationIdToBeInSync(final String allocationId) throws InterruptedException { + if (!pendingInSync.add(allocationId)) { + throw new IllegalStateException("there is already a pending sync in progress for allocation ID [" + allocationId + "]"); + } + try { + while (true) { + /* + * If the allocation has been cancelled and so removed from the tracking map from a cluster state update from the master it + * means that this recovery will be cancelled; we are here on a cancellable recovery thread and so this thread will throw + * an interrupted exception as soon as it tries to wait on the monitor. + */ + final long current = trackingLocalCheckpoints.getOrDefault(allocationId, Long.MIN_VALUE); + if (current >= globalCheckpoint) { + logger.trace("marked [{}] as in-sync with local checkpoint [{}]", allocationId, current); + trackingLocalCheckpoints.remove(allocationId); + /* + * This is prematurely adding the allocation ID to the in-sync map as at this point recovery is not yet finished and + * could still abort. At this point we will end up with a shard in the in-sync map holding back the global checkpoint + * because the shard never recovered and we would have to wait until either the recovery retries and completes + * successfully, or the master fails the shard and issues a cluster state update that removes the shard from the set of + * active allocation IDs. + */ + inSyncLocalCheckpoints.put(allocationId, current); + break; + } else { + waitForLocalCheckpointToAdvance(); + } + } + } finally { + pendingInSync.remove(allocationId); + } + } + + @SuppressForbidden(reason = "Object#wait for local checkpoint advancement") + private synchronized void waitForLocalCheckpointToAdvance() throws InterruptedException { + this.wait(); } /** @@ -213,7 +309,7 @@ synchronized long getLocalCheckpointForAllocationId(final String allocationId) { if (inSyncLocalCheckpoints.containsKey(allocationId)) { return inSyncLocalCheckpoints.get(allocationId); } - return UNASSIGNED_SEQ_NO; + return SequenceNumbersService.UNASSIGNED_SEQ_NO; } } diff --git a/core/src/main/java/org/elasticsearch/index/seqno/SequenceNumbersService.java b/core/src/main/java/org/elasticsearch/index/seqno/SequenceNumbersService.java index ddc0669f892bf..4b14ce8fff526 100644 --- a/core/src/main/java/org/elasticsearch/index/seqno/SequenceNumbersService.java +++ b/core/src/main/java/org/elasticsearch/index/seqno/SequenceNumbersService.java @@ -127,12 +127,13 @@ public void updateLocalCheckpointForShard(final String allocationId, final long /** * Marks the shard with the provided allocation ID as in-sync with the primary shard. See - * {@link GlobalCheckpointTracker#markAllocationIdAsInSync(String)} for additional details. + * {@link GlobalCheckpointTracker#markAllocationIdAsInSync(String, long)} for additional details. * - * @param allocationId the allocation ID of the shard to mark as in-sync + * @param allocationId the allocation ID of the shard to mark as in-sync + * @param localCheckpoint the current local checkpoint on the shard */ - public void markAllocationIdAsInSync(final String allocationId) { - globalCheckpointTracker.markAllocationIdAsInSync(allocationId); + public void markAllocationIdAsInSync(final String allocationId, final long localCheckpoint) throws InterruptedException { + globalCheckpointTracker.markAllocationIdAsInSync(allocationId, localCheckpoint); } /** @@ -166,10 +167,10 @@ public boolean updateGlobalCheckpointOnPrimary() { /** * Updates the global checkpoint on a replica shard after it has been updated by the primary. * - * @param checkpoint the global checkpoint + * @param globalCheckpoint the global checkpoint */ - public void updateGlobalCheckpointOnReplica(final long checkpoint) { - globalCheckpointTracker.updateCheckpointOnReplica(checkpoint); + public void updateGlobalCheckpointOnReplica(final long globalCheckpoint) { + globalCheckpointTracker.updateGlobalCheckpointOnReplica(globalCheckpoint); } /** diff --git a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java index 1da5e6763bc66..f1cef1fb66350 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -1475,13 +1475,14 @@ public void waitForOpsToComplete(final long seqNo) throws InterruptedException { /** * Marks the shard with the provided allocation ID as in-sync with the primary shard. See - * {@link GlobalCheckpointTracker#markAllocationIdAsInSync(String)} for additional details. + * {@link GlobalCheckpointTracker#markAllocationIdAsInSync(String, long)} for additional details. * - * @param allocationId the allocation ID of the shard to mark as in-sync + * @param allocationId the allocation ID of the shard to mark as in-sync + * @param localCheckpoint the current local checkpoint on the shard */ - public void markAllocationIdAsInSync(final String allocationId) { + public void markAllocationIdAsInSync(final String allocationId, final long localCheckpoint) throws InterruptedException { verifyPrimary(); - getEngine().seqNoService().markAllocationIdAsInSync(allocationId); + getEngine().seqNoService().markAllocationIdAsInSync(allocationId, localCheckpoint); } /** @@ -1516,11 +1517,26 @@ public void updateGlobalCheckpointOnPrimary() { /** * Updates the global checkpoint on a replica shard after it has been updated by the primary. * - * @param checkpoint the global checkpoint + * @param globalCheckpoint the global checkpoint */ - public void updateGlobalCheckpointOnReplica(final long checkpoint) { + public void updateGlobalCheckpointOnReplica(final long globalCheckpoint) { verifyReplicationTarget(); - getEngine().seqNoService().updateGlobalCheckpointOnReplica(checkpoint); + final SequenceNumbersService seqNoService = getEngine().seqNoService(); + final long localCheckpoint = seqNoService.getLocalCheckpoint(); + if (globalCheckpoint <= localCheckpoint) { + seqNoService.updateGlobalCheckpointOnReplica(globalCheckpoint); + } else { + /* + * This can happen during recovery when the shard has started its engine but recovery is not finalized and is receiving global + * checkpoint updates from in-flight operations. However, since this shard is not yet contributing to calculating the global + * checkpoint, it can be the case that the global checkpoint update from the primary is ahead of the local checkpoint on this + * shard. In this case, we ignore the global checkpoint update. This should only happen if we are in the translog stage of + * recovery. Prior to this, the engine is not opened and this shard will not receive global checkpoint updates, and after this + * the shard will be contributing to calculations of the the global checkpoint. + */ + assert recoveryState().getStage() == RecoveryState.Stage.TRANSLOG + : "expected recovery stage [" + RecoveryState.Stage.TRANSLOG + "] but was [" + recoveryState().getStage() + "]"; + } } /** diff --git a/core/src/main/java/org/elasticsearch/index/translog/ChannelFactory.java b/core/src/main/java/org/elasticsearch/index/translog/ChannelFactory.java index 7e1dcec14df07..ccb362a350772 100644 --- a/core/src/main/java/org/elasticsearch/index/translog/ChannelFactory.java +++ b/core/src/main/java/org/elasticsearch/index/translog/ChannelFactory.java @@ -28,7 +28,7 @@ * only for testing until we have a disk-full FileSystem */ @FunctionalInterface -interface ChannelFactory { +public interface ChannelFactory { default FileChannel open(Path path) throws IOException { return open(path, StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE_NEW); } diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java b/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java index 400395d1b20f5..8435fe4ee1e80 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java @@ -348,20 +348,24 @@ private StartRecoveryRequest getStartRecoveryRequest(final RecoveryTarget recove */ public static long getStartingSeqNo(final RecoveryTarget recoveryTarget) { try { - final long globalCheckpoint = Translog.readGlobalCheckpoint(recoveryTarget.indexShard().shardPath().resolveTranslog()); + final long globalCheckpoint = Translog.readGlobalCheckpoint(recoveryTarget.translogLocation()); final SeqNoStats seqNoStats = recoveryTarget.store().loadSeqNoStats(globalCheckpoint); if (seqNoStats.getMaxSeqNo() <= seqNoStats.getGlobalCheckpoint()) { - // commit point is good for seq no based recovery as the maximum seq# including in it - // is below the global checkpoint (i.e., it excludes any ops thay may not be on the primary) - // Recovery will start at the first op after the local check point stored in the commit. + /* + * Commit point is good for sequence-number based recovery as the maximum sequence number included in it is below the global + * checkpoint (i.e., it excludes any operations that may not be on the primary). Recovery will start at the first operation + * after the local checkpoint stored in the commit. + */ return seqNoStats.getLocalCheckpoint() + 1; } else { return SequenceNumbersService.UNASSIGNED_SEQ_NO; } } catch (final IOException e) { - // this can happen, for example, if a phase one of the recovery completed successfully, a network partition happens before the - // translog on the recovery target is opened, the recovery enters a retry loop seeing now that the index files are on disk and - // proceeds to attempt a sequence-number-based recovery + /* + * This can happen, for example, if a phase one of the recovery completed successfully, a network partition happens before the + * translog on the recovery target is opened, the recovery enters a retry loop seeing now that the index files are on disk and + * proceeds to attempt a sequence-number-based recovery. + */ return SequenceNumbersService.UNASSIGNED_SEQ_NO; } } @@ -418,7 +422,7 @@ public void messageReceived(final RecoveryTranslogOperationsRequest request, fin final RecoveryTarget recoveryTarget = recoveryRef.target(); try { recoveryTarget.indexTranslogOperations(request.operations(), request.totalTranslogOps()); - channel.sendResponse(TransportResponse.Empty.INSTANCE); + channel.sendResponse(new RecoveryTranslogOperationsResponse(recoveryTarget.indexShard().getLocalCheckpoint())); } catch (TranslogRecoveryPerformer.BatchOperationException exception) { MapperException mapperException = (MapperException) ExceptionsHelper.unwrap(exception, MapperException.class); if (mapperException == null) { diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java b/core/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java index c53a46ef222fe..4c8c31779ac0d 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java @@ -58,6 +58,7 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.StreamSupport; @@ -179,14 +180,16 @@ public RecoveryResponse recoverToTarget() throws IOException { } logger.trace("snapshot translog for recovery; current size is [{}]", translogView.totalOperations()); + final long targetLocalCheckpoint; try { - phase2(isSequenceNumberBasedRecoveryPossible ? request.startingSeqNo() : SequenceNumbersService.UNASSIGNED_SEQ_NO, - translogView.snapshot()); + final long startingSeqNo = + isSequenceNumberBasedRecoveryPossible ? request.startingSeqNo() : SequenceNumbersService.UNASSIGNED_SEQ_NO; + targetLocalCheckpoint = phase2(startingSeqNo, translogView.snapshot()); } catch (Exception e) { throw new RecoveryEngineException(shard.shardId(), 2, "phase2 failed", e); } - finalizeRecovery(); + finalizeRecovery(targetLocalCheckpoint); } return response; } @@ -410,8 +413,10 @@ void prepareTargetForTranslog(final int totalTranslogOps) throws IOException { * @param startingSeqNo the sequence number to start recovery from, or {@link SequenceNumbersService#UNASSIGNED_SEQ_NO} if all * ops should be sent * @param snapshot a snapshot of the translog + * + * @return the local checkpoint on the target */ - void phase2(final long startingSeqNo, final Translog.Snapshot snapshot) throws IOException { + long phase2(final long startingSeqNo, final Translog.Snapshot snapshot) throws IOException { if (shard.state() == IndexShardState.CLOSED) { throw new IndexShardClosedException(request.shardId()); } @@ -422,18 +427,19 @@ void phase2(final long startingSeqNo, final Translog.Snapshot snapshot) throws I logger.trace("recovery [phase2]: sending transaction log operations"); // send all the snapshot's translog operations to the target - final int totalOperations = sendSnapshot(startingSeqNo, snapshot); + final SendSnapshotResult result = sendSnapshot(startingSeqNo, snapshot); stopWatch.stop(); logger.trace("recovery [phase2]: took [{}]", stopWatch.totalTime()); response.phase2Time = stopWatch.totalTime().millis(); - response.phase2Operations = totalOperations; + response.phase2Operations = result.totalOperations; + return result.targetLocalCheckpoint; } /* * finalizes the recovery process */ - public void finalizeRecovery() { + public void finalizeRecovery(final long targetLocalCheckpoint) { if (shard.state() == IndexShardState.CLOSED) { throw new IndexShardClosedException(request.shardId()); } @@ -441,7 +447,7 @@ public void finalizeRecovery() { StopWatch stopWatch = new StopWatch().start(); logger.trace("finalizing recovery"); cancellableThreads.execute(() -> { - shard.markAllocationIdAsInSync(request.targetAllocationId()); + shard.markAllocationIdAsInSync(request.targetAllocationId(), targetLocalCheckpoint); recoveryTarget.finalizeRecovery(shard.getGlobalCheckpoint()); }); @@ -467,6 +473,18 @@ public void finalizeRecovery() { logger.trace("finalizing recovery took [{}]", stopWatch.totalTime()); } + static class SendSnapshotResult { + + final long targetLocalCheckpoint; + final int totalOperations; + + SendSnapshotResult(final long targetLocalCheckpoint, final int totalOperations) { + this.targetLocalCheckpoint = targetLocalCheckpoint; + this.totalOperations = totalOperations; + } + + } + /** * Send the given snapshot's operations with a sequence number greater than the specified staring sequence number to this handler's * target node. @@ -475,19 +493,25 @@ public void finalizeRecovery() { * * @param startingSeqNo the sequence number for which only operations with a sequence number greater than this will be sent * @param snapshot the translog snapshot to replay operations from - * @return the total number of translog operations that were sent + * @return the local checkpoint on the target and the total number of operations sent * @throws IOException if an I/O exception occurred reading the translog snapshot */ - protected int sendSnapshot(final long startingSeqNo, final Translog.Snapshot snapshot) throws IOException { + protected SendSnapshotResult sendSnapshot(final long startingSeqNo, final Translog.Snapshot snapshot) throws IOException { int ops = 0; long size = 0; - int totalOperations = 0; + int skippedOps = 0; + int totalSentOps = 0; + final AtomicLong targetLocalCheckpoint = new AtomicLong(SequenceNumbersService.UNASSIGNED_SEQ_NO); final List operations = new ArrayList<>(); - if (snapshot.totalOperations() == 0) { + final int expectedTotalOps = snapshot.totalOperations(); + if (expectedTotalOps == 0) { logger.trace("no translog operations to send"); } + final CancellableThreads.Interruptable sendBatch = + () -> targetLocalCheckpoint.set(recoveryTarget.indexTranslogOperations(operations, expectedTotalOps)); + // send operations in batches Translog.Operation operation; while ((operation = snapshot.next()) != null) { @@ -495,39 +519,41 @@ protected int sendSnapshot(final long startingSeqNo, final Translog.Snapshot sna throw new IndexShardClosedException(request.shardId()); } cancellableThreads.checkForCancel(); - // if we are doing a sequence-number-based recovery, we have to skip older ops for which no sequence number was assigned, and - // any ops before the starting sequence number + /* + * If we are doing a sequence-number-based recovery, we have to skip older ops for which no sequence number was assigned, and + * any ops before the starting sequence number. + */ final long seqNo = operation.seqNo(); - if (startingSeqNo >= 0 && (seqNo == SequenceNumbersService.UNASSIGNED_SEQ_NO || seqNo < startingSeqNo)) continue; + if (startingSeqNo >= 0 && (seqNo == SequenceNumbersService.UNASSIGNED_SEQ_NO || seqNo < startingSeqNo)) { + skippedOps++; + continue; + } operations.add(operation); ops++; size += operation.estimateSize(); - totalOperations++; + totalSentOps++; // check if this request is past bytes threshold, and if so, send it off if (size >= chunkSizeInBytes) { - cancellableThreads.execute(() -> recoveryTarget.indexTranslogOperations(operations, snapshot.totalOperations())); - if (logger.isTraceEnabled()) { - logger.trace("sent batch of [{}][{}] (total: [{}]) translog operations", ops, new ByteSizeValue(size), - snapshot.totalOperations()); - } + cancellableThreads.execute(sendBatch); + logger.trace("sent batch of [{}][{}] (total: [{}]) translog operations", ops, new ByteSizeValue(size), expectedTotalOps); ops = 0; size = 0; operations.clear(); } } - // send the leftover operations - if (!operations.isEmpty()) { - cancellableThreads.execute(() -> recoveryTarget.indexTranslogOperations(operations, snapshot.totalOperations())); + if (!operations.isEmpty() || totalSentOps == 0) { + // send the leftover operations or if no operations were sent, request the target to respond with its local checkpoint + cancellableThreads.execute(sendBatch); } - if (logger.isTraceEnabled()) { - logger.trace("sent final batch of [{}][{}] (total: [{}]) translog operations", ops, new ByteSizeValue(size), - snapshot.totalOperations()); - } + assert expectedTotalOps == skippedOps + totalSentOps + : "expected total [" + expectedTotalOps + "], skipped [" + skippedOps + "], total sent [" + totalSentOps + "]"; + + logger.trace("sent final batch of [{}][{}] (total: [{}]) translog operations", ops, new ByteSizeValue(size), expectedTotalOps); - return totalOperations; + return new SendSnapshotResult(targetLocalCheckpoint.get(), totalSentOps); } /** diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java index 7b48edfa0424f..d9540019e803a 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java @@ -48,6 +48,7 @@ import org.elasticsearch.index.translog.Translog; import java.io.IOException; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; @@ -374,12 +375,13 @@ public void ensureClusterStateVersion(long clusterStateVersion) { } @Override - public void indexTranslogOperations(List operations, int totalTranslogOps) throws TranslogRecoveryPerformer - .BatchOperationException { + public long indexTranslogOperations( + List operations, int totalTranslogOps) throws TranslogRecoveryPerformer.BatchOperationException { final RecoveryState.Translog translog = state().getTranslog(); translog.totalOperations(totalTranslogOps); assert indexShard().recoveryState() == state(); indexShard().performBatchRecovery(operations); + return indexShard().getLocalCheckpoint(); } @Override @@ -470,4 +472,9 @@ public void writeFileChunk(StoreFileMetaData fileMetaData, long position, BytesR assert remove == null || remove == indexOutput; // remove maybe null if we got finished } } + + Path translogLocation() { + return indexShard().shardPath().resolveTranslog(); + } + } diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTargetHandler.java b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTargetHandler.java index dec6387744820..38f412fed734a 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTargetHandler.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTargetHandler.java @@ -53,8 +53,10 @@ public interface RecoveryTargetHandler { * Index a set of translog operations on the target * @param operations operations to index * @param totalTranslogOps current number of total operations expected to be indexed + * + * @return the local checkpoint on the target shard */ - void indexTranslogOperations(List operations, int totalTranslogOps); + long indexTranslogOperations(List operations, int totalTranslogOps); /** * Notifies the target of the files it is going to receive diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTranslogOperationsResponse.java b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTranslogOperationsResponse.java new file mode 100644 index 0000000000000..7427e631fb070 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTranslogOperationsResponse.java @@ -0,0 +1,71 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.indices.recovery; + +import org.elasticsearch.Version; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.index.seqno.SequenceNumbersService; +import org.elasticsearch.transport.FutureTransportResponseHandler; +import org.elasticsearch.transport.TransportResponse; +import org.elasticsearch.transport.TransportResponseHandler; + +import java.io.IOException; + +public class RecoveryTranslogOperationsResponse extends TransportResponse { + + long localCheckpoint; + + RecoveryTranslogOperationsResponse() { + + } + + RecoveryTranslogOperationsResponse(final long localCheckpoint) { + this.localCheckpoint = localCheckpoint; + } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + // before 6.0.0 we responded with an empty response so we have to maintain that + if (out.getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { + out.writeZLong(localCheckpoint); + } + } + + @Override + public void readFrom(final StreamInput in) throws IOException { + // before 6.0.0 we received an empty response so we have to maintain that + if (in.getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { + localCheckpoint = in.readZLong(); + } + else { + localCheckpoint = SequenceNumbersService.UNASSIGNED_SEQ_NO; + } + } + + static TransportResponseHandler HANDLER = + new FutureTransportResponseHandler() { + @Override + public RecoveryTranslogOperationsResponse newInstance() { + return new RecoveryTranslogOperationsResponse(); + } + }; + +} diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RemoteRecoveryTargetHandler.java b/core/src/main/java/org/elasticsearch/indices/recovery/RemoteRecoveryTargetHandler.java index f21d61f2f718d..a4f24b710b2fa 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RemoteRecoveryTargetHandler.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RemoteRecoveryTargetHandler.java @@ -28,7 +28,10 @@ import org.elasticsearch.index.store.StoreFileMetaData; import org.elasticsearch.index.translog.Translog; import org.elasticsearch.transport.EmptyTransportResponseHandler; +import org.elasticsearch.transport.FutureTransportResponseHandler; +import org.elasticsearch.transport.TransportFuture; import org.elasticsearch.transport.TransportRequestOptions; +import org.elasticsearch.transport.TransportResponse; import org.elasticsearch.transport.TransportService; import java.io.IOException; @@ -98,11 +101,16 @@ public void ensureClusterStateVersion(long clusterStateVersion) { } @Override - public void indexTranslogOperations(List operations, int totalTranslogOps) { - final RecoveryTranslogOperationsRequest translogOperationsRequest = new RecoveryTranslogOperationsRequest( - recoveryId, shardId, operations, totalTranslogOps); - transportService.submitRequest(targetNode, PeerRecoveryTargetService.Actions.TRANSLOG_OPS, translogOperationsRequest, - translogOpsRequestOptions, EmptyTransportResponseHandler.INSTANCE_SAME).txGet(); + public long indexTranslogOperations(List operations, int totalTranslogOps) { + final RecoveryTranslogOperationsRequest translogOperationsRequest = + new RecoveryTranslogOperationsRequest(recoveryId, shardId, operations, totalTranslogOps); + final TransportFuture future = transportService.submitRequest( + targetNode, + PeerRecoveryTargetService.Actions.TRANSLOG_OPS, + translogOperationsRequest, + translogOpsRequestOptions, + RecoveryTranslogOperationsResponse.HANDLER); + return future.txGet().localCheckpoint; } @Override diff --git a/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java b/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java index 2243a5769b99a..68373ce529aff 100644 --- a/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java +++ b/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java @@ -296,9 +296,9 @@ public synchronized DiscoveryNode getPrimaryNode() { return getDiscoveryNode(primary.routingEntry().currentNodeId()); } - public Future asyncRecoverReplica(IndexShard replica, BiFunction targetSupplier) - throws IOException { - FutureTask task = new FutureTask<>(() -> { + public Future asyncRecoverReplica( + final IndexShard replica, final BiFunction targetSupplier) throws IOException { + final FutureTask task = new FutureTask<>(() -> { recoverReplica(replica, targetSupplier); return null; }); diff --git a/core/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java b/core/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java index 349258785f03a..7ee40c5c90eac 100644 --- a/core/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java +++ b/core/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java @@ -43,16 +43,17 @@ import org.elasticsearch.test.junit.annotations.TestLogging; import java.io.IOException; +import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; -import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.not; public class RecoveryDuringReplicationTests extends ESIndexLevelReplicationTestCase { @@ -205,60 +206,40 @@ public void testRecoveryAfterPrimaryPromotion() throws Exception { } } - @TestLogging("_root:DEBUG,org.elasticsearch.action.bulk:TRACE,org.elasticsearch.action.get:TRACE," + - "org.elasticsearch.discovery:TRACE," + - "org.elasticsearch.cluster.service:TRACE,org.elasticsearch.indices.recovery:TRACE," + - "org.elasticsearch.indices.cluster:TRACE,org.elasticsearch.index.shard:TRACE," + - "org.elasticsearch.index.seqno:TRACE" - ) + @TestLogging( + "_root:DEBUG," + + "org.elasticsearch.action.bulk:TRACE," + + "org.elasticsearch.action.get:TRACE," + + "org.elasticsearch.cluster.service:TRACE," + + "org.elasticsearch.discovery:TRACE," + + "org.elasticsearch.indices.cluster:TRACE," + + "org.elasticsearch.indices.recovery:TRACE," + + "org.elasticsearch.index.seqno:TRACE," + + "org.elasticsearch.index.shard:TRACE") public void testWaitForPendingSeqNo() throws Exception { IndexMetaData metaData = buildIndexMetaData(1); final int pendingDocs = randomIntBetween(1, 5); - final AtomicReference blockIndexingOnPrimary = new AtomicReference<>(); - final CountDownLatch blockedIndexers = new CountDownLatch(pendingDocs); + final BlockingEngineFactory primaryEngineFactory = new BlockingEngineFactory(); try (ReplicationGroup shards = new ReplicationGroup(metaData) { @Override protected EngineFactory getEngineFactory(ShardRouting routing) { if (routing.primary()) { - return new EngineFactory() { - @Override - public Engine newReadWriteEngine(EngineConfig config) { - return InternalEngineTests.createInternalEngine((directory, writerConfig) -> - new IndexWriter(directory, writerConfig) { - @Override - public long addDocument(Iterable doc) throws IOException { - Semaphore block = blockIndexingOnPrimary.get(); - if (block != null) { - blockedIndexers.countDown(); - try { - block.acquire(); - } catch (InterruptedException e) { - throw new AssertionError("unexpectedly interrupted", e); - } - } - return super.addDocument(doc); - } - - }, null, config); - } - }; + return primaryEngineFactory; } else { return null; } } }) { shards.startAll(); - int docs = shards.indexDocs(randomIntBetween(1,10)); + int docs = shards.indexDocs(randomIntBetween(1, 10)); IndexShard replica = shards.getReplicas().get(0); shards.removeReplica(replica); closeShards(replica); docs += pendingDocs; - final Semaphore pendingDocsSemaphore = new Semaphore(pendingDocs); - blockIndexingOnPrimary.set(pendingDocsSemaphore); - blockIndexingOnPrimary.get().acquire(pendingDocs); + primaryEngineFactory.latchIndexers(); CountDownLatch pendingDocsDone = new CountDownLatch(pendingDocs); for (int i = 0; i < pendingDocs; i++) { final String id = "pending_" + i; @@ -274,9 +255,9 @@ public long addDocument(Iterable doc) throws IOExcepti } // wait for the pending ops to "hang" - blockedIndexers.await(); + primaryEngineFactory.awaitIndexersLatch(); - blockIndexingOnPrimary.set(null); + primaryEngineFactory.allowIndexing(); // index some more docs += shards.indexDocs(randomInt(5)); @@ -298,11 +279,12 @@ public void prepareForTranslogOperations(int totalTranslogOps) throws IOExceptio recoveryStart.await(); - for (int i = 0; i < pendingDocs; i++) { - assertFalse((pendingDocs - i) + " pending operations, recovery should wait", preparedForTranslog.get()); - pendingDocsSemaphore.release(); - } + // index some more + docs += shards.indexDocs(randomInt(5)); + + assertFalse("recovery should wait on pending docs", preparedForTranslog.get()); + primaryEngineFactory.releaseLatchedIndexers(); pendingDocsDone.await(); // now recovery can finish @@ -312,6 +294,114 @@ public void prepareForTranslogOperations(int totalTranslogOps) throws IOExceptio assertThat(newReplica.recoveryState().getTranslog().recoveredOperations(), equalTo(docs)); shards.assertAllEqual(docs); + } finally { + primaryEngineFactory.close(); + } + } + + @TestLogging( + "_root:DEBUG," + + "org.elasticsearch.action.bulk:TRACE," + + "org.elasticsearch.action.get:TRACE," + + "org.elasticsearch.cluster.service:TRACE," + + "org.elasticsearch.discovery:TRACE," + + "org.elasticsearch.indices.cluster:TRACE," + + "org.elasticsearch.indices.recovery:TRACE," + + "org.elasticsearch.index.seqno:TRACE," + + "org.elasticsearch.index.shard:TRACE") + public void testCheckpointsAndMarkingInSync() throws Exception { + final IndexMetaData metaData = buildIndexMetaData(0); + final BlockingEngineFactory replicaEngineFactory = new BlockingEngineFactory(); + try ( + ReplicationGroup shards = new ReplicationGroup(metaData) { + @Override + protected EngineFactory getEngineFactory(final ShardRouting routing) { + if (routing.primary()) { + return null; + } else { + return replicaEngineFactory; + } + } + }; + AutoCloseable ignored = replicaEngineFactory // make sure we release indexers before closing + ) { + shards.startPrimary(); + final int docs = shards.indexDocs(randomIntBetween(1, 10)); + logger.info("indexed [{}] docs", docs); + final CountDownLatch pendingDocDone = new CountDownLatch(1); + final CountDownLatch pendingDocActiveWithExtraDocIndexed = new CountDownLatch(1); + final IndexShard replica = shards.addReplica(); + final Future recoveryFuture = shards.asyncRecoverReplica( + replica, + (indexShard, node) -> new RecoveryTarget(indexShard, node, recoveryListener, l -> {}) { + @Override + public long indexTranslogOperations(final List operations, final int totalTranslogOps) { + // index a doc which is not part of the snapshot, but also does not complete on replica + replicaEngineFactory.latchIndexers(); + threadPool.generic().submit(() -> { + try { + shards.index(new IndexRequest(index.getName(), "type", "pending").source("{}", XContentType.JSON)); + } catch (final Exception e) { + throw new RuntimeException(e); + } finally { + pendingDocDone.countDown(); + } + }); + try { + // the pending doc is latched in the engine + replicaEngineFactory.awaitIndexersLatch(); + // unblock indexing for the next doc + replicaEngineFactory.allowIndexing(); + shards.index(new IndexRequest(index.getName(), "type", "completed").source("{}", XContentType.JSON)); + /* + * We want to test that the global checkpoint is blocked from advancing on the primary when a replica shard + * is pending being marked in-sync. We also want to test the the global checkpoint does not advance on the + * replica when its local checkpoint is behind the global checkpoint on the primary. Finally, advancing the + * global checkpoint here forces recovery to block until the pending doc is indexing on the replica. + */ + shards.getPrimary().updateGlobalCheckpointOnPrimary(); + pendingDocActiveWithExtraDocIndexed.countDown(); + } catch (final Exception e) { + throw new AssertionError(e); + } + return super.indexTranslogOperations(operations, totalTranslogOps); + } + }); + pendingDocActiveWithExtraDocIndexed.await(); + assertThat(pendingDocDone.getCount(), equalTo(1L)); + { + final long expectedDocs = docs + 2L; + assertThat(shards.getPrimary().getLocalCheckpoint(), equalTo(expectedDocs - 1)); + // recovery has not completed, therefore the global checkpoint can have advance on the primary + assertThat(shards.getPrimary().getGlobalCheckpoint(), equalTo(expectedDocs - 1)); + // the pending document is not done, the checkpoints can not have advanced on the replica + assertThat(replica.getLocalCheckpoint(), lessThan(expectedDocs - 1)); + assertThat(replica.getGlobalCheckpoint(), lessThan(expectedDocs - 1)); + } + + shards.getPrimary().updateGlobalCheckpointOnPrimary(); + { + final long expectedDocs = docs + 3L; + shards.index(new IndexRequest(index.getName(), "type", "last").source("{}", XContentType.JSON)); + assertThat(shards.getPrimary().getLocalCheckpoint(), equalTo(expectedDocs - 1)); + assertThat(shards.getPrimary().getGlobalCheckpoint(), equalTo(expectedDocs - 2)); + assertThat(replica.getLocalCheckpoint(), lessThan(expectedDocs - 2)); + assertThat(replica.getGlobalCheckpoint(), lessThan(expectedDocs - 2)); + } + + replicaEngineFactory.releaseLatchedIndexers(); + pendingDocDone.await(); + recoveryFuture.get(); + shards.getPrimary().updateGlobalCheckpointOnPrimary(); + { + final long expectedDocs = docs + 3L; + assertBusy(() -> { + assertThat(shards.getPrimary().getLocalCheckpoint(), equalTo(expectedDocs - 1)); + assertThat(shards.getPrimary().getGlobalCheckpoint(), equalTo(expectedDocs - 1)); + assertThat(replica.getLocalCheckpoint(), equalTo(expectedDocs - 1)); + assertThat(replica.getGlobalCheckpoint(), equalTo(expectedDocs - 1)); + }); + } } } @@ -354,11 +444,11 @@ private void blockIfNeeded(RecoveryState.Stage currentStage) { } @Override - public void indexTranslogOperations(List operations, int totalTranslogOps) { + public long indexTranslogOperations(List operations, int totalTranslogOps) { if (hasBlocked() == false) { blockIfNeeded(RecoveryState.Stage.TRANSLOG); } - super.indexTranslogOperations(operations, totalTranslogOps); + return super.indexTranslogOperations(operations, totalTranslogOps); } @Override @@ -379,4 +469,66 @@ public void finalizeRecovery(long globalCheckpoint) { } + static class BlockingEngineFactory implements EngineFactory, AutoCloseable { + + private final List blocks = new ArrayList<>(); + + private final AtomicReference blockReference = new AtomicReference<>(); + private final AtomicReference blockedIndexers = new AtomicReference<>(); + + public synchronized void latchIndexers() { + final CountDownLatch block = new CountDownLatch(1); + blocks.add(block); + blockedIndexers.set(new CountDownLatch(1)); + assert blockReference.compareAndSet(null, block); + } + + public void awaitIndexersLatch() throws InterruptedException { + blockedIndexers.get().await(); + } + + public synchronized void allowIndexing() { + final CountDownLatch previous = blockReference.getAndSet(null); + assert previous == null || blocks.contains(previous); + } + + public synchronized void releaseLatchedIndexers() { + allowIndexing(); + blocks.forEach(CountDownLatch::countDown); + blocks.clear(); + } + + @Override + public Engine newReadWriteEngine(final EngineConfig config) { + return InternalEngineTests.createInternalEngine( + (directory, writerConfig) -> + new IndexWriter(directory, writerConfig) { + @Override + public long addDocument(final Iterable doc) throws IOException { + final CountDownLatch block = blockReference.get(); + if (block != null) { + final CountDownLatch latch = blockedIndexers.get(); + if (latch != null) { + latch.countDown(); + } + try { + block.await(); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + } + return super.addDocument(doc); + } + }, + null, + config); + } + + @Override + public void close() throws Exception { + releaseLatchedIndexers(); + } + + } + } diff --git a/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTests.java b/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTests.java deleted file mode 100644 index 58f66dc62e773..0000000000000 --- a/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTests.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -package org.elasticsearch.index.seqno; - -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.util.set.Sets; -import org.elasticsearch.index.shard.ShardId; -import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.IndexSettingsModule; -import org.junit.Before; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Stream; - -import static org.elasticsearch.index.seqno.SequenceNumbersService.UNASSIGNED_SEQ_NO; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.not; - -public class GlobalCheckpointTests extends ESTestCase { - - GlobalCheckpointTracker tracker; - - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - tracker = - new GlobalCheckpointTracker( - new ShardId("test", "_na_", 0), - IndexSettingsModule.newIndexSettings("test", Settings.EMPTY), - UNASSIGNED_SEQ_NO); - } - - public void testEmptyShards() { - assertFalse("checkpoint shouldn't be updated when the are no active shards", tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); - } - - private final AtomicInteger aIdGenerator = new AtomicInteger(); - - private Map randomAllocationsWithLocalCheckpoints(int min, int max) { - Map allocations = new HashMap<>(); - for (int i = randomIntBetween(min, max); i > 0; i--) { - allocations.put("id_" + aIdGenerator.incrementAndGet(), (long) randomInt(1000)); - } - return allocations; - } - - public void testGlobalCheckpointUpdate() { - Map allocations = new HashMap<>(); - Map activeWithCheckpoints = randomAllocationsWithLocalCheckpoints(0, 5); - Set active = new HashSet<>(activeWithCheckpoints.keySet()); - allocations.putAll(activeWithCheckpoints); - Map initializingWithCheckpoints = randomAllocationsWithLocalCheckpoints(0, 5); - Set initializing = new HashSet<>(initializingWithCheckpoints.keySet()); - allocations.putAll(initializingWithCheckpoints); - assertThat(allocations.size(), equalTo(active.size() + initializing.size())); - - // note: allocations can never be empty in practice as we always have at least one primary shard active/in sync - // it is however nice not to assume this on this level and check we do the right thing. - final long maxLocalCheckpoint = allocations.values().stream().min(Long::compare).orElse(UNASSIGNED_SEQ_NO); - - assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); - - logger.info("--> using allocations"); - allocations.keySet().forEach(aId -> { - final String type; - if (active.contains(aId)) { - type = "active"; - } else if (initializing.contains(aId)) { - type = "init"; - } else { - throw new IllegalStateException(aId + " not found in any map"); - } - logger.info(" - [{}], local checkpoint [{}], [{}]", aId, allocations.get(aId), type); - }); - - tracker.updateAllocationIdsFromMaster(active, initializing); - initializing.forEach(aId -> tracker.markAllocationIdAsInSync(aId)); - allocations.keySet().forEach(aId -> tracker.updateLocalCheckpoint(aId, allocations.get(aId))); - - - assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); - - assertThat(tracker.updateCheckpointOnPrimary(), equalTo(maxLocalCheckpoint != UNASSIGNED_SEQ_NO)); - assertThat(tracker.getCheckpoint(), equalTo(maxLocalCheckpoint)); - - // increment checkpoints - active.forEach(aId -> allocations.put(aId, allocations.get(aId) + 1 + randomInt(4))); - initializing.forEach(aId -> allocations.put(aId, allocations.get(aId) + 1 + randomInt(4))); - allocations.keySet().forEach(aId -> tracker.updateLocalCheckpoint(aId, allocations.get(aId))); - - // now insert an unknown active/insync id , the checkpoint shouldn't change but a refresh should be requested. - final String extraId = "extra_" + randomAlphaOfLength(5); - - // first check that adding it without the master blessing doesn't change anything. - tracker.updateLocalCheckpoint(extraId, maxLocalCheckpoint + 1 + randomInt(4)); - assertThat(tracker.getLocalCheckpointForAllocationId(extraId), equalTo(UNASSIGNED_SEQ_NO)); - - Set newActive = new HashSet<>(active); - newActive.add(extraId); - tracker.updateAllocationIdsFromMaster(newActive, initializing); - - // we should ask for a refresh , but not update the checkpoint - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), equalTo(maxLocalCheckpoint)); - - // now notify for the new id - tracker.updateLocalCheckpoint(extraId, maxLocalCheckpoint + 1 + randomInt(4)); - - // now it should be incremented - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), greaterThan(maxLocalCheckpoint)); - } - - public void testMissingActiveIdsPreventAdvance() { - final Map active = randomAllocationsWithLocalCheckpoints(1, 5); - final Map initializing = randomAllocationsWithLocalCheckpoints(0, 5); - final Map assigned = new HashMap<>(); - assigned.putAll(active); - assigned.putAll(initializing); - tracker.updateAllocationIdsFromMaster( - new HashSet<>(randomSubsetOf(randomInt(active.size() - 1), active.keySet())), - initializing.keySet()); - randomSubsetOf(initializing.keySet()).forEach(tracker::markAllocationIdAsInSync); - assigned.forEach(tracker::updateLocalCheckpoint); - - // now mark all active shards - tracker.updateAllocationIdsFromMaster(active.keySet(), initializing.keySet()); - - // global checkpoint can't be advanced, but we need a sync - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); - - // update again - assigned.forEach(tracker::updateLocalCheckpoint); - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), not(equalTo(UNASSIGNED_SEQ_NO))); - } - - public void testMissingInSyncIdsPreventAdvance() { - final Map active = randomAllocationsWithLocalCheckpoints(0, 5); - final Map initializing = randomAllocationsWithLocalCheckpoints(1, 5); - tracker.updateAllocationIdsFromMaster(active.keySet(), initializing.keySet()); - initializing.keySet().forEach(tracker::markAllocationIdAsInSync); - randomSubsetOf(randomInt(initializing.size() - 1), - initializing.keySet()).forEach(aId -> tracker.updateLocalCheckpoint(aId, initializing.get(aId))); - - active.forEach(tracker::updateLocalCheckpoint); - - // global checkpoint can't be advanced, but we need a sync - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); - - // update again - initializing.forEach(tracker::updateLocalCheckpoint); - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), not(equalTo(UNASSIGNED_SEQ_NO))); - } - - public void testInSyncIdsAreIgnoredIfNotValidatedByMaster() { - final Map active = randomAllocationsWithLocalCheckpoints(1, 5); - final Map initializing = randomAllocationsWithLocalCheckpoints(1, 5); - final Map nonApproved = randomAllocationsWithLocalCheckpoints(1, 5); - tracker.updateAllocationIdsFromMaster(active.keySet(), initializing.keySet()); - initializing.keySet().forEach(tracker::markAllocationIdAsInSync); - nonApproved.keySet().forEach(tracker::markAllocationIdAsInSync); - - List> allocations = Arrays.asList(active, initializing, nonApproved); - Collections.shuffle(allocations, random()); - allocations.forEach(a -> a.forEach(tracker::updateLocalCheckpoint)); - - // global checkpoint can be advanced, but we need a sync - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), not(equalTo(UNASSIGNED_SEQ_NO))); - } - - public void testInSyncIdsAreRemovedIfNotValidatedByMaster() { - final Map activeToStay = randomAllocationsWithLocalCheckpoints(1, 5); - final Map initializingToStay = randomAllocationsWithLocalCheckpoints(1, 5); - final Map activeToBeRemoved = randomAllocationsWithLocalCheckpoints(1, 5); - final Map initializingToBeRemoved = randomAllocationsWithLocalCheckpoints(1, 5); - final Set active = Sets.union(activeToStay.keySet(), activeToBeRemoved.keySet()); - final Set initializing = Sets.union(initializingToStay.keySet(), initializingToBeRemoved.keySet()); - final Map allocations = new HashMap<>(); - allocations.putAll(activeToStay); - if (randomBoolean()) { - allocations.putAll(activeToBeRemoved); - } - allocations.putAll(initializingToStay); - if (randomBoolean()) { - allocations.putAll(initializingToBeRemoved); - } - tracker.updateAllocationIdsFromMaster(active, initializing); - if (randomBoolean()) { - initializingToStay.keySet().forEach(tracker::markAllocationIdAsInSync); - } else { - initializing.forEach(tracker::markAllocationIdAsInSync); - } - if (randomBoolean()) { - allocations.forEach(tracker::updateLocalCheckpoint); - } - - // global checkpoint may be advanced, but we need a sync in any case - assertTrue(tracker.updateCheckpointOnPrimary()); - - // now remove shards - if (randomBoolean()) { - tracker.updateAllocationIdsFromMaster(activeToStay.keySet(), initializingToStay.keySet()); - allocations.forEach((aid, ckp) -> tracker.updateLocalCheckpoint(aid, ckp + 10L)); - } else { - allocations.forEach((aid, ckp) -> tracker.updateLocalCheckpoint(aid, ckp + 10L)); - tracker.updateAllocationIdsFromMaster(activeToStay.keySet(), initializingToStay.keySet()); - } - - final long checkpoint = Stream.concat(activeToStay.values().stream(), initializingToStay.values().stream()) - .min(Long::compare).get() + 10; // we added 10 to make sure it's advanced in the second time - - // global checkpoint is advanced and we need a sync - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), equalTo(checkpoint)); - } -} diff --git a/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java b/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java new file mode 100644 index 0000000000000..3f5882e6c577e --- /dev/null +++ b/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java @@ -0,0 +1,490 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.index.seqno; + +import org.elasticsearch.common.Randomness; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.set.Sets; +import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.IndexSettingsModule; +import org.junit.Before; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static org.elasticsearch.index.seqno.SequenceNumbersService.UNASSIGNED_SEQ_NO; +import static org.hamcrest.Matchers.comparesEqualTo; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.not; + +public class GlobalCheckpointTrackerTests extends ESTestCase { + + GlobalCheckpointTracker tracker; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + tracker = + new GlobalCheckpointTracker( + new ShardId("test", "_na_", 0), + IndexSettingsModule.newIndexSettings("test", Settings.EMPTY), + UNASSIGNED_SEQ_NO); + } + + public void testEmptyShards() { + assertFalse("checkpoint shouldn't be updated when the are no active shards", tracker.updateCheckpointOnPrimary()); + assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); + } + + private final AtomicInteger aIdGenerator = new AtomicInteger(); + + private Map randomAllocationsWithLocalCheckpoints(int min, int max) { + Map allocations = new HashMap<>(); + for (int i = randomIntBetween(min, max); i > 0; i--) { + allocations.put("id_" + aIdGenerator.incrementAndGet(), (long) randomInt(1000)); + } + return allocations; + } + + public void testGlobalCheckpointUpdate() { + Map allocations = new HashMap<>(); + Map activeWithCheckpoints = randomAllocationsWithLocalCheckpoints(0, 5); + Set active = new HashSet<>(activeWithCheckpoints.keySet()); + allocations.putAll(activeWithCheckpoints); + Map initializingWithCheckpoints = randomAllocationsWithLocalCheckpoints(0, 5); + Set initializing = new HashSet<>(initializingWithCheckpoints.keySet()); + allocations.putAll(initializingWithCheckpoints); + assertThat(allocations.size(), equalTo(active.size() + initializing.size())); + + // note: allocations can never be empty in practice as we always have at least one primary shard active/in sync + // it is however nice not to assume this on this level and check we do the right thing. + final long maxLocalCheckpoint = allocations.values().stream().min(Long::compare).orElse(UNASSIGNED_SEQ_NO); + + assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); + + logger.info("--> using allocations"); + allocations.keySet().forEach(aId -> { + final String type; + if (active.contains(aId)) { + type = "active"; + } else if (initializing.contains(aId)) { + type = "init"; + } else { + throw new IllegalStateException(aId + " not found in any map"); + } + logger.info(" - [{}], local checkpoint [{}], [{}]", aId, allocations.get(aId), type); + }); + + tracker.updateAllocationIdsFromMaster(active, initializing); + initializing.forEach(aId -> markAllocationIdAsInSyncQuietly(tracker, aId, tracker.getCheckpoint())); + allocations.keySet().forEach(aId -> tracker.updateLocalCheckpoint(aId, allocations.get(aId))); + + + assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); + + assertThat(tracker.updateCheckpointOnPrimary(), equalTo(maxLocalCheckpoint != UNASSIGNED_SEQ_NO)); + assertThat(tracker.getCheckpoint(), equalTo(maxLocalCheckpoint)); + + // increment checkpoints + active.forEach(aId -> allocations.put(aId, allocations.get(aId) + 1 + randomInt(4))); + initializing.forEach(aId -> allocations.put(aId, allocations.get(aId) + 1 + randomInt(4))); + allocations.keySet().forEach(aId -> tracker.updateLocalCheckpoint(aId, allocations.get(aId))); + + // now insert an unknown active/insync id , the checkpoint shouldn't change but a refresh should be requested. + final String extraId = "extra_" + randomAlphaOfLength(5); + + // first check that adding it without the master blessing doesn't change anything. + tracker.updateLocalCheckpoint(extraId, maxLocalCheckpoint + 1 + randomInt(4)); + assertThat(tracker.getLocalCheckpointForAllocationId(extraId), equalTo(UNASSIGNED_SEQ_NO)); + + Set newActive = new HashSet<>(active); + newActive.add(extraId); + tracker.updateAllocationIdsFromMaster(newActive, initializing); + + // we should ask for a refresh , but not update the checkpoint + assertTrue(tracker.updateCheckpointOnPrimary()); + assertThat(tracker.getCheckpoint(), equalTo(maxLocalCheckpoint)); + + // now notify for the new id + tracker.updateLocalCheckpoint(extraId, maxLocalCheckpoint + 1 + randomInt(4)); + + // now it should be incremented + assertTrue(tracker.updateCheckpointOnPrimary()); + assertThat(tracker.getCheckpoint(), greaterThan(maxLocalCheckpoint)); + } + + public void testMissingActiveIdsPreventAdvance() { + final Map active = randomAllocationsWithLocalCheckpoints(1, 5); + final Map initializing = randomAllocationsWithLocalCheckpoints(0, 5); + final Map assigned = new HashMap<>(); + assigned.putAll(active); + assigned.putAll(initializing); + tracker.updateAllocationIdsFromMaster( + new HashSet<>(randomSubsetOf(randomInt(active.size() - 1), active.keySet())), + initializing.keySet()); + randomSubsetOf(initializing.keySet()).forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getCheckpoint())); + assigned.forEach(tracker::updateLocalCheckpoint); + + // now mark all active shards + tracker.updateAllocationIdsFromMaster(active.keySet(), initializing.keySet()); + + // global checkpoint can't be advanced, but we need a sync + assertTrue(tracker.updateCheckpointOnPrimary()); + assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); + + // update again + assigned.forEach(tracker::updateLocalCheckpoint); + assertTrue(tracker.updateCheckpointOnPrimary()); + assertThat(tracker.getCheckpoint(), not(equalTo(UNASSIGNED_SEQ_NO))); + } + + public void testMissingInSyncIdsPreventAdvance() { + final Map active = randomAllocationsWithLocalCheckpoints(0, 5); + final Map initializing = randomAllocationsWithLocalCheckpoints(1, 5); + tracker.updateAllocationIdsFromMaster(active.keySet(), initializing.keySet()); + initializing.keySet().forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getCheckpoint())); + randomSubsetOf(randomInt(initializing.size() - 1), + initializing.keySet()).forEach(aId -> tracker.updateLocalCheckpoint(aId, initializing.get(aId))); + + active.forEach(tracker::updateLocalCheckpoint); + + // global checkpoint can't be advanced, but we need a sync + assertTrue(tracker.updateCheckpointOnPrimary()); + assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); + + // update again + initializing.forEach(tracker::updateLocalCheckpoint); + assertTrue(tracker.updateCheckpointOnPrimary()); + assertThat(tracker.getCheckpoint(), not(equalTo(UNASSIGNED_SEQ_NO))); + } + + public void testInSyncIdsAreIgnoredIfNotValidatedByMaster() { + final Map active = randomAllocationsWithLocalCheckpoints(1, 5); + final Map initializing = randomAllocationsWithLocalCheckpoints(1, 5); + final Map nonApproved = randomAllocationsWithLocalCheckpoints(1, 5); + tracker.updateAllocationIdsFromMaster(active.keySet(), initializing.keySet()); + initializing.keySet().forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getCheckpoint())); + nonApproved.keySet().forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getCheckpoint())); + + List> allocations = Arrays.asList(active, initializing, nonApproved); + Collections.shuffle(allocations, random()); + allocations.forEach(a -> a.forEach(tracker::updateLocalCheckpoint)); + + // global checkpoint can be advanced, but we need a sync + assertTrue(tracker.updateCheckpointOnPrimary()); + assertThat(tracker.getCheckpoint(), not(equalTo(UNASSIGNED_SEQ_NO))); + } + + public void testInSyncIdsAreRemovedIfNotValidatedByMaster() { + final Map activeToStay = randomAllocationsWithLocalCheckpoints(1, 5); + final Map initializingToStay = randomAllocationsWithLocalCheckpoints(1, 5); + final Map activeToBeRemoved = randomAllocationsWithLocalCheckpoints(1, 5); + final Map initializingToBeRemoved = randomAllocationsWithLocalCheckpoints(1, 5); + final Set active = Sets.union(activeToStay.keySet(), activeToBeRemoved.keySet()); + final Set initializing = Sets.union(initializingToStay.keySet(), initializingToBeRemoved.keySet()); + final Map allocations = new HashMap<>(); + allocations.putAll(activeToStay); + if (randomBoolean()) { + allocations.putAll(activeToBeRemoved); + } + allocations.putAll(initializingToStay); + if (randomBoolean()) { + allocations.putAll(initializingToBeRemoved); + } + tracker.updateAllocationIdsFromMaster(active, initializing); + if (randomBoolean()) { + initializingToStay.keySet().forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getCheckpoint())); + } else { + initializing.forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getCheckpoint())); + } + if (randomBoolean()) { + allocations.forEach(tracker::updateLocalCheckpoint); + } + + // global checkpoint may be advanced, but we need a sync in any case + assertTrue(tracker.updateCheckpointOnPrimary()); + + // now remove shards + if (randomBoolean()) { + tracker.updateAllocationIdsFromMaster(activeToStay.keySet(), initializingToStay.keySet()); + allocations.forEach((aid, ckp) -> tracker.updateLocalCheckpoint(aid, ckp + 10L)); + } else { + allocations.forEach((aid, ckp) -> tracker.updateLocalCheckpoint(aid, ckp + 10L)); + tracker.updateAllocationIdsFromMaster(activeToStay.keySet(), initializingToStay.keySet()); + } + + final long checkpoint = Stream.concat(activeToStay.values().stream(), initializingToStay.values().stream()) + .min(Long::compare).get() + 10; // we added 10 to make sure it's advanced in the second time + + // global checkpoint is advanced and we need a sync + assertTrue(tracker.updateCheckpointOnPrimary()); + assertThat(tracker.getCheckpoint(), equalTo(checkpoint)); + } + + public void testWaitForAllocationIdToBeInSync() throws BrokenBarrierException, InterruptedException { + final int localCheckpoint = randomIntBetween(1, 32); + final int globalCheckpoint = randomIntBetween(localCheckpoint + 1, 64); + final CyclicBarrier barrier = new CyclicBarrier(2); + final AtomicBoolean complete = new AtomicBoolean(); + final String inSyncAllocationId =randomAlphaOfLength(16); + final String trackingAllocationId = randomAlphaOfLength(16); + tracker.updateAllocationIdsFromMaster(Collections.singleton(inSyncAllocationId), Collections.singleton(trackingAllocationId)); + tracker.updateLocalCheckpoint(inSyncAllocationId, globalCheckpoint); + tracker.updateCheckpointOnPrimary(); + final Thread thread = new Thread(() -> { + try { + // synchronize starting with the test thread + barrier.await(); + tracker.markAllocationIdAsInSync(trackingAllocationId, localCheckpoint); + complete.set(true); + // synchronize with the test thread checking if we are no longer waiting + barrier.await(); + } catch (final BrokenBarrierException | InterruptedException e) { + throw new RuntimeException(e); + } + }); + + thread.start(); + + // synchronize starting with the waiting thread + barrier.await(); + + final List elements = IntStream.rangeClosed(0, globalCheckpoint - 1).boxed().collect(Collectors.toList()); + Randomness.shuffle(elements); + for (int i = 0; i < elements.size(); i++) { + tracker.updateLocalCheckpoint(trackingAllocationId, elements.get(i)); + assertFalse(complete.get()); + assertTrue(awaitBusy(() -> tracker.trackingLocalCheckpoints.containsKey(trackingAllocationId))); + assertTrue(awaitBusy(() -> tracker.pendingInSync.contains(trackingAllocationId))); + assertFalse(tracker.inSyncLocalCheckpoints.containsKey(trackingAllocationId)); + } + + tracker.updateLocalCheckpoint(trackingAllocationId, randomIntBetween(globalCheckpoint, 64)); + // synchronize with the waiting thread to mark that it is complete + barrier.await(); + assertTrue(complete.get()); + assertTrue(tracker.trackingLocalCheckpoints.isEmpty()); + assertTrue(tracker.pendingInSync.isEmpty()); + assertTrue(tracker.inSyncLocalCheckpoints.containsKey(trackingAllocationId)); + + thread.join(); + } + + public void testWaitForAllocationIdToBeInSyncCanBeInterrupted() throws BrokenBarrierException, InterruptedException { + final int localCheckpoint = randomIntBetween(1, 32); + final int globalCheckpoint = randomIntBetween(localCheckpoint + 1, 64); + final CyclicBarrier barrier = new CyclicBarrier(2); + final AtomicBoolean interrupted = new AtomicBoolean(); + final String inSyncAllocationId = randomAlphaOfLength(16); + final String trackingAllocationId = randomAlphaOfLength(32); + tracker.updateAllocationIdsFromMaster(Collections.singleton(inSyncAllocationId), Collections.singleton(trackingAllocationId)); + tracker.updateLocalCheckpoint(inSyncAllocationId, globalCheckpoint); + tracker.updateCheckpointOnPrimary(); + final Thread thread = new Thread(() -> { + try { + // synchronize starting with the test thread + barrier.await(); + } catch (final BrokenBarrierException | InterruptedException e) { + throw new RuntimeException(e); + } + try { + tracker.markAllocationIdAsInSync(trackingAllocationId, localCheckpoint); + } catch (final InterruptedException e) { + interrupted.set(true); + // synchronize with the test thread checking if we are interrupted + } + try { + barrier.await(); + } catch (final BrokenBarrierException | InterruptedException e) { + throw new RuntimeException(e); + } + }); + + thread.start(); + + // synchronize starting with the waiting thread + barrier.await(); + + thread.interrupt(); + + // synchronize with the waiting thread to mark that it is complete + barrier.await(); + + assertTrue(interrupted.get()); + + thread.join(); + } + + public void testUpdateAllocationIdsFromMaster() throws Exception { + final int numberOfActiveAllocationsIds = randomIntBetween(2, 16); + final Set activeAllocationIds = + IntStream.range(0, numberOfActiveAllocationsIds).mapToObj(i -> randomAlphaOfLength(16)).collect(Collectors.toSet()); + final int numberOfInitializingIds = randomIntBetween(2, 16); + final Set initializingIds = + IntStream.range(0, numberOfInitializingIds).mapToObj(i -> { + do { + final String initializingId = randomAlphaOfLength(16); + // ensure we do not duplicate an allocation ID in active and initializing sets + if (!activeAllocationIds.contains(initializingId)) { + return initializingId; + } + } while (true); + }).collect(Collectors.toSet()); + tracker.updateAllocationIdsFromMaster(activeAllocationIds, initializingIds); + + // first we assert that the in-sync and tracking sets are set up correctly + assertTrue(activeAllocationIds.stream().allMatch(a -> tracker.inSyncLocalCheckpoints.containsKey(a))); + assertTrue( + activeAllocationIds + .stream() + .allMatch(a -> tracker.inSyncLocalCheckpoints.get(a) == SequenceNumbersService.UNASSIGNED_SEQ_NO)); + assertTrue(initializingIds.stream().allMatch(a -> tracker.trackingLocalCheckpoints.containsKey(a))); + assertTrue( + initializingIds + .stream() + .allMatch(a -> tracker.trackingLocalCheckpoints.get(a) == SequenceNumbersService.UNASSIGNED_SEQ_NO)); + + // now we will remove some allocation IDs from these and ensure that they propagate through + final List removingActiveAllocationIds = randomSubsetOf(activeAllocationIds); + final Set newActiveAllocationIds = + activeAllocationIds.stream().filter(a -> !removingActiveAllocationIds.contains(a)).collect(Collectors.toSet()); + final List removingInitializingAllocationIds = randomSubsetOf(initializingIds); + final Set newInitializingAllocationIds = + initializingIds.stream().filter(a -> !removingInitializingAllocationIds.contains(a)).collect(Collectors.toSet()); + tracker.updateAllocationIdsFromMaster(newActiveAllocationIds, newInitializingAllocationIds); + assertTrue(newActiveAllocationIds.stream().allMatch(a -> tracker.inSyncLocalCheckpoints.containsKey(a))); + assertTrue(removingActiveAllocationIds.stream().noneMatch(a -> tracker.inSyncLocalCheckpoints.containsKey(a))); + assertTrue(newInitializingAllocationIds.stream().allMatch(a -> tracker.trackingLocalCheckpoints.containsKey(a))); + assertTrue(removingInitializingAllocationIds.stream().noneMatch(a -> tracker.trackingLocalCheckpoints.containsKey(a))); + + /* + * Now we will add an allocation ID to each of active and initializing and ensure they propagate through. Using different lengths + * than we have been using above ensures that we can not collide with a previous allocation ID + */ + newActiveAllocationIds.add(randomAlphaOfLength(32)); + newInitializingAllocationIds.add(randomAlphaOfLength(64)); + tracker.updateAllocationIdsFromMaster(newActiveAllocationIds, newInitializingAllocationIds); + assertTrue(newActiveAllocationIds.stream().allMatch(a -> tracker.inSyncLocalCheckpoints.containsKey(a))); + assertTrue( + newActiveAllocationIds + .stream() + .allMatch(a -> tracker.inSyncLocalCheckpoints.get(a) == SequenceNumbersService.UNASSIGNED_SEQ_NO)); + assertTrue(newInitializingAllocationIds.stream().allMatch(a -> tracker.trackingLocalCheckpoints.containsKey(a))); + assertTrue( + newInitializingAllocationIds + .stream() + .allMatch(a -> tracker.trackingLocalCheckpoints.get(a) == SequenceNumbersService.UNASSIGNED_SEQ_NO)); + + // the tracking allocation IDs should play no role in determining the global checkpoint + final Map activeLocalCheckpoints = + newActiveAllocationIds.stream().collect(Collectors.toMap(Function.identity(), a -> randomIntBetween(1, 1024))); + activeLocalCheckpoints.forEach((a, l) -> tracker.updateLocalCheckpoint(a, l)); + final Map initializingLocalCheckpoints = + newInitializingAllocationIds.stream().collect(Collectors.toMap(Function.identity(), a -> randomIntBetween(1, 1024))); + initializingLocalCheckpoints.forEach((a, l) -> tracker.updateLocalCheckpoint(a, l)); + assertTrue( + activeLocalCheckpoints + .entrySet() + .stream() + .allMatch(e -> tracker.getLocalCheckpointForAllocationId(e.getKey()) == e.getValue())); + assertTrue( + initializingLocalCheckpoints + .entrySet() + .stream() + .allMatch(e -> tracker.trackingLocalCheckpoints.get(e.getKey()) == e.getValue())); + assertTrue(tracker.updateCheckpointOnPrimary()); + final long minimumActiveLocalCheckpoint = (long) activeLocalCheckpoints.values().stream().min(Integer::compareTo).get(); + assertThat(tracker.getCheckpoint(), equalTo(minimumActiveLocalCheckpoint)); + final long minimumInitailizingLocalCheckpoint = (long) initializingLocalCheckpoints.values().stream().min(Integer::compareTo).get(); + + // now we are going to add a new allocation ID and bring it in sync which should move it to the in-sync allocation IDs + final long localCheckpoint = + randomIntBetween(0, Math.toIntExact(Math.min(minimumActiveLocalCheckpoint, minimumInitailizingLocalCheckpoint) - 1)); + + // using a different length than we have been using above ensures that we can not collide with a previous allocation ID + final String newSyncingAllocationId = randomAlphaOfLength(128); + newInitializingAllocationIds.add(newSyncingAllocationId); + tracker.updateAllocationIdsFromMaster(newActiveAllocationIds, newInitializingAllocationIds); + final CyclicBarrier barrier = new CyclicBarrier(2); + final Thread thread = new Thread(() -> { + try { + barrier.await(); + tracker.markAllocationIdAsInSync(newSyncingAllocationId, localCheckpoint); + barrier.await(); + } catch (final BrokenBarrierException | InterruptedException e) { + throw new RuntimeException(e); + } + }); + + thread.start(); + + barrier.await(); + + assertBusy(() -> { + assertTrue(tracker.pendingInSync.contains(newSyncingAllocationId)); + assertTrue(tracker.trackingLocalCheckpoints.containsKey(newSyncingAllocationId)); + }); + + tracker.updateLocalCheckpoint(newSyncingAllocationId, randomIntBetween(Math.toIntExact(minimumActiveLocalCheckpoint), 1024)); + + barrier.await(); + + assertFalse(tracker.pendingInSync.contains(newSyncingAllocationId)); + assertFalse(tracker.trackingLocalCheckpoints.containsKey(newSyncingAllocationId)); + assertTrue(tracker.inSyncLocalCheckpoints.containsKey(newSyncingAllocationId)); + + /* + * The new in-sync allocation ID is in the in-sync set now yet the master does not know this; the allocation ID should still be in + * the in-sync set even if we receive a cluster state update that does not reflect this. + * + */ + tracker.updateAllocationIdsFromMaster(newActiveAllocationIds, newInitializingAllocationIds); + assertFalse(tracker.trackingLocalCheckpoints.containsKey(newSyncingAllocationId)); + assertTrue(tracker.inSyncLocalCheckpoints.containsKey(newSyncingAllocationId)); + } + + + private void markAllocationIdAsInSyncQuietly( + final GlobalCheckpointTracker tracker, final String allocationId, final long localCheckpoint) { + try { + tracker.markAllocationIdAsInSync(allocationId, localCheckpoint); + } catch (final InterruptedException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java index 3f01a0c0a9ae2..a0d356c03e79c 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java +++ b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java @@ -1275,9 +1275,10 @@ public void testTranslogRecoverySyncsTranslog() throws IOException { new RecoveryTarget(shard, discoveryNode, recoveryListener, aLong -> { }) { @Override - public void indexTranslogOperations(List operations, int totalTranslogOps) { - super.indexTranslogOperations(operations, totalTranslogOps); + public long indexTranslogOperations(List operations, int totalTranslogOps) { + final long localCheckpoint = super.indexTranslogOperations(operations, totalTranslogOps); assertFalse(replica.getTranslog().syncNeeded()); + return localCheckpoint; } }, true); @@ -1331,10 +1332,11 @@ public void prepareForTranslogOperations(int totalTranslogOps) throws IOExceptio } @Override - public void indexTranslogOperations(List operations, int totalTranslogOps) { - super.indexTranslogOperations(operations, totalTranslogOps); + public long indexTranslogOperations(List operations, int totalTranslogOps) { + final long localCheckpoint = super.indexTranslogOperations(operations, totalTranslogOps); // Shard should now be active since we did recover: assertTrue(replica.isActive()); + return localCheckpoint; } }, false); diff --git a/core/src/test/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetServiceTests.java b/core/src/test/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetServiceTests.java index 261e53064fe01..1c588caadcd45 100644 --- a/core/src/test/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetServiceTests.java +++ b/core/src/test/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetServiceTests.java @@ -29,6 +29,16 @@ import org.elasticsearch.index.seqno.SequenceNumbersService; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.IndexShardTestCase; +import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.index.translog.Translog; +import org.elasticsearch.index.translog.TranslogConfig; +import org.elasticsearch.index.translog.TranslogWriter; + +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReference; import static org.hamcrest.Matchers.equalTo; @@ -36,7 +46,13 @@ public class PeerRecoveryTargetServiceTests extends IndexShardTestCase { public void testGetStartingSeqNo() throws Exception { IndexShard replica = newShard(false); - RecoveryTarget recoveryTarget = new RecoveryTarget(replica, null, null, null); + final AtomicReference translogLocation = new AtomicReference<>(); + RecoveryTarget recoveryTarget = new RecoveryTarget(replica, null, null, null) { + @Override + Path translogLocation() { + return translogLocation.get(); + } + }; try { recoveryEmptyReplica(replica); int docs = randomIntBetween(1, 10); @@ -56,22 +72,28 @@ public void testGetStartingSeqNo() throws Exception { final long maxSeqNo = replica.seqNoStats().getMaxSeqNo(); final long localCheckpoint = replica.getLocalCheckpoint(); + translogLocation.set(replica.getTranslog().location()); + assertThat(PeerRecoveryTargetService.getStartingSeqNo(recoveryTarget), equalTo(SequenceNumbersService.UNASSIGNED_SEQ_NO)); - replica.updateGlobalCheckpointOnReplica(maxSeqNo - 1); - replica.getTranslog().sync(); + final Translog translog = replica.getTranslog(); + translogLocation.set( + writeTranslog(replica.shardId(), translog.getTranslogUUID(), translog.currentFileGeneration(), maxSeqNo - 1)); - // commit is enough, global checkpoint is below max *committed* which is NO_OPS_PERFORMED + // commit is good, global checkpoint is at least max *committed* which is NO_OPS_PERFORMED assertThat(PeerRecoveryTargetService.getStartingSeqNo(recoveryTarget), equalTo(0L)); replica.flush(new FlushRequest()); - // commit is still not good enough, global checkpoint is below max + translogLocation.set(replica.getTranslog().location()); + + // commit is not good, global checkpoint is below max assertThat(PeerRecoveryTargetService.getStartingSeqNo(recoveryTarget), equalTo(SequenceNumbersService.UNASSIGNED_SEQ_NO)); - replica.updateGlobalCheckpointOnReplica(maxSeqNo); - replica.getTranslog().sync(); - // commit is enough, global checkpoint is below max + translogLocation.set( + writeTranslog(replica.shardId(), translog.getTranslogUUID(), translog.currentFileGeneration(), maxSeqNo)); + + // commit is good, global checkpoint is above max assertThat(PeerRecoveryTargetService.getStartingSeqNo(recoveryTarget), equalTo(localCheckpoint + 1)); } finally { closeShards(replica); @@ -79,4 +101,23 @@ public void testGetStartingSeqNo() throws Exception { } } + private Path writeTranslog( + final ShardId shardId, + final String translogUUID, + final long generation, + final long globalCheckpoint + ) throws IOException { + final Path tempDir = createTempDir(); + final Path resolve = tempDir.resolve(Translog.getFilename(generation)); + Files.createFile(tempDir.resolve(Translog.CHECKPOINT_FILE_NAME)); + try (TranslogWriter ignored = TranslogWriter.create( + shardId, + translogUUID, + generation, + resolve, + FileChannel::open, + TranslogConfig.DEFAULT_BUFFER_SIZE, () -> globalCheckpoint)) {} + return tempDir; + } + } diff --git a/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java b/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java index 468a5a5500e5c..17497af8838bc 100644 --- a/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java +++ b/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java @@ -180,7 +180,7 @@ public void testSendSnapshotSendsOps() throws IOException { operations.add(new Translog.Index(index, new Engine.IndexResult(1, i - initialNumberOfDocs, true))); } operations.add(null); - int totalOperations = handler.sendSnapshot(startingSeqNo, new Translog.Snapshot() { + RecoverySourceHandler.SendSnapshotResult result = handler.sendSnapshot(startingSeqNo, new Translog.Snapshot() { private int counter = 0; @Override @@ -194,9 +194,9 @@ public Translog.Operation next() throws IOException { } }); if (startingSeqNo == SequenceNumbersService.UNASSIGNED_SEQ_NO) { - assertThat(totalOperations, equalTo(initialNumberOfDocs + numberOfDocsWithValidSequenceNumbers)); + assertThat(result.totalOperations, equalTo(initialNumberOfDocs + numberOfDocsWithValidSequenceNumbers)); } else { - assertThat(totalOperations, equalTo(Math.toIntExact(numberOfDocsWithValidSequenceNumbers - startingSeqNo))); + assertThat(result.totalOperations, equalTo(Math.toIntExact(numberOfDocsWithValidSequenceNumbers - startingSeqNo))); } } @@ -403,8 +403,9 @@ void prepareTargetForTranslog(final int totalTranslogOps) throws IOException { } @Override - void phase2(long startingSeqNo, Translog.Snapshot snapshot) throws IOException { + long phase2(long startingSeqNo, Translog.Snapshot snapshot) throws IOException { phase2Called.set(true); + return SequenceNumbersService.UNASSIGNED_SEQ_NO; } }; @@ -494,8 +495,9 @@ void prepareTargetForTranslog(final int totalTranslogOps) throws IOException { } @Override - void phase2(long startingSeqNo, Translog.Snapshot snapshot) throws IOException { + long phase2(long startingSeqNo, Translog.Snapshot snapshot) throws IOException { phase2Called.set(true); + return SequenceNumbersService.UNASSIGNED_SEQ_NO; } }; From 2ac90b3de96b375e4fb61035eb6679917e7c017d Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Wed, 3 May 2017 13:27:20 +0200 Subject: [PATCH 174/619] Add parsing methods for InternalDateHistogram and InternalHistogram (#24213) --- .../common/xcontent/XContentParserUtils.java | 8 +- .../ParsedMultiBucketAggregation.java | 181 ++++++++++++++++++ .../bucket/histogram/ParsedDateHistogram.java | 80 ++++++++ .../bucket/histogram/ParsedHistogram.java | 73 +++++++ .../elasticsearch/search/suggest/Suggest.java | 1 + .../xcontent/XContentParserUtilsTests.java | 8 +- .../InternalAggregationTestCase.java | 6 + ...nternalMultiBucketAggregationTestCase.java | 142 ++++++++++++++ .../histogram/InternalDateHistogramTests.java | 33 +++- .../histogram/InternalHistogramTests.java | 32 +++- 10 files changed, 536 insertions(+), 28 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedDateHistogram.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedHistogram.java create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/XContentParserUtils.java b/core/src/main/java/org/elasticsearch/common/xcontent/XContentParserUtils.java index 169202e40d7b2..30199afa98cd6 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/XContentParserUtils.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/XContentParserUtils.java @@ -111,10 +111,9 @@ public static Object parseStoredFieldsValue(XContentParser parser) throws IOExce } /** - * This method expects that the current token is a {@code XContentParser.Token.FIELD_NAME} and - * that the current field name is the concatenation of a type, delimiter and name (ex: terms#foo - * where "terms" refers to the type of a registered {@link NamedXContentRegistry.Entry}, "#" is - * the delimiter and "foo" the name of the object to parse). + * This method expects that the current field name is the concatenation of a type, a delimiter and a name + * (ex: terms#foo where "terms" refers to the type of a registered {@link NamedXContentRegistry.Entry}, + * "#" is the delimiter and "foo" the name of the object to parse). * * The method splits the field's name to extract the type and name and then parses the object * using the {@link XContentParser#namedObject(Class, String, Object)} method. @@ -128,7 +127,6 @@ public static Object parseStoredFieldsValue(XContentParser parser) throws IOExce * from the field's name */ public static T parseTypedKeysObject(XContentParser parser, String delimiter, Class objectClass) throws IOException { - ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.currentToken(), parser::getTokenLocation); String currentFieldName = parser.currentName(); if (Strings.hasLength(currentFieldName)) { int position = currentFieldName.indexOf(delimiter); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java new file mode 100644 index 0000000000000..b2823669a75f3 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java @@ -0,0 +1,181 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations; + +import org.elasticsearch.common.CheckedFunction; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParserUtils; +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; + +public abstract class ParsedMultiBucketAggregation extends ParsedAggregation implements MultiBucketsAggregation { + + protected final List> buckets = new ArrayList<>(); + protected boolean keyed; + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + if (keyed) { + builder.startObject(CommonFields.BUCKETS.getPreferredName()); + } else { + builder.startArray(CommonFields.BUCKETS.getPreferredName()); + } + for (ParsedBucket bucket : buckets) { + bucket.toXContent(builder, params); + } + if (keyed) { + builder.endObject(); + } else { + builder.endArray(); + } + return builder; + } + + protected static void declareMultiBucketAggregationFields(final ObjectParser objectParser, + final CheckedFunction, IOException> bucketParser, + final CheckedFunction, IOException> keyedBucketParser) { + declareAggregationFields(objectParser); + objectParser.declareField((parser, aggregation, context) -> { + XContentParser.Token token = parser.currentToken(); + if (token == XContentParser.Token.START_OBJECT) { + aggregation.keyed = true; + while (parser.nextToken() != XContentParser.Token.END_OBJECT) { + aggregation.buckets.add(keyedBucketParser.apply(parser)); + } + } else if (token == XContentParser.Token.START_ARRAY) { + aggregation.keyed = false; + while (parser.nextToken() != XContentParser.Token.END_ARRAY) { + aggregation.buckets.add(bucketParser.apply(parser)); + } + } + }, CommonFields.BUCKETS, ObjectParser.ValueType.OBJECT_ARRAY); + } + + public static class ParsedBucket implements MultiBucketsAggregation.Bucket { + + private Aggregations aggregations; + private T key; + private String keyAsString; + private long docCount; + private boolean keyed; + + protected void setKey(T key) { + this.key = key; + } + + @Override + public Object getKey() { + return key; + } + + protected void setKeyAsString(String keyAsString) { + this.keyAsString = keyAsString; + } + + @Override + public String getKeyAsString() { + return keyAsString; + } + + protected void setDocCount(long docCount) { + this.docCount = docCount; + } + + @Override + public long getDocCount() { + return docCount; + } + + public void setKeyed(boolean keyed) { + this.keyed = keyed; + } + + protected void setAggregations(Aggregations aggregations) { + this.aggregations = aggregations; + } + + @Override + public Aggregations getAggregations() { + return aggregations; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + if (keyed) { + // Subclasses can override the getKeyAsString method to handle specific cases like + // keyed bucket with RAW doc value format where the key_as_string field is not printed + // out but we still need to have a string version of the key to use as the bucket's name. + builder.startObject(getKeyAsString()); + } else { + builder.startObject(); + } + if (keyAsString != null) { + builder.field(CommonFields.KEY_AS_STRING.getPreferredName(), getKeyAsString()); + } + builder.field(CommonFields.KEY.getPreferredName(), key); + builder.field(CommonFields.DOC_COUNT.getPreferredName(), docCount); + aggregations.toXContentInternal(builder, params); + builder.endObject(); + return builder; + } + + protected static > B parseXContent(final XContentParser parser, + final boolean keyed, + final Supplier bucketSupplier, + final CheckedFunction keyParser) + throws IOException { + final B bucket = bucketSupplier.get(); + bucket.setKeyed(keyed); + XContentParser.Token token = parser.currentToken(); + String currentFieldName = parser.currentName(); + if (keyed) { + ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation); + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + } + + List aggregations = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (CommonFields.KEY_AS_STRING.getPreferredName().equals(currentFieldName)) { + bucket.setKeyAsString(parser.text()); + } else if (CommonFields.KEY.getPreferredName().equals(currentFieldName)) { + bucket.setKey(keyParser.apply(parser)); + } else if (CommonFields.DOC_COUNT.getPreferredName().equals(currentFieldName)) { + bucket.setDocCount(parser.longValue()); + } + } else if (token == XContentParser.Token.START_OBJECT) { + aggregations.add(XContentParserUtils.parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Aggregation.class)); + } + } + bucket.setAggregations(new Aggregations(aggregations)); + return bucket; + } + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedDateHistogram.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedDateHistogram.java new file mode 100644 index 0000000000000..27ba2c029d2d5 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedDateHistogram.java @@ -0,0 +1,80 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.histogram; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +public class ParsedDateHistogram extends ParsedMultiBucketAggregation implements Histogram { + + @Override + protected String getType() { + return DateHistogramAggregationBuilder.NAME; + } + + @Override + public List getBuckets() { + return buckets.stream().map(bucket -> (Histogram.Bucket) bucket).collect(Collectors.toList()); + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedDateHistogram.class.getSimpleName(), true, ParsedDateHistogram::new); + static { + declareMultiBucketAggregationFields(PARSER, + parser -> ParsedBucket.fromXContent(parser, false), + parser -> ParsedBucket.fromXContent(parser, true)); + } + + public static ParsedDateHistogram fromXContent(XContentParser parser, String name) throws IOException { + ParsedDateHistogram aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } + + public static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements Histogram.Bucket { + + @Override + public Object getKey() { + return new DateTime(super.getKey(), DateTimeZone.UTC); + } + + @Override + public String getKeyAsString() { + String keyAsString = super.getKeyAsString(); + if (keyAsString != null) { + return keyAsString; + } else { + return DocValueFormat.RAW.format((Long) super.getKey()); + } + } + + static ParsedBucket fromXContent(XContentParser parser, boolean keyed) throws IOException { + return parseXContent(parser, keyed, ParsedBucket::new, XContentParser::longValue); + } + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedHistogram.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedHistogram.java new file mode 100644 index 0000000000000..2b6730df2cc02 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedHistogram.java @@ -0,0 +1,73 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.histogram; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +public class ParsedHistogram extends ParsedMultiBucketAggregation implements Histogram { + + @Override + protected String getType() { + return HistogramAggregationBuilder.NAME; + } + + @Override + public List getBuckets() { + return buckets.stream().map(bucket -> (Histogram.Bucket) bucket).collect(Collectors.toList()); + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedHistogram.class.getSimpleName(), true, ParsedHistogram::new); + static { + declareMultiBucketAggregationFields(PARSER, + parser -> ParsedBucket.fromXContent(parser, false), + parser -> ParsedBucket.fromXContent(parser, true)); + } + + public static ParsedHistogram fromXContent(XContentParser parser, String name) throws IOException { + ParsedHistogram aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } + + static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements Histogram.Bucket { + + @Override + public String getKeyAsString() { + String keyAsString = super.getKeyAsString(); + if (keyAsString != null) { + return keyAsString; + } else { + return DocValueFormat.RAW.format((Double) getKey()); + } + } + + static ParsedBucket fromXContent(XContentParser parser, boolean keyed) throws IOException { + return parseXContent(parser, keyed, ParsedBucket::new, XContentParser::doubleValue); + } + } +} diff --git a/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java b/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java index 73cad2310f848..4af794ab42c60 100644 --- a/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java +++ b/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java @@ -386,6 +386,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @SuppressWarnings("unchecked") public static Suggestion> fromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.currentToken(), parser::getTokenLocation); return XContentParserUtils.parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Suggestion.class); } diff --git a/core/src/test/java/org/elasticsearch/common/xcontent/XContentParserUtilsTests.java b/core/src/test/java/org/elasticsearch/common/xcontent/XContentParserUtilsTests.java index d0426e1e1040e..4b90016eaa4e8 100644 --- a/core/src/test/java/org/elasticsearch/common/xcontent/XContentParserUtilsTests.java +++ b/core/src/test/java/org/elasticsearch/common/xcontent/XContentParserUtilsTests.java @@ -65,12 +65,10 @@ public void testParseTypedKeysObject() throws IOException { BytesReference bytes = toXContent((builder, params) -> builder.field("test", 0), xContentType, randomBoolean()); try (XContentParser parser = xContentType.xContent().createParser(namedXContentRegistry, bytes)) { - parser.nextToken(); - ParsingException e = expectThrows(ParsingException.class, () -> parseTypedKeysObject(parser, delimiter, Boolean.class)); - assertEquals("Failed to parse object: expecting token of type [FIELD_NAME] but found [START_OBJECT]", e.getMessage()); + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.nextToken(), parser::getTokenLocation); - parser.nextToken(); - e = expectThrows(ParsingException.class, () -> parseTypedKeysObject(parser, delimiter, Boolean.class)); + ParsingException e = expectThrows(ParsingException.class, () -> parseTypedKeysObject(parser, delimiter, Boolean.class)); assertEquals("Cannot parse object of class [Boolean] without type information. Set [typed_keys] parameter " + "on the request to ensure the type information is added to the response output", e.getMessage()); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index e99ad8da131fb..782e98e888555 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -34,6 +34,10 @@ import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.SearchModule; +import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram; +import org.elasticsearch.search.aggregations.bucket.histogram.ParsedHistogram; import org.elasticsearch.search.aggregations.metrics.avg.AvgAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.avg.ParsedAvg; import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder; @@ -121,6 +125,8 @@ static List getNamedXContents() { (p, c) -> ParsedExtendedStatsBucket.fromXContent(p, (String) c)); namedXContents.put(GeoBoundsAggregationBuilder.NAME, (p, c) -> ParsedGeoBounds.fromXContent(p, (String) c)); namedXContents.put(GeoCentroidAggregationBuilder.NAME, (p, c) -> ParsedGeoCentroid.fromXContent(p, (String) c)); + namedXContents.put(HistogramAggregationBuilder.NAME, (p, c) -> ParsedHistogram.fromXContent(p, (String) c)); + namedXContents.put(DateHistogramAggregationBuilder.NAME, (p, c) -> ParsedDateHistogram.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java new file mode 100644 index 0000000000000..ae293d5ac3189 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java @@ -0,0 +1,142 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations; + +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.junit.Before; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.emptyMap; + +public abstract class InternalMultiBucketAggregationTestCase + extends InternalAggregationTestCase { + + private boolean hasSubAggregations; + + @Before + public void initHasSubAggregations() { + hasSubAggregations = randomBoolean(); + } + + @Override + protected final T createTestInstance(String name, List pipelineAggregators, Map metaData) { + List internal = new ArrayList<>(); + if (hasSubAggregations) { + final int numAggregations = randomIntBetween(1, 3); + for (int i = 0; i pipelineAggregators, + Map metaData, InternalAggregations aggregations); + + protected abstract Class implementationClass(); + + @Override + protected final void assertFromXContent(T aggregation, ParsedAggregation parsedAggregation) { + assertMultiBucketsAggregation(aggregation, parsedAggregation, false); + } + + public void testIterators() throws IOException { + final T aggregation = createTestInstance(); + assertMultiBucketsAggregation(aggregation, parseAndAssert(aggregation, false), true); + } + + private void assertMultiBucketsAggregation(Aggregation expected, Aggregation actual, boolean checkOrder) { + assertTrue(expected instanceof MultiBucketsAggregation); + MultiBucketsAggregation expectedMultiBucketsAggregation = (MultiBucketsAggregation) expected; + + assertTrue(actual instanceof MultiBucketsAggregation); + MultiBucketsAggregation actualMultiBucketsAggregation = (MultiBucketsAggregation) actual; + + Class parsedClass = implementationClass(); + assertTrue(parsedClass != null && parsedClass.isInstance(actual)); + + assertTrue(expected instanceof InternalAggregation && actual instanceof ParsedAggregation); + assertEquals(expected.getName(), actual.getName()); + assertEquals(expected.getMetaData(), actual.getMetaData()); + assertEquals(((InternalAggregation) expected).getType(), ((ParsedAggregation) actual).getType()); + + List expectedBuckets = expectedMultiBucketsAggregation.getBuckets(); + List actualBuckets = actualMultiBucketsAggregation.getBuckets(); + assertEquals(expectedBuckets.size(), actualBuckets.size()); + + if (checkOrder) { + Iterator expectedIt = expectedBuckets.iterator(); + Iterator actualIt = actualBuckets.iterator(); + while (expectedIt.hasNext()) { + MultiBucketsAggregation.Bucket expectedBucket = expectedIt.next(); + MultiBucketsAggregation.Bucket actualBucket = actualIt.next(); + assertBucket(expectedBucket, actualBucket, true); + } + } else { + for (MultiBucketsAggregation.Bucket expectedBucket : expectedBuckets) { + boolean found = false; + for (MultiBucketsAggregation.Bucket actualBucket : actualBuckets) { + if (actualBucket.getKey().equals(expectedBucket.getKey())) { + found = true; + assertBucket(expectedBucket, actualBucket, false); + break; + } + } + assertTrue("Failed to find bucket with key [" + expectedBucket.getKey() + "]", found); + } + } + } + + private void assertBucket(MultiBucketsAggregation.Bucket expected, MultiBucketsAggregation.Bucket actual, boolean checkOrder) { + assertTrue(expected instanceof InternalMultiBucketAggregation.InternalBucket); + assertTrue(actual instanceof ParsedMultiBucketAggregation.ParsedBucket); + + assertEquals(expected.getKey(), actual.getKey()); + assertEquals(expected.getKeyAsString(), actual.getKeyAsString()); + assertEquals(expected.getDocCount(), actual.getDocCount()); + + Aggregations expectedAggregations = expected.getAggregations(); + Aggregations actualAggregations = actual.getAggregations(); + assertEquals(expectedAggregations.asList().size(), actualAggregations.asList().size()); + + if (checkOrder) { + Iterator expectedIt = expectedAggregations.iterator(); + Iterator actualIt = actualAggregations.iterator(); + + while (expectedIt.hasNext()) { + Aggregation expectedAggregation = expectedIt.next(); + Aggregation actualAggregation = actualIt.next(); + assertMultiBucketsAggregation(expectedAggregation, actualAggregation, true); + } + } else { + for (Aggregation expectedAggregation : expectedAggregations) { + Aggregation actualAggregation = actualAggregations.get(expectedAggregation.getName()); + assertNotNull(actualAggregation); + assertMultiBucketsAggregation(expectedAggregation, actualAggregation, false); + } + } + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java index 40f268e6556e8..c4410713ac8a0 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java @@ -19,14 +19,14 @@ package org.elasticsearch.search.aggregations.bucket.histogram; -import org.apache.lucene.util.TestUtil; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.InternalMultiBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.joda.time.DateTime; +import org.junit.Before; import java.util.ArrayList; import java.util.List; @@ -37,14 +37,22 @@ import static org.elasticsearch.common.unit.TimeValue.timeValueMinutes; import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds; -public class InternalDateHistogramTests extends InternalAggregationTestCase { +public class InternalDateHistogramTests extends InternalMultiBucketAggregationTestCase { - @Override - protected InternalDateHistogram createTestInstance(String name, List pipelineAggregators, - Map metaData) { + private boolean keyed; + private DocValueFormat format; + + @Before + public void init() { + keyed = randomBoolean(); + format = randomNumericDocValueFormat(); + } - boolean keyed = randomBoolean(); - DocValueFormat format = DocValueFormat.RAW; + @Override + protected InternalDateHistogram createTestInstance(String name, + List pipelineAggregators, + Map metaData, + InternalAggregations aggregations) { int nbBuckets = randomInt(10); List buckets = new ArrayList<>(nbBuckets); long startingDate = System.currentTimeMillis(); @@ -54,7 +62,7 @@ protected InternalDateHistogram createTestInstance(String name, List instanceReader() { return InternalDateHistogram::new; } + + @Override + protected Class implementationClass() { + return ParsedDateHistogram.class; + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java index 093496738fd61..bfcfe78ff7d4b 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java @@ -24,30 +24,42 @@ import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.InternalMultiBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.junit.Before; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; -public class InternalHistogramTests extends InternalAggregationTestCase { +public class InternalHistogramTests extends InternalMultiBucketAggregationTestCase { + + private boolean keyed; + private DocValueFormat format; + + @Before + public void init() { + keyed = randomBoolean(); + format = randomNumericDocValueFormat(); + } @Override - protected InternalHistogram createTestInstance(String name, List pipelineAggregators, - Map metaData) { - final boolean keyed = randomBoolean(); - final DocValueFormat format = DocValueFormat.RAW; + protected InternalHistogram createTestInstance(String name, + List pipelineAggregators, + Map metaData, + InternalAggregations aggregations) { final int base = randomInt(50) - 30; final int numBuckets = randomInt(10); final int interval = randomIntBetween(1, 3); List buckets = new ArrayList<>(); for (int i = 0; i < numBuckets; ++i) { final int docCount = TestUtil.nextInt(random(), 1, 50); - buckets.add(new InternalHistogram.Bucket(base + i * interval, docCount, keyed, format, InternalAggregations.EMPTY)); + buckets.add(new InternalHistogram.Bucket(base + i * interval, docCount, keyed, format, aggregations)); } - return new InternalHistogram(name, buckets, (InternalOrder) InternalHistogram.Order.KEY_ASC, - 1, null, format, keyed, pipelineAggregators, metaData); + InternalOrder order = (InternalOrder) randomFrom(InternalHistogram.Order.KEY_ASC, InternalHistogram.Order.KEY_DESC); + return new InternalHistogram(name, buckets, order, 1, null, format, keyed, pipelineAggregators, metaData); } @Override @@ -72,4 +84,8 @@ protected Reader instanceReader() { return InternalHistogram::new; } + @Override + protected Class implementationClass() { + return ParsedHistogram.class; + } } From 79857357bf24bf580270df503363500a603f1033 Mon Sep 17 00:00:00 2001 From: Dimitrios Liappis Date: Wed, 3 May 2017 14:27:31 +0300 Subject: [PATCH 175/619] Docs: Update production notes for Docker Add info about the base image used and the github repo of elasticsearch-docker. Clarify that setting `memlock=-1:-1` is only a requirement when `bootstrap_memory_lock=true` and the alternatives we document elsewhere in docs for disabling swap are valid for Docker as well. Additionally, with latest versions of docker-ce shipping with unlimited (or high enough) defaults for `nofile` and `nproc`, clarify that explicitly setting those per ES container is not required, unless they are not defined in the Docker daemon. Finally simplify production `docker-compose.yml` example by removing unneeded options. Relates #24389 --- .../reference/setup/bootstrap-checks.asciidoc | 1 + docs/reference/setup/install/docker.asciidoc | 30 ++++++++----------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/docs/reference/setup/bootstrap-checks.asciidoc b/docs/reference/setup/bootstrap-checks.asciidoc index 6f32d5054fbdc..e37cd67eb6813 100644 --- a/docs/reference/setup/bootstrap-checks.asciidoc +++ b/docs/reference/setup/bootstrap-checks.asciidoc @@ -99,6 +99,7 @@ that *if* the `bootstrap.memory_lock` setting is enabled, that the JVM was successfully able to lock the heap. To pass the memory lock check, you might have to configure <>. +[[max-number-threads-check]] === Maximum number of threads check Elasticsearch executes requests by breaking the request down into stages diff --git a/docs/reference/setup/install/docker.asciidoc b/docs/reference/setup/install/docker.asciidoc index 95923070e924b..b0db98cfa1557 100644 --- a/docs/reference/setup/install/docker.asciidoc +++ b/docs/reference/setup/install/docker.asciidoc @@ -2,7 +2,8 @@ === Install Elasticsearch with Docker Elasticsearch is also available as a Docker image. -The image is built with {xpack}/index.html[X-Pack]. +The image is built with {xpack}/index.html[X-Pack] and uses https://hub.docker.com/_/centos/[centos:7] as the base image. +The source code can be found on https://github.com/elastic/elasticsearch-docker/tree/{branch}[GitHub]. ==== Security note @@ -153,12 +154,7 @@ services: memlock: soft: -1 hard: -1 - nofile: - soft: 65536 - hard: 65536 mem_limit: 1g - cap_add: - - IPC_LOCK volumes: - esdata1:/usr/share/elasticsearch/data ports: @@ -176,12 +172,7 @@ services: memlock: soft: -1 hard: -1 - nofile: - soft: 65536 - hard: 65536 mem_limit: 1g - cap_add: - - IPC_LOCK volumes: - esdata2:/usr/share/elasticsearch/data networks: @@ -195,7 +186,6 @@ volumes: networks: esnet: - driver: bridge -------------------------------------------- endif::[] @@ -273,15 +263,19 @@ We have collected a number of best practices for production use. NOTE: Any Docker parameters mentioned below assume the use of `docker run`. -. Elasticsearch inside the container runs as user `elasticsearch` using uid:gid `1000:1000`. If you are bind mounting a local directory or file, ensure it is readable by this user while the https://www.elastic.co/guide/en/elasticsearch/reference/current/important-settings.html#path-settings[data and log dirs] additionally require write access. - -. It is important to correctly set capabilities and ulimits via the Docker CLI. As seen earlier in the example <>, the following options are required: +. Elasticsearch runs inside the container as user `elasticsearch` using uid:gid `1000:1000`. If you are bind-mounting a local directory or file, ensure it is readable by this user, while the <> additionally require write access. ++ +. It is important to ensure increased ulimits for <> and <> are available for the Elasticsearch containers. Verify the https://github.com/moby/moby/tree/ea4d1243953e6b652082305a9c3cda8656edab26/contrib/init[init system] for the Docker daemon is already setting those to acceptable values and, if needed, adjust them in the Daemon, or override them per container, for example using `docker run`: ++ + --ulimit nofile=65536:65536 ++ +NOTE: One way of checking the Docker daemon defaults for the aforementioned ulimits is by running: + - --cap-add=IPC_LOCK --ulimit memlock=-1:-1 --ulimit nofile=65536:65536 + docker run --rm centos:7 /bin/bash -c 'ulimit -Hn && ulimit -Sn && ulimit -Hu && ulimit -Su' + -. Ensure `bootstrap.memory_lock` is set to `true` as explained in "<>". +. Swapping needs to be disabled for performance and node stability. This can be achieved through any of the methods mentioned in the <>. If you opt for the `boostrap.memory_lock: true` approach, apart from defining it through any of the <>, you will additionally need the `memlock: true` ulimit, either defined in the https://docs.docker.com/engine/reference/commandline/dockerd/#default-ulimits[Docker Daemon] or specifically set for the container. This has been demonstrated earlier in the <>, or using `docker run`: + -This can be achieved through any of the <>, e.g. by setting the appropriate environments variable with `-e "bootstrap.memory_lock=true"`. + -e "bootstrap_memory_lock=true" --ulimit memlock=-1:-1 + . The image https://docs.docker.com/engine/reference/builder/#/expose[exposes] TCP ports 9200 and 9300. For clusters it is recommended to randomize the published ports with `--publish-all`, unless you are pinning one container per host. + From a45e2efa00d18eccd03a54770a6d7b4aff187515 Mon Sep 17 00:00:00 2001 From: javanna Date: Wed, 3 May 2017 15:00:30 +0200 Subject: [PATCH 176/619] fix typo in migrate_6_0/java.asciidoc --- docs/reference/migration/migrate_6_0/java.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/migration/migrate_6_0/java.asciidoc b/docs/reference/migration/migrate_6_0/java.asciidoc index b82a46501e2c7..5693d50852649 100644 --- a/docs/reference/migration/migrate_6_0/java.asciidoc +++ b/docs/reference/migration/migrate_6_0/java.asciidoc @@ -16,7 +16,7 @@ as a result. From version 6.0.0, a `DeleteByQueryRequest` requires an explicit q ==== `InternalStats` and `Stats` getCountAsString() method removed -The `count` value in the stats aggregation represents a doc count that shouldnn't require a formatted +The `count` value in the stats aggregation represents a doc count that shouldn't require a formatted version. This method was deprecated in 5.4 in favour of just using `String.valueOf(getCount())` if needed From 16af0a9ce23abbe2efce8ea950107b3e8bc3c0f8 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Wed, 3 May 2017 08:25:14 -0400 Subject: [PATCH 177/619] Remove unnecessary field from UnicastZenPing This field was only ever used in the constructor, where it was set and then passed around. As such, there's no need for it to be a field and we can remove it. --- .../java/org/elasticsearch/discovery/zen/UnicastZenPing.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/UnicastZenPing.java b/core/src/main/java/org/elasticsearch/discovery/zen/UnicastZenPing.java index f6f5bafa275ec..efb537e752051 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/UnicastZenPing.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/UnicastZenPing.java @@ -111,8 +111,6 @@ public class UnicastZenPing extends AbstractComponent implements ZenPing { private final TransportService transportService; private final ClusterName clusterName; - private final int concurrentConnects; - private final List configuredHosts; private final int limitPortCounts; @@ -145,7 +143,7 @@ public UnicastZenPing(Settings settings, ThreadPool threadPool, TransportService this.clusterName = ClusterName.CLUSTER_NAME_SETTING.get(settings); this.hostsProvider = unicastHostsProvider; - this.concurrentConnects = DISCOVERY_ZEN_PING_UNICAST_CONCURRENT_CONNECTS_SETTING.get(settings); + final int concurrentConnects = DISCOVERY_ZEN_PING_UNICAST_CONCURRENT_CONNECTS_SETTING.get(settings); if (DISCOVERY_ZEN_PING_UNICAST_HOSTS_SETTING.exists(settings)) { configuredHosts = DISCOVERY_ZEN_PING_UNICAST_HOSTS_SETTING.get(settings); // we only limit to 1 addresses, makes no sense to ping 100 ports From 144f96eaeb57ef27aace0d90556a595b30cb98dd Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 3 May 2017 16:22:26 +0200 Subject: [PATCH 178/619] Open/Close index api to allow_no_indices by default (#24401) Open/Close index api have allow_no_indices set to false by default, while delete index has it set to true. The flag controls where a wildcard expression that matches no indices will be ignored or an error will be thrown instead. This commit aligns open/close default behaviour to that of delete index. --- .../indices/close/CloseIndexRequest.java | 2 +- .../admin/indices/open/OpenIndexRequest.java | 2 +- .../indices/IndicesOptionsIntegrationIT.java | 26 +++++++++++--- .../indices/state/OpenCloseIndexIT.java | 35 ------------------- .../migration/migrate_6_0/indices.asciidoc | 7 ++++ 5 files changed, 30 insertions(+), 42 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/close/CloseIndexRequest.java b/core/src/main/java/org/elasticsearch/action/admin/indices/close/CloseIndexRequest.java index 092a65f9293f0..df0dcd9ff54a5 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/close/CloseIndexRequest.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/close/CloseIndexRequest.java @@ -37,7 +37,7 @@ public class CloseIndexRequest extends AcknowledgedRequest implements IndicesRequest.Replaceable { private String[] indices; - private IndicesOptions indicesOptions = IndicesOptions.fromOptions(false, false, true, false); + private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpen(); public CloseIndexRequest() { } diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/open/OpenIndexRequest.java b/core/src/main/java/org/elasticsearch/action/admin/indices/open/OpenIndexRequest.java index 3f3b5f5c31c8e..06affe8ee69a3 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/open/OpenIndexRequest.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/open/OpenIndexRequest.java @@ -37,7 +37,7 @@ public class OpenIndexRequest extends AcknowledgedRequest implements IndicesRequest.Replaceable { private String[] indices; - private IndicesOptions indicesOptions = IndicesOptions.fromOptions(false, false, false, true); + private IndicesOptions indicesOptions = IndicesOptions.fromOptions(false, true, false, true); public OpenIndexRequest() { } diff --git a/core/src/test/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java b/core/src/test/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java index ce2b5088ac994..b776b13dae9c4 100644 --- a/core/src/test/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java +++ b/core/src/test/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java @@ -474,20 +474,36 @@ public void testCloseApiSpecifiedIndices() throws Exception { verify(search("t*"), false); } - public void testCloseApiWildcards() throws Exception { + public void testOpenCloseApiWildcards() throws Exception { createIndex("foo", "foobar", "bar", "barbaz"); ensureGreen(); + // if there are no indices to open/close and allow_no_indices=true (default), the open/close is a no-op + verify(client().admin().indices().prepareClose("bar*"), false); verify(client().admin().indices().prepareClose("bar*"), false); - verify(client().admin().indices().prepareClose("bar*"), true); verify(client().admin().indices().prepareClose("foo*"), false); - verify(client().admin().indices().prepareClose("foo*"), true); - verify(client().admin().indices().prepareClose("_all"), true); + verify(client().admin().indices().prepareClose("foo*"), false); + verify(client().admin().indices().prepareClose("_all"), false); verify(client().admin().indices().prepareOpen("bar*"), false); verify(client().admin().indices().prepareOpen("_all"), false); - verify(client().admin().indices().prepareOpen("_all"), true); + verify(client().admin().indices().prepareOpen("_all"), false); + + // if there are no indices to open/close throw an exception + IndicesOptions openIndicesOptions = IndicesOptions.fromOptions(false, false, false, true); + IndicesOptions closeIndicesOptions = IndicesOptions.fromOptions(false, false, true, false); + + verify(client().admin().indices().prepareClose("bar*").setIndicesOptions(closeIndicesOptions), false); + verify(client().admin().indices().prepareClose("bar*").setIndicesOptions(closeIndicesOptions), true); + + verify(client().admin().indices().prepareClose("foo*").setIndicesOptions(closeIndicesOptions), false); + verify(client().admin().indices().prepareClose("foo*").setIndicesOptions(closeIndicesOptions), true); + verify(client().admin().indices().prepareClose("_all").setIndicesOptions(closeIndicesOptions), true); + + verify(client().admin().indices().prepareOpen("bar*").setIndicesOptions(openIndicesOptions), false); + verify(client().admin().indices().prepareOpen("_all").setIndicesOptions(openIndicesOptions), false); + verify(client().admin().indices().prepareOpen("_all").setIndicesOptions(openIndicesOptions), true); } public void testDeleteIndex() throws Exception { diff --git a/core/src/test/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java b/core/src/test/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java index e94cdd75acb88..8dbaaf3e9477a 100644 --- a/core/src/test/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java +++ b/core/src/test/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java @@ -191,41 +191,6 @@ public void testCloseOpenAllWildcard() { assertIndexIsOpened("test1", "test2", "test3"); } - // if there are no indices to open/close throw an exception - public void testOpenCloseWildCardsNoIndicesDefault() { - expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareOpen("test").execute().actionGet()); - expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareClose("test").execute().actionGet()); - - expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareOpen("test*").execute().actionGet()); - expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareClose("test*").execute().actionGet()); - - expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareOpen("*").execute().actionGet()); - expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareClose("*").execute().actionGet()); - - expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareOpen("_all").execute().actionGet()); - expectThrows(IndexNotFoundException.class, () -> client().admin().indices().prepareClose("_all").execute().actionGet()); - } - - // if there are no indices to open/close and allow_no_indices=true, the open/close is a no-op - public void testOpenCloseWildCardsNoIndicesAllowNoIndices() throws InterruptedException, ExecutionException { - IndicesOptions openIndicesOptions = IndicesOptions.fromOptions(false, true, false, true); - IndicesOptions closeIndicesOptions = IndicesOptions.fromOptions(false, true, true, false); - - expectThrows(IndexNotFoundException.class, - () -> client().admin().indices().prepareOpen("test").setIndicesOptions(openIndicesOptions).execute().actionGet()); - expectThrows(IndexNotFoundException.class, - () -> client().admin().indices().prepareClose("test").setIndicesOptions(closeIndicesOptions).execute().actionGet()); - - assertAcked(client().admin().indices().prepareOpen("test*").setIndicesOptions(openIndicesOptions).execute().get()); - assertAcked(client().admin().indices().prepareClose("test*").setIndicesOptions(closeIndicesOptions).execute().get()); - - assertAcked(client().admin().indices().prepareOpen("*").setIndicesOptions(openIndicesOptions).execute().get()); - assertAcked(client().admin().indices().prepareClose("*").setIndicesOptions(closeIndicesOptions).execute().get()); - - assertAcked(client().admin().indices().prepareOpen("_all").setIndicesOptions(openIndicesOptions).execute().get()); - assertAcked(client().admin().indices().prepareClose("_all").setIndicesOptions(closeIndicesOptions).execute().get()); - } - public void testCloseNoIndex() { Client client = client(); Exception e = expectThrows(ActionRequestValidationException.class, () -> diff --git a/docs/reference/migration/migrate_6_0/indices.asciidoc b/docs/reference/migration/migrate_6_0/indices.asciidoc index 2e198be59cb9e..a1d1ffd578d32 100644 --- a/docs/reference/migration/migrate_6_0/indices.asciidoc +++ b/docs/reference/migration/migrate_6_0/indices.asciidoc @@ -37,3 +37,10 @@ following settings: - `index.shared_filesystem` - `index.shadow_replicas` - `node.add_lock_id_to_custom_path` + +=== Open/Close index API allows wildcard expressions that match no indices by default + +The default value of the `allow_no_indices` option for the Open/Close index API +has been changed from `false` to `true` so it is aligned with the behaviour of the +Delete index API. As a result, Open/Close index API don't return an error by +default when a provided wildcard expression doesn't match any closed/open index. From 7ecb79a8e1e087fd92324ad1efb09d363b39587f Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Wed, 3 May 2017 16:29:51 +0200 Subject: [PATCH 179/619] Remove DiscoveryNodesProvider interface (#24461) The DiscoveryNodesProvider interface provides an unnecessary abstraction and is just used in conjunction with the existing PingContextProvider interface. This commit removes it. --- .../discovery/zen/DiscoveryNodesProvider.java | 28 --- .../discovery/zen/PingContextProvider.java | 4 +- .../discovery/zen/UnicastZenPing.java | 16 +- .../discovery/zen/ZenDiscovery.java | 14 +- .../elasticsearch/discovery/zen/ZenPing.java | 2 +- .../single/SingleNodeDiscoveryIT.java | 24 +-- .../zen/PublishClusterStateActionTests.java | 3 +- .../discovery/zen/UnicastZenPingTests.java | 167 +++++------------- 8 files changed, 67 insertions(+), 191 deletions(-) delete mode 100644 core/src/main/java/org/elasticsearch/discovery/zen/DiscoveryNodesProvider.java diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/DiscoveryNodesProvider.java b/core/src/main/java/org/elasticsearch/discovery/zen/DiscoveryNodesProvider.java deleted file mode 100644 index 247839397e095..0000000000000 --- a/core/src/main/java/org/elasticsearch/discovery/zen/DiscoveryNodesProvider.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.discovery.zen; - -import org.elasticsearch.cluster.node.DiscoveryNodes; - -public interface DiscoveryNodesProvider { - - DiscoveryNodes nodes(); - -} diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/PingContextProvider.java b/core/src/main/java/org/elasticsearch/discovery/zen/PingContextProvider.java index b705c91839202..7567b69cfe459 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/PingContextProvider.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/PingContextProvider.java @@ -20,11 +20,9 @@ package org.elasticsearch.discovery.zen; import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.discovery.zen.DiscoveryNodesProvider; -public interface PingContextProvider extends DiscoveryNodesProvider { +public interface PingContextProvider { /** return the current cluster state of the node */ ClusterState clusterState(); - } diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/UnicastZenPing.java b/core/src/main/java/org/elasticsearch/discovery/zen/UnicastZenPing.java index efb537e752051..3a68b2b4cd8a3 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/UnicastZenPing.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/UnicastZenPing.java @@ -27,6 +27,7 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.Version; import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.common.component.AbstractComponent; @@ -306,7 +307,7 @@ protected void ping(final Consumer resultsConsumer, throw new RuntimeException(e); } seedNodes.addAll(hostsProvider.buildDynamicNodes()); - final DiscoveryNodes nodes = contextProvider.nodes(); + final DiscoveryNodes nodes = contextProvider.clusterState().nodes(); // add all possible master nodes that were active in the last known cluster configuration for (ObjectCursor masterNode : nodes.getMasterNodes().values()) { seedNodes.add(masterNode.value); @@ -457,9 +458,9 @@ protected void sendPings(final TimeValue timeout, final PingingRound pingingRoun final UnicastPingRequest pingRequest = new UnicastPingRequest(); pingRequest.id = pingingRound.id(); pingRequest.timeout = timeout; - DiscoveryNodes discoNodes = contextProvider.nodes(); + ClusterState lastState = contextProvider.clusterState(); - pingRequest.pingResponse = createPingResponse(discoNodes); + pingRequest.pingResponse = createPingResponse(lastState); Set nodesFromResponses = temporalResponses.stream().map(pingResponse -> { assert clusterName.equals(pingResponse.clusterName()) : @@ -476,7 +477,7 @@ protected void sendPings(final TimeValue timeout, final PingingRound pingingRoun // resolve what we can via the latest cluster state final Set nodesToPing = uniqueNodesByAddress.values().stream() .map(node -> { - DiscoveryNode foundNode = discoNodes.findByAddress(node.getAddress()); + DiscoveryNode foundNode = lastState.nodes().findByAddress(node.getAddress()); if (foundNode == null) { return node; } else { @@ -594,7 +595,7 @@ private UnicastPingResponse handlePingRequest(final UnicastPingRequest request) () -> temporalResponses.remove(request.pingResponse)); List pingResponses = CollectionUtils.iterableAsArrayList(temporalResponses); - pingResponses.add(createPingResponse(contextProvider.nodes())); + pingResponses.add(createPingResponse(contextProvider.clusterState())); UnicastPingResponse unicastPingResponse = new UnicastPingResponse(); unicastPingResponse.id = request.id; @@ -647,8 +648,9 @@ public void writeTo(StreamOutput out) throws IOException { } } - private PingResponse createPingResponse(DiscoveryNodes discoNodes) { - return new PingResponse(discoNodes.getLocalNode(), discoNodes.getMasterNode(), contextProvider.clusterState()); + private PingResponse createPingResponse(ClusterState clusterState) { + DiscoveryNodes discoNodes = clusterState.nodes(); + return new PingResponse(discoNodes.getLocalNode(), discoNodes.getMasterNode(), clusterState); } static class UnicastPingResponse extends TransportResponse { diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java index af32afb99c984..fe94cea597dbb 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java @@ -258,7 +258,7 @@ protected void doStop() { masterFD.stop("zen disco stop"); nodesFD.stop(); Releasables.close(zenPing); // stop any ongoing pinging - DiscoveryNodes nodes = nodes(); + DiscoveryNodes nodes = clusterState().nodes(); if (sendLeaveRequest) { if (nodes.getMasterNode() == null) { // if we don't know who the master is, nothing to do here @@ -290,12 +290,6 @@ protected void doClose() throws IOException { IOUtils.close(masterFD, nodesFD); } - /** start of {@link PingContextProvider } implementation */ - @Override - public DiscoveryNodes nodes() { - return clusterState().nodes(); - } - @Override public ClusterState clusterState() { ClusterState clusterState = state.get(); @@ -303,8 +297,6 @@ public ClusterState clusterState() { return clusterState; } - /** end of {@link PingContextProvider } implementation */ - @Override public void publish(ClusterChangedEvent clusterChangedEvent, AckListener ackListener) { ClusterState newState = clusterChangedEvent.state(); @@ -677,7 +669,7 @@ private void handleLeaveRequest(final DiscoveryNode node) { } if (localNodeMaster()) { removeNode(node, "zen-disco-node-left", "left"); - } else if (node.equals(nodes().getMasterNode())) { + } else if (node.equals(clusterState().nodes().getMasterNode())) { handleMasterGone(node, null, "shut_down"); } } @@ -1041,7 +1033,7 @@ protected void rejoin(String reason) { } private boolean localNodeMaster() { - return nodes().isLocalNodeElectedMaster(); + return clusterState().nodes().isLocalNodeElectedMaster(); } private void handleAnotherMaster(ClusterState localClusterState, final DiscoveryNode otherMaster, long otherClusterStateVersion, String reason) { diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/ZenPing.java b/core/src/main/java/org/elasticsearch/discovery/zen/ZenPing.java index 622c4649db228..016d2a5423ce1 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/ZenPing.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/ZenPing.java @@ -72,7 +72,7 @@ private PingResponse() { * @param clusterStateVersion the current cluster state version of that node * ({@link ElectMasterService.MasterCandidate#UNRECOVERED_CLUSTER_VERSION} for not recovered) */ - public PingResponse(DiscoveryNode node, DiscoveryNode master, ClusterName clusterName, long clusterStateVersion) { + PingResponse(DiscoveryNode node, DiscoveryNode master, ClusterName clusterName, long clusterStateVersion) { this.id = idGenerator.incrementAndGet(); this.node = node; this.master = master; diff --git a/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java b/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java index 1b529bf5bd0d9..d2a520c32846e 100644 --- a/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java +++ b/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java @@ -93,26 +93,14 @@ protected void finishPingingRound(PingingRound pingingRound) { super.finishPingingRound(pingingRound); } }; - final DiscoveryNodes nodes = - DiscoveryNodes.builder().add(pingTransport.getLocalNode()).build(); + final DiscoveryNodes nodes = DiscoveryNodes.builder() + .add(nodeTransport.getLocalNode()) + .add(pingTransport.getLocalNode()) + .localNodeId(pingTransport.getLocalNode().getId()) + .build(); final ClusterName clusterName = new ClusterName(internalCluster().getClusterName()); final ClusterState state = ClusterState.builder(clusterName).nodes(nodes).build(); - unicastZenPing.start(new PingContextProvider() { - @Override - public ClusterState clusterState() { - return state; - } - - @Override - public DiscoveryNodes nodes() { - return DiscoveryNodes - .builder() - .add(nodeTransport.getLocalNode()) - .add(pingTransport.getLocalNode()) - .localNodeId(pingTransport.getLocalNode().getId()) - .build(); - } - }); + unicastZenPing.start(() -> state); closeables.push(unicastZenPing); final CompletableFuture responses = new CompletableFuture<>(); unicastZenPing.ping(responses::complete, TimeValue.timeValueSeconds(3)); diff --git a/core/src/test/java/org/elasticsearch/discovery/zen/PublishClusterStateActionTests.java b/core/src/test/java/org/elasticsearch/discovery/zen/PublishClusterStateActionTests.java index 3e90f414760dd..863bf80085bb2 100644 --- a/core/src/test/java/org/elasticsearch/discovery/zen/PublishClusterStateActionTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/zen/PublishClusterStateActionTests.java @@ -90,7 +90,7 @@ public class PublishClusterStateActionTests extends ESTestCase { protected ThreadPool threadPool; protected Map nodes = new HashMap<>(); - public static class MockNode implements PublishClusterStateAction.NewPendingClusterStateListener, DiscoveryNodesProvider { + public static class MockNode implements PublishClusterStateAction.NewPendingClusterStateListener { public final DiscoveryNode discoveryNode; public final MockTransportService service; public MockPublishAction action; @@ -142,7 +142,6 @@ public void onNewClusterState(String reason) { action.pendingStatesQueue().markAsProcessed(newClusterState); } - @Override public DiscoveryNodes nodes() { return clusterState.nodes(); } diff --git a/core/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java b/core/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java index 5df6bd214f318..fa53f94f42cab 100644 --- a/core/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java @@ -189,31 +189,18 @@ public void connectToNode(DiscoveryNode node, ConnectionProfile connectionProfil Settings hostsSettingsMismatch = Settings.builder().put(hostsSettings).put(settingsMismatch).build(); TestUnicastZenPing zenPingA = new TestUnicastZenPing(hostsSettings, threadPool, handleA, EMPTY_HOSTS_PROVIDER); - zenPingA.start(new PingContextProvider() { - @Override - public DiscoveryNodes nodes() { - return DiscoveryNodes.builder().add(handleA.node).localNodeId("UZP_A").build(); - } - - @Override - public ClusterState clusterState() { - return ClusterState.builder(state).blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)).build(); - } - }); + ClusterState stateA = ClusterState.builder(state) + .blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)) + .nodes(DiscoveryNodes.builder().add(handleA.node).localNodeId("UZP_A")) + .build(); + zenPingA.start(() -> stateA); closeables.push(zenPingA); TestUnicastZenPing zenPingB = new TestUnicastZenPing(hostsSettings, threadPool, handleB, EMPTY_HOSTS_PROVIDER); - zenPingB.start(new PingContextProvider() { - @Override - public DiscoveryNodes nodes() { - return DiscoveryNodes.builder().add(handleB.node).localNodeId("UZP_B").build(); - } - - @Override - public ClusterState clusterState() { - return state; - } - }); + ClusterState stateB = ClusterState.builder(state) + .nodes(DiscoveryNodes.builder().add(handleB.node).localNodeId("UZP_B")) + .build(); + zenPingB.start(() -> stateB); closeables.push(zenPingB); TestUnicastZenPing zenPingC = new TestUnicastZenPing(hostsSettingsMismatch, threadPool, handleC, @@ -223,32 +210,18 @@ protected Version getVersion() { return versionD; } }; - zenPingC.start(new PingContextProvider() { - @Override - public DiscoveryNodes nodes() { - return DiscoveryNodes.builder().add(handleC.node).localNodeId("UZP_C").build(); - } - - @Override - public ClusterState clusterState() { - return stateMismatch; - } - }); + ClusterState stateC = ClusterState.builder(stateMismatch) + .nodes(DiscoveryNodes.builder().add(handleC.node).localNodeId("UZP_C")) + .build(); + zenPingC.start(() -> stateC); closeables.push(zenPingC); TestUnicastZenPing zenPingD = new TestUnicastZenPing(hostsSettingsMismatch, threadPool, handleD, EMPTY_HOSTS_PROVIDER); - zenPingD.start(new PingContextProvider() { - @Override - public DiscoveryNodes nodes() { - return DiscoveryNodes.builder().add(handleD.node).localNodeId("UZP_D").build(); - } - - @Override - public ClusterState clusterState() { - return stateMismatch; - } - }); + ClusterState stateD = ClusterState.builder(stateMismatch) + .nodes(DiscoveryNodes.builder().add(handleD.node).localNodeId("UZP_D")) + .build(); + zenPingD.start(() -> stateD); closeables.push(zenPingD); logger.info("ping from UZP_A"); @@ -339,45 +312,25 @@ public TransportAddress[] addressesFromString(String address, int perAddressLimi final ClusterState state = ClusterState.builder(new ClusterName("test")).version(randomNonNegativeLong()).build(); final TestUnicastZenPing zenPingA = new TestUnicastZenPing(hostsSettings, threadPool, handleA, EMPTY_HOSTS_PROVIDER); - zenPingA.start(new PingContextProvider() { - @Override - public DiscoveryNodes nodes() { - return DiscoveryNodes.builder().add(handleA.node).localNodeId("UZP_A").build(); - } - - @Override - public ClusterState clusterState() { - return ClusterState.builder(state).blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)).build(); - } - }); + ClusterState stateA = ClusterState.builder(state) + .blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)) + .nodes(DiscoveryNodes.builder().add(handleA.node).localNodeId("UZP_A")) + .build(); + zenPingA.start(() -> stateA); closeables.push(zenPingA); TestUnicastZenPing zenPingB = new TestUnicastZenPing(hostsSettings, threadPool, handleB, EMPTY_HOSTS_PROVIDER); - zenPingB.start(new PingContextProvider() { - @Override - public DiscoveryNodes nodes() { - return DiscoveryNodes.builder().add(handleB.node).localNodeId("UZP_B").build(); - } - - @Override - public ClusterState clusterState() { - return state; - } - }); + ClusterState stateB = ClusterState.builder(state) + .nodes(DiscoveryNodes.builder().add(handleB.node).localNodeId("UZP_B")) + .build(); + zenPingB.start(() -> stateB); closeables.push(zenPingB); TestUnicastZenPing zenPingC = new TestUnicastZenPing(hostsSettings, threadPool, handleC, EMPTY_HOSTS_PROVIDER); - zenPingC.start(new PingContextProvider() { - @Override - public DiscoveryNodes nodes() { - return DiscoveryNodes.builder().add(handleC.node).localNodeId("UZP_C").build(); - } - - @Override - public ClusterState clusterState() { - return state; - } - }); + ClusterState stateC = ClusterState.builder(state) + .nodes(DiscoveryNodes.builder().add(handleC.node).localNodeId("UZP_C")) + .build(); + zenPingC.start(() -> stateC); closeables.push(zenPingC); // the presence of an unresolvable host should not prevent resolvable hosts from being pinged @@ -657,31 +610,18 @@ public void onConnectionOpened(DiscoveryNode node) { }); final TestUnicastZenPing zenPingA = new TestUnicastZenPing(hostsSettings, threadPool, handleA, EMPTY_HOSTS_PROVIDER); - zenPingA.start(new PingContextProvider() { - @Override - public DiscoveryNodes nodes() { - return DiscoveryNodes.builder().add(handleA.node).add(handleB.node).localNodeId("UZP_A").build(); - } - - @Override - public ClusterState clusterState() { - return ClusterState.builder(state).blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)).build(); - } - }); + final ClusterState stateA = ClusterState.builder(state) + .blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)) + .nodes(DiscoveryNodes.builder().add(handleA.node).add(handleB.node).localNodeId("UZP_A")) + .build(); + zenPingA.start(() -> stateA); closeables.push(zenPingA); TestUnicastZenPing zenPingB = new TestUnicastZenPing(hostsSettings, threadPool, handleB, EMPTY_HOSTS_PROVIDER); - zenPingB.start(new PingContextProvider() { - @Override - public DiscoveryNodes nodes() { - return DiscoveryNodes.builder().add(handleB.node).localNodeId("UZP_B").build(); - } - - @Override - public ClusterState clusterState() { - return state; - } - }); + final ClusterState stateB = ClusterState.builder(state) + .nodes(DiscoveryNodes.builder().add(handleB.node).localNodeId("UZP_B")) + .build(); + zenPingB.start(() -> stateB); closeables.push(zenPingB); Collection pingResponses = zenPingA.pingAndWait().toList(); @@ -716,34 +656,19 @@ public void testPingingTemporalPings() throws ExecutionException, InterruptedExc .put("discovery.zen.ping.unicast.hosts", (String) null) // use nodes for simplicity .build(); final ClusterState state = ClusterState.builder(new ClusterName("test")).version(randomNonNegativeLong()).build(); + final ClusterState stateA = ClusterState.builder(state) + .blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)) + .nodes(DiscoveryNodes.builder().add(handleA.node).add(handleB.node).localNodeId("UZP_A")).build(); final TestUnicastZenPing zenPingA = new TestUnicastZenPing(hostsSettings, threadPool, handleA, EMPTY_HOSTS_PROVIDER); - zenPingA.start(new PingContextProvider() { - @Override - public DiscoveryNodes nodes() { - return DiscoveryNodes.builder().add(handleA.node).add(handleB.node).localNodeId("UZP_A").build(); - } - - @Override - public ClusterState clusterState() { - return ClusterState.builder(state).blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)).build(); - } - }); + zenPingA.start(() -> stateA); closeables.push(zenPingA); // Node B doesn't know about A! + final ClusterState stateB = ClusterState.builder(state).nodes( + DiscoveryNodes.builder().add(handleB.node).localNodeId("UZP_B")).build(); TestUnicastZenPing zenPingB = new TestUnicastZenPing(hostsSettings, threadPool, handleB, EMPTY_HOSTS_PROVIDER); - zenPingB.start(new PingContextProvider() { - @Override - public DiscoveryNodes nodes() { - return DiscoveryNodes.builder().add(handleB.node).localNodeId("UZP_B").build(); - } - - @Override - public ClusterState clusterState() { - return state; - } - }); + zenPingB.start(() -> stateB); closeables.push(zenPingB); { From 7311aaa2eb6b67d9bce873728b60c251682b5ba3 Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Wed, 3 May 2017 16:44:14 +0200 Subject: [PATCH 180/619] Fix PercolatorQuerySearchIT to not create multiple types. --- .../percolator/PercolatorQuerySearchIT.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java index 382fbcb3cf7be..40dee843e93b3 100644 --- a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java +++ b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java @@ -613,6 +613,7 @@ public void testPercolateQueryWithNestedDocuments() throws Exception { public void testPercolateQueryWithNestedDocuments_doNotLeakBitsetCacheEntries() throws Exception { XContentBuilder mapping = XContentFactory.jsonBuilder(); mapping.startObject().startObject("properties").startObject("companyname").field("type", "text").endObject() + .startObject("query").field("type", "percolator").endObject() .startObject("employee").field("type", "nested").startObject("properties") .startObject("name").field("type", "text").endObject().endObject().endObject().endObject() .endObject(); @@ -620,9 +621,8 @@ public void testPercolateQueryWithNestedDocuments_doNotLeakBitsetCacheEntries() // to avoid normal document from being cached by BitsetFilterCache .setSettings(Settings.builder().put(BitsetFilterCache.INDEX_LOAD_RANDOM_ACCESS_FILTERS_EAGERLY_SETTING.getKey(), false)) .addMapping("employee", mapping) - .addMapping("queries", "query", "type=percolator") ); - client().prepareIndex("test", "queries", "q1").setSource(jsonBuilder().startObject() + client().prepareIndex("test", "employee", "q1").setSource(jsonBuilder().startObject() .field("query", QueryBuilders.nestedQuery("employee", QueryBuilders.matchQuery("employee.name", "virginia potts").operator(Operator.AND), ScoreMode.Avg) ).endObject()) @@ -659,6 +659,11 @@ public void testPercolateQueryWithNestedDocuments_doLeakFieldDataCacheEntries() mapping.startObject(); { mapping.startObject("properties"); + { + mapping.startObject("query"); + mapping.field("type", "percolator"); + mapping.endObject(); + } { mapping.startObject("companyname"); mapping.field("type", "text"); @@ -684,10 +689,9 @@ public void testPercolateQueryWithNestedDocuments_doLeakFieldDataCacheEntries() mapping.endObject(); createIndex("test", client().admin().indices().prepareCreate("test") .addMapping("employee", mapping) - .addMapping("queries", "query", "type=percolator") ); Script script = new Script(ScriptType.INLINE, MockScriptPlugin.NAME, "use_fielddata_please", Collections.emptyMap()); - client().prepareIndex("test", "queries", "q1").setSource(jsonBuilder().startObject() + client().prepareIndex("test", "employee", "q1").setSource(jsonBuilder().startObject() .field("query", QueryBuilders.nestedQuery("employees", QueryBuilders.scriptQuery(script), ScoreMode.Avg) ).endObject()).get(); From 855b64b0ee23f537efd7073c1e49d17e52b5ce93 Mon Sep 17 00:00:00 2001 From: Tim Brooks Date: Wed, 3 May 2017 10:30:54 -0500 Subject: [PATCH 181/619] Add non-dispatching listenable action future (#24412) Currently the only implementation of `ListenableActionFuture` requires dispatching listener execution to a thread pool. This commit renames that variant to `DispatchingListenableActionFuture` and converts `AbstractListenableActionFuture` to be a variant that does not require dispatching. That class is now named `PlainListenableActionFuture`. --- .../AbstractListenableActionFuture.java | 106 ---------------- .../support/PlainListenableActionFuture.java | 113 +++++++++++++++++- .../support/ListenableActionFutureTests.java | 7 +- .../TransportActionFilterChainTests.java | 4 +- .../transport/TransportClientRetryIT.java | 2 +- 5 files changed, 118 insertions(+), 114 deletions(-) delete mode 100644 core/src/main/java/org/elasticsearch/action/support/AbstractListenableActionFuture.java diff --git a/core/src/main/java/org/elasticsearch/action/support/AbstractListenableActionFuture.java b/core/src/main/java/org/elasticsearch/action/support/AbstractListenableActionFuture.java deleted file mode 100644 index d6e06613d59b4..0000000000000 --- a/core/src/main/java/org/elasticsearch/action/support/AbstractListenableActionFuture.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.action.support; - -import org.apache.logging.log4j.Logger; -import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.ListenableActionFuture; -import org.elasticsearch.common.logging.Loggers; -import org.elasticsearch.threadpool.ThreadPool; - -import java.util.ArrayList; -import java.util.List; - -public abstract class AbstractListenableActionFuture extends AdapterActionFuture implements ListenableActionFuture { - - private static final Logger logger = Loggers.getLogger(AbstractListenableActionFuture.class); - - final ThreadPool threadPool; - volatile Object listeners; - boolean executedListeners = false; - - protected AbstractListenableActionFuture(ThreadPool threadPool) { - this.threadPool = threadPool; - } - - public ThreadPool threadPool() { - return threadPool; - } - - @Override - public void addListener(final ActionListener listener) { - internalAddListener(listener); - } - - public void internalAddListener(ActionListener listener) { - listener = new ThreadedActionListener<>(logger, threadPool, ThreadPool.Names.LISTENER, listener, false); - boolean executeImmediate = false; - synchronized (this) { - if (executedListeners) { - executeImmediate = true; - } else { - Object listeners = this.listeners; - if (listeners == null) { - listeners = listener; - } else if (listeners instanceof List) { - ((List) this.listeners).add(listener); - } else { - Object orig = listeners; - listeners = new ArrayList<>(2); - ((List) listeners).add(orig); - ((List) listeners).add(listener); - } - this.listeners = listeners; - } - } - if (executeImmediate) { - executeListener(listener); - } - } - - @Override - protected void done() { - super.done(); - synchronized (this) { - executedListeners = true; - } - Object listeners = this.listeners; - if (listeners != null) { - if (listeners instanceof List) { - List list = (List) listeners; - for (Object listener : list) { - executeListener((ActionListener) listener); - } - } else { - executeListener((ActionListener) listeners); - } - } - } - - private void executeListener(final ActionListener listener) { - try { - // we use a timeout of 0 to by pass assertion forbidding to call actionGet() (blocking) on a network thread. - // here we know we will never block - listener.onResponse(actionGet(0)); - } catch (Exception e) { - listener.onFailure(e); - } - } -} diff --git a/core/src/main/java/org/elasticsearch/action/support/PlainListenableActionFuture.java b/core/src/main/java/org/elasticsearch/action/support/PlainListenableActionFuture.java index c9b0cf9d82f43..749bf1fea019d 100644 --- a/core/src/main/java/org/elasticsearch/action/support/PlainListenableActionFuture.java +++ b/core/src/main/java/org/elasticsearch/action/support/PlainListenableActionFuture.java @@ -19,17 +19,120 @@ package org.elasticsearch.action.support; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.ListenableActionFuture; +import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.threadpool.ThreadPool; -public class PlainListenableActionFuture extends AbstractListenableActionFuture { +import java.util.ArrayList; +import java.util.List; - public PlainListenableActionFuture(ThreadPool threadPool) { - super(threadPool); +public class PlainListenableActionFuture extends AdapterActionFuture implements ListenableActionFuture { + + volatile Object listeners; + boolean executedListeners = false; + + private PlainListenableActionFuture() {} + + /** + * This method returns a listenable future. The listeners will be called on completion of the future. + * The listeners will be executed by the same thread that completes the future. + * + * @param the result of the future + * @return a listenable future + */ + public static PlainListenableActionFuture newListenableFuture() { + return new PlainListenableActionFuture<>(); + } + + /** + * This method returns a listenable future. The listeners will be called on completion of the future. + * The listeners will be executed on the LISTENER thread pool. + * @param threadPool the thread pool used to execute listeners + * @param the result of the future + * @return a listenable future + */ + public static PlainListenableActionFuture newDispatchingListenableFuture(ThreadPool threadPool) { + return new DispatchingListenableActionFuture<>(threadPool); } @Override - protected T convert(T response) { - return response; + public void addListener(final ActionListener listener) { + internalAddListener(listener); } + @Override + protected void done() { + super.done(); + synchronized (this) { + executedListeners = true; + } + Object listeners = this.listeners; + if (listeners != null) { + if (listeners instanceof List) { + List list = (List) listeners; + for (Object listener : list) { + executeListener((ActionListener) listener); + } + } else { + executeListener((ActionListener) listeners); + } + } + } + + @Override + protected T convert(T listenerResponse) { + return listenerResponse; + } + + private void internalAddListener(ActionListener listener) { + boolean executeImmediate = false; + synchronized (this) { + if (executedListeners) { + executeImmediate = true; + } else { + Object listeners = this.listeners; + if (listeners == null) { + listeners = listener; + } else if (listeners instanceof List) { + ((List) this.listeners).add(listener); + } else { + Object orig = listeners; + listeners = new ArrayList<>(2); + ((List) listeners).add(orig); + ((List) listeners).add(listener); + } + this.listeners = listeners; + } + } + if (executeImmediate) { + executeListener(listener); + } + } + + private void executeListener(final ActionListener listener) { + try { + // we use a timeout of 0 to by pass assertion forbidding to call actionGet() (blocking) on a network thread. + // here we know we will never block + listener.onResponse(actionGet(0)); + } catch (Exception e) { + listener.onFailure(e); + } + } + + private static final class DispatchingListenableActionFuture extends PlainListenableActionFuture { + + private static final Logger logger = Loggers.getLogger(DispatchingListenableActionFuture.class); + private final ThreadPool threadPool; + + private DispatchingListenableActionFuture(ThreadPool threadPool) { + this.threadPool = threadPool; + } + + @Override + public void addListener(final ActionListener listener) { + super.addListener(new ThreadedActionListener<>(logger, threadPool, ThreadPool.Names.LISTENER, listener, false)); + } + } } diff --git a/core/src/test/java/org/elasticsearch/action/support/ListenableActionFutureTests.java b/core/src/test/java/org/elasticsearch/action/support/ListenableActionFutureTests.java index 8169a674bedcf..0dbf4603450ae 100644 --- a/core/src/test/java/org/elasticsearch/action/support/ListenableActionFutureTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/ListenableActionFutureTests.java @@ -34,7 +34,12 @@ public class ListenableActionFutureTests extends ESTestCase { public void testListenerIsCallableFromNetworkThreads() throws Throwable { ThreadPool threadPool = new TestThreadPool("testListenerIsCallableFromNetworkThreads"); try { - final PlainListenableActionFuture future = new PlainListenableActionFuture<>(threadPool); + final PlainListenableActionFuture future; + if (randomBoolean()) { + future = PlainListenableActionFuture.newDispatchingListenableFuture(threadPool); + } else { + future = PlainListenableActionFuture.newListenableFuture(); + } final CountDownLatch listenerCalled = new CountDownLatch(1); final AtomicReference error = new AtomicReference<>(); final Object response = new Object(); diff --git a/core/src/test/java/org/elasticsearch/action/support/TransportActionFilterChainTests.java b/core/src/test/java/org/elasticsearch/action/support/TransportActionFilterChainTests.java index d5aab07f24734..3eb1616348d84 100644 --- a/core/src/test/java/org/elasticsearch/action/support/TransportActionFilterChainTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/TransportActionFilterChainTests.java @@ -91,7 +91,8 @@ protected void doExecute(TestRequest request, ActionListener liste } } - PlainActionFuture future = new PlainActionFuture<>(); + PlainActionFuture future = PlainActionFuture.newFuture(); + transportAction.execute(new TestRequest(), future); try { assertThat(future.get(), notNullValue()); @@ -104,6 +105,7 @@ protected void doExecute(TestRequest request, ActionListener liste for (ActionFilter actionFilter : actionFilters.filters()) { testFiltersByLastExecution.add((RequestTestFilter) actionFilter); } + testFiltersByLastExecution.sort(Comparator.comparingInt(o -> o.executionToken)); ArrayList finalTestFilters = new ArrayList<>(); diff --git a/core/src/test/java/org/elasticsearch/client/transport/TransportClientRetryIT.java b/core/src/test/java/org/elasticsearch/client/transport/TransportClientRetryIT.java index 899d1b0ce4a5a..0247d8f343f85 100644 --- a/core/src/test/java/org/elasticsearch/client/transport/TransportClientRetryIT.java +++ b/core/src/test/java/org/elasticsearch/client/transport/TransportClientRetryIT.java @@ -69,7 +69,7 @@ public void testRetry() throws IOException, ExecutionException, InterruptedExcep if (randomBoolean()) { clusterState = client.admin().cluster().state(clusterStateRequest).get().getState(); } else { - PlainActionFuture future = new PlainActionFuture<>(); + PlainActionFuture future = PlainActionFuture.newFuture(); client.admin().cluster().state(clusterStateRequest, future); clusterState = future.get().getState(); } From cacba6bc46b66ac025a241c4eda261c3ea8c7716 Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Wed, 3 May 2017 12:51:41 -0400 Subject: [PATCH 182/619] Allow plugins to upgrade templates and index metadata on startup (#24379) The UpgraderPlugin adds two additional extension points called during cluster upgrade and when old indices are introduced into the cluster state during initial recovery, restore from a snapshot or as a dangling index. One extension point allows plugin to update old templates and another extension points allows to update/check index metadata. --- .../metadata/MetaDataIndexUpgradeService.java | 16 ++- .../gateway/GatewayMetaState.java | 48 +++++-- .../java/org/elasticsearch/node/Node.java | 22 ++- .../plugins/MetaDataUpgrader.java | 14 +- .../org/elasticsearch/plugins/Plugin.java | 39 +++++- .../MetaDataIndexUpgradeServiceTests.java | 44 +++++- .../gateway/GatewayMetaStateTests.java | 127 ++++++++++++++++-- .../indices/cluster/ClusterStateChanges.java | 3 +- 8 files changed, 273 insertions(+), 40 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeService.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeService.java index 614d12547fc4d..5280be3e78ac2 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeService.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeService.java @@ -35,11 +35,14 @@ import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.indices.mapper.MapperRegistry; +import org.elasticsearch.plugins.Plugin; import java.util.AbstractMap; +import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; +import java.util.function.UnaryOperator; /** * This service is responsible for upgrading legacy index metadata to the current version @@ -54,14 +57,23 @@ public class MetaDataIndexUpgradeService extends AbstractComponent { private final NamedXContentRegistry xContentRegistry; private final MapperRegistry mapperRegistry; private final IndexScopedSettings indexScopedSettings; + private final UnaryOperator upgraders; @Inject public MetaDataIndexUpgradeService(Settings settings, NamedXContentRegistry xContentRegistry, MapperRegistry mapperRegistry, - IndexScopedSettings indexScopedSettings) { + IndexScopedSettings indexScopedSettings, + Collection> indexMetaDataUpgraders) { super(settings); this.xContentRegistry = xContentRegistry; this.mapperRegistry = mapperRegistry; this.indexScopedSettings = indexScopedSettings; + this.upgraders = indexMetaData -> { + IndexMetaData newIndexMetaData = indexMetaData; + for (UnaryOperator upgrader : indexMetaDataUpgraders) { + newIndexMetaData = upgrader.apply(newIndexMetaData); + } + return newIndexMetaData; + }; } /** @@ -84,6 +96,8 @@ public IndexMetaData upgradeIndexMetaData(IndexMetaData indexMetaData, Version m newMetaData = archiveBrokenIndexSettings(newMetaData); // only run the check with the upgraded settings!! checkMappingsCompatibility(newMetaData); + // apply plugin checks + newMetaData = upgraders.apply(newMetaData); return markAsUpgraded(newMetaData); } diff --git a/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java b/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java index 153ca8b3a8784..d798f8b7745b1 100644 --- a/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java +++ b/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java @@ -31,6 +31,7 @@ import org.elasticsearch.cluster.routing.RoutingNode; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; @@ -50,6 +51,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; @@ -219,8 +223,8 @@ private void ensureNoPre019State() throws Exception { final String name = stateFile.getFileName().toString(); if (name.startsWith("metadata-")) { throw new IllegalStateException("Detected pre 0.19 metadata file please upgrade to a version before " - + Version.CURRENT.minimumCompatibilityVersion() - + " first to upgrade state structures - metadata found: [" + stateFile.getParent().toAbsolutePath()); + + Version.CURRENT.minimumCompatibilityVersion() + + " first to upgrade state structures - metadata found: [" + stateFile.getParent().toAbsolutePath()); } } } @@ -247,23 +251,41 @@ static MetaData upgradeMetaData(MetaData metaData, changed |= indexMetaData != newMetaData; upgradedMetaData.put(newMetaData, false); } - // collect current customs - Map existingCustoms = new HashMap<>(); - for (ObjectObjectCursor customCursor : metaData.customs()) { - existingCustoms.put(customCursor.key, customCursor.value); - } // upgrade global custom meta data - Map upgradedCustoms = metaDataUpgrader.customMetaDataUpgraders.apply(existingCustoms); - if (upgradedCustoms.equals(existingCustoms) == false) { - existingCustoms.keySet().forEach(upgradedMetaData::removeCustom); - for (Map.Entry upgradedCustomEntry : upgradedCustoms.entrySet()) { - upgradedMetaData.putCustom(upgradedCustomEntry.getKey(), upgradedCustomEntry.getValue()); - } + if (applyPluginUpgraders(metaData.getCustoms(), metaDataUpgrader.customMetaDataUpgraders, + upgradedMetaData::removeCustom,upgradedMetaData::putCustom)) { + changed = true; + } + // upgrade current templates + if (applyPluginUpgraders(metaData.getTemplates(), metaDataUpgrader.indexTemplateMetaDataUpgraders, + upgradedMetaData::removeTemplate, (s, indexTemplateMetaData) -> upgradedMetaData.put(indexTemplateMetaData))) { changed = true; } return changed ? upgradedMetaData.build() : metaData; } + private static boolean applyPluginUpgraders(ImmutableOpenMap existingData, + UnaryOperator> upgrader, + Consumer removeData, + BiConsumer putData) { + // collect current data + Map existingMap = new HashMap<>(); + for (ObjectObjectCursor customCursor : existingData) { + existingMap.put(customCursor.key, customCursor.value); + } + // upgrade global custom meta data + Map upgradedCustoms = upgrader.apply(existingMap); + if (upgradedCustoms.equals(existingMap) == false) { + // remove all data first so a plugin can remove custom metadata or templates if needed + existingMap.keySet().forEach(removeData); + for (Map.Entry upgradedCustomEntry : upgradedCustoms.entrySet()) { + putData.accept(upgradedCustomEntry.getKey(), upgradedCustomEntry.getValue()); + } + return true; + } + return false; + } + // shard state BWC private void ensureNoPre019ShardState(NodeEnvironment nodeEnv) throws Exception { for (Path dataLocation : nodeEnv.nodeDataPaths()) { diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index c2d72efec6872..9c933b1da708c 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -43,6 +43,8 @@ import org.elasticsearch.cluster.InternalClusterInfoService; import org.elasticsearch.cluster.NodeConnectionsService; import org.elasticsearch.cluster.action.index.MappingUpdatedAction; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.metadata.MetaDataIndexUpgradeService; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -400,14 +402,20 @@ protected Node(final Environment environment, Collection .flatMap(p -> p.createComponents(client, clusterService, threadPool, resourceWatcherService, scriptModule.getScriptService(), xContentRegistry).stream()) .collect(Collectors.toList()); - Collection>> customMetaDataUpgraders = - pluginsService.filterPlugins(Plugin.class).stream() - .map(Plugin::getCustomMetaDataUpgrader) - .collect(Collectors.toList()); final RestController restController = actionModule.getRestController(); final NetworkModule networkModule = new NetworkModule(settings, false, pluginsService.filterPlugins(NetworkPlugin.class), threadPool, bigArrays, circuitBreakerService, namedWriteableRegistry, xContentRegistry, networkService, restController); - final MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader(customMetaDataUpgraders); + Collection>> customMetaDataUpgraders = + pluginsService.filterPlugins(Plugin.class).stream() + .map(Plugin::getCustomMetaDataUpgrader) + .collect(Collectors.toList()); + Collection>> indexTemplateMetaDataUpgraders = + pluginsService.filterPlugins(Plugin.class).stream() + .map(Plugin::getIndexTemplateMetaDataUpgrader) + .collect(Collectors.toList()); + Collection> indexMetaDataUpgraders = pluginsService.filterPlugins(Plugin.class).stream() + .map(Plugin::getIndexMetaDataUpgrader).collect(Collectors.toList()); + final MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader(customMetaDataUpgraders, indexTemplateMetaDataUpgraders); final Transport transport = networkModule.getTransportSupplier().get(); final TransportService transportService = newTransportService(settings, transport, threadPool, networkModule.getTransportInterceptor(), localNodeFactory, settingsModule.getClusterSettings()); @@ -462,8 +470,8 @@ protected Node(final Environment environment, Collection b.bind(TransportService.class).toInstance(transportService); b.bind(NetworkService.class).toInstance(networkService); b.bind(UpdateHelper.class).toInstance(new UpdateHelper(settings, scriptModule.getScriptService())); - b.bind(MetaDataIndexUpgradeService.class).toInstance(new MetaDataIndexUpgradeService(settings, - xContentRegistry, indicesModule.getMapperRegistry(), settingsModule.getIndexScopedSettings())); + b.bind(MetaDataIndexUpgradeService.class).toInstance(new MetaDataIndexUpgradeService(settings, xContentRegistry, + indicesModule.getMapperRegistry(), settingsModule.getIndexScopedSettings(), indexMetaDataUpgraders)); b.bind(ClusterInfoService.class).toInstance(clusterInfoService); b.bind(Discovery.class).toInstance(discoveryModule.getDiscovery()); { diff --git a/core/src/main/java/org/elasticsearch/plugins/MetaDataUpgrader.java b/core/src/main/java/org/elasticsearch/plugins/MetaDataUpgrader.java index aaeddbec11974..49dd91901d756 100644 --- a/core/src/main/java/org/elasticsearch/plugins/MetaDataUpgrader.java +++ b/core/src/main/java/org/elasticsearch/plugins/MetaDataUpgrader.java @@ -19,6 +19,7 @@ package org.elasticsearch.plugins; +import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; import org.elasticsearch.cluster.metadata.MetaData; import java.util.Collection; @@ -32,7 +33,10 @@ public class MetaDataUpgrader { public final UnaryOperator> customMetaDataUpgraders; - public MetaDataUpgrader(Collection>> customMetaDataUpgraders) { + public final UnaryOperator> indexTemplateMetaDataUpgraders; + + public MetaDataUpgrader(Collection>> customMetaDataUpgraders, + Collection>> indexTemplateMetaDataUpgraders) { this.customMetaDataUpgraders = customs -> { Map upgradedCustoms = new HashMap<>(customs); for (UnaryOperator> customMetaDataUpgrader : customMetaDataUpgraders) { @@ -40,5 +44,13 @@ public MetaDataUpgrader(Collection>> } return upgradedCustoms; }; + + this.indexTemplateMetaDataUpgraders = templates -> { + Map upgradedTemplates = new HashMap<>(templates); + for (UnaryOperator> upgrader : indexTemplateMetaDataUpgraders) { + upgradedTemplates = upgrader.apply(upgradedTemplates); + } + return upgradedTemplates; + }; } } diff --git a/core/src/main/java/org/elasticsearch/plugins/Plugin.java b/core/src/main/java/org/elasticsearch/plugins/Plugin.java index 87c5ef9a8c694..bf5bc49d279c0 100644 --- a/core/src/main/java/org/elasticsearch/plugins/Plugin.java +++ b/core/src/main/java/org/elasticsearch/plugins/Plugin.java @@ -23,6 +23,8 @@ import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.ClusterModule; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.component.LifecycleComponent; @@ -153,14 +155,49 @@ public void onIndexModule(IndexModule indexModule) {} * Provides a function to modify global custom meta data on startup. *

* Plugins should return the input custom map via {@link UnaryOperator#identity()} if no upgrade is required. + *

+ * The order of custom meta data upgraders calls is undefined and can change between runs so, it is expected that + * plugins will modify only data owned by them to avoid conflicts. + *

* @return Never {@code null}. The same or upgraded {@code MetaData.Custom} map. * @throws IllegalStateException if the node should not start because at least one {@code MetaData.Custom} - * is unsupported + * is unsupported */ public UnaryOperator> getCustomMetaDataUpgrader() { return UnaryOperator.identity(); } + /** + * Provides a function to modify index template meta data on startup. + *

+ * Plugins should return the input template map via {@link UnaryOperator#identity()} if no upgrade is required. + *

+ * The order of the template upgrader calls is undefined and can change between runs so, it is expected that + * plugins will modify only templates owned by them to avoid conflicts. + *

+ * @return Never {@code null}. The same or upgraded {@code IndexTemplateMetaData} map. + * @throws IllegalStateException if the node should not start because at least one {@code IndexTemplateMetaData} + * cannot be upgraded + */ + public UnaryOperator> getIndexTemplateMetaDataUpgrader() { + return UnaryOperator.identity(); + } + + /** + * Provides a function to modify index meta data when an index is introduced into the cluster state for the first time. + *

+ * Plugins should return the input index metadata via {@link UnaryOperator#identity()} if no upgrade is required. + *

+ * The order of the index upgrader calls for the same index is undefined and can change between runs so, it is expected that + * plugins will modify only indices owned by them to avoid conflicts. + *

+ * @return Never {@code null}. The same or upgraded {@code IndexMetaData}. + * @throws IllegalStateException if the node should not start because the index is unsupported + */ + public UnaryOperator getIndexMetaDataUpgrader() { + return UnaryOperator.identity(); + } + /** * Provides the list of this plugin's custom thread pools, empty if * none. diff --git a/core/src/test/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeServiceTests.java b/core/src/test/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeServiceTests.java index 24e969d06d62d..98dad8810d8cc 100644 --- a/core/src/test/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeServiceTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeServiceTests.java @@ -31,7 +31,8 @@ public class MetaDataIndexUpgradeServiceTests extends ESTestCase { public void testArchiveBrokenIndexSettings() { MetaDataIndexUpgradeService service = new MetaDataIndexUpgradeService(Settings.EMPTY, xContentRegistry(), - new MapperRegistry(Collections.emptyMap(), Collections.emptyMap()), IndexScopedSettings.DEFAULT_SCOPED_SETTINGS); + new MapperRegistry(Collections.emptyMap(), Collections.emptyMap()), IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + Collections.emptyList()); IndexMetaData src = newIndexMeta("foo", Settings.EMPTY); IndexMetaData indexMetaData = service.archiveBrokenIndexSettings(src); assertSame(indexMetaData, src); @@ -58,7 +59,8 @@ public void testArchiveBrokenIndexSettings() { public void testUpgrade() { MetaDataIndexUpgradeService service = new MetaDataIndexUpgradeService(Settings.EMPTY, xContentRegistry(), - new MapperRegistry(Collections.emptyMap(), Collections.emptyMap()), IndexScopedSettings.DEFAULT_SCOPED_SETTINGS); + new MapperRegistry(Collections.emptyMap(), Collections.emptyMap()), IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + Collections.emptyList()); IndexMetaData src = newIndexMeta("foo", Settings.builder().put("index.refresh_interval", "-200").build()); assertFalse(service.isUpgraded(src)); src = service.upgradeIndexMetaData(src, Version.CURRENT.minimumIndexCompatibilityVersion()); @@ -70,7 +72,8 @@ public void testUpgrade() { public void testIsUpgraded() { MetaDataIndexUpgradeService service = new MetaDataIndexUpgradeService(Settings.EMPTY, xContentRegistry(), - new MapperRegistry(Collections.emptyMap(), Collections.emptyMap()), IndexScopedSettings.DEFAULT_SCOPED_SETTINGS); + new MapperRegistry(Collections.emptyMap(), Collections.emptyMap()), IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + Collections.emptyList()); IndexMetaData src = newIndexMeta("foo", Settings.builder().put("index.refresh_interval", "-200").build()); assertFalse(service.isUpgraded(src)); Version version = VersionUtils.randomVersionBetween(random(), VersionUtils.getFirstVersion(), VersionUtils.getPreviousVersion()); @@ -82,7 +85,8 @@ public void testIsUpgraded() { public void testFailUpgrade() { MetaDataIndexUpgradeService service = new MetaDataIndexUpgradeService(Settings.EMPTY, xContentRegistry(), - new MapperRegistry(Collections.emptyMap(), Collections.emptyMap()), IndexScopedSettings.DEFAULT_SCOPED_SETTINGS); + new MapperRegistry(Collections.emptyMap(), Collections.emptyMap()), IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + Collections.emptyList()); final IndexMetaData metaData = newIndexMeta("foo", Settings.builder() .put(IndexMetaData.SETTING_VERSION_UPGRADED, Version.V_5_0_0_beta1) .put(IndexMetaData.SETTING_VERSION_CREATED, Version.fromString("2.4.0")) @@ -99,6 +103,38 @@ public void testFailUpgrade() { service.upgradeIndexMetaData(goodMeta, Version.V_5_0_0.minimumIndexCompatibilityVersion()); } + public void testPluginUpgrade() { + MetaDataIndexUpgradeService service = new MetaDataIndexUpgradeService(Settings.EMPTY, xContentRegistry(), + new MapperRegistry(Collections.emptyMap(), Collections.emptyMap()), IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + Collections.singletonList( + indexMetaData -> IndexMetaData.builder(indexMetaData) + .settings( + Settings.builder() + .put(indexMetaData.getSettings()) + .put("index.refresh_interval", "10s") + ).build())); + IndexMetaData src = newIndexMeta("foo", Settings.builder().put("index.refresh_interval", "200s").build()); + assertFalse(service.isUpgraded(src)); + src = service.upgradeIndexMetaData(src, Version.CURRENT.minimumIndexCompatibilityVersion()); + assertTrue(service.isUpgraded(src)); + assertEquals("10s", src.getSettings().get("index.refresh_interval")); + assertSame(src, service.upgradeIndexMetaData(src, Version.CURRENT.minimumIndexCompatibilityVersion())); // no double upgrade + } + + public void testPluginUpgradeFailure() { + MetaDataIndexUpgradeService service = new MetaDataIndexUpgradeService(Settings.EMPTY, xContentRegistry(), + new MapperRegistry(Collections.emptyMap(), Collections.emptyMap()), IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + Collections.singletonList( + indexMetaData -> { + throw new IllegalStateException("Cannot upgrade index " + indexMetaData.getIndex().getName()); + } + )); + IndexMetaData src = newIndexMeta("foo", Settings.EMPTY); + String message = expectThrows(IllegalStateException.class, () -> service.upgradeIndexMetaData(src, + Version.CURRENT.minimumIndexCompatibilityVersion())).getMessage(); + assertEquals(message, "Cannot upgrade index foo"); + } + public static IndexMetaData newIndexMeta(String name, Settings indexSettings) { Settings build = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1) diff --git a/core/src/test/java/org/elasticsearch/gateway/GatewayMetaStateTests.java b/core/src/test/java/org/elasticsearch/gateway/GatewayMetaStateTests.java index 7b5530bba1061..e7daa9a791d43 100644 --- a/core/src/test/java/org/elasticsearch/gateway/GatewayMetaStateTests.java +++ b/core/src/test/java/org/elasticsearch/gateway/GatewayMetaStateTests.java @@ -22,7 +22,9 @@ import org.elasticsearch.Version; import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ESAllocationTestCase; import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.metadata.MetaDataIndexUpgradeService; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -33,7 +35,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.Index; import org.elasticsearch.plugins.MetaDataUpgrader; -import org.elasticsearch.cluster.ESAllocationTestCase; import org.elasticsearch.test.TestCustomMetaData; import java.util.Arrays; @@ -258,7 +259,8 @@ public void testAddCustomMetaDataOnUpgrade() throws Exception { Collections.singletonList(customs -> { customs.put(CustomMetaData1.TYPE, new CustomMetaData1("modified_data1")); return customs; - }) + }), + Collections.emptyList() ); MetaData upgrade = GatewayMetaState.upgradeMetaData(metaData, new MockMetaDataIndexUpgradeService(false), metaDataUpgrader); assertTrue(upgrade != metaData); @@ -273,7 +275,8 @@ public void testRemoveCustomMetaDataOnUpgrade() throws Exception { Collections.singletonList(customs -> { customs.remove(CustomMetaData1.TYPE); return customs; - }) + }), + Collections.emptyList() ); MetaData upgrade = GatewayMetaState.upgradeMetaData(metaData, new MockMetaDataIndexUpgradeService(false), metaDataUpgrader); assertTrue(upgrade != metaData); @@ -287,7 +290,8 @@ public void testUpdateCustomMetaDataOnUpgrade() throws Exception { Collections.singletonList(customs -> { customs.put(CustomMetaData1.TYPE, new CustomMetaData1("modified_data1")); return customs; - }) + }), + Collections.emptyList() ); MetaData upgrade = GatewayMetaState.upgradeMetaData(metaData, new MockMetaDataIndexUpgradeService(false), metaDataUpgrader); @@ -297,9 +301,27 @@ public void testUpdateCustomMetaDataOnUpgrade() throws Exception { assertThat(((TestCustomMetaData) upgrade.custom(CustomMetaData1.TYPE)).getData(), equalTo("modified_data1")); } + + public void testUpdateTemplateMetaDataOnUpgrade() throws Exception { + MetaData metaData = randomMetaData(); + MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader( + Collections.emptyList(), + Collections.singletonList( + templates -> { + templates.put("added_test_template", IndexTemplateMetaData.builder("added_test_template").build()); + return templates; + } + )); + + MetaData upgrade = GatewayMetaState.upgradeMetaData(metaData, new MockMetaDataIndexUpgradeService(false), metaDataUpgrader); + assertTrue(upgrade != metaData); + assertFalse(MetaData.isGlobalStateEquals(upgrade, metaData)); + assertTrue(upgrade.templates().containsKey("added_test_template")); + } + public void testNoMetaDataUpgrade() throws Exception { MetaData metaData = randomMetaData(new CustomMetaData1("data")); - MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader(Collections.emptyList()); + MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader(Collections.emptyList(), Collections.emptyList()); MetaData upgrade = GatewayMetaState.upgradeMetaData(metaData, new MockMetaDataIndexUpgradeService(false), metaDataUpgrader); assertTrue(upgrade == metaData); assertTrue(MetaData.isGlobalStateEquals(upgrade, metaData)); @@ -314,7 +336,7 @@ public void testCustomMetaDataValidation() throws Exception { customs -> { throw new IllegalStateException("custom meta data too old"); } - )); + ), Collections.emptyList()); try { GatewayMetaState.upgradeMetaData(metaData, new MockMetaDataIndexUpgradeService(false), metaDataUpgrader); } catch (IllegalStateException e) { @@ -334,7 +356,8 @@ public void testMultipleCustomMetaDataUpgrade() throws Exception { case 2: metaData = randomMetaData(); break; - default: throw new IllegalStateException("should never happen"); + default: + throw new IllegalStateException("should never happen"); } MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader( Arrays.asList( @@ -345,8 +368,8 @@ public void testMultipleCustomMetaDataUpgrade() throws Exception { customs -> { customs.put(CustomMetaData2.TYPE, new CustomMetaData1("modified_data2")); return customs; - }) - ); + } + ), Collections.emptyList()); MetaData upgrade = GatewayMetaState.upgradeMetaData(metaData, new MockMetaDataIndexUpgradeService(false), metaDataUpgrader); assertTrue(upgrade != metaData); assertFalse(MetaData.isGlobalStateEquals(upgrade, metaData)); @@ -361,7 +384,7 @@ public void testMultipleCustomMetaDataUpgrade() throws Exception { public void testIndexMetaDataUpgrade() throws Exception { MetaData metaData = randomMetaData(); - MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader(Collections.emptyList()); + MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader(Collections.emptyList(), Collections.emptyList()); MetaData upgrade = GatewayMetaState.upgradeMetaData(metaData, new MockMetaDataIndexUpgradeService(true), metaDataUpgrader); assertTrue(upgrade != metaData); assertTrue(MetaData.isGlobalStateEquals(upgrade, metaData)); @@ -372,7 +395,8 @@ public void testIndexMetaDataUpgrade() throws Exception { public void testCustomMetaDataNoChange() throws Exception { MetaData metaData = randomMetaData(new CustomMetaData1("data")); - MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader(Collections.singletonList(HashMap::new)); + MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader(Collections.singletonList(HashMap::new), + Collections.singletonList(HashMap::new)); MetaData upgrade = GatewayMetaState.upgradeMetaData(metaData, new MockMetaDataIndexUpgradeService(false), metaDataUpgrader); assertTrue(upgrade == metaData); assertTrue(MetaData.isGlobalStateEquals(upgrade, metaData)); @@ -381,13 +405,71 @@ public void testCustomMetaDataNoChange() throws Exception { } } + public void testIndexTemplateValidation() throws Exception { + MetaData metaData = randomMetaData(); + MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader( + Collections.emptyList(), + Collections.singletonList( + customs -> { + throw new IllegalStateException("template is incompatible"); + })); + String message = expectThrows(IllegalStateException.class, + () -> GatewayMetaState.upgradeMetaData(metaData, new MockMetaDataIndexUpgradeService(false), metaDataUpgrader)).getMessage(); + assertThat(message, equalTo("template is incompatible")); + } + + + public void testMultipleIndexTemplateUpgrade() throws Exception { + final MetaData metaData; + switch (randomIntBetween(0, 2)) { + case 0: + metaData = randomMetaDataWithIndexTemplates("template1", "template2"); + break; + case 1: + metaData = randomMetaDataWithIndexTemplates(randomBoolean() ? "template1" : "template2"); + break; + case 2: + metaData = randomMetaData(); + break; + default: + throw new IllegalStateException("should never happen"); + } + MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader( + Collections.emptyList(), + Arrays.asList( + indexTemplateMetaDatas -> { + indexTemplateMetaDatas.put("template1", IndexTemplateMetaData.builder("template1").settings( + Settings.builder().put(IndexMetaData.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 20).build()).build()); + return indexTemplateMetaDatas; + + }, + indexTemplateMetaDatas -> { + indexTemplateMetaDatas.put("template2", IndexTemplateMetaData.builder("template2").settings( + Settings.builder().put(IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 10).build()).build()); + return indexTemplateMetaDatas; + + } + )); + MetaData upgrade = GatewayMetaState.upgradeMetaData(metaData, new MockMetaDataIndexUpgradeService(false), metaDataUpgrader); + assertTrue(upgrade != metaData); + assertFalse(MetaData.isGlobalStateEquals(upgrade, metaData)); + assertNotNull(upgrade.templates().get("template1")); + assertThat(IndexMetaData.INDEX_NUMBER_OF_SHARDS_SETTING.get(upgrade.templates().get("template1").settings()), equalTo(20)); + assertNotNull(upgrade.templates().get("template2")); + assertThat(IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING.get(upgrade.templates().get("template2").settings()), equalTo(10)); + for (IndexMetaData indexMetaData : upgrade) { + assertTrue(metaData.hasIndexMetaData(indexMetaData)); + } + } + private static class MockMetaDataIndexUpgradeService extends MetaDataIndexUpgradeService { private final boolean upgrade; MockMetaDataIndexUpgradeService(boolean upgrade) { - super(Settings.EMPTY, null, null, null); + super(Settings.EMPTY, null, null, null, null); this.upgrade = upgrade; } + @Override public IndexMetaData upgradeIndexMetaData(IndexMetaData indexMetaData, Version minimumIndexCompatibilityVersion) { return upgrade ? IndexMetaData.builder(indexMetaData).build() : indexMetaData; @@ -445,4 +527,25 @@ private static MetaData randomMetaData(TestCustomMetaData... customMetaDatas) { } return builder.build(); } + + private static MetaData randomMetaDataWithIndexTemplates(String... templates) { + MetaData.Builder builder = MetaData.builder(); + for (String template : templates) { + IndexTemplateMetaData templateMetaData = IndexTemplateMetaData.builder(template) + .settings(settings(Version.CURRENT) + .put(IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), randomIntBetween(0, 3)) + .put(IndexMetaData.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), randomIntBetween(1, 5))) + .build(); + builder.put(templateMetaData); + } + for (int i = 0; i < randomIntBetween(1, 5); i++) { + builder.put( + IndexMetaData.builder(randomAlphaOfLength(10)) + .settings(settings(Version.CURRENT)) + .numberOfReplicas(randomIntBetween(0, 3)) + .numberOfShards(randomIntBetween(1, 5)) + ); + } + return builder.build(); + } } diff --git a/core/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java b/core/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java index f3bd58fd38a1c..e7c3024cfdc6b 100644 --- a/core/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java +++ b/core/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java @@ -162,7 +162,8 @@ public ClusterStateChanges(NamedXContentRegistry xContentRegistry, ThreadPool th TransportService transportService = new TransportService(settings, transport, threadPool, TransportService.NOOP_TRANSPORT_INTERCEPTOR, boundAddress -> DiscoveryNode.createLocal(settings, boundAddress.publishAddress(), UUIDs.randomBase64UUID()), clusterSettings); - MetaDataIndexUpgradeService metaDataIndexUpgradeService = new MetaDataIndexUpgradeService(settings, xContentRegistry, null, null) { + MetaDataIndexUpgradeService metaDataIndexUpgradeService = new MetaDataIndexUpgradeService(settings, xContentRegistry, null, null, + null) { // metaData upgrader should do nothing @Override public IndexMetaData upgradeIndexMetaData(IndexMetaData indexMetaData, Version minimumIndexCompatibilityVersion) { From 06364cf6f00feb5ed7494c90b5ac72bdf0703949 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Wed, 3 May 2017 18:49:08 -0400 Subject: [PATCH 183/619] You had one job Netty logging guard In pre-release versions of Elasticsearch 5.0.0, users were subject to log messages of the form "your platform does not.*reliably.*potential system instability". This is because we disable Netty from being unsafe, and Netty throws up this scary info-level message when unsafe is unavailable, even if it was unavailable because the user requested that it be unavailabe. Users were rightly confused, and concerned. So, we contributed a guard to Netty to prevent this log message from showing up when unsafe was explicitly disabled. This guard shipped with all versions of Netty that shipped starting with Elasticsearch 5.0.0. Unfortunately, this guard was lost in an unrelated refactoring and now with the 4.1.10.Final upgrade, users will again see this message. This commit is a hack around this until we can get a fix upstream again. Relates #24469 --- .../transport/netty4/Netty4InternalESLogger.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java index 91bbe1c1a9b3c..3d509db65617b 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java @@ -101,7 +101,11 @@ public boolean isInfoEnabled() { @Override public void info(String msg) { - logger.info(msg); + if (!("Your platform does not provide complete low-level API for accessing direct buffers reliably. " + + "Unless explicitly requested, heap buffer will always be preferred to avoid potential system " + + "instability.").equals(msg)) { + logger.info(msg); + } } @Override From 2edd4d11f24ccbfd572dff61934e514e5ec342c5 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Wed, 3 May 2017 18:51:50 -0400 Subject: [PATCH 184/619] Revise issue template This commit revises the issue template, hoping to make it clearer that following the guidelines is a really good thing to do. Relates #24470 --- .github/ISSUE_TEMPLATE.md | 43 +++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 7822d8ef2b328..ea89d18ca8abc 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,27 +1,40 @@ + + **Elasticsearch version**: **Plugins installed**: [] -**JVM version**: +**JVM version** (`java -version`): -**OS version**: +**OS version** (`uname -a` if on a Unix-like system): **Description of the problem including expected versus actual behavior**: @@ -33,8 +46,8 @@ request block and provide responses for all of the below items. **Provide logs (if relevant)**: **Describe the feature**: From 4862544934e0b9e3a5d8e4d37fb14ab70fbf7202 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Wed, 3 May 2017 20:11:41 -0400 Subject: [PATCH 185/619] Increase logging in IndexRecoveryIT#rerouteTest This test started failing but the logging here is insufficient to discern what is happening. This commit increases the logging level on this test until the failure can be understood. --- .../indices/recovery/IndexRecoveryIT.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/src/test/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java b/core/src/test/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java index a21f7757eac24..5fb59245935c5 100644 --- a/core/src/test/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java +++ b/core/src/test/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java @@ -240,6 +240,16 @@ public void testReplicaRecovery() throws Exception { validateIndexRecoveryState(nodeBRecoveryState.getIndex()); } + @TestLogging( + "_root:DEBUG," + + "org.elasticsearch.action.bulk:TRACE," + + "org.elasticsearch.action.get:TRACE," + + "org.elasticsearch.cluster.service:TRACE," + + "org.elasticsearch.discovery:TRACE," + + "org.elasticsearch.indices.cluster:TRACE," + + "org.elasticsearch.indices.recovery:TRACE," + + "org.elasticsearch.index.seqno:TRACE," + + "org.elasticsearch.index.shard:TRACE") public void testRerouteRecovery() throws Exception { logger.info("--> start node A"); final String nodeA = internalCluster().startNode(); From 45dd3780e27893d6b6221772df84d79d1d59b1d7 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 3 May 2017 20:58:23 -0400 Subject: [PATCH 186/619] CONSOLEify remaining _cat docs Relates to #18160 --- docs/build.gradle | 3 -- docs/reference/cat/snapshots.asciidoc | 21 +++++++-- docs/reference/cat/templates.asciidoc | 23 +++++++--- docs/reference/cat/thread_pool.asciidoc | 60 ++++++++++++++++--------- 4 files changed, 73 insertions(+), 34 deletions(-) diff --git a/docs/build.gradle b/docs/build.gradle index 7effa7401b06e..699cda15872ae 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -38,9 +38,6 @@ buildRestTests.expectedUnconvertedCandidates = [ 'reference/aggregations/metrics/percentile-rank-aggregation.asciidoc', 'reference/aggregations/metrics/scripted-metric-aggregation.asciidoc', 'reference/aggregations/metrics/tophits-aggregation.asciidoc', - 'reference/cat/snapshots.asciidoc', - 'reference/cat/templates.asciidoc', - 'reference/cat/thread_pool.asciidoc', 'reference/cluster/allocation-explain.asciidoc', 'reference/cluster/nodes-info.asciidoc', 'reference/cluster/nodes-stats.asciidoc', diff --git a/docs/reference/cat/snapshots.asciidoc b/docs/reference/cat/snapshots.asciidoc index 3d34cd51e6d9e..5677a0f2a7cd4 100644 --- a/docs/reference/cat/snapshots.asciidoc +++ b/docs/reference/cat/snapshots.asciidoc @@ -5,15 +5,30 @@ The `snapshots` command shows all snapshots that belong to a specific repository To find a list of available repositories to query, the command `/_cat/repositories` can be used. Querying the snapshots of a repository named `repo1` then looks as follows. -[source,sh] +[source,js] +-------------------------------------------------- +GET /_cat/snapshots/repo1?v&s=id +-------------------------------------------------- +// CONSOLE +// TEST[s/^/PUT \/_snapshot\/repo1\/snap1?wait_for_completion=true\n/] +// TEST[s/^/PUT \/_snapshot\/repo1\/snap2?wait_for_completion=true\n/] +// TEST[s/^/PUT \/_snapshot\/repo1\n{"type": "fs", "settings": {"location": "repo\/1"}}\n/] + +Which looks like: + +[source,txt] -------------------------------------------------- -% curl 'localhost:9200/_cat/snapshots/repo1?v' id status start_epoch start_time end_epoch end_time duration indices successful_shards failed_shards total_shards snap1 FAILED 1445616705 18:11:45 1445616978 18:16:18 4.6m 1 4 1 5 snap2 SUCCESS 1445634298 23:04:58 1445634672 23:11:12 6.2m 2 10 0 10 -------------------------------------------------- +// TESTRESPONSE[s/FAILED/SUCCESS/ s/14456\d+/\\d+/ s/\d+(\.\d+)?(m|s|ms)/\\d+(\\.\\d+)?(m|s|ms)/] +// TESTRESPONSE[s/\d+:\d+:\d+/\\d+:\\d+:\\d+/] +// TESTRESPONSE[s/1 4 1 5/\\d+ \\d+ \\d+ \\d+/] +// TESTRESPONSE[s/2 10 0 10/\\d+ \\d+ \\d+ \\d+/] +// TESTRESPONSE[_cat] Each snapshot contains information about when it was started and stopped. Start and stop timestamps are available in two formats. The `HH:MM:SS` output is simply for quick human consumption. -The epoch time retains more information, including date, and is machine sortable if the snapshot process spans days. \ No newline at end of file +The epoch time retains more information, including date, and is machine sortable if the snapshot process spans days. diff --git a/docs/reference/cat/templates.asciidoc b/docs/reference/cat/templates.asciidoc index 51ab34d054613..bc221d13552c0 100644 --- a/docs/reference/cat/templates.asciidoc +++ b/docs/reference/cat/templates.asciidoc @@ -3,14 +3,25 @@ The `templates` command provides information about existing templates. -[source, sh] +[source,js] -------------------------------------------------- -% curl 'localhost:9200/_cat/templates?v=true' -name template order version -template0 te* 0 -template1 tea* 1 -template2 teak* 2 7 +GET /_cat/templates?v&s=name -------------------------------------------------- +// CONSOLE +// TEST[s/^/PUT _template\/template0\n{"index_patterns": "te*", "order": 0}\n/] +// TEST[s/^/PUT _template\/template1\n{"index_patterns": "tea*", "order": 1}\n/] +// TEST[s/^/PUT _template\/template2\n{"index_patterns": "teak*", "order": 2, "version": 7}\n/] + +which looks like + +[source,txt] +-------------------------------------------------- +name index_patterns order version +template0 [te*] 0 +template1 [tea*] 1 +template2 [teak*] 2 7 +-------------------------------------------------- +// TESTRESPONSE[s/\*/\\*/ s/\[/\\[/ s/\]/\\]/ _cat] The output shows that there are three existing templates, with template2 having a version value. diff --git a/docs/reference/cat/thread_pool.asciidoc b/docs/reference/cat/thread_pool.asciidoc index 7da1aa5ce0f8c..721d85e46a0cb 100644 --- a/docs/reference/cat/thread_pool.asciidoc +++ b/docs/reference/cat/thread_pool.asciidoc @@ -4,35 +4,43 @@ The `thread_pool` command shows cluster wide thread pool statistics per node. By default the active, queue and rejected statistics are returned for all thread pools. -[source,sh] +[source,js] -------------------------------------------------- -% curl 192.168.56.10:9200/_cat/thread_pool -0EWUhXe bulk 0 0 0 -0EWUhXe fetch_shard_started 0 0 0 -0EWUhXe fetch_shard_store 0 0 0 -0EWUhXe flush 0 0 0 -0EWUhXe force_merge 0 0 0 -0EWUhXe generic 0 0 0 -0EWUhXe get 0 0 0 -0EWUhXe index 0 0 0 -0EWUhXe listener 0 0 0 -0EWUhXe management 1 0 0 -0EWUhXe refresh 0 0 0 -0EWUhXe search 0 0 0 -0EWUhXe snapshot 0 0 0 -0EWUhXe warmer 0 0 0 +GET /_cat/thread_pool -------------------------------------------------- +// CONSOLE + +Which looks like: + +[source,txt] +-------------------------------------------------- +node-0 bulk 0 0 0 +node-0 fetch_shard_started 0 0 0 +node-0 fetch_shard_store 0 0 0 +node-0 flush 0 0 0 +node-0 force_merge 0 0 0 +node-0 generic 0 0 0 +node-0 get 0 0 0 +node-0 index 0 0 0 +node-0 listener 0 0 0 +node-0 management 1 0 0 +node-0 refresh 0 0 0 +node-0 search 0 0 0 +node-0 snapshot 0 0 0 +node-0 warmer 0 0 0 +-------------------------------------------------- +// TESTRESPONSE[s/\d+/\\d+/ _cat] The first column is the node name -[source,sh] +[source,txt] -------------------------------------------------- node_name -0EWUhXe +node-0 -------------------------------------------------- The second column is the thread pool name -[source,sh] +[source,txt] -------------------------------------------------- name bulk @@ -54,7 +62,7 @@ warmer The next three columns show the active, queue, and rejected statistics for each thread pool -[source,sh] +[source,txt] -------------------------------------------------- active queue rejected 0 0 0 @@ -76,12 +84,20 @@ active queue rejected The cat thread pool API accepts a `thread_pool_patterns` URL parameter for specifying a comma-separated list of regular expressions to match thread pool names. -[source,sh] +[source,js] +-------------------------------------------------- +GET /_cat/thread_pool/generic?v&h=id,name,active,rejected,completed +-------------------------------------------------- +// CONSOLE + +which looks like: + +[source,js] -------------------------------------------------- -% curl 'localhost:9200/_cat/thread_pool/generic?v&h=id,name,active,rejected,completed' id name active rejected completed 0EWUhXeBQtaVGlexUeVwMg generic 0 0 70 -------------------------------------------------- +// TESTRESPONSE[s/0EWUhXeBQtaVGlexUeVwMg/[\\w-]+/ s/\d+/\\d+/ _cat] Here the host columns and the active, rejected and completed suggest thread pool statistics are displayed. From 48031a2c5a98866f6ee1cf0492f2cfca10259f32 Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Wed, 3 May 2017 22:58:43 -0400 Subject: [PATCH 187/619] [DOCS] Fixes the documentation on leading forward slashes in the (#24478) [DOCS] Fixes the documentation on leading forward slashes in the base_path of S3 repositories Closes #23435 --- docs/plugins/repository-s3.asciidoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/plugins/repository-s3.asciidoc b/docs/plugins/repository-s3.asciidoc index 58ca5dab2d7b0..a0fe757ab4031 100644 --- a/docs/plugins/repository-s3.asciidoc +++ b/docs/plugins/repository-s3.asciidoc @@ -168,6 +168,9 @@ The following settings are supported: Specifies the path within bucket to repository data. Defaults to value of `repositories.s3.base_path` or to root directory if not set. + Previously, the base_path could take a leading `/` (forward slash). + However, this has been deprecated and setting the base_path now should + omit the leading `/`. `access_key`:: From d0e71510adf6cea814c04a75bfa8c2bd0ef109bc Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Thu, 4 May 2017 10:27:50 +0200 Subject: [PATCH 188/619] Enforce at most one type. (#24428) This is a follow-up to #24317, which did the hard work but was merged in such a way that it exposes the setting while still allowing indices to have multiple types by default in order to give time to people who test against master to add this setting to their index settings. --- .../java/org/elasticsearch/index/mapper/MapperService.java | 6 ++---- .../org/elasticsearch/index/mapper/MapperServiceTests.java | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java b/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java index cca285198ef33..bd65d123f81d6 100755 --- a/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java @@ -99,12 +99,10 @@ public enum MergeReason { public static final Setting INDEX_MAPPING_SINGLE_TYPE_SETTING; static { Function defValue = settings -> { - // TODO: uncomment this - /*boolean singleType = true; + boolean singleType = true; if (settings.getAsVersion(IndexMetaData.SETTING_VERSION_CREATED, null) != null) { singleType = Version.indexCreated(settings).onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED); - }*/ - boolean singleType = false; + } return Boolean.valueOf(singleType).toString(); }; INDEX_MAPPING_SINGLE_TYPE_SETTING = Setting.boolSetting("index.mapping.single_type", defValue, Property.IndexScope, Property.Final); diff --git a/core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java b/core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java index 4d467d641d9fa..cc853b5b9e5be 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java @@ -310,7 +310,6 @@ public void testIndexSortWithNestedFields() throws IOException { containsString("cannot have nested fields when index sort is activated")); } - @AwaitsFix(bugUrl="https://github.com/elastic/elasticsearch/pull/24317#issuecomment-297624290") public void testForbidMultipleTypes() throws IOException { String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").endObject().endObject().string(); MapperService mapperService = createIndex("test").mapperService(); From 9a3ab3e8000be059ccf4edeee53b62ddb5913fc6 Mon Sep 17 00:00:00 2001 From: Dimitrios Liappis Date: Thu, 4 May 2017 12:04:53 +0300 Subject: [PATCH 189/619] Tests: Switch to Fedora-25 for packaging tests Switch the Fedora Vagrant box for packaging tests to Fedora-25 as Fedora-24 will become EOL one month after Fedora-26 is out. Relates #24466 --- Vagrantfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index a818d666655c6..00cc9bd638f96 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -56,8 +56,8 @@ Vagrant.configure(2) do |config| config.vm.box = "elastic/oraclelinux-7-x86_64" rpm_common config end - config.vm.define "fedora-24" do |config| - config.vm.box = "elastic/fedora-24-x86_64" + config.vm.define "fedora-25" do |config| + config.vm.box = "elastic/fedora-25-x86_64" dnf_common config end config.vm.define "opensuse-13" do |config| From 14e57bf9f8902e6d3ec85e115c11bfc2ffdbb04a Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Thu, 4 May 2017 11:44:54 +0200 Subject: [PATCH 190/619] Add cross cluster support to `_field_caps` (#24463) To support kibana this commit adds an internal optimization to support the cross cluster syntax for indices on the `_field_caps` API. Closes #24334 --- .../FieldCapabilitiesIndexResponse.java | 7 +- .../fieldcaps/FieldCapabilitiesRequest.java | 29 +++- .../fieldcaps/FieldCapabilitiesResponse.java | 29 ++++ .../TransportFieldCapabilitiesAction.java | 144 ++++++++++++------ ...TransportFieldCapabilitiesIndexAction.java | 45 ++---- .../action/search/TransportSearchAction.java | 30 +--- .../transport/RemoteClusterAware.java | 4 + .../transport/RemoteClusterConnection.java | 19 ++- .../transport/RemoteClusterService.java | 39 ++++- .../RemoteClusterConnectionTests.java | 5 +- .../test/multi_cluster/30_field_caps.yaml | 66 ++++++++ .../test/remote_cluster/10_basic.yaml | 48 ++++++ .../test/field_caps/10_basic.yaml | 6 +- 13 files changed, 347 insertions(+), 124 deletions(-) create mode 100644 qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yaml diff --git a/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java b/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java index de520ee6274f6..1e4686245165b 100644 --- a/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java +++ b/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java @@ -22,6 +22,7 @@ import org.elasticsearch.action.ActionResponse; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.io.stream.Writeable; import java.io.IOException; import java.util.Map; @@ -29,7 +30,7 @@ /** * Response for {@link FieldCapabilitiesIndexRequest} requests. */ -public class FieldCapabilitiesIndexResponse extends ActionResponse { +public class FieldCapabilitiesIndexResponse extends ActionResponse implements Writeable { private String indexName; private Map responseMap; @@ -41,6 +42,10 @@ public class FieldCapabilitiesIndexResponse extends ActionResponse { FieldCapabilitiesIndexResponse() { } + FieldCapabilitiesIndexResponse(StreamInput input) throws IOException { + this.readFrom(input); + } + /** * Get the index name diff --git a/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java b/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java index 7eab911216282..ce1ba28289997 100644 --- a/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java +++ b/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java @@ -19,6 +19,7 @@ package org.elasticsearch.action.fieldcaps; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.IndicesRequest; @@ -38,13 +39,14 @@ import static org.elasticsearch.common.xcontent.ObjectParser.fromList; -public class FieldCapabilitiesRequest extends ActionRequest - implements IndicesRequest.Replaceable { +public final class FieldCapabilitiesRequest extends ActionRequest implements IndicesRequest.Replaceable { public static final ParseField FIELDS_FIELD = new ParseField("fields"); public static final String NAME = "field_caps_request"; private String[] indices = Strings.EMPTY_ARRAY; private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpen(); private String[] fields = Strings.EMPTY_ARRAY; + // pkg private API mainly for cross cluster search to signal that we do multiple reductions ie. the results should not be merged + private boolean mergeResults = true; private static ObjectParser PARSER = new ObjectParser<>(NAME, FieldCapabilitiesRequest::new); @@ -56,16 +58,39 @@ public class FieldCapabilitiesRequest extends ActionRequest public FieldCapabilitiesRequest() {} + /** + * Returns true iff the results should be merged. + */ + boolean isMergeResults() { + return mergeResults; + } + + /** + * if set to true the response will contain only a merged view of the per index field capabilities. Otherwise only + * unmerged per index field capabilities are returned. + */ + void setMergeResults(boolean mergeResults) { + this.mergeResults = mergeResults; + } + @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); fields = in.readStringArray(); + if (in.getVersion().onOrAfter(Version.V_5_5_0_UNRELEASED)) { + mergeResults = in.readBoolean(); + } else { + mergeResults = true; + } } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeStringArray(fields); + if (out.getVersion().onOrAfter(Version.V_5_5_0_UNRELEASED)) { + out.writeBoolean(mergeResults); + } } public static FieldCapabilitiesRequest parseFields(XContentParser parser) throws IOException { diff --git a/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponse.java b/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponse.java index 9ff2cf3850b1f..3e2dc46a01d50 100644 --- a/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponse.java +++ b/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponse.java @@ -19,6 +19,7 @@ package org.elasticsearch.action.fieldcaps; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -27,6 +28,7 @@ import java.io.IOException; import java.util.Collections; +import java.util.List; import java.util.Map; /** @@ -34,9 +36,20 @@ */ public class FieldCapabilitiesResponse extends ActionResponse implements ToXContent { private Map> responseMap; + private List indexResponses; FieldCapabilitiesResponse(Map> responseMap) { + this(responseMap, Collections.emptyList()); + } + + FieldCapabilitiesResponse(List indexResponses) { + this(Collections.emptyMap(), indexResponses); + } + + private FieldCapabilitiesResponse(Map> responseMap, + List indexResponses) { this.responseMap = responseMap; + this.indexResponses = indexResponses; } /** @@ -53,6 +66,13 @@ public Map> get() { return responseMap; } + + /** + * Returns the actual per-index field caps responses + */ + List getIndexResponses() { + return indexResponses; + } /** * * Get the field capabilities per type for the provided {@code field}. @@ -66,6 +86,11 @@ public void readFrom(StreamInput in) throws IOException { super.readFrom(in); this.responseMap = in.readMap(StreamInput::readString, FieldCapabilitiesResponse::readField); + if (in.getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { + indexResponses = in.readList(FieldCapabilitiesIndexResponse::new); + } else { + indexResponses = Collections.emptyList(); + } } private static Map readField(StreamInput in) throws IOException { @@ -76,6 +101,10 @@ private static Map readField(StreamInput in) throws I public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeMap(responseMap, StreamOutput::writeString, FieldCapabilitiesResponse::writeField); + if (out.getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { + out.writeList(indexResponses); + } + } private static void writeField(StreamOutput out, diff --git a/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java b/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java index a7f268eaf5d8d..6491b8ce4c757 100644 --- a/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java +++ b/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java @@ -20,6 +20,7 @@ package org.elasticsearch.action.fieldcaps; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.cluster.ClusterState; @@ -27,18 +28,27 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.CountDown; import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.RemoteClusterAware; +import org.elasticsearch.transport.RemoteClusterService; +import org.elasticsearch.transport.Transport; +import org.elasticsearch.transport.TransportException; +import org.elasticsearch.transport.TransportRequestOptions; +import org.elasticsearch.transport.TransportResponseHandler; import org.elasticsearch.transport.TransportService; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReferenceArray; -public class TransportFieldCapabilitiesAction - extends HandledTransportAction { +public class TransportFieldCapabilitiesAction extends HandledTransportAction { private final ClusterService clusterService; private final TransportFieldCapabilitiesIndexAction shardAction; + private final RemoteClusterService remoteClusterService; + private final TransportService transportService; @Inject public TransportFieldCapabilitiesAction(Settings settings, TransportService transportService, @@ -50,71 +60,97 @@ public TransportFieldCapabilitiesAction(Settings settings, TransportService tran super(settings, FieldCapabilitiesAction.NAME, threadPool, transportService, actionFilters, indexNameExpressionResolver, FieldCapabilitiesRequest::new); this.clusterService = clusterService; + this.remoteClusterService = transportService.getRemoteClusterService(); + this.transportService = transportService; this.shardAction = shardAction; } @Override protected void doExecute(FieldCapabilitiesRequest request, final ActionListener listener) { - ClusterState clusterState = clusterService.state(); - String[] concreteIndices = - indexNameExpressionResolver.concreteIndexNames(clusterState, request); - final AtomicInteger indexCounter = new AtomicInteger(); - final AtomicInteger completionCounter = new AtomicInteger(concreteIndices.length); - final AtomicReferenceArray indexResponses = - new AtomicReferenceArray<>(concreteIndices.length); - if (concreteIndices.length == 0) { + final ClusterState clusterState = clusterService.state(); + final Map remoteClusterIndices = remoteClusterService.groupIndices(request.indicesOptions(), + request.indices(), idx -> indexNameExpressionResolver.hasIndexOrAlias(idx, clusterState)); + final OriginalIndices localIndices = remoteClusterIndices.remove(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY); + final String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(clusterState, localIndices); + final int totalNumRequest = concreteIndices.length + remoteClusterIndices.size(); + final CountDown completionCounter = new CountDown(totalNumRequest); + final List indexResponses = Collections.synchronizedList(new ArrayList<>()); + final Runnable onResponse = () -> { + if (completionCounter.countDown()) { + if (request.isMergeResults()) { + listener.onResponse(merge(indexResponses)); + } else { + listener.onResponse(new FieldCapabilitiesResponse(indexResponses)); + } + } + }; + if (totalNumRequest == 0) { listener.onResponse(new FieldCapabilitiesResponse()); } else { + ActionListener innerListener = new ActionListener() { + @Override + public void onResponse(FieldCapabilitiesIndexResponse result) { + indexResponses.add(result); + onResponse.run(); + } + + @Override + public void onFailure(Exception e) { + // TODO we should somehow inform the user that we failed + onResponse.run(); + } + }; for (String index : concreteIndices) { - FieldCapabilitiesIndexRequest indexRequest = - new FieldCapabilitiesIndexRequest(request.fields(), index); - shardAction.execute(indexRequest, - new ActionListener () { + shardAction.execute(new FieldCapabilitiesIndexRequest(request.fields(), index), innerListener); + } + + // this is the cross cluster part of this API - we force the other cluster to not merge the results but instead + // send us back all individual index results. + for (Map.Entry remoteIndices : remoteClusterIndices.entrySet()) { + String clusterAlias = remoteIndices.getKey(); + OriginalIndices originalIndices = remoteIndices.getValue(); + Transport.Connection connection = remoteClusterService.getConnection(remoteIndices.getKey()); + FieldCapabilitiesRequest remoteRequest = new FieldCapabilitiesRequest(); + remoteRequest.setMergeResults(false); // we need to merge on this node + remoteRequest.indicesOptions(originalIndices.indicesOptions()); + remoteRequest.indices(originalIndices.indices()); + remoteRequest.fields(request.fields()); + transportService.sendRequest(connection, FieldCapabilitiesAction.NAME, remoteRequest, TransportRequestOptions.EMPTY, + new TransportResponseHandler() { @Override - public void onResponse(FieldCapabilitiesIndexResponse result) { - indexResponses.set(indexCounter.getAndIncrement(), result); - if (completionCounter.decrementAndGet() == 0) { - listener.onResponse(merge(indexResponses)); - } + public FieldCapabilitiesResponse newInstance() { + return new FieldCapabilitiesResponse(); } @Override - public void onFailure(Exception e) { - indexResponses.set(indexCounter.getAndIncrement(), e); - if (completionCounter.decrementAndGet() == 0) { - listener.onResponse(merge(indexResponses)); + public void handleResponse(FieldCapabilitiesResponse response) { + for (FieldCapabilitiesIndexResponse res : response.getIndexResponses()) { + indexResponses.add(new FieldCapabilitiesIndexResponse(RemoteClusterAware.buildRemoteIndexName(clusterAlias, + res.getIndexName()), res.get())); } + onResponse.run(); + } + + @Override + public void handleException(TransportException exp) { + onResponse.run(); + } + + @Override + public String executor() { + return ThreadPool.Names.SAME; } }); } + } } - private FieldCapabilitiesResponse merge(AtomicReferenceArray indexResponses) { + private FieldCapabilitiesResponse merge(List indexResponses) { Map> responseMapBuilder = new HashMap<> (); - for (int i = 0; i < indexResponses.length(); i++) { - Object element = indexResponses.get(i); - if (element instanceof FieldCapabilitiesIndexResponse == false) { - assert element instanceof Exception; - continue; - } - FieldCapabilitiesIndexResponse response = (FieldCapabilitiesIndexResponse) element; - for (String field : response.get().keySet()) { - Map typeMap = responseMapBuilder.get(field); - if (typeMap == null) { - typeMap = new HashMap<> (); - responseMapBuilder.put(field, typeMap); - } - FieldCapabilities fieldCap = response.getField(field); - FieldCapabilities.Builder builder = typeMap.get(fieldCap.getType()); - if (builder == null) { - builder = new FieldCapabilities.Builder(field, fieldCap.getType()); - typeMap.put(fieldCap.getType(), builder); - } - builder.add(response.getIndexName(), - fieldCap.isSearchable(), fieldCap.isAggregatable()); - } + for (FieldCapabilitiesIndexResponse response : indexResponses) { + innerMerge(responseMapBuilder, response.getIndexName(), response.get()); } Map> responseMap = new HashMap<>(); @@ -131,4 +167,16 @@ private FieldCapabilitiesResponse merge(AtomicReferenceArray indexRespon return new FieldCapabilitiesResponse(responseMap); } + + private void innerMerge(Map> responseMapBuilder, String indexName, + Map map) { + for (Map.Entry entry : map.entrySet()) { + final String field = entry.getKey(); + final FieldCapabilities fieldCap = entry.getValue(); + Map typeMap = responseMapBuilder.computeIfAbsent(field, f -> new HashMap<>()); + FieldCapabilities.Builder builder = typeMap.computeIfAbsent(fieldCap.getType(), key -> new FieldCapabilities.Builder(field, + key)); + builder.add(indexName, fieldCap.isSearchable(), fieldCap.isAggregatable()); + } + } } diff --git a/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java b/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java index 5bab727686015..e7db685b4a882 100644 --- a/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java +++ b/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java @@ -41,34 +41,19 @@ import java.util.Map; import java.util.Set; -public class TransportFieldCapabilitiesIndexAction - extends TransportSingleShardAction { private static final String ACTION_NAME = FieldCapabilitiesAction.NAME + "[index]"; - protected final ClusterService clusterService; private final IndicesService indicesService; @Inject - public TransportFieldCapabilitiesIndexAction(Settings settings, - ClusterService clusterService, - TransportService transportService, - IndicesService indicesService, - ThreadPool threadPool, - ActionFilters actionFilters, - IndexNameExpressionResolver - indexNameExpressionResolver) { - super(settings, - ACTION_NAME, - threadPool, - clusterService, - transportService, - actionFilters, - indexNameExpressionResolver, - FieldCapabilitiesIndexRequest::new, - ThreadPool.Names.MANAGEMENT); - this.clusterService = clusterService; + public TransportFieldCapabilitiesIndexAction(Settings settings, ClusterService clusterService, TransportService transportService, + IndicesService indicesService, ThreadPool threadPool, ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver) { + super(settings, ACTION_NAME, threadPool, clusterService, transportService, actionFilters, indexNameExpressionResolver, + FieldCapabilitiesIndexRequest::new, ThreadPool.Names.MANAGEMENT); this.indicesService = indicesService; } @@ -86,11 +71,8 @@ protected ShardsIterator shards(ClusterState state, InternalRequest request) { } @Override - protected FieldCapabilitiesIndexResponse shardOperation( - final FieldCapabilitiesIndexRequest request, - ShardId shardId) { - MapperService mapperService = - indicesService.indexServiceSafe(shardId.getIndex()).mapperService(); + protected FieldCapabilitiesIndexResponse shardOperation(final FieldCapabilitiesIndexRequest request, ShardId shardId) { + MapperService mapperService = indicesService.indexServiceSafe(shardId.getIndex()).mapperService(); Set fieldNames = new HashSet<>(); for (String field : request.fields()) { fieldNames.addAll(mapperService.simpleMatchToIndexNames(field)); @@ -98,10 +80,7 @@ protected FieldCapabilitiesIndexResponse shardOperation( Map responseMap = new HashMap<>(); for (String field : fieldNames) { MappedFieldType ft = mapperService.fullName(field); - FieldCapabilities fieldCap = new FieldCapabilities(field, - ft.typeName(), - ft.isSearchable(), - ft.isAggregatable()); + FieldCapabilities fieldCap = new FieldCapabilities(field, ft.typeName(), ft.isSearchable(), ft.isAggregatable()); responseMap.put(field, fieldCap); } return new FieldCapabilitiesIndexResponse(shardId.getIndexName(), responseMap); @@ -113,9 +92,7 @@ protected FieldCapabilitiesIndexResponse newResponse() { } @Override - protected ClusterBlockException checkRequestBlock(ClusterState state, - InternalRequest request) { - return state.blocks().indexBlockedException(ClusterBlockLevel.METADATA_READ, - request.concreteIndex()); + protected ClusterBlockException checkRequestBlock(ClusterState state, InternalRequest request) { + return state.blocks().indexBlockedException(ClusterBlockLevel.METADATA_READ, request.concreteIndex()); } } diff --git a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 91a23bf6a6ff2..501504631bca4 100644 --- a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -178,35 +178,17 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< final SearchTimeProvider timeProvider = new SearchTimeProvider(absoluteStartMillis, relativeStartNanos, System::nanoTime); - final OriginalIndices localIndices; - final Map remoteClusterIndices; - final ClusterState clusterState = clusterService.state(); - if (remoteClusterService.isCrossClusterSearchEnabled()) { - final Map> groupedIndices = remoteClusterService.groupClusterIndices(searchRequest.indices(), - // empty string is not allowed - idx -> indexNameExpressionResolver.hasIndexOrAlias(idx, clusterState)); - List remove = groupedIndices.remove(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY); - String[] indices = remove == null ? Strings.EMPTY_ARRAY : remove.toArray(new String[remove.size()]); - localIndices = new OriginalIndices(indices, searchRequest.indicesOptions()); - Map originalIndicesMap = new HashMap<>(); - for (Map.Entry> entry : groupedIndices.entrySet()) { - String clusterAlias = entry.getKey(); - List originalIndices = entry.getValue(); - originalIndicesMap.put(clusterAlias, - new OriginalIndices(originalIndices.toArray(new String[originalIndices.size()]), searchRequest.indicesOptions())); - } - remoteClusterIndices = Collections.unmodifiableMap(originalIndicesMap); - } else { - remoteClusterIndices = Collections.emptyMap(); - localIndices = new OriginalIndices(searchRequest); - } + final ClusterState clusterState = clusterService.state(); + final Map remoteClusterIndices = remoteClusterService.groupIndices(searchRequest.indicesOptions(), + searchRequest.indices(), idx -> indexNameExpressionResolver.hasIndexOrAlias(idx, clusterState)); + OriginalIndices localIndices = remoteClusterIndices.remove(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY); if (remoteClusterIndices.isEmpty()) { executeSearch((SearchTask)task, timeProvider, searchRequest, localIndices, Collections.emptyList(), (clusterName, nodeId) -> null, clusterState, Collections.emptyMap(), listener); } else { - remoteClusterService.collectSearchShards(searchRequest, remoteClusterIndices, - ActionListener.wrap((searchShardsResponses) -> { + remoteClusterService.collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), + remoteClusterIndices, ActionListener.wrap((searchShardsResponses) -> { List remoteShardIterators = new ArrayList<>(); Map remoteAliasFilters = new HashMap<>(); BiFunction clusterNodeLookup = processRemoteShards(searchShardsResponses, diff --git a/core/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java b/core/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java index 42ab7315234bc..b07c7fe3605c2 100644 --- a/core/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java +++ b/core/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java @@ -159,4 +159,8 @@ private static InetSocketAddress parseSeedAddress(String remoteHost) { throw new IllegalArgumentException("port must be a number", e); } } + + public static final String buildRemoteIndexName(String clusterAlias, String indexName) { + return clusterAlias + REMOTE_CLUSTER_INDEX_SEPARATOR + indexName; + } } diff --git a/core/src/main/java/org/elasticsearch/transport/RemoteClusterConnection.java b/core/src/main/java/org/elasticsearch/transport/RemoteClusterConnection.java index 5c7e072f65063..e4f0d0d4af766 100644 --- a/core/src/main/java/org/elasticsearch/transport/RemoteClusterConnection.java +++ b/core/src/main/java/org/elasticsearch/transport/RemoteClusterConnection.java @@ -152,7 +152,7 @@ public void onNodeDisconnected(DiscoveryNode node) { /** * Fetches all shards for the search request from this remote connection. This is used to later run the search on the remote end. */ - public void fetchSearchShards(SearchRequest searchRequest, final String[] indices, + public void fetchSearchShards(ClusterSearchShardsRequest searchRequest, ActionListener listener) { if (connectedNodes.isEmpty()) { // just in case if we are not connected for some reason we try to connect and if we fail we have to notify the listener @@ -160,18 +160,15 @@ public void fetchSearchShards(SearchRequest searchRequest, final String[] indice // we can't proceed with a search on a cluster level. // in the future we might want to just skip the remote nodes in such a case but that can already be implemented on the caller // end since they provide the listener. - connectHandler.connect(ActionListener.wrap((x) -> fetchShardsInternal(searchRequest, indices, listener), listener::onFailure)); + connectHandler.connect(ActionListener.wrap((x) -> fetchShardsInternal(searchRequest, listener), listener::onFailure)); } else { - fetchShardsInternal(searchRequest, indices, listener); + fetchShardsInternal(searchRequest, listener); } } - private void fetchShardsInternal(SearchRequest searchRequest, String[] indices, + private void fetchShardsInternal(ClusterSearchShardsRequest searchShardsRequest, final ActionListener listener) { final DiscoveryNode node = nodeSupplier.get(); - ClusterSearchShardsRequest searchShardsRequest = new ClusterSearchShardsRequest(indices) - .indicesOptions(searchRequest.indicesOptions()).local(true).preference(searchRequest.preference()) - .routing(searchRequest.routing()); transportService.sendRequest(node, ClusterSearchShardsAction.NAME, searchShardsRequest, new TransportResponseHandler() { @@ -224,7 +221,13 @@ public void close() throws IOException { }; } - @Override + Transport.Connection getConnection() { + DiscoveryNode discoveryNode = nodeSupplier.get(); + return transportService.getConnection(discoveryNode); + } + + + @Override public void close() throws IOException { connectHandler.close(); } diff --git a/core/src/main/java/org/elasticsearch/transport/RemoteClusterService.java b/core/src/main/java/org/elasticsearch/transport/RemoteClusterService.java index 92dce9d53f13a..91a5cebbcd2a2 100644 --- a/core/src/main/java/org/elasticsearch/transport/RemoteClusterService.java +++ b/core/src/main/java/org/elasticsearch/transport/RemoteClusterService.java @@ -24,10 +24,12 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsGroup; +import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsRequest; import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsResponse; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchShardIterator; import org.elasticsearch.action.support.GroupedActionListener; +import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.Booleans; @@ -176,6 +178,25 @@ boolean isRemoteNodeConnected(final String remoteCluster, final DiscoveryNode no return remoteClusters.get(remoteCluster).isNodeConnected(node); } + public Map groupIndices(IndicesOptions indicesOptions, String[] indices, Predicate indexExists) { + Map originalIndicesMap = new HashMap<>(); + if (isCrossClusterSearchEnabled()) { + final Map> groupedIndices = groupClusterIndices(indices, indexExists); + for (Map.Entry> entry : groupedIndices.entrySet()) { + String clusterAlias = entry.getKey(); + List originalIndices = entry.getValue(); + originalIndicesMap.put(clusterAlias, + new OriginalIndices(originalIndices.toArray(new String[originalIndices.size()]), indicesOptions)); + } + if (originalIndicesMap.containsKey(LOCAL_CLUSTER_GROUP_KEY) == false) { + originalIndicesMap.put(LOCAL_CLUSTER_GROUP_KEY, new OriginalIndices(Strings.EMPTY_ARRAY, indicesOptions)); + } + } else { + originalIndicesMap.put(LOCAL_CLUSTER_GROUP_KEY, new OriginalIndices(indices, indicesOptions)); + } + return originalIndicesMap; + } + /** * Returns true iff the given cluster is configured as a remote cluster. Otherwise false */ @@ -183,8 +204,9 @@ boolean isRemoteClusterRegistered(String clusterName) { return remoteClusters.containsKey(clusterName); } - public void collectSearchShards(SearchRequest searchRequest, Map remoteIndicesByCluster, - ActionListener> listener) { + public void collectSearchShards(IndicesOptions indicesOptions, String preference, String routing, + Map remoteIndicesByCluster, + ActionListener> listener) { final CountDown responsesCountDown = new CountDown(remoteIndicesByCluster.size()); final Map searchShardsResponses = new ConcurrentHashMap<>(); final AtomicReference transportException = new AtomicReference<>(); @@ -195,7 +217,10 @@ public void collectSearchShards(SearchRequest searchRequest, Map() { @Override public void onResponse(ClusterSearchShardsResponse clusterSearchShardsResponse) { @@ -240,6 +265,14 @@ public Transport.Connection getConnection(DiscoveryNode node, String cluster) { return connection.getConnection(node); } + public Transport.Connection getConnection(String cluster) { + RemoteClusterConnection connection = remoteClusters.get(cluster); + if (connection == null) { + throw new IllegalArgumentException("no such remote cluster: " + cluster); + } + return connection.getConnection(); + } + @Override protected Set getRemoteClusterNames() { return this.remoteClusters.keySet(); diff --git a/core/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java b/core/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java index 1ce73aee905d0..f7ab29c95c5b4 100644 --- a/core/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java +++ b/core/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java @@ -385,7 +385,10 @@ public void testFetchShards() throws Exception { failReference.set(x); responseLatch.countDown(); }); - connection.fetchSearchShards(request, new String[]{"test-index"}, shardsListener); + ClusterSearchShardsRequest searchShardsRequest = new ClusterSearchShardsRequest(new String[]{"test-index"}) + .indicesOptions(request.indicesOptions()).local(true).preference(request.preference()) + .routing(request.routing()); + connection.fetchSearchShards(searchShardsRequest, shardsListener); responseLatch.await(); assertNull(failReference.get()); assertNotNull(reference.get()); diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yaml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yaml new file mode 100644 index 0000000000000..d3922ca46cb73 --- /dev/null +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yaml @@ -0,0 +1,66 @@ +--- +"Get simple field caps from remote cluster": + - skip: + version: " - 5.99.99" + reason: this uses a new API that has been added in 6.0.0 + + - do: + indices.create: + index: field_caps_index_2 + body: + mappings: + t: + properties: + text: + type: text + keyword: + type: keyword + number: + type: double + geo: + type: geo_point + object: + type: object + properties: + nested1 : + type : text + index: true + nested2: + type: float + doc_values: true + + - do: + field_caps: + index: 'field_caps_index_2,my_remote_cluster:field_*' + fields: [text, keyword, number, geo] + + - match: {fields.text.text.searchable: true} + - match: {fields.text.text.aggregatable: false} + - is_false: fields.text.text.indices + - is_false: fields.text.text.non_searchable_indices + - is_false: fields.text.text.non_aggregatable_indices + - match: {fields.keyword.keyword.searchable: true} + - match: {fields.keyword.keyword.aggregatable: true} + - is_false: fields.text.keyword.indices + - is_false: fields.text.keyword.non_searchable_indices + - is_false: fields.text.keyword.non_aggregatable_indices + - match: {fields.number.double.searchable: true} + - match: {fields.number.double.aggregatable: true} + - match: {fields.number.double.indices: ["field_caps_index_2", "my_remote_cluster:field_caps_index_1"]} + - is_false: fields.number.double.non_searchable_indices + - is_false: fields.number.double.non_aggregatable_indices + - match: {fields.number.long.searchable: true} + - match: {fields.number.long.aggregatable: true} + - match: {fields.number.long.indices: ["my_remote_cluster:field_caps_index_3"]} + - is_false: fields.number.long.non_searchable_indices + - is_false: fields.number.long.non_aggregatable_indices + - match: {fields.geo.geo_point.searchable: true} + - match: {fields.geo.geo_point.aggregatable: true} + - match: {fields.geo.geo_point.indices: ["field_caps_index_2", "my_remote_cluster:field_caps_index_1"]} + - is_false: fields.geo.geo_point.non_searchable_indices + - is_false: fields.geo.geo_point.non_aggregatable_indices + - match: {fields.geo.keyword.searchable: true} + - match: {fields.geo.keyword.aggregatable: true} + - match: {fields.geo.keyword.indices: ["my_remote_cluster:field_caps_index_3"]} + - is_false: fields.geo.keyword.non_searchable_indices + - is_false: fields.geo.keyword.on_aggregatable_indices diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/remote_cluster/10_basic.yaml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/remote_cluster/10_basic.yaml index 5c68a59114236..c316238384397 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/remote_cluster/10_basic.yaml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/remote_cluster/10_basic.yaml @@ -1,6 +1,54 @@ --- "Index data and search on the old cluster": + - do: + indices.create: + index: field_caps_index_1 + body: + mappings: + t: + properties: + text: + type: text + keyword: + type: keyword + number: + type: double + geo: + type: geo_point + object: + type: object + properties: + nested1 : + type : text + index: false + nested2: + type: float + doc_values: false + - do: + indices.create: + index: field_caps_index_3 + body: + mappings: + t: + properties: + text: + type: text + keyword: + type: keyword + number: + type: long + geo: + type: keyword + object: + type: object + properties: + nested1 : + type : long + index: false + nested2: + type: keyword + doc_values: false - do: indices.create: index: test_index diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml index cef72b6e3fe43..be2fd820a124a 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml @@ -76,7 +76,7 @@ setup: --- "Get simple field caps": - skip: - version: " - 5.3.99" + version: " - 5.99.99" # temporarily disabled until bwc code is backported and propagated reason: this uses a new API that has been added in 5.4.0 - do: @@ -117,7 +117,7 @@ setup: --- "Get nested field caps": - skip: - version: " - 5.3.99" + version: " - 5.99.99" # temporarily disabled until bwc code is backported and propagated reason: this uses a new API that has been added in 5.4.0 - do: @@ -148,7 +148,7 @@ setup: --- "Get prefix field caps": - skip: - version: " - 5.3.99" + version: " - 5.99.99" # temporarily disabled until bwc code is backported and propagated reason: this uses a new API that has been added in 5.4.0 - do: From c4eea8571365446b75fb3ac3b5d3fb3454501c3d Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Thu, 4 May 2017 11:51:47 +0200 Subject: [PATCH 191/619] Use V_5_5_UNRELEASED constant consitently on both request and response --- .../action/fieldcaps/FieldCapabilitiesResponse.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponse.java b/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponse.java index 3e2dc46a01d50..00c424e1fe70a 100644 --- a/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponse.java +++ b/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponse.java @@ -86,7 +86,7 @@ public void readFrom(StreamInput in) throws IOException { super.readFrom(in); this.responseMap = in.readMap(StreamInput::readString, FieldCapabilitiesResponse::readField); - if (in.getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_5_0_UNRELEASED)) { indexResponses = in.readList(FieldCapabilitiesIndexResponse::new); } else { indexResponses = Collections.emptyList(); @@ -101,7 +101,7 @@ private static Map readField(StreamInput in) throws I public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeMap(responseMap, StreamOutput::writeString, FieldCapabilitiesResponse::writeField); - if (out.getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_5_0_UNRELEASED)) { out.writeList(indexResponses); } From 01872f364904cc3ec953745db6792ce0d5df1a04 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Thu, 4 May 2017 13:29:03 +0200 Subject: [PATCH 192/619] Add version for 5.4.1 and bwc indices for 5.4.0 --- .../main/java/org/elasticsearch/Version.java | 4 ++++ .../test/resources/indices/bwc/index-5.4.0.zip | Bin 0 -> 385816 bytes .../test/resources/indices/bwc/repo-5.4.0.zip | Bin 0 -> 189447 bytes 3 files changed, 4 insertions(+) create mode 100644 core/src/test/resources/indices/bwc/index-5.4.0.zip create mode 100644 core/src/test/resources/indices/bwc/repo-5.4.0.zip diff --git a/core/src/main/java/org/elasticsearch/Version.java b/core/src/main/java/org/elasticsearch/Version.java index a7f01ef552848..21b0af5f8821e 100644 --- a/core/src/main/java/org/elasticsearch/Version.java +++ b/core/src/main/java/org/elasticsearch/Version.java @@ -80,6 +80,8 @@ public class Version implements Comparable { public static final Version V_5_3_2_UNRELEASED = new Version(V_5_3_2_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_2); public static final int V_5_4_0_ID_UNRELEASED = 5040099; public static final Version V_5_4_0_UNRELEASED = new Version(V_5_4_0_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_5_0); + public static final int V_5_4_1_ID_UNRELEASED = 5040199; + public static final Version V_5_4_1_UNRELEASED = new Version(V_5_4_1_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_5_0); public static final int V_5_5_0_ID_UNRELEASED = 5050099; public static final Version V_5_5_0_UNRELEASED = new Version(V_5_5_0_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_5_0); public static final int V_6_0_0_alpha1_ID_UNRELEASED = 6000001; @@ -104,6 +106,8 @@ public static Version fromId(int id) { return V_6_0_0_alpha1_UNRELEASED; case V_5_5_0_ID_UNRELEASED: return V_5_5_0_UNRELEASED; + case V_5_4_1_ID_UNRELEASED: + return V_5_4_1_UNRELEASED; case V_5_4_0_ID_UNRELEASED: return V_5_4_0_UNRELEASED; case V_5_3_2_ID_UNRELEASED: diff --git a/core/src/test/resources/indices/bwc/index-5.4.0.zip b/core/src/test/resources/indices/bwc/index-5.4.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..e8473b3aa30bb25e1d777f3320d0f7f1e62dde96 GIT binary patch literal 385816 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

VSnop??DX6! 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
*H{0u>}=J82vwz`8 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

rS2`r*Di7z`} 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 ZAaDN`F-943gem=YuUI&fiaGg-gdZ?Yf1fuqX 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%0A1hG)B961M*+BUbzK*T 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 OkVrwGJzxY7=>Gr~slns` literal 0 HcmV?d00001 diff --git a/core/src/test/resources/indices/bwc/repo-5.4.0.zip b/core/src/test/resources/indices/bwc/repo-5.4.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..53c565b4776273c188114fbd3d261905ff085d0e GIT binary patch literal 189447 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 Date: Thu, 4 May 2017 14:21:28 +0300 Subject: [PATCH 193/619] Tests: Switch to fedora-25 in test plugin Use fedora-25 Vagrant box in VagrantTestPlugin, which was missing from 9a3ab3e8000be059ccf4edeee53b62ddb5913fc6 causing packaging test failures. Additionally update TESTING.asciidoc --- TESTING.asciidoc | 6 +++--- .../elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TESTING.asciidoc b/TESTING.asciidoc index 216100c07da39..f8a54abee894e 100644 --- a/TESTING.asciidoc +++ b/TESTING.asciidoc @@ -350,7 +350,7 @@ These are the linux flavors the Vagrantfile currently supports: * debian-8 aka jessie, the current debian stable distribution * centos-6 * centos-7 -* fedora-24 +* fedora-25 * oel-6 aka Oracle Enterprise Linux 6 * oel-7 aka Oracle Enterprise Linux 7 * sles-12 @@ -436,13 +436,13 @@ vagrantUbuntu1604#up. Once up, you can then connect to the VM using SSH from the elasticsearch directory: ------------------------------------------------- -vagrant ssh fedora-24 +vagrant ssh fedora-25 ------------------------------------------------- Or from another directory: ------------------------------------------------- -VAGRANT_CWD=/path/to/elasticsearch vagrant ssh fedora-24 +VAGRANT_CWD=/path/to/elasticsearch vagrant ssh fedora-25 ------------------------------------------------- Note: Starting vagrant VM outside of the elasticsearch folder requires to diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy index 2fb047e93051d..d314258766a43 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy @@ -17,7 +17,7 @@ class VagrantTestPlugin implements Plugin { 'centos-6', 'centos-7', 'debian-8', - 'fedora-24', + 'fedora-25', 'oel-6', 'oel-7', 'opensuse-13', From 07f106d39c0acd71d1ad7773d2743896e513199b Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Thu, 4 May 2017 14:14:22 +0200 Subject: [PATCH 194/619] [TEST] Rollback temporarily disabled field_caps test (#24483) --- .../rest-api-spec/test/multi_cluster/30_field_caps.yaml | 4 ++-- .../resources/rest-api-spec/test/field_caps/10_basic.yaml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yaml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yaml index d3922ca46cb73..b5be2f7e124cd 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yaml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yaml @@ -1,8 +1,8 @@ --- "Get simple field caps from remote cluster": - skip: - version: " - 5.99.99" - reason: this uses a new API that has been added in 6.0.0 + version: " - 5.4.99" + reason: this uses a new API functionality that has been added in 5.5.0 - do: indices.create: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml index be2fd820a124a..cef72b6e3fe43 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml @@ -76,7 +76,7 @@ setup: --- "Get simple field caps": - skip: - version: " - 5.99.99" # temporarily disabled until bwc code is backported and propagated + version: " - 5.3.99" reason: this uses a new API that has been added in 5.4.0 - do: @@ -117,7 +117,7 @@ setup: --- "Get nested field caps": - skip: - version: " - 5.99.99" # temporarily disabled until bwc code is backported and propagated + version: " - 5.3.99" reason: this uses a new API that has been added in 5.4.0 - do: @@ -148,7 +148,7 @@ setup: --- "Get prefix field caps": - skip: - version: " - 5.99.99" # temporarily disabled until bwc code is backported and propagated + version: " - 5.3.99" reason: this uses a new API that has been added in 5.4.0 - do: From 8356df0846b41f1384ac66912f705eed9ab28801 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Thu, 4 May 2017 14:29:59 +0200 Subject: [PATCH 195/619] [TEST] Add a test that alias requests are dense for all indices --- .../action/search/TransportSearchAction.java | 10 +++++----- .../elasticsearch/search/internal/AliasFilter.java | 2 ++ .../rest-api-spec/test/multi_cluster/10_basic.yaml | 12 ++++++++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 501504631bca4..9c8d1a879b13d 100644 --- a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -212,25 +212,25 @@ static BiFunction processRemoteShards(Map indicesAndFilters = searchShardsResponse.getIndicesAndFilters(); + final Map indicesAndFilters = searchShardsResponse.getIndicesAndFilters(); for (ClusterSearchShardsGroup clusterSearchShardsGroup : searchShardsResponse.getGroups()) { //add the cluster name to the remote index names for indices disambiguation //this ends up in the hits returned with the search response ShardId shardId = clusterSearchShardsGroup.getShardId(); Index remoteIndex = shardId.getIndex(); - Index index = new Index(clusterAlias + RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR + remoteIndex.getName(), + Index index = new Index(RemoteClusterAware.buildRemoteIndexName(clusterAlias, remoteIndex.getName()), remoteIndex.getUUID()); OriginalIndices originalIndices = remoteIndicesByCluster.get(clusterAlias); assert originalIndices != null; SearchShardIterator shardIterator = new SearchShardIterator(clusterAlias, new ShardId(index, shardId.getId()), Arrays.asList(clusterSearchShardsGroup.getShards()), originalIndices); remoteShardIterators.add(shardIterator); - AliasFilter aliasFilter; + final AliasFilter aliasFilter; if (indicesAndFilters == null) { - aliasFilter = new AliasFilter(null, Strings.EMPTY_ARRAY); + aliasFilter = AliasFilter.EMPTY; } else { aliasFilter = indicesAndFilters.get(shardId.getIndexName()); - assert aliasFilter != null; + assert aliasFilter != null : "alias filter must not be null for index: " + shardId.getIndex(); } // here we have to map the filters to the UUID since from now on we use the uuid for the lookup aliasFilterMap.put(remoteIndex.getUUID(), aliasFilter); diff --git a/core/src/main/java/org/elasticsearch/search/internal/AliasFilter.java b/core/src/main/java/org/elasticsearch/search/internal/AliasFilter.java index f053b3d48fee5..46fba77627749 100644 --- a/core/src/main/java/org/elasticsearch/search/internal/AliasFilter.java +++ b/core/src/main/java/org/elasticsearch/search/internal/AliasFilter.java @@ -44,6 +44,8 @@ public final class AliasFilter implements Writeable { private final QueryBuilder filter; private final boolean reparseAliases; + public static final AliasFilter EMPTY = new AliasFilter(null, Strings.EMPTY_ARRAY); + public AliasFilter(QueryBuilder filter, String... aliases) { this.aliases = aliases == null ? Strings.EMPTY_ARRAY : aliases; this.filter = filter; diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yaml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yaml index bca0703d457bf..e6b0e9d13c0fc 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yaml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yaml @@ -153,3 +153,15 @@ - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } - match: { hits.hits.0._index: "my_remote_cluster:test_index" } + +--- +"Search an filtered alias and empty index on the remote cluster": + + - do: + search: + index: my_remote_cluster:aliased_test_index,my_remote_cluster:field_caps_index_1 + + - match: { _shards.total: 8 } + - match: { hits.total: 2 } + - match: { hits.hits.0._source.filter_field: 1 } + - match: { hits.hits.0._index: "my_remote_cluster:test_index" } From c4002e5ca44fb799bc51a0d67ee9a68961b0b0de Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Thu, 4 May 2017 14:50:05 +0200 Subject: [PATCH 196/619] Bump the Lucene version for ES V5_4_1 --- core/src/main/java/org/elasticsearch/Version.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/Version.java b/core/src/main/java/org/elasticsearch/Version.java index 21b0af5f8821e..ea25a1bad8ae2 100644 --- a/core/src/main/java/org/elasticsearch/Version.java +++ b/core/src/main/java/org/elasticsearch/Version.java @@ -81,7 +81,7 @@ public class Version implements Comparable { public static final int V_5_4_0_ID_UNRELEASED = 5040099; public static final Version V_5_4_0_UNRELEASED = new Version(V_5_4_0_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_5_0); public static final int V_5_4_1_ID_UNRELEASED = 5040199; - public static final Version V_5_4_1_UNRELEASED = new Version(V_5_4_1_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_5_0); + public static final Version V_5_4_1_UNRELEASED = new Version(V_5_4_1_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_5_1); public static final int V_5_5_0_ID_UNRELEASED = 5050099; public static final Version V_5_5_0_UNRELEASED = new Version(V_5_5_0_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_5_0); public static final int V_6_0_0_alpha1_ID_UNRELEASED = 6000001; From 1fc777b6e36d30da7bb93f25da45944322febacf Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Thu, 4 May 2017 09:22:29 -0400 Subject: [PATCH 197/619] Change logging level on reroute test We are still chasing a test failure here and increasing the logging level stopped the failures. We have a theory as to what is going on so this commit reduces the logging level to hopefully trigger the failure again and give us the logging that we need to confirm the theory. --- .../org/elasticsearch/indices/recovery/IndexRecoveryIT.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java b/core/src/test/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java index 5fb59245935c5..f0f38ba48c477 100644 --- a/core/src/test/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java +++ b/core/src/test/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java @@ -242,13 +242,9 @@ public void testReplicaRecovery() throws Exception { @TestLogging( "_root:DEBUG," - + "org.elasticsearch.action.bulk:TRACE," - + "org.elasticsearch.action.get:TRACE," + "org.elasticsearch.cluster.service:TRACE," - + "org.elasticsearch.discovery:TRACE," + "org.elasticsearch.indices.cluster:TRACE," + "org.elasticsearch.indices.recovery:TRACE," - + "org.elasticsearch.index.seqno:TRACE," + "org.elasticsearch.index.shard:TRACE") public void testRerouteRecovery() throws Exception { logger.info("--> start node A"); From 953add8e70ac7ddd789bade84819ba86605c7bc4 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Thu, 4 May 2017 15:56:47 +0200 Subject: [PATCH 198/619] Add 5.4.0 to bwc versions --- qa/vagrant/versions | 1 + 1 file changed, 1 insertion(+) diff --git a/qa/vagrant/versions b/qa/vagrant/versions index 996b8f7e8a29e..e65c667a24073 100644 --- a/qa/vagrant/versions +++ b/qa/vagrant/versions @@ -9,3 +9,4 @@ 5.3.0 5.3.1 5.3.2 +5.4.0 From d928ae210d6bf1a10b50d8057d94641e3ffd0cc5 Mon Sep 17 00:00:00 2001 From: James Baiera Date: Thu, 4 May 2017 10:17:55 -0400 Subject: [PATCH 199/619] Add Vagrant based testing fixture (#24249) --- .../gradle/test/AntFixture.groovy | 291 ++++++++++++++++++ .../gradle/test/ClusterFormationTasks.groovy | 2 +- .../elasticsearch/gradle/test/Fixture.groovy | 265 +--------------- .../gradle/test/RestIntegTestTask.groovy | 4 +- .../gradle/test/VagrantFixture.groovy | 54 ++++ .../gradle/vagrant/BatsOverVagrantTask.groovy | 11 +- .../gradle/vagrant/VagrantCommandTask.groovy | 36 ++- .../gradle/vagrant/VagrantTestPlugin.groovy | 13 +- plugins/jvm-example/build.gradle | 2 +- plugins/repository-hdfs/build.gradle | 2 +- 10 files changed, 401 insertions(+), 279 deletions(-) create mode 100644 buildSrc/src/main/groovy/org/elasticsearch/gradle/test/AntFixture.groovy create mode 100644 buildSrc/src/main/groovy/org/elasticsearch/gradle/test/VagrantFixture.groovy diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/AntFixture.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/AntFixture.groovy new file mode 100644 index 0000000000000..34c3046aa2b6b --- /dev/null +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/AntFixture.groovy @@ -0,0 +1,291 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.gradle.test + +import org.apache.tools.ant.taskdefs.condition.Os +import org.elasticsearch.gradle.AntTask +import org.elasticsearch.gradle.LoggedExec +import org.gradle.api.GradleException +import org.gradle.api.Task +import org.gradle.api.tasks.Exec +import org.gradle.api.tasks.Input + +/** + * A fixture for integration tests which runs in a separate process launched by Ant. + */ +public class AntFixture extends AntTask implements Fixture { + + /** The path to the executable that starts the fixture. */ + @Input + String executable + + private final List arguments = new ArrayList<>() + + @Input + public void args(Object... args) { + arguments.addAll(args) + } + + /** + * Environment variables for the fixture process. The value can be any object, which + * will have toString() called at execution time. + */ + private final Map environment = new HashMap<>() + + @Input + public void env(String key, Object value) { + environment.put(key, value) + } + + /** A flag to indicate whether the command should be executed from a shell. */ + @Input + boolean useShell = false + + /** + * A flag to indicate whether the fixture should be run in the foreground, or spawned. + * It is protected so subclasses can override (eg RunTask). + */ + protected boolean spawn = true + + /** + * A closure to call before the fixture is considered ready. The closure is passed the fixture object, + * as well as a groovy AntBuilder, to enable running ant condition checks. The default wait + * condition is for http on the http port. + */ + @Input + Closure waitCondition = { AntFixture fixture, AntBuilder ant -> + File tmpFile = new File(fixture.cwd, 'wait.success') + ant.get(src: "http://${fixture.addressAndPort}", + dest: tmpFile.toString(), + ignoreerrors: true, // do not fail on error, so logging information can be flushed + retries: 10) + return tmpFile.exists() + } + + private final Task stopTask + + public AntFixture() { + stopTask = createStopTask() + finalizedBy(stopTask) + } + + @Override + public Task getStopTask() { + return stopTask + } + + @Override + protected void runAnt(AntBuilder ant) { + project.delete(baseDir) // reset everything + cwd.mkdirs() + final String realExecutable + final List realArgs = new ArrayList<>() + final Map realEnv = environment + // We need to choose which executable we are using. In shell mode, or when we + // are spawning and thus using the wrapper script, the executable is the shell. + if (useShell || spawn) { + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + realExecutable = 'cmd' + realArgs.add('/C') + realArgs.add('"') // quote the entire command + } else { + realExecutable = 'sh' + } + } else { + realExecutable = executable + realArgs.addAll(arguments) + } + if (spawn) { + writeWrapperScript(executable) + realArgs.add(wrapperScript) + realArgs.addAll(arguments) + } + if (Os.isFamily(Os.FAMILY_WINDOWS) && (useShell || spawn)) { + realArgs.add('"') + } + commandString.eachLine { line -> logger.info(line) } + + ant.exec(executable: realExecutable, spawn: spawn, dir: cwd, taskname: name) { + realEnv.each { key, value -> env(key: key, value: value) } + realArgs.each { arg(value: it) } + } + + String failedProp = "failed${name}" + // first wait for resources, or the failure marker from the wrapper script + ant.waitfor(maxwait: '30', maxwaitunit: 'second', checkevery: '500', checkeveryunit: 'millisecond', timeoutproperty: failedProp) { + or { + resourceexists { + file(file: failureMarker.toString()) + } + and { + resourceexists { + file(file: pidFile.toString()) + } + resourceexists { + file(file: portsFile.toString()) + } + } + } + } + + if (ant.project.getProperty(failedProp) || failureMarker.exists()) { + fail("Failed to start ${name}") + } + + // the process is started (has a pid) and is bound to a network interface + // so now wait undil the waitCondition has been met + // TODO: change this to a loop? + boolean success + try { + success = waitCondition(this, ant) == false + } catch (Exception e) { + String msg = "Wait condition caught exception for ${name}" + logger.error(msg, e) + fail(msg, e) + } + if (success == false) { + fail("Wait condition failed for ${name}") + } + } + + /** Returns a debug string used to log information about how the fixture was run. */ + protected String getCommandString() { + String commandString = "\n${name} configuration:\n" + commandString += "-----------------------------------------\n" + commandString += " cwd: ${cwd}\n" + commandString += " command: ${executable} ${arguments.join(' ')}\n" + commandString += ' environment:\n' + environment.each { k, v -> commandString += " ${k}: ${v}\n" } + if (spawn) { + commandString += "\n [${wrapperScript.name}]\n" + wrapperScript.eachLine('UTF-8', { line -> commandString += " ${line}\n"}) + } + return commandString + } + + /** + * Writes a script to run the real executable, so that stdout/stderr can be captured. + * TODO: this could be removed if we do use our own ProcessBuilder and pump output from the process + */ + private void writeWrapperScript(String executable) { + wrapperScript.parentFile.mkdirs() + String argsPasser = '"$@"' + String exitMarker = "; if [ \$? != 0 ]; then touch run.failed; fi" + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + argsPasser = '%*' + exitMarker = "\r\n if \"%errorlevel%\" neq \"0\" ( type nul >> run.failed )" + } + wrapperScript.setText("\"${executable}\" ${argsPasser} > run.log 2>&1 ${exitMarker}", 'UTF-8') + } + + /** Fail the build with the given message, and logging relevant info*/ + private void fail(String msg, Exception... suppressed) { + if (logger.isInfoEnabled() == false) { + // We already log the command at info level. No need to do it twice. + commandString.eachLine { line -> logger.error(line) } + } + logger.error("${name} output:") + logger.error("-----------------------------------------") + logger.error(" failure marker exists: ${failureMarker.exists()}") + logger.error(" pid file exists: ${pidFile.exists()}") + logger.error(" ports file exists: ${portsFile.exists()}") + // also dump the log file for the startup script (which will include ES logging output to stdout) + if (runLog.exists()) { + logger.error("\n [log]") + runLog.eachLine { line -> logger.error(" ${line}") } + } + logger.error("-----------------------------------------") + GradleException toThrow = new GradleException(msg) + for (Exception e : suppressed) { + toThrow.addSuppressed(e) + } + throw toThrow + } + + /** Adds a task to kill an elasticsearch node with the given pidfile */ + private Task createStopTask() { + final AntFixture fixture = this + final Object pid = "${ -> fixture.pid }" + Exec stop = project.tasks.create(name: "${name}#stop", type: LoggedExec) + stop.onlyIf { fixture.pidFile.exists() } + stop.doFirst { + logger.info("Shutting down ${fixture.name} with pid ${pid}") + } + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + stop.executable = 'Taskkill' + stop.args('/PID', pid, '/F') + } else { + stop.executable = 'kill' + stop.args('-9', pid) + } + stop.doLast { + project.delete(fixture.pidFile) + } + return stop + } + + /** + * A path relative to the build dir that all configuration and runtime files + * will live in for this fixture + */ + protected File getBaseDir() { + return new File(project.buildDir, "fixtures/${name}") + } + + /** Returns the working directory for the process. Defaults to "cwd" inside baseDir. */ + protected File getCwd() { + return new File(baseDir, 'cwd') + } + + /** Returns the file the process writes its pid to. Defaults to "pid" inside baseDir. */ + protected File getPidFile() { + return new File(baseDir, 'pid') + } + + /** Reads the pid file and returns the process' pid */ + public int getPid() { + return Integer.parseInt(pidFile.getText('UTF-8').trim()) + } + + /** Returns the file the process writes its bound ports to. Defaults to "ports" inside baseDir. */ + protected File getPortsFile() { + return new File(baseDir, 'ports') + } + + /** Returns an address and port suitable for a uri to connect to this node over http */ + public String getAddressAndPort() { + return portsFile.readLines("UTF-8").get(0) + } + + /** Returns a file that wraps around the actual command when {@code spawn == true}. */ + protected File getWrapperScript() { + return new File(cwd, Os.isFamily(Os.FAMILY_WINDOWS) ? 'run.bat' : 'run') + } + + /** Returns a file that the wrapper script writes when the command failed. */ + protected File getFailureMarker() { + return new File(cwd, 'run.failed') + } + + /** Returns a file that the wrapper script writes when the command failed. */ + protected File getRunLog() { + return new File(cwd, 'run.log') + } +} diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/ClusterFormationTasks.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/ClusterFormationTasks.groovy index 8a4dc72d15a2a..4e9d8a63f0f0e 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/ClusterFormationTasks.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/ClusterFormationTasks.groovy @@ -208,7 +208,7 @@ class ClusterFormationTasks { start.finalizedBy(stop) for (Object dependency : config.dependencies) { if (dependency instanceof Fixture) { - Task depStop = ((Fixture)dependency).stopTask + def depStop = ((Fixture)dependency).stopTask runner.finalizedBy(depStop) start.finalizedBy(depStop) } diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/Fixture.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/Fixture.groovy index 46b81624ba3fa..498a1627b3598 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/Fixture.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/Fixture.groovy @@ -16,272 +16,15 @@ * specific language governing permissions and limitations * under the License. */ - package org.elasticsearch.gradle.test -import org.apache.tools.ant.taskdefs.condition.Os -import org.elasticsearch.gradle.AntTask -import org.elasticsearch.gradle.LoggedExec -import org.gradle.api.GradleException -import org.gradle.api.Task -import org.gradle.api.tasks.Exec -import org.gradle.api.tasks.Input - /** - * A fixture for integration tests which runs in a separate process. + * Any object that can produce an accompanying stop task, meant to tear down + * a previously instantiated service. */ -public class Fixture extends AntTask { - - /** The path to the executable that starts the fixture. */ - @Input - String executable - - private final List arguments = new ArrayList<>() - - @Input - public void args(Object... args) { - arguments.addAll(args) - } - - /** - * Environment variables for the fixture process. The value can be any object, which - * will have toString() called at execution time. - */ - private final Map environment = new HashMap<>() - - @Input - public void env(String key, Object value) { - environment.put(key, value) - } - - /** A flag to indicate whether the command should be executed from a shell. */ - @Input - boolean useShell = false - - /** - * A flag to indicate whether the fixture should be run in the foreground, or spawned. - * It is protected so subclasses can override (eg RunTask). - */ - protected boolean spawn = true - - /** - * A closure to call before the fixture is considered ready. The closure is passed the fixture object, - * as well as a groovy AntBuilder, to enable running ant condition checks. The default wait - * condition is for http on the http port. - */ - @Input - Closure waitCondition = { Fixture fixture, AntBuilder ant -> - File tmpFile = new File(fixture.cwd, 'wait.success') - ant.get(src: "http://${fixture.addressAndPort}", - dest: tmpFile.toString(), - ignoreerrors: true, // do not fail on error, so logging information can be flushed - retries: 10) - return tmpFile.exists() - } +public interface Fixture { /** A task which will stop this fixture. This should be used as a finalizedBy for any tasks that use the fixture. */ - public final Task stopTask - - public Fixture() { - stopTask = createStopTask() - finalizedBy(stopTask) - } - - @Override - protected void runAnt(AntBuilder ant) { - project.delete(baseDir) // reset everything - cwd.mkdirs() - final String realExecutable - final List realArgs = new ArrayList<>() - final Map realEnv = environment - // We need to choose which executable we are using. In shell mode, or when we - // are spawning and thus using the wrapper script, the executable is the shell. - if (useShell || spawn) { - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - realExecutable = 'cmd' - realArgs.add('/C') - realArgs.add('"') // quote the entire command - } else { - realExecutable = 'sh' - } - } else { - realExecutable = executable - realArgs.addAll(arguments) - } - if (spawn) { - writeWrapperScript(executable) - realArgs.add(wrapperScript) - realArgs.addAll(arguments) - } - if (Os.isFamily(Os.FAMILY_WINDOWS) && (useShell || spawn)) { - realArgs.add('"') - } - commandString.eachLine { line -> logger.info(line) } - - ant.exec(executable: realExecutable, spawn: spawn, dir: cwd, taskname: name) { - realEnv.each { key, value -> env(key: key, value: value) } - realArgs.each { arg(value: it) } - } - - String failedProp = "failed${name}" - // first wait for resources, or the failure marker from the wrapper script - ant.waitfor(maxwait: '30', maxwaitunit: 'second', checkevery: '500', checkeveryunit: 'millisecond', timeoutproperty: failedProp) { - or { - resourceexists { - file(file: failureMarker.toString()) - } - and { - resourceexists { - file(file: pidFile.toString()) - } - resourceexists { - file(file: portsFile.toString()) - } - } - } - } - - if (ant.project.getProperty(failedProp) || failureMarker.exists()) { - fail("Failed to start ${name}") - } - - // the process is started (has a pid) and is bound to a network interface - // so now wait undil the waitCondition has been met - // TODO: change this to a loop? - boolean success - try { - success = waitCondition(this, ant) == false - } catch (Exception e) { - String msg = "Wait condition caught exception for ${name}" - logger.error(msg, e) - fail(msg, e) - } - if (success == false) { - fail("Wait condition failed for ${name}") - } - } - - /** Returns a debug string used to log information about how the fixture was run. */ - protected String getCommandString() { - String commandString = "\n${name} configuration:\n" - commandString += "-----------------------------------------\n" - commandString += " cwd: ${cwd}\n" - commandString += " command: ${executable} ${arguments.join(' ')}\n" - commandString += ' environment:\n' - environment.each { k, v -> commandString += " ${k}: ${v}\n" } - if (spawn) { - commandString += "\n [${wrapperScript.name}]\n" - wrapperScript.eachLine('UTF-8', { line -> commandString += " ${line}\n"}) - } - return commandString - } - - /** - * Writes a script to run the real executable, so that stdout/stderr can be captured. - * TODO: this could be removed if we do use our own ProcessBuilder and pump output from the process - */ - private void writeWrapperScript(String executable) { - wrapperScript.parentFile.mkdirs() - String argsPasser = '"$@"' - String exitMarker = "; if [ \$? != 0 ]; then touch run.failed; fi" - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - argsPasser = '%*' - exitMarker = "\r\n if \"%errorlevel%\" neq \"0\" ( type nul >> run.failed )" - } - wrapperScript.setText("\"${executable}\" ${argsPasser} > run.log 2>&1 ${exitMarker}", 'UTF-8') - } - - /** Fail the build with the given message, and logging relevant info*/ - private void fail(String msg, Exception... suppressed) { - if (logger.isInfoEnabled() == false) { - // We already log the command at info level. No need to do it twice. - commandString.eachLine { line -> logger.error(line) } - } - logger.error("${name} output:") - logger.error("-----------------------------------------") - logger.error(" failure marker exists: ${failureMarker.exists()}") - logger.error(" pid file exists: ${pidFile.exists()}") - logger.error(" ports file exists: ${portsFile.exists()}") - // also dump the log file for the startup script (which will include ES logging output to stdout) - if (runLog.exists()) { - logger.error("\n [log]") - runLog.eachLine { line -> logger.error(" ${line}") } - } - logger.error("-----------------------------------------") - GradleException toThrow = new GradleException(msg) - for (Exception e : suppressed) { - toThrow.addSuppressed(e) - } - throw toThrow - } - - /** Adds a task to kill an elasticsearch node with the given pidfile */ - private Task createStopTask() { - final Fixture fixture = this - final Object pid = "${ -> fixture.pid }" - Exec stop = project.tasks.create(name: "${name}#stop", type: LoggedExec) - stop.onlyIf { fixture.pidFile.exists() } - stop.doFirst { - logger.info("Shutting down ${fixture.name} with pid ${pid}") - } - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - stop.executable = 'Taskkill' - stop.args('/PID', pid, '/F') - } else { - stop.executable = 'kill' - stop.args('-9', pid) - } - stop.doLast { - project.delete(fixture.pidFile) - } - return stop - } - - /** - * A path relative to the build dir that all configuration and runtime files - * will live in for this fixture - */ - protected File getBaseDir() { - return new File(project.buildDir, "fixtures/${name}") - } - - /** Returns the working directory for the process. Defaults to "cwd" inside baseDir. */ - protected File getCwd() { - return new File(baseDir, 'cwd') - } - - /** Returns the file the process writes its pid to. Defaults to "pid" inside baseDir. */ - protected File getPidFile() { - return new File(baseDir, 'pid') - } - - /** Reads the pid file and returns the process' pid */ - public int getPid() { - return Integer.parseInt(pidFile.getText('UTF-8').trim()) - } - - /** Returns the file the process writes its bound ports to. Defaults to "ports" inside baseDir. */ - protected File getPortsFile() { - return new File(baseDir, 'ports') - } - - /** Returns an address and port suitable for a uri to connect to this node over http */ - public String getAddressAndPort() { - return portsFile.readLines("UTF-8").get(0) - } - - /** Returns a file that wraps around the actual command when {@code spawn == true}. */ - protected File getWrapperScript() { - return new File(cwd, Os.isFamily(Os.FAMILY_WINDOWS) ? 'run.bat' : 'run') - } - - /** Returns a file that the wrapper script writes when the command failed. */ - protected File getFailureMarker() { - return new File(cwd, 'run.failed') - } + public Object getStopTask() - /** Returns a file that the wrapper script writes when the command failed. */ - protected File getRunLog() { - return new File(cwd, 'run.log') - } } diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/RestIntegTestTask.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/RestIntegTestTask.groovy index 98ee91e37a8ab..0b19822a7f154 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/RestIntegTestTask.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/RestIntegTestTask.groovy @@ -129,7 +129,7 @@ public class RestIntegTestTask extends DefaultTask { runner.dependsOn(dependencies) for (Object dependency : dependencies) { if (dependency instanceof Fixture) { - runner.finalizedBy(((Fixture)dependency).stopTask) + runner.finalizedBy(((Fixture)dependency).getStopTask()) } } return this @@ -140,7 +140,7 @@ public class RestIntegTestTask extends DefaultTask { runner.setDependsOn(dependencies) for (Object dependency : dependencies) { if (dependency instanceof Fixture) { - runner.finalizedBy(((Fixture)dependency).stopTask) + runner.finalizedBy(((Fixture)dependency).getStopTask()) } } } diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/VagrantFixture.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/VagrantFixture.groovy new file mode 100644 index 0000000000000..fa08a8f9c6667 --- /dev/null +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/VagrantFixture.groovy @@ -0,0 +1,54 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.gradle.test + +import org.elasticsearch.gradle.vagrant.VagrantCommandTask +import org.gradle.api.Task + +/** + * A fixture for integration tests which runs in a virtual machine launched by Vagrant. + */ +class VagrantFixture extends VagrantCommandTask implements Fixture { + + private VagrantCommandTask stopTask + + public VagrantFixture() { + this.stopTask = project.tasks.create(name: "${name}#stop", type: VagrantCommandTask) { + command 'halt' + } + finalizedBy this.stopTask + } + + @Override + void setBoxName(String boxName) { + super.setBoxName(boxName) + this.stopTask.setBoxName(boxName) + } + + @Override + void setEnvironmentVars(Map environmentVars) { + super.setEnvironmentVars(environmentVars) + this.stopTask.setEnvironmentVars(environmentVars) + } + + @Override + public Task getStopTask() { + return this.stopTask + } +} diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/BatsOverVagrantTask.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/BatsOverVagrantTask.groovy index 65b90c4d9a0cd..110f2fc7e8461 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/BatsOverVagrantTask.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/BatsOverVagrantTask.groovy @@ -27,12 +27,15 @@ import org.gradle.api.tasks.Input public class BatsOverVagrantTask extends VagrantCommandTask { @Input - String command + String remoteCommand BatsOverVagrantTask() { - project.afterEvaluate { - args 'ssh', boxName, '--command', command - } + command = 'ssh' + } + + void setRemoteCommand(String remoteCommand) { + this.remoteCommand = Objects.requireNonNull(remoteCommand) + setArgs(['--command', remoteCommand]) } @Override diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantCommandTask.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantCommandTask.groovy index abc6af9e09d97..aab120e8d049a 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantCommandTask.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantCommandTask.groovy @@ -21,9 +21,15 @@ package org.elasticsearch.gradle.vagrant import org.apache.commons.io.output.TeeOutputStream import org.elasticsearch.gradle.LoggedExec import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.TaskAction import org.gradle.internal.logging.progress.ProgressLoggerFactory import javax.inject.Inject +import java.util.concurrent.CountDownLatch +import java.util.concurrent.locks.Lock +import java.util.concurrent.locks.ReadWriteLock +import java.util.concurrent.locks.ReentrantLock /** * Runs a vagrant command. Pretty much like Exec task but with a nicer output @@ -31,6 +37,12 @@ import javax.inject.Inject */ public class VagrantCommandTask extends LoggedExec { + @Input + String command + + @Input @Optional + String subcommand + @Input String boxName @@ -40,11 +52,27 @@ public class VagrantCommandTask extends LoggedExec { public VagrantCommandTask() { executable = 'vagrant' + // We're using afterEvaluate here to slot in some logic that captures configurations and + // modifies the command line right before the main execution happens. The reason that we + // call doFirst instead of just doing the work in the afterEvaluate is that the latter + // restricts how subclasses can extend functionality. Calling afterEvaluate is like having + // all the logic of a task happening at construction time, instead of at execution time + // where a subclass can override or extend the logic. project.afterEvaluate { - // It'd be nice if --machine-readable were, well, nice - standardOutput = new TeeOutputStream(standardOutput, createLoggerOutputStream()) - if (environmentVars != null) { - environment environmentVars + doFirst { + if (environmentVars != null) { + environment environmentVars + } + + // Build our command line for vagrant + def vagrantCommand = [executable, command] + if (subcommand != null) { + vagrantCommand = vagrantCommand + subcommand + } + commandLine([*vagrantCommand, boxName, *args]) + + // It'd be nice if --machine-readable were, well, nice + standardOutput = new TeeOutputStream(standardOutput, createLoggerOutputStream()) } } } diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy index d314258766a43..9df6d36ef01b0 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy @@ -391,21 +391,23 @@ class VagrantTestPlugin implements Plugin { // always add a halt task for all boxes, so clean makes sure they are all shutdown Task halt = project.tasks.create("vagrant${boxTask}#halt", VagrantCommandTask) { + command 'halt' boxName box environmentVars vagrantEnvVars - args 'halt', box } stop.dependsOn(halt) Task update = project.tasks.create("vagrant${boxTask}#update", VagrantCommandTask) { + command 'box' + subcommand 'update' boxName box environmentVars vagrantEnvVars - args 'box', 'update', box dependsOn vagrantCheckVersion, virtualboxCheckVersion } update.mustRunAfter(setupBats) Task up = project.tasks.create("vagrant${boxTask}#up", VagrantCommandTask) { + command 'up' boxName box environmentVars vagrantEnvVars /* Its important that we try to reprovision the box even if it already @@ -418,7 +420,7 @@ class VagrantTestPlugin implements Plugin { vagrant's default but its possible to change that default and folks do. But the boxes that we use are unlikely to work properly with other virtualization providers. Thus the lock. */ - args 'up', box, '--provision', '--provider', 'virtualbox' + args '--provision', '--provider', 'virtualbox' /* It'd be possible to check if the box is already up here and output SKIPPED but that would require running vagrant status which is slow! */ dependsOn update @@ -434,11 +436,11 @@ class VagrantTestPlugin implements Plugin { vagrantSmokeTest.dependsOn(smoke) Task packaging = project.tasks.create("vagrant${boxTask}#packagingTest", BatsOverVagrantTask) { + remoteCommand BATS_TEST_COMMAND boxName box environmentVars vagrantEnvVars dependsOn up, setupBats finalizedBy halt - command BATS_TEST_COMMAND } TaskExecutionAdapter packagingReproListener = new TaskExecutionAdapter() { @@ -461,11 +463,12 @@ class VagrantTestPlugin implements Plugin { } Task platform = project.tasks.create("vagrant${boxTask}#platformTest", VagrantCommandTask) { + command 'ssh' boxName box environmentVars vagrantEnvVars dependsOn up finalizedBy halt - args 'ssh', boxName, '--command', PLATFORM_TEST_COMMAND + " -Dtests.seed=${-> project.extensions.esvagrant.formattedTestSeed}" + args '--command', PLATFORM_TEST_COMMAND + " -Dtests.seed=${-> project.extensions.esvagrant.formattedTestSeed}" } TaskExecutionAdapter platformReproListener = new TaskExecutionAdapter() { @Override diff --git a/plugins/jvm-example/build.gradle b/plugins/jvm-example/build.gradle index e8a37a144a51c..fb362e6fa3603 100644 --- a/plugins/jvm-example/build.gradle +++ b/plugins/jvm-example/build.gradle @@ -33,7 +33,7 @@ dependencies { exampleFixture project(':test:fixtures:example-fixture') } -task exampleFixture(type: org.elasticsearch.gradle.test.Fixture) { +task exampleFixture(type: org.elasticsearch.gradle.test.AntFixture) { dependsOn project.configurations.exampleFixture executable = new File(project.javaHome, 'bin/java') args '-cp', "${ -> project.configurations.exampleFixture.asPath }", diff --git a/plugins/repository-hdfs/build.gradle b/plugins/repository-hdfs/build.gradle index 82548f3410e70..f17819dba40af 100644 --- a/plugins/repository-hdfs/build.gradle +++ b/plugins/repository-hdfs/build.gradle @@ -60,7 +60,7 @@ dependencyLicenses { mapping from: /hadoop-.*/, to: 'hadoop' } -task hdfsFixture(type: org.elasticsearch.gradle.test.Fixture) { +task hdfsFixture(type: org.elasticsearch.gradle.test.AntFixture) { dependsOn project.configurations.hdfsFixture executable = new File(project.javaHome, 'bin/java') env 'CLASSPATH', "${ -> project.configurations.hdfsFixture.asPath }" From 977016ba2503648af98be9aa831b4107bf6f1d1c Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Thu, 4 May 2017 16:29:35 +0200 Subject: [PATCH 200/619] Do not index `_type` when there is at most one type. (#24363) This change makes `_type` behave pretty much like `_index` when `index.mapping.single_type` is true. --- .../common/lucene/search/Queries.java | 35 +----- ...dData.java => ConstantIndexFieldData.java} | 28 +++-- .../index/mapper/DocumentMapper.java | 7 +- .../index/mapper/IndexFieldMapper.java | 4 +- .../index/mapper/TypeFieldMapper.java | 117 ++++++++++-------- .../index/query/HasChildQueryBuilder.java | 4 +- .../index/query/HasParentQueryBuilder.java | 6 +- .../index/query/QueryRewriteContext.java | 4 +- .../index/query/TypeQueryBuilder.java | 2 +- .../search/DefaultSearchContext.java | 11 +- .../children/ChildrenAggregationBuilder.java | 4 +- .../fetch/subphase/InnerHitsContext.java | 2 +- .../mapper/FieldNamesFieldMapperTests.java | 4 +- .../index/mapper/MapperServiceTests.java | 6 +- .../index/mapper/TypeFieldMapperTests.java | 82 ++++++++++-- .../index/mapper/TypeFieldTypeTests.java | 71 +++++++++-- .../ParentToChildrenAggregatorTests.java | 5 +- docs/reference/search/validate.asciidoc | 6 +- 18 files changed, 255 insertions(+), 143 deletions(-) rename core/src/main/java/org/elasticsearch/index/fielddata/plain/{IndexIndexFieldData.java => ConstantIndexFieldData.java} (83%) diff --git a/core/src/main/java/org/elasticsearch/common/lucene/search/Queries.java b/core/src/main/java/org/elasticsearch/common/lucene/search/Queries.java index acf3f9ffdf8a3..6067377754624 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/search/Queries.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/search/Queries.java @@ -21,7 +21,6 @@ import org.apache.lucene.index.Term; import org.apache.lucene.queries.ExtendedCommonTermsQuery; -import org.apache.lucene.search.AutomatonQuery; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanQuery; @@ -31,9 +30,6 @@ import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.automaton.Automata; -import org.apache.lucene.util.automaton.Automaton; -import org.apache.lucene.util.automaton.Operations; import org.elasticsearch.common.Nullable; import org.elasticsearch.index.mapper.TypeFieldMapper; @@ -42,29 +38,6 @@ public class Queries { - private static final Automaton NON_NESTED_TYPE_AUTOMATON; - static { - Automaton nestedTypeAutomaton = Operations.concatenate( - Automata.makeString("__"), - Automata.makeAnyString()); - NON_NESTED_TYPE_AUTOMATON = Operations.complement(nestedTypeAutomaton, Operations.DEFAULT_MAX_DETERMINIZED_STATES); - } - - // We use a custom class rather than AutomatonQuery directly in order to - // have a better toString - private static class NonNestedQuery extends AutomatonQuery { - - NonNestedQuery() { - super(new Term(TypeFieldMapper.NAME), NON_NESTED_TYPE_AUTOMATON); - } - - @Override - public String toString(String field) { - return "_type:[^_].*"; - } - - } - public static Query newMatchAllQuery() { return new MatchAllDocsQuery(); } @@ -79,9 +52,11 @@ public static Query newNestedFilter() { } public static Query newNonNestedFilter() { - // we use this automaton query rather than a negation of newNestedFilter - // since purely negative queries against high-cardinality clauses are costly - return new NonNestedQuery(); + // TODO: this is slow, make it a positive query + return new BooleanQuery.Builder() + .add(new MatchAllDocsQuery(), Occur.FILTER) + .add(newNestedFilter(), Occur.MUST_NOT) + .build(); } public static BooleanQuery filtered(@Nullable Query query, @Nullable Query filter) { diff --git a/core/src/main/java/org/elasticsearch/index/fielddata/plain/IndexIndexFieldData.java b/core/src/main/java/org/elasticsearch/index/fielddata/plain/ConstantIndexFieldData.java similarity index 83% rename from core/src/main/java/org/elasticsearch/index/fielddata/plain/IndexIndexFieldData.java rename to core/src/main/java/org/elasticsearch/index/fielddata/plain/ConstantIndexFieldData.java index 53832c0b5b153..ebf959e92e1a6 100644 --- a/core/src/main/java/org/elasticsearch/index/fielddata/plain/IndexIndexFieldData.java +++ b/core/src/main/java/org/elasticsearch/index/fielddata/plain/ConstantIndexFieldData.java @@ -44,26 +44,33 @@ import java.io.IOException; import java.util.Collection; import java.util.Collections; +import java.util.function.Function; -public class IndexIndexFieldData extends AbstractIndexOrdinalsFieldData { +public class ConstantIndexFieldData extends AbstractIndexOrdinalsFieldData { public static class Builder implements IndexFieldData.Builder { + private final Function valueFunction; + + public Builder(Function valueFunction) { + this.valueFunction = valueFunction; + } + @Override public IndexFieldData build(IndexSettings indexSettings, MappedFieldType fieldType, IndexFieldDataCache cache, CircuitBreakerService breakerService, MapperService mapperService) { - return new IndexIndexFieldData(indexSettings, fieldType.name()); + return new ConstantIndexFieldData(indexSettings, fieldType.name(), valueFunction.apply(mapperService)); } } - private static class IndexAtomicFieldData extends AbstractAtomicOrdinalsFieldData { + private static class ConstantAtomicFieldData extends AbstractAtomicOrdinalsFieldData { - private final String index; + private final String value; - IndexAtomicFieldData(String index) { + ConstantAtomicFieldData(String value) { super(DEFAULT_SCRIPT_FUNCTION); - this.index = index; + this.value = value; } @Override @@ -78,7 +85,7 @@ public Collection getChildResources() { @Override public SortedSetDocValues getOrdinalsValues() { - final BytesRef term = new BytesRef(index); + final BytesRef term = new BytesRef(value); final SortedDocValues sortedValues = new AbstractSortedDocValues() { private int docID = -1; @@ -120,12 +127,12 @@ public void close() { private final AtomicOrdinalsFieldData atomicFieldData; - private IndexIndexFieldData(IndexSettings indexSettings, String name) { + private ConstantIndexFieldData(IndexSettings indexSettings, String name, String value) { super(indexSettings, name, null, null, TextFieldMapper.Defaults.FIELDDATA_MIN_FREQUENCY, TextFieldMapper.Defaults.FIELDDATA_MAX_FREQUENCY, TextFieldMapper.Defaults.FIELDDATA_MIN_SEGMENT_SIZE); - atomicFieldData = new IndexAtomicFieldData(index().getName()); + atomicFieldData = new ConstantAtomicFieldData(value); } @Override @@ -144,7 +151,8 @@ public AtomicOrdinalsFieldData loadDirect(LeafReaderContext context) } @Override - public SortField sortField(@Nullable Object missingValue, MultiValueMode sortMode, XFieldComparatorSource.Nested nested, boolean reverse) { + public SortField sortField(@Nullable Object missingValue, MultiValueMode sortMode, XFieldComparatorSource.Nested nested, + boolean reverse) { final XFieldComparatorSource source = new BytesRefFieldComparatorSource(this, missingValue, sortMode, nested); return new SortField(getFieldName(), source, reverse); } diff --git a/core/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java b/core/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java index 39280bcee20d9..a8d172a51123b 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java @@ -24,17 +24,16 @@ import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; import org.elasticsearch.ElasticsearchGenerationException; -import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.text.Text; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.analysis.IndexAnalyzers; import org.elasticsearch.index.mapper.MetadataFieldMapper.TypeParser; +import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; @@ -241,8 +240,8 @@ public IndexFieldMapper IndexFieldMapper() { return metadataMapper(IndexFieldMapper.class); } - public Query typeFilter() { - return typeMapper().fieldType().termQuery(type, null); + public Query typeFilter(QueryShardContext context) { + return typeMapper().fieldType().termQuery(type, context); } public boolean hasNestedObjects() { diff --git a/core/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java b/core/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java index c961b74d77af8..c85f5d7d9fa9d 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java @@ -30,7 +30,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.fielddata.plain.IndexIndexFieldData; +import org.elasticsearch.index.fielddata.plain.ConstantIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; import java.io.IOException; @@ -157,7 +157,7 @@ private boolean isSameIndex(Object value, String indexName) { @Override public IndexFieldData.Builder fielddataBuilder() { - return new IndexIndexFieldData.Builder(); + return new ConstantIndexFieldData.Builder(mapperService -> mapperService.index().getName()); } } diff --git a/core/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java b/core/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java index c24747e62c8cc..9d4a4a6987ba3 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java @@ -30,26 +30,29 @@ import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TermInSetQuery; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.Nullable; +import org.elasticsearch.action.fieldstats.FieldStats; import org.elasticsearch.common.lucene.Lucene; +import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.plain.DocValuesIndexFieldData; -import org.elasticsearch.index.fielddata.plain.PagedBytesIndexFieldData; +import org.elasticsearch.index.fielddata.plain.ConstantIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; import java.io.IOException; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; +import java.util.function.Function; public class TypeFieldMapper extends MetadataFieldMapper { @@ -88,29 +91,12 @@ public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext c } static final class TypeFieldType extends StringFieldType { - private boolean fielddata; TypeFieldType() { - this.fielddata = false; } protected TypeFieldType(TypeFieldType ref) { super(ref); - this.fielddata = ref.fielddata; - } - - @Override - public boolean equals(Object o) { - if (super.equals(o) == false) { - return false; - } - TypeFieldType that = (TypeFieldType) o; - return fielddata == that.fielddata; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), fielddata); } @Override @@ -123,49 +109,76 @@ public String typeName() { return CONTENT_TYPE; } - public boolean fielddata() { - return fielddata; - } - - public void setFielddata(boolean fielddata) { - checkIfFrozen(); - this.fielddata = fielddata; - } - @Override public IndexFieldData.Builder fielddataBuilder() { if (hasDocValues()) { return new DocValuesIndexFieldData.Builder(); + } else { + // means the index has a single type and the type field is implicit + Function typeFunction = mapperService -> { + Collection types = mapperService.types(); + if (types.size() > 1) { + throw new AssertionError(); + } + // If we reach here, there is necessarily one type since we were able to find a `_type` field + String type = types.iterator().next(); + return type; + }; + return new ConstantIndexFieldData.Builder(typeFunction); } - assert indexOptions() != IndexOptions.NONE; - if (fielddata) { - return new PagedBytesIndexFieldData.Builder(TextFieldMapper.Defaults.FIELDDATA_MIN_FREQUENCY, - TextFieldMapper.Defaults.FIELDDATA_MAX_FREQUENCY, - TextFieldMapper.Defaults.FIELDDATA_MIN_SEGMENT_SIZE); - } - return super.fielddataBuilder(); } @Override - public Query termQuery(Object value, @Nullable QueryShardContext context) { - if (indexOptions() == IndexOptions.NONE) { - throw new AssertionError(); + public FieldStats stats(IndexReader reader) throws IOException { + if (reader.maxDoc() == 0) { + return null; } - return new TypesQuery(indexedValueForSearch(value)); + return new FieldStats.Text(reader.maxDoc(), reader.numDocs(), reader.maxDoc(), reader.maxDoc(), + isSearchable(), isAggregatable()); } @Override - public void checkCompatibility(MappedFieldType other, - List conflicts, boolean strict) { - super.checkCompatibility(other, conflicts, strict); - TypeFieldType otherType = (TypeFieldType) other; - if (strict) { - if (fielddata() != otherType.fielddata()) { - conflicts.add("mapper [" + name() + "] is used by multiple types. Set update_all_types to true to update [fielddata] " - + "across all types."); + public boolean isSearchable() { + return true; + } + + @Override + public Query termQuery(Object value, QueryShardContext context) { + return termsQuery(Arrays.asList(value), context); + } + + @Override + public Query termsQuery(List values, QueryShardContext context) { + if (context.getIndexSettings().getValue(MapperService.INDEX_MAPPING_SINGLE_TYPE_SETTING)) { + Collection indexTypes = context.getMapperService().types(); + if (indexTypes.isEmpty()) { + return new MatchNoDocsQuery("No types"); + } + assert indexTypes.size() == 1; + BytesRef indexType = indexedValueForSearch(indexTypes.iterator().next()); + if (values.stream() + .map(this::indexedValueForSearch) + .anyMatch(indexType::equals)) { + if (context.getMapperService().hasNested()) { + // type filters are expected not to match nested docs + return Queries.newNonNestedFilter(); + } else { + return new MatchAllDocsQuery(); + } + } else { + return new MatchNoDocsQuery("Type list does not contain the index type"); } + } else { + if (indexOptions() == IndexOptions.NONE) { + throw new AssertionError(); + } + final BytesRef[] types = values.stream() + .map(this::indexedValueForSearch) + .toArray(size -> new BytesRef[size]); + return new TypesQuery(types); } } + } /** @@ -261,7 +274,13 @@ private TypeFieldMapper(MappedFieldType fieldType, Settings indexSettings) { private static MappedFieldType defaultFieldType(Settings indexSettings) { MappedFieldType defaultFieldType = Defaults.FIELD_TYPE.clone(); - defaultFieldType.setHasDocValues(true); + if (MapperService.INDEX_MAPPING_SINGLE_TYPE_SETTING.get(indexSettings)) { + defaultFieldType.setIndexOptions(IndexOptions.NONE); + defaultFieldType.setHasDocValues(false); + } else { + defaultFieldType.setIndexOptions(IndexOptions.DOCS); + defaultFieldType.setHasDocValues(true); + } return defaultFieldType; } diff --git a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java index 37d06f79eb285..18ad7f9f310b5 100644 --- a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java @@ -338,10 +338,10 @@ protected Query doToQuery(QueryShardContext context) throws IOException { } // wrap the query with type query - innerQuery = Queries.filtered(innerQuery, childDocMapper.typeFilter()); + innerQuery = Queries.filtered(innerQuery, childDocMapper.typeFilter(context)); final ParentChildIndexFieldData parentChildIndexFieldData = context.getForField(parentFieldMapper.fieldType()); - return new LateParsingQuery(parentDocMapper.typeFilter(), innerQuery, minChildren(), maxChildren(), + return new LateParsingQuery(parentDocMapper.typeFilter(context), innerQuery, minChildren(), maxChildren(), parentType, scoreMode, parentChildIndexFieldData, context.getSearchSimilarity()); } diff --git a/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java index 0dd0aea15c3c3..63c9484691766 100644 --- a/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java @@ -185,18 +185,18 @@ protected Query doToQuery(QueryShardContext context) throws IOException { Query childrenQuery; if (childTypes.size() == 1) { DocumentMapper documentMapper = context.getMapperService().documentMapper(childTypes.iterator().next()); - childrenQuery = documentMapper.typeFilter(); + childrenQuery = documentMapper.typeFilter(context); } else { BooleanQuery.Builder childrenFilter = new BooleanQuery.Builder(); for (String childrenTypeStr : childTypes) { DocumentMapper documentMapper = context.getMapperService().documentMapper(childrenTypeStr); - childrenFilter.add(documentMapper.typeFilter(), BooleanClause.Occur.SHOULD); + childrenFilter.add(documentMapper.typeFilter(context), BooleanClause.Occur.SHOULD); } childrenQuery = childrenFilter.build(); } // wrap the query with type query - innerQuery = Queries.filtered(innerQuery, parentDocMapper.typeFilter()); + innerQuery = Queries.filtered(innerQuery, parentDocMapper.typeFilter(context)); return new HasChildQueryBuilder.LateParsingQuery(childrenQuery, innerQuery, HasChildQueryBuilder.DEFAULT_MIN_CHILDREN, diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java b/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java index 036421159767c..24e42b41b2022 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java @@ -69,14 +69,14 @@ public Client getClient() { * Returns the index settings for this context. This might return null if the * context has not index scope. */ - public final IndexSettings getIndexSettings() { + public IndexSettings getIndexSettings() { return indexSettings; } /** * Return the MapperService. */ - public final MapperService getMapperService() { + public MapperService getMapperService() { return mapperService; } diff --git a/core/src/main/java/org/elasticsearch/index/query/TypeQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/TypeQueryBuilder.java index 88e655555f8da..fc4df8efa8190 100644 --- a/core/src/main/java/org/elasticsearch/index/query/TypeQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/TypeQueryBuilder.java @@ -132,7 +132,7 @@ protected Query doToQuery(QueryShardContext context) throws IOException { // no type means no documents return new MatchNoDocsQuery(); } else { - return documentMapper.typeFilter(); + return documentMapper.typeFilter(context); } } diff --git a/core/src/main/java/org/elasticsearch/search/DefaultSearchContext.java b/core/src/main/java/org/elasticsearch/search/DefaultSearchContext.java index 6619c1ab9e5a9..45b8967545414 100644 --- a/core/src/main/java/org/elasticsearch/search/DefaultSearchContext.java +++ b/core/src/main/java/org/elasticsearch/search/DefaultSearchContext.java @@ -76,6 +76,7 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -290,13 +291,13 @@ && new NestedHelper(mapperService()).mightMatchNestedDocs(query) } } - private static Query createTypeFilter(String[] types) { + private Query createTypeFilter(String[] types) { if (types != null && types.length >= 1) { - BytesRef[] typesBytes = new BytesRef[types.length]; - for (int i = 0; i < typesBytes.length; i++) { - typesBytes[i] = new BytesRef(types[i]); + MappedFieldType ft = mapperService().fullName(TypeFieldMapper.NAME); + if (ft != null) { + // ft might be null if no documents have been indexed yet + return ft.termsQuery(Arrays.asList(types), queryShardContext); } - return new TypeFieldMapper.TypesQuery(typesBytes); } return null; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregationBuilder.java index ddd252a6f5353..3a0d2fff982d8 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregationBuilder.java @@ -98,8 +98,8 @@ protected ValuesSourceConfig resolveConfig(SearchContext context) { parentType = parentFieldMapper.type(); DocumentMapper parentDocMapper = context.mapperService().documentMapper(parentType); if (parentDocMapper != null) { - parentFilter = parentDocMapper.typeFilter(); - childFilter = childDocMapper.typeFilter(); + parentFilter = parentDocMapper.typeFilter(context.getQueryShardContext()); + childFilter = childDocMapper.typeFilter(context.getQueryShardContext()); ParentChildIndexFieldData parentChildIndexFieldData = context.fieldData() .getForField(parentFieldMapper.fieldType()); config.fieldContext(new FieldContext(parentFieldMapper.fieldType().name(), parentChildIndexFieldData, diff --git a/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsContext.java b/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsContext.java index aa4168bd0402b..e84fcc1f51dc8 100644 --- a/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsContext.java +++ b/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsContext.java @@ -180,7 +180,7 @@ public TopDocs topDocs(SearchContext context, FetchSubPhase.HitContext hitContex // Only include docs that have the current hit as parent .add(hitQuery, Occur.FILTER) // Only include docs that have this inner hits type - .add(documentMapper.typeFilter(), Occur.FILTER) + .add(documentMapper.typeFilter(context.getQueryShardContext()), Occur.FILTER) .build(); if (size() == 0) { final int count = context.searcher().count(q); diff --git a/core/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java index aa66526bf42f7..aedd332471ae6 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java @@ -99,7 +99,7 @@ public void testInjectIntoDocDuringParsing() throws Exception { .bytes(), XContentType.JSON)); - assertFieldNames(set("a", "a.keyword", "b", "b.c", "_uid", "_type", "_version", "_seq_no", "_primary_term", "_source"), doc); + assertFieldNames(set("a", "a.keyword", "b", "b.c", "_uid", "_version", "_seq_no", "_primary_term", "_source"), doc); } public void testExplicitEnabled() throws Exception { @@ -117,7 +117,7 @@ public void testExplicitEnabled() throws Exception { .bytes(), XContentType.JSON)); - assertFieldNames(set("field", "field.keyword", "_uid", "_type", "_version", "_seq_no", "_primary_term", "_source"), doc); + assertFieldNames(set("field", "field.keyword", "_uid", "_version", "_seq_no", "_primary_term", "_source"), doc); } public void testDisabled() throws Exception { diff --git a/core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java b/core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java index cc853b5b9e5be..cb0b922e19703 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java @@ -290,13 +290,13 @@ public void testPartitionedConstraints() { public void testIndexSortWithNestedFields() throws IOException { Settings settings = Settings.builder() - .put("index.sort.field", "_type") + .put("index.sort.field", "foo") .build(); IllegalArgumentException invalidNestedException = expectThrows(IllegalArgumentException.class, - () -> createIndex("test", settings, "t", "nested_field", "type=nested")); + () -> createIndex("test", settings, "t", "nested_field", "type=nested", "foo", "type=keyword")); assertThat(invalidNestedException.getMessage(), containsString("cannot have nested fields when index sort is activated")); - IndexService indexService = createIndex("test", settings, "t"); + IndexService indexService = createIndex("test", settings, "t", "foo", "type=keyword"); CompressedXContent nestedFieldMapping = new CompressedXContent(XContentFactory.jsonBuilder().startObject() .startObject("properties") .startObject("nested_field") diff --git a/core/src/test/java/org/elasticsearch/index/mapper/TypeFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/TypeFieldMapperTests.java index 6fade26ca02f4..d3091ac3459bb 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/TypeFieldMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/TypeFieldMapperTests.java @@ -19,16 +19,31 @@ package org.elasticsearch.index.mapper; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.DocValuesType; +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.SortedSetDocValues; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.compress.CompressedXContent; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.index.fielddata.plain.DocValuesIndexFieldData; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.fielddata.AtomicOrdinalsFieldData; +import org.elasticsearch.index.fielddata.IndexFieldDataCache; +import org.elasticsearch.index.fielddata.IndexOrdinalsFieldData; +import org.elasticsearch.index.mapper.MapperService.MergeReason; +import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.test.InternalSettingsPlugin; +import java.io.IOException; +import java.util.Arrays; import java.util.Collection; - -import static org.hamcrest.Matchers.instanceOf; +import java.util.Collections; public class TypeFieldMapperTests extends ESSingleNodeTestCase { @@ -37,13 +52,60 @@ protected Collection> getPlugins() { return pluginList(InternalSettingsPlugin.class); } - public void testDocValues() throws Exception { - String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").endObject().endObject().string(); + public void testDocValuesMultipleTypes() throws Exception { + testDocValues(false); + } - DocumentMapper docMapper = createIndex("test").mapperService().documentMapperParser().parse("type", new CompressedXContent(mapping)); - TypeFieldMapper typeMapper = docMapper.metadataMapper(TypeFieldMapper.class); - assertTrue(typeMapper.fieldType().hasDocValues()); - assertThat(typeMapper.fieldType().fielddataBuilder(), instanceOf(DocValuesIndexFieldData.Builder.class)); + public void testDocValuesSingleType() throws Exception { + testDocValues(true); } + public void testDocValues(boolean singleType) throws IOException { + Settings indexSettings = Settings.builder() + .put("index.mapping.single_type", singleType) + .build(); + MapperService mapperService = createIndex("test", indexSettings).mapperService(); + DocumentMapper mapper = mapperService.merge("type", new CompressedXContent("{\"type\":{}}"), MergeReason.MAPPING_UPDATE, false); + ParsedDocument document = mapper.parse(SourceToParse.source("index", "type", "id", new BytesArray("{}"), XContentType.JSON)); + + Directory dir = newDirectory(); + IndexWriter w = new IndexWriter(dir, newIndexWriterConfig()); + w.addDocument(document.rootDoc()); + DirectoryReader r = DirectoryReader.open(w); + w.close(); + + MappedFieldType ft = mapperService.fullName(TypeFieldMapper.NAME); + IndexOrdinalsFieldData fd = (IndexOrdinalsFieldData) ft.fielddataBuilder().build(mapperService.getIndexSettings(), + ft, new IndexFieldDataCache.None(), new NoneCircuitBreakerService(), mapperService); + AtomicOrdinalsFieldData afd = fd.load(r.leaves().get(0)); + SortedSetDocValues values = afd.getOrdinalsValues(); + assertTrue(values.advanceExact(0)); + assertEquals(0, values.nextOrd()); + assertEquals(SortedSetDocValues.NO_MORE_ORDS, values.nextOrd()); + assertEquals(new BytesRef("type"), values.lookupOrd(0)); + r.close(); + dir.close(); + } + + public void testDefaultsMultipleTypes() throws IOException { + Settings indexSettings = Settings.builder() + .put("index.mapping.single_type", false) + .build(); + MapperService mapperService = createIndex("test", indexSettings).mapperService(); + DocumentMapper mapper = mapperService.merge("type", new CompressedXContent("{\"type\":{}}"), MergeReason.MAPPING_UPDATE, false); + ParsedDocument document = mapper.parse(SourceToParse.source("index", "type", "id", new BytesArray("{}"), XContentType.JSON)); + IndexableField[] fields = document.rootDoc().getFields(TypeFieldMapper.NAME); + assertEquals(IndexOptions.DOCS, fields[0].fieldType().indexOptions()); + assertEquals(DocValuesType.SORTED_SET, fields[1].fieldType().docValuesType()); + } + + public void testDefaultsSingleType() throws IOException { + Settings indexSettings = Settings.builder() + .put("index.mapping.single_type", true) + .build(); + MapperService mapperService = createIndex("test", indexSettings).mapperService(); + DocumentMapper mapper = mapperService.merge("type", new CompressedXContent("{\"type\":{}}"), MergeReason.MAPPING_UPDATE, false); + ParsedDocument document = mapper.parse(SourceToParse.source("index", "type", "id", new BytesArray("{}"), XContentType.JSON)); + assertEquals(Collections.emptyList(), Arrays.asList(document.rootDoc().getFields(TypeFieldMapper.NAME))); + } } diff --git a/core/src/test/java/org/elasticsearch/index/mapper/TypeFieldTypeTests.java b/core/src/test/java/org/elasticsearch/index/mapper/TypeFieldTypeTests.java index 3c80f095f83f5..b8a2805efe9bb 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/TypeFieldTypeTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/TypeFieldTypeTests.java @@ -30,15 +30,25 @@ import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.lucene.store.Directory; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.IOUtils; -import org.junit.Before; +import org.elasticsearch.Version; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.UUIDs; +import org.elasticsearch.common.lucene.search.Queries; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.IndexSettings; +import org.elasticsearch.index.query.QueryShardContext; +import org.mockito.Mockito; import java.io.IOException; +import java.util.Collections; +import java.util.Set; public class TypeFieldTypeTests extends FieldTypeTestCase { @Override @@ -46,25 +56,62 @@ protected MappedFieldType createDefaultFieldType() { return new TypeFieldMapper.TypeFieldType(); } - @Before - public void setupProperties() { - addModifier(new Modifier("fielddata", true) { - @Override - public void modify(MappedFieldType ft) { - TypeFieldMapper.TypeFieldType tft = (TypeFieldMapper.TypeFieldType) ft; - tft.setFielddata(tft.fielddata() == false); - } - }); + public void testTermsQueryWhenTypesAreDisabled() throws Exception { + QueryShardContext context = Mockito.mock(QueryShardContext.class); + Settings indexSettings = Settings.builder() + .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0) + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()) + .put("index.mapping.single_type", true).build(); + IndexMetaData indexMetaData = IndexMetaData.builder(IndexMetaData.INDEX_UUID_NA_VALUE).settings(indexSettings).build(); + IndexSettings mockSettings = new IndexSettings(indexMetaData, Settings.EMPTY); + Mockito.when(context.getIndexSettings()).thenReturn(mockSettings); + + MapperService mapperService = Mockito.mock(MapperService.class); + Set types = Collections.emptySet(); + Mockito.when(mapperService.types()).thenReturn(types); + Mockito.when(context.getMapperService()).thenReturn(mapperService); + + TypeFieldMapper.TypeFieldType ft = new TypeFieldMapper.TypeFieldType(); + ft.setName(TypeFieldMapper.NAME); + Query query = ft.termQuery("my_type", context); + assertEquals(new MatchNoDocsQuery(), query); + + types = Collections.singleton("my_type"); + Mockito.when(mapperService.types()).thenReturn(types); + query = ft.termQuery("my_type", context); + assertEquals(new MatchAllDocsQuery(), query); + + Mockito.when(mapperService.hasNested()).thenReturn(true); + query = ft.termQuery("my_type", context); + assertEquals(Queries.newNonNestedFilter(), query); + + types = Collections.singleton("other_type"); + Mockito.when(mapperService.types()).thenReturn(types); + query = ft.termQuery("my_type", context); + assertEquals(new MatchNoDocsQuery(), query); } - public void testTermsQuery() throws Exception { + public void testTermsQueryWhenTypesAreEnabled() throws Exception { Directory dir = newDirectory(); IndexWriter w = new IndexWriter(dir, newIndexWriterConfig()); IndexReader reader = openReaderWithNewType("my_type", w); + QueryShardContext context = Mockito.mock(QueryShardContext.class); + Settings indexSettings = Settings.builder() + .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0) + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()) + .put("index.mapping.single_type", false).build(); + IndexMetaData indexMetaData = IndexMetaData.builder(IndexMetaData.INDEX_UUID_NA_VALUE).settings(indexSettings).build(); + IndexSettings mockSettings = new IndexSettings(indexMetaData, Settings.EMPTY); + Mockito.when(context.getIndexSettings()).thenReturn(mockSettings); + TypeFieldMapper.TypeFieldType ft = new TypeFieldMapper.TypeFieldType(); ft.setName(TypeFieldMapper.NAME); - Query query = ft.termQuery("my_type", null); + Query query = ft.termQuery("my_type", context); assertEquals(new MatchAllDocsQuery(), query.rewrite(reader)); // Make sure that Lucene actually simplifies the query when there is a single type diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregatorTests.java index 47aa35bf92454..17152bc450a22 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregatorTests.java @@ -52,6 +52,7 @@ import org.elasticsearch.search.aggregations.AggregatorTestCase; import org.elasticsearch.search.aggregations.metrics.min.InternalMin; import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder; +import org.mockito.Mockito; import java.io.IOException; import java.util.Arrays; @@ -165,8 +166,8 @@ protected MapperService mapperServiceMock() { when(mapperService.documentMapper(CHILD_TYPE)).thenReturn(childDocMapper); when(mapperService.documentMapper(PARENT_TYPE)).thenReturn(parentDocMapper); when(mapperService.docMappers(false)).thenReturn(Arrays.asList(new DocumentMapper[] { childDocMapper, parentDocMapper })); - when(parentDocMapper.typeFilter()).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(PARENT_TYPE))); - when(childDocMapper.typeFilter()).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(CHILD_TYPE))); + when(parentDocMapper.typeFilter(Mockito.any())).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(PARENT_TYPE))); + when(childDocMapper.typeFilter(Mockito.any())).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(CHILD_TYPE))); return mapperService; } diff --git a/docs/reference/search/validate.asciidoc b/docs/reference/search/validate.asciidoc index 2b0ce48152ef3..7218f6e6b23c3 100644 --- a/docs/reference/search/validate.asciidoc +++ b/docs/reference/search/validate.asciidoc @@ -227,13 +227,13 @@ Response: "index": "twitter", "shard": 0, "valid": true, - "explanation": "+MatchNoDocsQuery(\"empty BooleanQuery\") #ConstantScore(MatchNoDocsQuery(\"empty BooleanQuery\"))" + "explanation": "user:kimchy~2" }, { "index": "twitter", "shard": 1, "valid": true, - "explanation": "+MatchNoDocsQuery(\"empty BooleanQuery\") #ConstantScore(MatchNoDocsQuery(\"empty BooleanQuery\"))" + "explanation": "user:kimchy~2" }, { "index": "twitter", @@ -251,7 +251,7 @@ Response: "index": "twitter", "shard": 4, "valid": true, - "explanation": "+MatchNoDocsQuery(\"empty BooleanQuery\") #ConstantScore(MatchNoDocsQuery(\"empty BooleanQuery\"))" + "explanation": "user:kimchy~2" } ] } From 50b617f73a7543b4d5ec64490a65311dca2d70d2 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Thu, 4 May 2017 10:29:00 -0400 Subject: [PATCH 201/619] Remove global checkpoint assertion in index shard Due to races, this assertion in index shard can be wrong. This commit removes that assertion and adjusts the explanatory comment. --- .../elasticsearch/index/shard/IndexShard.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java index f1cef1fb66350..5e004a4759c2d 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -1523,20 +1523,20 @@ public void updateGlobalCheckpointOnReplica(final long globalCheckpoint) { verifyReplicationTarget(); final SequenceNumbersService seqNoService = getEngine().seqNoService(); final long localCheckpoint = seqNoService.getLocalCheckpoint(); - if (globalCheckpoint <= localCheckpoint) { - seqNoService.updateGlobalCheckpointOnReplica(globalCheckpoint); - } else { + if (globalCheckpoint > localCheckpoint) { /* * This can happen during recovery when the shard has started its engine but recovery is not finalized and is receiving global - * checkpoint updates from in-flight operations. However, since this shard is not yet contributing to calculating the global - * checkpoint, it can be the case that the global checkpoint update from the primary is ahead of the local checkpoint on this - * shard. In this case, we ignore the global checkpoint update. This should only happen if we are in the translog stage of - * recovery. Prior to this, the engine is not opened and this shard will not receive global checkpoint updates, and after this - * the shard will be contributing to calculations of the the global checkpoint. + * checkpoint updates. However, since this shard is not yet contributing to calculating the global checkpoint, it can be the + * case that the global checkpoint update from the primary is ahead of the local checkpoint on this shard. In this case, we + * ignore the global checkpoint update. This can happen if we are in the translog stage of recovery. Prior to this, the engine + * is not opened and this shard will not receive global checkpoint updates, and after this the shard will be contributing to + * calculations of the the global checkpoint. However, we can not assert that we are in the translog stage of recovery here as + * while the global checkpoint update may have emanated from the primary when we were in that state, we could subsequently move + * to recovery finalization, or even finished recovery before the update arrives here. */ - assert recoveryState().getStage() == RecoveryState.Stage.TRANSLOG - : "expected recovery stage [" + RecoveryState.Stage.TRANSLOG + "] but was [" + recoveryState().getStage() + "]"; + return; } + seqNoService.updateGlobalCheckpointOnReplica(globalCheckpoint); } /** From cb46e97a046b61a20bc237b9d49fa30d2aef9edf Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Thu, 4 May 2017 10:41:20 -0400 Subject: [PATCH 202/619] Fix reschedule async fsync test This commit fixes the reschedule async fsync test in index service tests. This test was passing for the wrong reason. Namely, the test was trying to set translog durability to async, fire off an indexing request, and then assert that the translog eventually got fsynced. The problem here is that in the course of issuing the indexing request, a mapping update is trigger. The mapping update triggers the index settings to be refreshed. Since the test did not issue a cluster state update to change the durability from request to async but instead did this directly through index service, the mapping update flops the durability back to async. This means that when the indexing request executes, an fsync is performed after the request and the assertoin that an fsync is not needed passes but for the wrong reason (in short: the test wanted it to pass because an async fsync fired, but instead it passed because a request async fired). This commit fixes this by going through the index settings API so that a proper cluster state update is triggered and so the mapping update does not flop the durability back to request. --- .../index/IndexServiceTests.java | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/index/IndexServiceTests.java b/core/src/test/java/org/elasticsearch/index/IndexServiceTests.java index 385770426f5ae..c202db1470e10 100644 --- a/core/src/test/java/org/elasticsearch/index/IndexServiceTests.java +++ b/core/src/test/java/org/elasticsearch/index/IndexServiceTests.java @@ -238,31 +238,43 @@ public void testAsyncFsyncActuallyWorks() throws Exception { } public void testRescheduleAsyncFsync() throws Exception { - Settings settings = Settings.builder() - .put(IndexSettings.INDEX_TRANSLOG_SYNC_INTERVAL_SETTING.getKey(), "100ms") // very often :) - .put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), Translog.Durability.REQUEST) + final Settings settings = Settings.builder() + .put(IndexSettings.INDEX_TRANSLOG_SYNC_INTERVAL_SETTING.getKey(), "100ms") + .put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), Translog.Durability.REQUEST) .build(); - IndexService indexService = createIndex("test", settings); + final IndexService indexService = createIndex("test", settings); ensureGreen("test"); assertNull(indexService.getFsyncTask()); - IndexMetaData metaData = IndexMetaData.builder(indexService.getMetaData()).settings(Settings.builder().put(indexService.getMetaData().getSettings()).put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), Translog.Durability.ASYNC)).build(); - indexService.updateMetaData(metaData); + + client() + .admin() + .indices() + .prepareUpdateSettings("test") + .setSettings(Settings.builder().put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), Translog.Durability.ASYNC)) + .get(); + assertNotNull(indexService.getFsyncTask()); - assertTrue(indexService.getRefreshTask().mustReschedule()); + assertTrue(indexService.getFsyncTask().mustReschedule()); client().prepareIndex("test", "test", "1").setSource("{\"foo\": \"bar\"}", XContentType.JSON).get(); - IndexShard shard = indexService.getShard(0); - assertBusy(() -> { - assertFalse(shard.getTranslog().syncNeeded()); - }); - - metaData = IndexMetaData.builder(indexService.getMetaData()).settings(Settings.builder().put(indexService.getMetaData().getSettings()).put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), Translog.Durability.REQUEST)).build(); - indexService.updateMetaData(metaData); + assertNotNull(indexService.getFsyncTask()); + final IndexShard shard = indexService.getShard(0); + assertBusy(() -> assertFalse(shard.getTranslog().syncNeeded())); + + client() + .admin() + .indices() + .prepareUpdateSettings("test") + .setSettings(Settings.builder().put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), Translog.Durability.REQUEST)) + .get(); assertNull(indexService.getFsyncTask()); - metaData = IndexMetaData.builder(indexService.getMetaData()).settings(Settings.builder().put(indexService.getMetaData().getSettings()).put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), Translog.Durability.ASYNC)).build(); - indexService.updateMetaData(metaData); + client() + .admin() + .indices() + .prepareUpdateSettings("test") + .setSettings(Settings.builder().put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), Translog.Durability.ASYNC)) + .get(); assertNotNull(indexService.getFsyncTask()); - } public void testIllegalFsyncInterval() { From 4b80e1a5cab3ca7a8b9c5eec2debdf5c53de7808 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 4 May 2017 09:51:25 -0400 Subject: [PATCH 203/619] Docs: fix list of testing images Fedora-24 is gone. Long live Fedora-25. --- TESTING.asciidoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TESTING.asciidoc b/TESTING.asciidoc index f8a54abee894e..35004570224ea 100644 --- a/TESTING.asciidoc +++ b/TESTING.asciidoc @@ -426,12 +426,12 @@ sudo -E bats $BATS_TESTS/*.bats You can also use Gradle to prepare the test environment and then starts a single VM: ------------------------------------------------- -gradle vagrantFedora24#up +gradle vagrantFedora25#up ------------------------------------------------- -Or any of vagrantCentos6#up, vagrantDebian8#up, vagrantFedora24#up, vagrantOel6#up, -vagrantOel7#up, vagrantOpensuse13#up, vagrantSles12#up, vagrantUbuntu1404#up, -vagrantUbuntu1604#up. +Or any of vagrantCentos6#up, vagrantCentos7#up, vagrantDebian8#up, +vagrantFedora25#up, vagrantOel6#up, vagrantOel7#up, vagrantOpensuse13#up, +vagrantSles12#up, vagrantUbuntu1404#up, vagrantUbuntu1604#up. Once up, you can then connect to the VM using SSH from the elasticsearch directory: From f5edd5049a2cb824eb66c75047f920bf76dd423e Mon Sep 17 00:00:00 2001 From: James Baiera Date: Thu, 4 May 2017 10:51:31 -0400 Subject: [PATCH 204/619] Fixing permission errors for `KERBEROS` security mode for HDFS Repository (#23439) Added missing permissions required for authenticating with Kerberos to HDFS. Also implemented code to support authentication in the form of using a Kerberos keytab file. In order to support HDFS authentication, users must install a Kerberos keytab file on each node and transfer it to the configuration directory. When a user specifies a Kerberos principal in the repository settings the plugin automatically enables security for Hadoop and begins the login process. There will be a separate PR and commit for the testing infrastructure to support these changes. --- docs/plugins/repository-hdfs.asciidoc | 110 +++++++++++++ .../repositories/hdfs/HdfsBlobStore.java | 17 +- .../repositories/hdfs/HdfsPlugin.java | 32 ++++ .../repositories/hdfs/HdfsRepository.java | 139 +++++++++++++---- .../hdfs/HdfsSecurityContext.java | 145 ++++++++++++++++++ .../plugin-metadata/plugin-security.policy | 45 +++++- 6 files changed, 446 insertions(+), 42 deletions(-) create mode 100644 plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsSecurityContext.java diff --git a/docs/plugins/repository-hdfs.asciidoc b/docs/plugins/repository-hdfs.asciidoc index 64b87801acbdd..20c62a5861a4e 100644 --- a/docs/plugins/repository-hdfs.asciidoc +++ b/docs/plugins/repository-hdfs.asciidoc @@ -68,3 +68,113 @@ The following settings are supported: Override the chunk size. (Disabled by default) +`security.principal`:: + + Kerberos principal to use when connecting to a secured HDFS cluster. + If you are using a service principal for your elasticsearch node, you may + use the `_HOST` pattern in the principal name and the plugin will replace + the pattern with the hostname of the node at runtime (see + link:repository-hdfs-security-runtime[Creating the Secure Repository]). + +[[repository-hdfs-security]] +==== Hadoop Security + +The HDFS Repository Plugin integrates seamlessly with Hadoop's authentication model. The following authentication +methods are supported by the plugin: + +[horizontal] +`simple`:: + + Also means "no security" and is enabled by default. Uses information from underlying operating system account + running elasticsearch to inform Hadoop of the name of the current user. Hadoop makes no attempts to verify this + information. + +`kerberos`:: + + Authenticates to Hadoop through the usage of a Kerberos principal and keytab. Interfacing with HDFS clusters + secured with Kerberos requires a few additional steps to enable (See <> and + <> for more info) + +[[repository-hdfs-security-keytabs]] +[float] +===== Principals and Keytabs +Before attempting to connect to a secured HDFS cluster, provision the Kerberos principals and keytabs that the +Elasticsearch nodes will use for authenticating to Kerberos. For maximum security and to avoid tripping up the Kerberos +replay protection, you should create a service principal per node, following the pattern of +`elasticsearch/hostname@REALM`. + +WARNING: In some cases, if the same principal is authenticating from multiple clients at once, services may reject +authentication for those principals under the assumption that they could be replay attacks. If you are running the +plugin in production with multiple nodes you should be using a unique service principal for each node. + +On each Elasticsearch node, place the appropriate keytab file in the node's configuration location under the +`repository-hdfs` directory using the name `krb5.keytab`: + +[source, bash] +---- +$> cd elasticsearch/config +$> ls +elasticsearch.yml jvm.options log4j2.properties repository-hdfs/ scripts/ +$> cd repository-hdfs +$> ls +krb5.keytab +---- +// TEST[skip:this is for demonstration purposes only + +NOTE: Make sure you have the correct keytabs! If you are using a service principal per node (like +`elasticsearch/hostname@REALM`) then each node will need its own unique keytab file for the principal assigned to that +host! + +// Setup at runtime (principal name) +[[repository-hdfs-security-runtime]] +[float] +===== Creating the Secure Repository +Once your keytab files are in place and your cluster is started, creating a secured HDFS repository is simple. Just +add the name of the principal that you will be authenticating as in the repository settings under the +`security.principal` option: + +[source,js] +---- +PUT _snapshot/my_hdfs_repository +{ + "type": "hdfs", + "settings": { + "uri": "hdfs://namenode:8020/", + "path": "/user/elasticsearch/respositories/my_hdfs_repository", + "security.principal": "elasticsearch@REALM" + } +} +---- +// CONSOLE +// TEST[skip:we don't have hdfs set up while testing this] + +If you are using different service principals for each node, you can use the `_HOST` pattern in your principal +name. Elasticsearch will automatically replace the pattern with the hostname of the node at runtime: + +[source,js] +---- +PUT _snapshot/my_hdfs_repository +{ + "type": "hdfs", + "settings": { + "uri": "hdfs://namenode:8020/", + "path": "/user/elasticsearch/respositories/my_hdfs_repository", + "security.principal": "elasticsearch/_HOST@REALM" + } +} +---- +// CONSOLE +// TEST[skip:we don't have hdfs set up while testing this] + +[[repository-hdfs-security-authorization]] +[float] +===== Authorization +Once Elasticsearch is connected and authenticated to HDFS, HDFS will infer a username to use for +authorizing file access for the client. By default, it picks this username from the primary part of +the kerberos principal used to authenticate to the service. For example, in the case of a principal +like `elasticsearch@REALM` or `elasticsearch/hostname@REALM` then the username that HDFS +extracts for file access checks will be `elasticsearch`. + +NOTE: The repository plugin makes no assumptions of what Elasticsearch's principal name is. The main fragment of the +Kerberos principal is not required to be `elasticsearch`. If you have a principal or service name that works better +for you or your organization then feel free to use it instead! \ No newline at end of file diff --git a/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsBlobStore.java b/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsBlobStore.java index 7ce5e8d3cd830..8d88b7fd07422 100644 --- a/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsBlobStore.java +++ b/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsBlobStore.java @@ -29,23 +29,21 @@ import org.elasticsearch.common.blobstore.BlobStore; import java.io.IOException; -import java.lang.reflect.ReflectPermission; -import java.net.SocketPermission; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; -import javax.security.auth.AuthPermission; - final class HdfsBlobStore implements BlobStore { private final Path root; private final FileContext fileContext; + private final HdfsSecurityContext securityContext; private final int bufferSize; private volatile boolean closed; HdfsBlobStore(FileContext fileContext, String path, int bufferSize) throws IOException { this.fileContext = fileContext; + this.securityContext = new HdfsSecurityContext(fileContext.getUgi()); this.bufferSize = bufferSize; this.root = execute(fileContext1 -> fileContext1.makeQualified(new Path(path))); try { @@ -107,9 +105,6 @@ interface Operation { /** * Executes the provided operation against this store */ - // we can do FS ops with only two elevated permissions: - // 1) hadoop dynamic proxy is messy with access rules - // 2) allow hadoop to add credentials to our Subject V execute(Operation operation) throws IOException { SpecialPermission.check(); if (closed) { @@ -117,8 +112,12 @@ V execute(Operation operation) throws IOException { } try { return AccessController.doPrivileged((PrivilegedExceptionAction) - () -> operation.run(fileContext), null, new ReflectPermission("suppressAccessChecks"), - new AuthPermission("modifyPrivateCredentials"), new SocketPermission("*", "connect")); + () -> { + securityContext.ensureLogin(); + return operation.run(fileContext); + }, + null, + securityContext.getRestrictedExecutionPermissions()); } catch (PrivilegedActionException pae) { throw (IOException) pae.getException(); } diff --git a/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsPlugin.java b/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsPlugin.java index 9ea53a5acf22e..4e51ab23b8091 100644 --- a/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsPlugin.java +++ b/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsPlugin.java @@ -26,6 +26,9 @@ import java.util.Collections; import java.util.Map; +import org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolPB; +import org.apache.hadoop.security.KerberosInfo; +import org.apache.hadoop.security.SecurityUtil; import org.elasticsearch.SpecialPermission; import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.xcontent.NamedXContentRegistry; @@ -40,6 +43,7 @@ public final class HdfsPlugin extends Plugin implements RepositoryPlugin { static { SpecialPermission.check(); AccessController.doPrivileged((PrivilegedAction) HdfsPlugin::evilHadoopInit); + AccessController.doPrivileged((PrivilegedAction) HdfsPlugin::eagerInit); } @SuppressForbidden(reason = "Needs a security hack for hadoop on windows, until HADOOP-XXXX is fixed") @@ -79,6 +83,34 @@ private static Void evilHadoopInit() { return null; } + private static Void eagerInit() { + /* + * Hadoop RPC wire serialization uses ProtocolBuffers. All proto classes for Hadoop + * come annotated with configurations that denote information about if they support + * certain security options like Kerberos, and how to send information with the + * message to support that authentication method. SecurityUtil creates a service loader + * in a static field during its clinit. This loader provides the implementations that + * pull the security information for each proto class. The service loader sources its + * services from the current thread's context class loader, which must contain the Hadoop + * jars. Since plugins don't execute with their class loaders installed as the thread's + * context class loader, we need to install the loader briefly, allow the util to be + * initialized, then restore the old loader since we don't actually own this thread. + */ + ClassLoader oldCCL = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(HdfsRepository.class.getClassLoader()); + KerberosInfo info = SecurityUtil.getKerberosInfo(ClientNamenodeProtocolPB.class, null); + // Make sure that the correct class loader was installed. + if (info == null) { + throw new RuntimeException("Could not initialize SecurityUtil: " + + "Unable to find services for [org.apache.hadoop.security.SecurityInfo]"); + } + } finally { + Thread.currentThread().setContextClassLoader(oldCCL); + } + return null; + } + @Override public Map getRepositories(Environment env, NamedXContentRegistry namedXContentRegistry) { return Collections.singletonMap("hdfs", (metadata) -> new HdfsRepository(metadata, env, namedXContentRegistry)); diff --git a/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsRepository.java b/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsRepository.java index d784e8bf09314..16ed9d06a5e8a 100644 --- a/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsRepository.java +++ b/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsRepository.java @@ -19,29 +19,31 @@ package org.elasticsearch.repositories.hdfs; import java.io.IOException; -import java.lang.reflect.Constructor; +import java.io.UncheckedIOException; +import java.net.InetAddress; import java.net.URI; +import java.net.UnknownHostException; import java.security.AccessController; -import java.security.Principal; import java.security.PrivilegedAction; -import java.util.Collections; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; -import javax.security.auth.Subject; - import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.AbstractFileSystem; import org.apache.hadoop.fs.FileContext; import org.apache.hadoop.fs.UnsupportedFileSystemException; -import org.elasticsearch.ElasticsearchGenerationException; +import org.apache.hadoop.security.SecurityUtil; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod; +import org.apache.logging.log4j.Logger; import org.elasticsearch.SpecialPermission; import org.elasticsearch.cluster.metadata.RepositoryMetaData; import org.elasticsearch.common.Strings; import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.blobstore.BlobPath; import org.elasticsearch.common.blobstore.BlobStore; +import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; @@ -51,9 +53,14 @@ public final class HdfsRepository extends BlobStoreRepository { - private final BlobPath basePath = BlobPath.cleanPath(); + private static final Logger LOGGER = Loggers.getLogger(HdfsRepository.class); + + private static final String CONF_SECURITY_PRINCIPAL = "security.principal"; + + private final Environment environment; private final ByteSizeValue chunkSize; private final boolean compress; + private final BlobPath basePath = BlobPath.cleanPath(); private HdfsBlobStore blobStore; @@ -65,6 +72,7 @@ public HdfsRepository(RepositoryMetaData metadata, Environment environment, NamedXContentRegistry namedXContentRegistry) throws IOException { super(metadata, environment.settings(), namedXContentRegistry); + this.environment = environment; this.chunkSize = metadata.settings().getAsBytesSize("chunk_size", null); this.compress = metadata.settings().getAsBoolean("compress", false); } @@ -101,49 +109,116 @@ protected void doStart() { blobStore = new HdfsBlobStore(fileContext, pathSetting, bufferSize); logger.debug("Using file-system [{}] for URI [{}], path [{}]", fileContext.getDefaultFileSystem(), fileContext.getDefaultFileSystem().getUri(), pathSetting); } catch (IOException e) { - throw new ElasticsearchGenerationException(String.format(Locale.ROOT, "Cannot create HDFS repository for uri [%s]", uri), e); + throw new UncheckedIOException(String.format(Locale.ROOT, "Cannot create HDFS repository for uri [%s]", uri), e); } super.doStart(); } // create hadoop filecontext - @SuppressForbidden(reason = "lesser of two evils (the other being a bunch of JNI/classloader nightmares)") - private static FileContext createContext(URI uri, Settings repositorySettings) { - Configuration cfg = new Configuration(repositorySettings.getAsBoolean("load_defaults", true)); - cfg.setClassLoader(HdfsRepository.class.getClassLoader()); - cfg.reloadConfiguration(); + private FileContext createContext(URI uri, Settings repositorySettings) { + Configuration hadoopConfiguration = new Configuration(repositorySettings.getAsBoolean("load_defaults", true)); + hadoopConfiguration.setClassLoader(HdfsRepository.class.getClassLoader()); + hadoopConfiguration.reloadConfiguration(); Map map = repositorySettings.getByPrefix("conf.").getAsMap(); for (Entry entry : map.entrySet()) { - cfg.set(entry.getKey(), entry.getValue()); + hadoopConfiguration.set(entry.getKey(), entry.getValue()); } - // create a hadoop user. if we want some auth, it must be done different anyway, and tested. - Subject subject; - try { - Class clazz = Class.forName("org.apache.hadoop.security.User"); - Constructor ctor = clazz.getConstructor(String.class); - ctor.setAccessible(true); - Principal principal = (Principal) ctor.newInstance(System.getProperty("user.name")); - subject = new Subject(false, Collections.singleton(principal), Collections.emptySet(), Collections.emptySet()); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } + // Create a hadoop user + UserGroupInformation ugi = login(hadoopConfiguration, repositorySettings); - // disable FS cache - cfg.setBoolean("fs.hdfs.impl.disable.cache", true); + // Disable FS cache + hadoopConfiguration.setBoolean("fs.hdfs.impl.disable.cache", true); - // create the filecontext with our user - return Subject.doAs(subject, (PrivilegedAction) () -> { + // Create the filecontext with our user information + // This will correctly configure the filecontext to have our UGI as it's internal user. + return ugi.doAs((PrivilegedAction) () -> { try { - AbstractFileSystem fs = AbstractFileSystem.get(uri, cfg); - return FileContext.getFileContext(fs, cfg); + AbstractFileSystem fs = AbstractFileSystem.get(uri, hadoopConfiguration); + return FileContext.getFileContext(fs, hadoopConfiguration); } catch (UnsupportedFileSystemException e) { - throw new RuntimeException(e); + throw new UncheckedIOException(e); } }); } + private UserGroupInformation login(Configuration hadoopConfiguration, Settings repositorySettings) { + // Validate the authentication method: + AuthenticationMethod authMethod = SecurityUtil.getAuthenticationMethod(hadoopConfiguration); + if (authMethod.equals(AuthenticationMethod.SIMPLE) == false + && authMethod.equals(AuthenticationMethod.KERBEROS) == false) { + throw new RuntimeException("Unsupported authorization mode ["+authMethod+"]"); + } + + // Check if the user added a principal to use, and that there is a keytab file provided + String kerberosPrincipal = repositorySettings.get(CONF_SECURITY_PRINCIPAL); + + // Check to see if the authentication method is compatible + if (kerberosPrincipal != null && authMethod.equals(AuthenticationMethod.SIMPLE)) { + LOGGER.warn("Hadoop authentication method is set to [SIMPLE], but a Kerberos principal is " + + "specified. Continuing with [KERBEROS] authentication."); + SecurityUtil.setAuthenticationMethod(AuthenticationMethod.KERBEROS, hadoopConfiguration); + } else if (kerberosPrincipal == null && authMethod.equals(AuthenticationMethod.KERBEROS)) { + throw new RuntimeException("HDFS Repository does not support [KERBEROS] authentication without " + + "a valid Kerberos principal and keytab. Please specify a principal in the repository settings with [" + + CONF_SECURITY_PRINCIPAL + "]."); + } + + // Now we can initialize the UGI with the configuration. + UserGroupInformation.setConfiguration(hadoopConfiguration); + + // Debugging + LOGGER.debug("Hadoop security enabled: [{}]", UserGroupInformation.isSecurityEnabled()); + LOGGER.debug("Using Hadoop authentication method: [{}]", SecurityUtil.getAuthenticationMethod(hadoopConfiguration)); + + // UserGroupInformation (UGI) instance is just a Hadoop specific wrapper around a Java Subject + try { + if (UserGroupInformation.isSecurityEnabled()) { + String principal = preparePrincipal(kerberosPrincipal); + String keytab = HdfsSecurityContext.locateKeytabFile(environment).toString(); + LOGGER.debug("Using kerberos principal [{}] and keytab located at [{}]", principal, keytab); + return UserGroupInformation.loginUserFromKeytabAndReturnUGI(principal, keytab); + } + return UserGroupInformation.getCurrentUser(); + } catch (IOException e) { + throw new UncheckedIOException("Could not retrieve the current user information", e); + } + } + + // Convert principals of the format 'service/_HOST@REALM' by subbing in the local address for '_HOST'. + private static String preparePrincipal(String originalPrincipal) { + String finalPrincipal = originalPrincipal; + // Don't worry about host name resolution if they don't have the _HOST pattern in the name. + if (originalPrincipal.contains("_HOST")) { + try { + finalPrincipal = SecurityUtil.getServerPrincipal(originalPrincipal, getHostName()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + if (originalPrincipal.equals(finalPrincipal) == false) { + LOGGER.debug("Found service principal. Converted original principal name [{}] to server principal [{}]", + originalPrincipal, finalPrincipal); + } + } + return finalPrincipal; + } + + @SuppressForbidden(reason = "InetAddress.getLocalHost(); Needed for filling in hostname for a kerberos principal name pattern.") + private static String getHostName() { + try { + /* + * This should not block since it should already be resolved via Log4J and Netty. The + * host information is cached by the JVM and the TTL for the cache entry is infinite + * when the SecurityManager is activated. + */ + return InetAddress.getLocalHost().getCanonicalHostName(); + } catch (UnknownHostException e) { + throw new RuntimeException("Could not locate host information", e); + } + } + @Override protected BlobStore blobStore() { return blobStore; diff --git a/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsSecurityContext.java b/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsSecurityContext.java new file mode 100644 index 0000000000000..3cd1a5a40fdc0 --- /dev/null +++ b/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsSecurityContext.java @@ -0,0 +1,145 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.repositories.hdfs; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.reflect.ReflectPermission; +import java.net.SocketPermission; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.Permission; +import java.util.Arrays; +import java.util.Locale; +import java.util.function.Supplier; +import javax.security.auth.AuthPermission; +import javax.security.auth.PrivateCredentialPermission; +import javax.security.auth.kerberos.ServicePermission; + +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.env.Environment; + +/** + * Oversees all the security specific logic for the HDFS Repository plugin. + * + * Keeps track of the current user for a given repository, as well as which + * permissions to grant the blob store restricted execution methods. + */ +class HdfsSecurityContext { + + private static final Logger LOGGER = Loggers.getLogger(HdfsSecurityContext.class); + + private static final Permission[] SIMPLE_AUTH_PERMISSIONS; + private static final Permission[] KERBEROS_AUTH_PERMISSIONS; + static { + // We can do FS ops with only a few elevated permissions: + SIMPLE_AUTH_PERMISSIONS = new Permission[]{ + new SocketPermission("*", "connect"), + // 1) hadoop dynamic proxy is messy with access rules + new ReflectPermission("suppressAccessChecks"), + // 2) allow hadoop to add credentials to our Subject + new AuthPermission("modifyPrivateCredentials") + }; + + // If Security is enabled, we need all the following elevated permissions: + KERBEROS_AUTH_PERMISSIONS = new Permission[] { + new SocketPermission("*", "connect"), + // 1) hadoop dynamic proxy is messy with access rules + new ReflectPermission("suppressAccessChecks"), + // 2) allow hadoop to add credentials to our Subject + new AuthPermission("modifyPrivateCredentials"), + // 3) allow hadoop to act as the logged in Subject + new AuthPermission("doAs"), + // 4) Listen and resolve permissions for kerberos server principals + new SocketPermission("localhost:0", "listen,resolve"), + // We add the following since hadoop requires the client to re-login when the kerberos ticket expires: + // 5) All the permissions needed for UGI to do its weird JAAS hack + new RuntimePermission("getClassLoader"), + new RuntimePermission("setContextClassLoader"), + // 6) Additional permissions for the login modules + new AuthPermission("modifyPrincipals"), + new PrivateCredentialPermission("org.apache.hadoop.security.Credentials * \"*\"", "read"), + new PrivateCredentialPermission("javax.security.auth.kerberos.KerberosTicket * \"*\"", "read"), + new PrivateCredentialPermission("javax.security.auth.kerberos.KeyTab * \"*\"", "read") + // Included later: + // 7) allow code to initiate kerberos connections as the logged in user + // Still far and away fewer permissions than the original full plugin policy + }; + } + + /** + * Locates the keytab file in the environment and verifies that it exists. + * Expects keytab file to exist at {@code $CONFIG_DIR$/repository-hdfs/krb5.keytab} + */ + static Path locateKeytabFile(Environment environment) { + Path keytabPath = environment.configFile().resolve("repository-hdfs").resolve("krb5.keytab"); + try { + if (Files.exists(keytabPath) == false) { + throw new RuntimeException("Could not locate keytab at [" + keytabPath + "]."); + } + } catch (SecurityException se) { + throw new RuntimeException("Could not locate keytab at [" + keytabPath + "]", se); + } + return keytabPath; + } + + private final UserGroupInformation ugi; + private final Permission[] restrictedExecutionPermissions; + + HdfsSecurityContext(UserGroupInformation ugi) { + this.ugi = ugi; + this.restrictedExecutionPermissions = renderPermissions(ugi); + } + + private Permission[] renderPermissions(UserGroupInformation ugi) { + Permission[] permissions; + if (ugi.isFromKeytab()) { + // KERBEROS + // Leave room to append one extra permission based on the logged in user's info. + int permlen = KERBEROS_AUTH_PERMISSIONS.length + 1; + permissions = new Permission[permlen]; + + System.arraycopy(KERBEROS_AUTH_PERMISSIONS, 0, permissions, 0, KERBEROS_AUTH_PERMISSIONS.length); + + // Append a kerberos.ServicePermission to only allow initiating kerberos connections + // as the logged in user. + permissions[permissions.length - 1] = new ServicePermission(ugi.getUserName(), "initiate"); + } else { + // SIMPLE + permissions = Arrays.copyOf(SIMPLE_AUTH_PERMISSIONS, SIMPLE_AUTH_PERMISSIONS.length); + } + return permissions; + } + + Permission[] getRestrictedExecutionPermissions() { + return restrictedExecutionPermissions; + } + + void ensureLogin() { + if (ugi.isFromKeytab()) { + try { + ugi.checkTGTAndReloginFromKeytab(); + } catch (IOException ioe) { + throw new UncheckedIOException("Could not re-authenticate", ioe); + } + } + } +} diff --git a/plugins/repository-hdfs/src/main/plugin-metadata/plugin-security.policy b/plugins/repository-hdfs/src/main/plugin-metadata/plugin-security.policy index b800f3eee46ca..f6476f290bc34 100644 --- a/plugins/repository-hdfs/src/main/plugin-metadata/plugin-security.policy +++ b/plugins/repository-hdfs/src/main/plugin-metadata/plugin-security.policy @@ -25,17 +25,60 @@ grant { permission java.lang.RuntimePermission "accessDeclaredMembers"; permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; + // Needed so that Hadoop can load the correct classes for SPI and JAAS + // org.apache.hadoop.security.SecurityUtil clinit + // org.apache.hadoop.security.UserGroupInformation.newLoginContext() + permission java.lang.RuntimePermission "setContextClassLoader"; + // org.apache.hadoop.util.StringUtils clinit permission java.util.PropertyPermission "*", "read,write"; // org.apache.hadoop.util.ShutdownHookManager clinit permission java.lang.RuntimePermission "shutdownHooks"; - // JAAS is used always, we use a fake subject, hurts nobody + // JAAS is used by Hadoop for authentication purposes + // The Hadoop Login JAAS module modifies a Subject's private credentials and principals + // The Hadoop RPC Layer must be able to read these credentials, and initiate Kerberos connections + + // org.apache.hadoop.security.UserGroupInformation.getCurrentUser() permission javax.security.auth.AuthPermission "getSubject"; + + // org.apache.hadoop.security.UserGroupInformation.doAs() permission javax.security.auth.AuthPermission "doAs"; + + // org.apache.hadoop.security.UserGroupInformation.getCredentialsInternal() + permission javax.security.auth.PrivateCredentialPermission "org.apache.hadoop.security.Credentials * \"*\"", "read"; + + // Hadoop depends on the Kerberos login module for kerberos authentication + // com.sun.security.auth.module.Krb5LoginModule.login() + permission java.lang.RuntimePermission "accessClassInPackage.sun.security.krb5"; + + // com.sun.security.auth.module.Krb5LoginModule.commit() permission javax.security.auth.AuthPermission "modifyPrivateCredentials"; + permission javax.security.auth.AuthPermission "modifyPrincipals"; + permission javax.security.auth.PrivateCredentialPermission "javax.security.auth.kerberos.KeyTab * \"*\"", "read"; + permission javax.security.auth.PrivateCredentialPermission "javax.security.auth.kerberos.KerberosTicket * \"*\"", "read"; + + // Hadoop depends on OS level user information for simple authentication + // Unix: UnixLoginModule: com.sun.security.auth.module.UnixSystem.UnixSystem init + permission java.lang.RuntimePermission "loadLibrary.jaas_unix"; + // Windows: NTLoginModule: com.sun.security.auth.module.NTSystem.loadNative + permission java.lang.RuntimePermission "loadLibrary.jaas_nt"; + permission javax.security.auth.AuthPermission "modifyPublicCredentials"; + + // org.apache.hadoop.security.SaslRpcServer.init() + permission java.security.SecurityPermission "putProviderProperty.SaslPlainServer"; + + // org.apache.hadoop.security.SaslPlainServer.SecurityProvider.SecurityProvider init + permission java.security.SecurityPermission "insertProvider.SaslPlainServer"; + + // org.apache.hadoop.security.SaslRpcClient.getServerPrincipal -> KerberosPrincipal init + permission javax.security.auth.kerberos.ServicePermission "*", "initiate"; // hdfs client opens socket connections for to access repository permission java.net.SocketPermission "*", "connect"; + + // client binds to the address returned from the host name of any principal set up as a service principal + // org.apache.hadoop.ipc.Client.Connection.setupConnection + permission java.net.SocketPermission "localhost:0", "listen,resolve"; }; From de65f51d34cb05dbdf846417a3c5023979dd863c Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Thu, 4 May 2017 11:19:41 -0400 Subject: [PATCH 205/619] Simplify file store Today we go to heroic lengths to workaround bugs in the JDK or around issues like BSD jails to get information about the underlying file store. For example, we went to lengths to work around a JDK bug where the file store returned would incorrectly report whether or not a path is writable in certain situations in Windows operating systems. Another bug prevented getting file store information on Windows on a virtual drive on Windows. We no longer need to work around these bugs, we could simply try to write to disk and let an I/O exception arise if we could not write to the disk or take advantage of the fact that these bugs are fixed in recent releases of the JDK (e.g., the file store bug is fixed since 8u72). Additionally, we collected information about all file stores on the system which meant that if the user had a stale NFS mount, Elasticsearch could hang and fail on startup if that mount point was not available. Finally, we collected information through Lucene about whether or not a disk was a spinning disk versus an SSD, information that we do not need since we assume SSDs by default. This commit takes into consideration that we simply do not need this heroic effort, we do not need information about all file stores, and we do not need information about whether or not a disk spins to greatly simplfy file store handling. Relates #24402 --- .../org/elasticsearch/env/ESFileStore.java | 132 ++---------------- .../org/elasticsearch/env/Environment.java | 39 +----- .../elasticsearch/env/NodeEnvironment.java | 35 +---- .../org/elasticsearch/monitor/fs/FsInfo.java | 24 +--- .../org/elasticsearch/monitor/fs/FsProbe.java | 1 - .../elasticsearch/bootstrap/security.policy | 1 + .../cluster/node/stats/NodeStatsTests.java | 1 - .../migration/migrate_6_0/stats.asciidoc | 10 ++ 8 files changed, 38 insertions(+), 205 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/env/ESFileStore.java b/core/src/main/java/org/elasticsearch/env/ESFileStore.java index 8ac6cf8a02a3f..bba1e1e609675 100644 --- a/core/src/main/java/org/elasticsearch/env/ESFileStore.java +++ b/core/src/main/java/org/elasticsearch/env/ESFileStore.java @@ -20,7 +20,6 @@ package org.elasticsearch.env; import org.apache.lucene.util.Constants; -import org.apache.lucene.util.IOUtils; import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.io.PathUtils; @@ -43,24 +42,15 @@ class ESFileStore extends FileStore { /** Underlying filestore */ final FileStore in; - /** Cached result of Lucene's {@code IOUtils.spins} on path. */ - final Boolean spins; - int majorDeviceNumber; - int minorDeviceNumber; + private int majorDeviceNumber; + private int minorDeviceNumber; @SuppressForbidden(reason = "tries to determine if disk is spinning") // TODO: move PathUtils to be package-private here instead of // public+forbidden api! - ESFileStore(FileStore in) { + ESFileStore(final FileStore in) { this.in = in; - Boolean spins; - // Lucene's IOUtils.spins only works on Linux today: if (Constants.LINUX) { - try { - spins = IOUtils.spins(PathUtils.get(getMountPointLinux(in))); - } catch (Exception e) { - spins = null; - } try { final List lines = Files.readAllLines(PathUtils.get("/proc/self/mountinfo")); for (final String line : lines) { @@ -70,20 +60,21 @@ class ESFileStore extends FileStore { final String[] deviceNumbers = fields[2].split(":"); majorDeviceNumber = Integer.parseInt(deviceNumbers[0]); minorDeviceNumber = Integer.parseInt(deviceNumbers[1]); + break; } } - } catch (Exception e) { + } catch (final Exception e) { majorDeviceNumber = -1; minorDeviceNumber = -1; } } else { - spins = null; + majorDeviceNumber = -1; + minorDeviceNumber = -1; } - this.spins = spins; } - + // these are hacks that are not guaranteed - private static String getMountPointLinux(FileStore store) { + private static String getMountPointLinux(final FileStore store) { String desc = store.toString(); int index = desc.lastIndexOf(" ("); if (index != -1) { @@ -92,109 +83,6 @@ private static String getMountPointLinux(FileStore store) { return desc; } } - - /** - * Files.getFileStore(Path) useless here! Don't complain, just try it yourself. - */ - @SuppressForbidden(reason = "works around the bugs") - static FileStore getMatchingFileStore(Path path, FileStore fileStores[]) throws IOException { - if (Constants.WINDOWS) { - return getFileStoreWindows(path, fileStores); - } - - final FileStore store; - try { - store = Files.getFileStore(path); - } catch (IOException unexpected) { - // give a better error message if a filestore cannot be retrieved from inside a FreeBSD jail. - if (Constants.FREE_BSD) { - throw new IOException("Unable to retrieve mount point data for " + path + - ". If you are running within a jail, set enforce_statfs=1. See jail(8)", unexpected); - } else { - throw unexpected; - } - } - - try { - String mount = getMountPointLinux(store); - FileStore sameMountPoint = null; - for (FileStore fs : fileStores) { - if (mount.equals(getMountPointLinux(fs))) { - if (sameMountPoint == null) { - sameMountPoint = fs; - } else { - // more than one filesystem has the same mount point; something is wrong! - // fall back to crappy one we got from Files.getFileStore - return store; - } - } - } - - if (sameMountPoint != null) { - // ok, we found only one, use it: - return sameMountPoint; - } else { - // fall back to crappy one we got from Files.getFileStore - return store; - } - } catch (Exception e) { - // ignore - } - - // fall back to crappy one we got from Files.getFileStore - return store; - } - - /** - * remove this code and just use getFileStore for windows on java 9 - * works around https://bugs.openjdk.java.net/browse/JDK-8034057 - */ - @SuppressForbidden(reason = "works around https://bugs.openjdk.java.net/browse/JDK-8034057") - static FileStore getFileStoreWindows(Path path, FileStore fileStores[]) throws IOException { - assert Constants.WINDOWS; - - try { - return Files.getFileStore(path); - } catch (FileSystemException possibleBug) { - final char driveLetter; - // look for a drive letter to see if its the SUBST bug, - // it might be some other type of path, like a windows share - // if something goes wrong, we just deliver the original exception - try { - String root = path.toRealPath().getRoot().toString(); - if (root.length() < 2) { - throw new RuntimeException("root isn't a drive letter: " + root); - } - driveLetter = Character.toLowerCase(root.charAt(0)); - if (Character.isAlphabetic(driveLetter) == false || root.charAt(1) != ':') { - throw new RuntimeException("root isn't a drive letter: " + root); - } - } catch (Exception checkFailed) { - // something went wrong, - possibleBug.addSuppressed(checkFailed); - throw possibleBug; - } - - // we have a drive letter: the hack begins!!!!!!!! - try { - // we have no choice but to parse toString of all stores and find the matching drive letter - for (FileStore store : fileStores) { - String toString = store.toString(); - int length = toString.length(); - if (length > 3 && toString.endsWith(":)") && toString.charAt(length - 4) == '(') { - if (Character.toLowerCase(toString.charAt(length - 3)) == driveLetter) { - return store; - } - } - } - throw new RuntimeException("no filestores matched"); - } catch (Exception weTried) { - IOException newException = new IOException("Unable to retrieve filestore for '" + path + "', tried matching against " + Arrays.toString(fileStores), weTried); - newException.addSuppressed(possibleBug); - throw newException; - } - } - } @Override public String name() { @@ -263,8 +151,6 @@ public V getFileStoreAttributeView(Class t @Override public Object getAttribute(String attribute) throws IOException { switch(attribute) { - // for the device - case "lucene:spins": return spins; // for the partition case "lucene:major_device_number": return majorDeviceNumber; case "lucene:minor_device_number": return minorDeviceNumber; diff --git a/core/src/main/java/org/elasticsearch/env/Environment.java b/core/src/main/java/org/elasticsearch/env/Environment.java index f431a7f646e09..0d30f7b576c06 100644 --- a/core/src/main/java/org/elasticsearch/env/Environment.java +++ b/core/src/main/java/org/elasticsearch/env/Environment.java @@ -27,6 +27,7 @@ import org.elasticsearch.common.settings.Settings; import java.io.IOException; +import java.io.UncheckedIOException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; @@ -35,7 +36,9 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.function.Function; @@ -97,22 +100,6 @@ public class Environment { /** Path to the temporary file directory used by the JDK */ private final Path tmpFile = PathUtils.get(System.getProperty("java.io.tmpdir")); - /** List of filestores on the system */ - private static final FileStore[] fileStores; - - /** - * We have to do this in clinit instead of init, because ES code is pretty messy, - * and makes these environments, throws them away, makes them again, etc. - */ - static { - // gather information about filesystems - ArrayList allStores = new ArrayList<>(); - for (FileStore store : PathUtils.getDefaultFileSystem().getFileStores()) { - allStores.add(new ESFileStore(store)); - } - fileStores = allStores.toArray(new ESFileStore[allStores.size()]); - } - public Environment(Settings settings) { final Path homeFile; if (PATH_HOME_SETTING.exists(settings)) { @@ -331,24 +318,8 @@ public Path tmpFile() { return tmpFile; } - /** - * Looks up the filestore associated with a Path. - *

- * This is an enhanced version of {@link Files#getFileStore(Path)}: - *

    - *
  • On *nix systems, the store returned for the root filesystem will contain - * the actual filesystem type (e.g. {@code ext4}) instead of {@code rootfs}. - *
  • On some systems, the custom attribute {@code lucene:spins} is supported - * via the {@link FileStore#getAttribute(String)} method. - *
  • Only requires the security permissions of {@link Files#getFileStore(Path)}, - * no permissions to the actual mount point are required. - *
  • Exception handling has the same semantics as {@link Files#getFileStore(Path)}. - *
  • Works around https://bugs.openjdk.java.net/browse/JDK-8034057. - *
  • Gives a better exception when filestore cannot be retrieved from inside a FreeBSD jail. - *
- */ - public static FileStore getFileStore(Path path) throws IOException { - return ESFileStore.getMatchingFileStore(path, fileStores); + public static FileStore getFileStore(final Path path) throws IOException { + return new ESFileStore(Files.getFileStore(path)); } /** diff --git a/core/src/main/java/org/elasticsearch/env/NodeEnvironment.java b/core/src/main/java/org/elasticsearch/env/NodeEnvironment.java index dec59f97f42fc..a24c35913749d 100644 --- a/core/src/main/java/org/elasticsearch/env/NodeEnvironment.java +++ b/core/src/main/java/org/elasticsearch/env/NodeEnvironment.java @@ -94,9 +94,6 @@ public static class NodePath { public final Path indicesPath; /** Cached FileStore from path */ public final FileStore fileStore; - /** Cached result of Lucene's {@code IOUtils.spins} on path. This is a trilean value: null means we could not determine it (we are - * not running on Linux, or we hit an exception trying), True means the device possibly spins and False means it does not. */ - public final Boolean spins; public final int majorDeviceNumber; public final int minorDeviceNumber; @@ -106,11 +103,9 @@ public NodePath(Path path) throws IOException { this.indicesPath = path.resolve(INDICES_FOLDER); this.fileStore = Environment.getFileStore(path); if (fileStore.supportsFileAttributeView("lucene")) { - this.spins = (Boolean) fileStore.getAttribute("lucene:spins"); this.majorDeviceNumber = (int) fileStore.getAttribute("lucene:major_device_number"); this.minorDeviceNumber = (int) fileStore.getAttribute("lucene:minor_device_number"); } else { - this.spins = null; this.majorDeviceNumber = -1; this.minorDeviceNumber = -1; } @@ -136,9 +131,13 @@ public Path resolve(Index index) { public String toString() { return "NodePath{" + "path=" + path + - ", spins=" + spins + + ", indicesPath=" + indicesPath + + ", fileStore=" + fileStore + + ", majorDeviceNumber=" + majorDeviceNumber + + ", minorDeviceNumber=" + minorDeviceNumber + '}'; } + } private final NodePath[] nodePaths; @@ -304,15 +303,6 @@ private void maybeLogPathDetails() throws IOException { for (NodePath nodePath : nodePaths) { sb.append('\n').append(" -> ").append(nodePath.path.toAbsolutePath()); - String spinsDesc; - if (nodePath.spins == null) { - spinsDesc = "unknown"; - } else if (nodePath.spins) { - spinsDesc = "possibly"; - } else { - spinsDesc = "no"; - } - FsInfo.Path fsPath = FsProbe.getFSInfo(nodePath); sb.append(", free_space [") .append(fsPath.getFree()) @@ -320,8 +310,6 @@ private void maybeLogPathDetails() throws IOException { .append(fsPath.getAvailable()) .append("], total_space [") .append(fsPath.getTotal()) - .append("], spins? [") - .append(spinsDesc) .append("], mount [") .append(fsPath.getMount()) .append("], type [") @@ -332,7 +320,6 @@ private void maybeLogPathDetails() throws IOException { } else if (logger.isInfoEnabled()) { FsInfo.Path totFSPath = new FsInfo.Path(); Set allTypes = new HashSet<>(); - Set allSpins = new HashSet<>(); Set allMounts = new HashSet<>(); for (NodePath nodePath : nodePaths) { FsInfo.Path fsPath = FsProbe.getFSInfo(nodePath); @@ -343,21 +330,13 @@ private void maybeLogPathDetails() throws IOException { if (type != null) { allTypes.add(type); } - Boolean spins = fsPath.getSpins(); - if (spins == null) { - allSpins.add("unknown"); - } else if (spins.booleanValue()) { - allSpins.add("possibly"); - } else { - allSpins.add("no"); - } totFSPath.add(fsPath); } } // Just log a 1-line summary: - logger.info("using [{}] data paths, mounts [{}], net usable_space [{}], net total_space [{}], spins? [{}], types [{}]", - nodePaths.length, allMounts, totFSPath.getAvailable(), totFSPath.getTotal(), toString(allSpins), toString(allTypes)); + logger.info("using [{}] data paths, mounts [{}], net usable_space [{}], net total_space [{}], types [{}]", + nodePaths.length, allMounts, totFSPath.getAvailable(), totFSPath.getTotal(), toString(allTypes)); } } diff --git a/core/src/main/java/org/elasticsearch/monitor/fs/FsInfo.java b/core/src/main/java/org/elasticsearch/monitor/fs/FsInfo.java index e20eb42427ffe..6f5e9a52b4fd0 100644 --- a/core/src/main/java/org/elasticsearch/monitor/fs/FsInfo.java +++ b/core/src/main/java/org/elasticsearch/monitor/fs/FsInfo.java @@ -49,10 +49,6 @@ public static class Path implements Writeable, ToXContent { long free = -1; long available = -1; - /** Uses Lucene's {@code IOUtils.spins} method to try to determine if the device backed by spinning media. - * This is null if we could not determine it, true if it possibly spins, else false. */ - Boolean spins = null; - public Path() { } @@ -74,7 +70,9 @@ public Path(StreamInput in) throws IOException { total = in.readLong(); free = in.readLong(); available = in.readLong(); - spins = in.readOptionalBoolean(); + if (in.getVersion().before(Version.V_6_0_0_alpha1_UNRELEASED)) { + in.readOptionalBoolean(); + } } @Override @@ -85,7 +83,9 @@ public void writeTo(StreamOutput out) throws IOException { out.writeLong(total); out.writeLong(free); out.writeLong(available); - out.writeOptionalBoolean(spins); + if (out.getVersion().before(Version.V_6_0_0_alpha1_UNRELEASED)) { + out.writeOptionalBoolean(null); + } } public String getPath() { @@ -112,10 +112,6 @@ public ByteSizeValue getAvailable() { return new ByteSizeValue(available); } - public Boolean getSpins() { - return spins; - } - private long addLong(long current, long other) { if (other == -1) { return current; @@ -140,10 +136,6 @@ public void add(Path path) { total = FsProbe.adjustForHugeFilesystems(addLong(total, path.total)); free = FsProbe.adjustForHugeFilesystems(addLong(free, path.free)); available = FsProbe.adjustForHugeFilesystems(addLong(available, path.available)); - if (path.spins != null && path.spins.booleanValue()) { - // Spinning is contagious! - spins = Boolean.TRUE; - } } static final class Fields { @@ -156,7 +148,6 @@ static final class Fields { static final String FREE_IN_BYTES = "free_in_bytes"; static final String AVAILABLE = "available"; static final String AVAILABLE_IN_BYTES = "available_in_bytes"; - static final String SPINS = "spins"; } @Override @@ -181,9 +172,6 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (available != -1) { builder.byteSizeField(Fields.AVAILABLE_IN_BYTES, Fields.AVAILABLE, available); } - if (spins != null) { - builder.field(Fields.SPINS, spins.toString()); - } builder.endObject(); return builder; diff --git a/core/src/main/java/org/elasticsearch/monitor/fs/FsProbe.java b/core/src/main/java/org/elasticsearch/monitor/fs/FsProbe.java index 1fdae49a6f16b..8e3cd53e74f1c 100644 --- a/core/src/main/java/org/elasticsearch/monitor/fs/FsProbe.java +++ b/core/src/main/java/org/elasticsearch/monitor/fs/FsProbe.java @@ -159,7 +159,6 @@ public static FsInfo.Path getFSInfo(NodePath nodePath) throws IOException { fsPath.available = nodePath.fileStore.getUsableSpace(); fsPath.type = nodePath.fileStore.type(); fsPath.mount = nodePath.fileStore.toString(); - fsPath.spins = nodePath.spins; return fsPath; } diff --git a/core/src/main/resources/org/elasticsearch/bootstrap/security.policy b/core/src/main/resources/org/elasticsearch/bootstrap/security.policy index 6d28944680ea4..7b1dcd788c287 100644 --- a/core/src/main/resources/org/elasticsearch/bootstrap/security.policy +++ b/core/src/main/resources/org/elasticsearch/bootstrap/security.policy @@ -120,6 +120,7 @@ grant { permission java.io.FilePermission "/proc/sys/vm/max_map_count", "read"; // io stats on Linux + permission java.io.FilePermission "/proc/self/mountinfo", "read"; permission java.io.FilePermission "/proc/diskstats", "read"; // control group stats on Linux diff --git a/core/src/test/java/org/elasticsearch/action/admin/cluster/node/stats/NodeStatsTests.java b/core/src/test/java/org/elasticsearch/action/admin/cluster/node/stats/NodeStatsTests.java index 0b6b14684f995..9591e31b2b6dc 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/cluster/node/stats/NodeStatsTests.java +++ b/core/src/test/java/org/elasticsearch/action/admin/cluster/node/stats/NodeStatsTests.java @@ -183,7 +183,6 @@ public void testSerialization() throws IOException { assertEquals(fs.getTotal().getFree(), deserializedFs.getTotal().getFree()); assertEquals(fs.getTotal().getMount(), deserializedFs.getTotal().getMount()); assertEquals(fs.getTotal().getPath(), deserializedFs.getTotal().getPath()); - assertEquals(fs.getTotal().getSpins(), deserializedFs.getTotal().getSpins()); assertEquals(fs.getTotal().getType(), deserializedFs.getTotal().getType()); FsInfo.IoStats ioStats = fs.getIoStats(); FsInfo.IoStats deserializedIoStats = deserializedFs.getIoStats(); diff --git a/docs/reference/migration/migrate_6_0/stats.asciidoc b/docs/reference/migration/migrate_6_0/stats.asciidoc index 633604f39ae17..ed70d1503c4c3 100644 --- a/docs/reference/migration/migrate_6_0/stats.asciidoc +++ b/docs/reference/migration/migrate_6_0/stats.asciidoc @@ -5,3 +5,13 @@ Given that store throttling has been removed, the `store` stats do not report `throttle_time` anymore. + +==== FS stats no longer reports if the disk spins + +Elasticsearch has defaulted to assuming that it is running on SSDs since +the 2.x series of Elasticsearch. As such, Elasticsearch no longer needs to +collect information from the operating system as to whether or not the +underlying disks of each data path spin or not. While this functionality was no +longer needed starting in the 2.x series of Elasticsearch, it was maintained in +the filesystem section of the nodes stats APIs. This information has now been +removed. From e9547d6a709541f775fbff9aa2b30a93d8836eea Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Thu, 4 May 2017 17:41:21 +0200 Subject: [PATCH 206/619] Rewrote the github issue template to be shorter and more likely to be read (#24486) --- .github/ISSUE_TEMPLATE.md | 49 +++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index ea89d18ca8abc..1e4d449a9a551 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,33 +1,27 @@ +1. GitHub is reserved for bug reports and feature requests. The best place to + ask a general question is at the Elastic [forums](https://discuss.elastic.co). + GitHub is not the place for general questions. - +2. Is this bug report or feature request for a supported OS? If not, it + is likely to be closed. See https://www.elastic.co/support/matrix#show_os + +3. Please fill out EITHER the feature request block or the bug report block + below, and delete the other block. - + + +**Describe the feature**: + + + **Elasticsearch version**: **Plugins installed**: [] @@ -39,15 +33,14 @@ and provide responses for all of the below items. **Description of the problem including expected versus actual behavior**: **Steps to reproduce**: + +Please include a *minimal* but *complete* recreation of the problem, including +(e.g.) index creation, mappings, settings, query etc. The easier you make for +us to reproduce it, the more likely that somebody will take the time to look at it. + 1. 2. 3. **Provide logs (if relevant)**: - - -**Describe the feature**: From 6002b41b5f8d6ccfc5b5569ee00958ca565bb7d2 Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Thu, 4 May 2017 12:22:54 -0400 Subject: [PATCH 207/619] Add StreamInput.readEnum and StreamOutput.writeEnum (#24475) Implements the common enum serialization/deserialization pattern for enumeration on the StreamInput/StreamOutput. --- .../cluster/node/DiscoveryNode.java | 8 ++--- .../common/geo/ShapeRelation.java | 8 ++--- .../common/geo/SpatialStrategy.java | 8 ++--- .../common/io/stream/StreamInput.java | 12 ++++++++ .../common/io/stream/StreamOutput.java | 7 +++++ .../search/function/CombineFunction.java | 8 ++--- .../function/FieldValueFactorFunction.java | 8 ++--- .../function/FiltersFunctionScoreQuery.java | 8 ++--- .../org/elasticsearch/index/VersionType.java | 6 ++-- .../elasticsearch/index/query/Operator.java | 8 ++--- .../elasticsearch/search/MultiValueMode.java | 8 ++--- .../search/aggregations/Aggregator.java | 8 ++--- .../percentiles/PercentilesMethod.java | 10 ++----- .../subphase/highlight/HighlightBuilder.java | 16 +++------- .../search/rescore/QueryRescoreMode.java | 10 ++----- .../search/sort/ScriptSortBuilder.java | 8 ++--- .../elasticsearch/search/sort/SortMode.java | 10 ++----- .../elasticsearch/search/sort/SortOrder.java | 8 ++--- .../elasticsearch/search/suggest/SortBy.java | 8 ++--- .../suggest/term/TermSuggestionBuilder.java | 16 +++------- .../common/io/stream/BytesStreamsTests.java | 30 +++++++++++++++++++ 21 files changed, 92 insertions(+), 121 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java b/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java index 0e8435d0f8e62..caafb82c657a7 100644 --- a/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java +++ b/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java @@ -238,11 +238,7 @@ public DiscoveryNode(StreamInput in) throws IOException { int rolesSize = in.readVInt(); this.roles = EnumSet.noneOf(Role.class); for (int i = 0; i < rolesSize; i++) { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= Role.values().length) { - throw new IOException("Unknown Role ordinal [" + ordinal + "]"); - } - this.roles.add(Role.values()[ordinal]); + this.roles.add(in.readEnum(Role.class)); } this.version = Version.readVersion(in); } @@ -262,7 +258,7 @@ public void writeTo(StreamOutput out) throws IOException { } out.writeVInt(roles.size()); for (Role role : roles) { - out.writeVInt(role.ordinal()); + out.writeEnum(role); } Version.writeVersion(version, out); } diff --git a/core/src/main/java/org/elasticsearch/common/geo/ShapeRelation.java b/core/src/main/java/org/elasticsearch/common/geo/ShapeRelation.java index e9966834a0119..e83e18ce43255 100644 --- a/core/src/main/java/org/elasticsearch/common/geo/ShapeRelation.java +++ b/core/src/main/java/org/elasticsearch/common/geo/ShapeRelation.java @@ -44,16 +44,12 @@ public enum ShapeRelation implements Writeable { } public static ShapeRelation readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown ShapeRelation ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(ShapeRelation.class); } @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(ordinal()); + out.writeEnum(this); } public static ShapeRelation getRelationByName(String name) { diff --git a/core/src/main/java/org/elasticsearch/common/geo/SpatialStrategy.java b/core/src/main/java/org/elasticsearch/common/geo/SpatialStrategy.java index acac5fd66905b..0b4f640fd2884 100644 --- a/core/src/main/java/org/elasticsearch/common/geo/SpatialStrategy.java +++ b/core/src/main/java/org/elasticsearch/common/geo/SpatialStrategy.java @@ -40,16 +40,12 @@ public String getStrategyName() { } public static SpatialStrategy readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown SpatialStrategy ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(SpatialStrategy.class); } @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(ordinal()); + out.writeEnum(this); } public static SpatialStrategy fromString(String strategyName) { diff --git a/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java b/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java index 0efb99a1fabb4..4681af3392ec5 100644 --- a/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java +++ b/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java @@ -901,6 +901,18 @@ public List readNamedWriteableList(Class catego return builder; } + /** + * Reads an enum with type E that was serialized based on the value of it's ordinal + */ + public > E readEnum(Class enumClass) throws IOException { + int ordinal = readVInt(); + E[] values = enumClass.getEnumConstants(); + if (ordinal < 0 || ordinal >= values.length) { + throw new IOException("Unknown " + enumClass.getSimpleName() + " ordinal [" + ordinal + "]"); + } + return values[ordinal]; + } + public static StreamInput wrap(byte[] bytes) { return wrap(bytes, 0, bytes.length); } diff --git a/core/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java b/core/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java index 4d57e7c1b8898..5816a9fd4691e 100644 --- a/core/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java +++ b/core/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java @@ -936,4 +936,11 @@ public void writeNamedWriteableList(List list) throws } } + /** + * Writes an enum with type E that by serialized it based on it's ordinal value + */ + public > void writeEnum(E enumValue) throws IOException { + writeVInt(enumValue.ordinal()); + } + } diff --git a/core/src/main/java/org/elasticsearch/common/lucene/search/function/CombineFunction.java b/core/src/main/java/org/elasticsearch/common/lucene/search/function/CombineFunction.java index e8bd288799810..90d110c38044d 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/search/function/CombineFunction.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/search/function/CombineFunction.java @@ -143,15 +143,11 @@ private static double deviation(double input) { // only with assert! @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(this.ordinal()); + out.writeEnum(this); } public static CombineFunction readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown CombineFunction ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(CombineFunction.class); } public static CombineFunction fromString(String combineFunction) { diff --git a/core/src/main/java/org/elasticsearch/common/lucene/search/function/FieldValueFactorFunction.java b/core/src/main/java/org/elasticsearch/common/lucene/search/function/FieldValueFactorFunction.java index e225df040ab28..1978c0acf0d84 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/search/function/FieldValueFactorFunction.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/search/function/FieldValueFactorFunction.java @@ -191,15 +191,11 @@ public double apply(double n) { @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(this.ordinal()); + out.writeEnum(this); } public static Modifier readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown Modifier ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(Modifier.class); } @Override diff --git a/core/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java b/core/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java index abf145406c518..2f2a70537c03d 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java @@ -81,15 +81,11 @@ public enum ScoreMode implements Writeable { @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(this.ordinal()); + out.writeEnum(this); } public static ScoreMode readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown ScoreMode ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(ScoreMode.class); } public static ScoreMode fromString(String scoreMode) { diff --git a/core/src/main/java/org/elasticsearch/index/VersionType.java b/core/src/main/java/org/elasticsearch/index/VersionType.java index fcbd6690a3899..c5094ea185db1 100644 --- a/core/src/main/java/org/elasticsearch/index/VersionType.java +++ b/core/src/main/java/org/elasticsearch/index/VersionType.java @@ -364,13 +364,11 @@ public static VersionType fromValue(byte value) { } public static VersionType readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - assert (ordinal == 0 || ordinal == 1 || ordinal == 2 || ordinal == 3); - return VersionType.values()[ordinal]; + return in.readEnum(VersionType.class); } @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(ordinal()); + out.writeEnum(this); } } diff --git a/core/src/main/java/org/elasticsearch/index/query/Operator.java b/core/src/main/java/org/elasticsearch/index/query/Operator.java index 7972dbb49ad81..de88abebad359 100644 --- a/core/src/main/java/org/elasticsearch/index/query/Operator.java +++ b/core/src/main/java/org/elasticsearch/index/query/Operator.java @@ -54,16 +54,12 @@ public QueryParser.Operator toQueryParserOperator() { } public static Operator readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown Operator ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(Operator.class); } @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(this.ordinal()); + out.writeEnum(this); } public static Operator fromString(String op) { diff --git a/core/src/main/java/org/elasticsearch/search/MultiValueMode.java b/core/src/main/java/org/elasticsearch/search/MultiValueMode.java index 2d6fd8a2b603a..231bc8bf3c050 100644 --- a/core/src/main/java/org/elasticsearch/search/MultiValueMode.java +++ b/core/src/main/java/org/elasticsearch/search/MultiValueMode.java @@ -948,14 +948,10 @@ public interface UnsortedNumericDoubleValues { @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(this.ordinal()); + out.writeEnum(this); } public static MultiValueMode readMultiValueModeFrom(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown MultiValueMode ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(MultiValueMode.class); } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/Aggregator.java b/core/src/main/java/org/elasticsearch/search/aggregations/Aggregator.java index 44adb33c01bc5..41a0fa6dd30a7 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/Aggregator.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/Aggregator.java @@ -139,16 +139,12 @@ public static SubAggCollectionMode parse(String value) { } public static SubAggCollectionMode readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown SubAggCollectionMode ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(SubAggCollectionMode.class); } @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(ordinal()); + out.writeEnum(this); } } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentilesMethod.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentilesMethod.java index b10880a13c0c8..3b8085793dc0a 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentilesMethod.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentilesMethod.java @@ -53,20 +53,16 @@ public ParseField getParseField() { } public static PercentilesMethod readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown PercentilesMethod ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(PercentilesMethod.class); } @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(ordinal()); + out.writeEnum(this); } @Override public String toString() { return parseField.getPreferredName(); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilder.java b/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilder.java index c7c9c547b511c..5e49aa7395d57 100644 --- a/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilder.java @@ -504,16 +504,12 @@ public enum Order implements Writeable { NONE, SCORE; public static Order readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown Order ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(Order.class); } @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(this.ordinal()); + out.writeEnum(this); } public static Order fromString(String order) { @@ -533,16 +529,12 @@ public enum BoundaryScannerType implements Writeable { CHARS, WORD, SENTENCE; public static BoundaryScannerType readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown BoundaryScannerType ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(BoundaryScannerType.class); } @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(this.ordinal()); + out.writeEnum(this); } public static BoundaryScannerType fromString(String boundaryScannerType) { diff --git a/core/src/main/java/org/elasticsearch/search/rescore/QueryRescoreMode.java b/core/src/main/java/org/elasticsearch/search/rescore/QueryRescoreMode.java index 70718b56c0c5c..51db82652fce0 100644 --- a/core/src/main/java/org/elasticsearch/search/rescore/QueryRescoreMode.java +++ b/core/src/main/java/org/elasticsearch/search/rescore/QueryRescoreMode.java @@ -86,16 +86,12 @@ public String toString() { public abstract float combine(float primary, float secondary); public static QueryRescoreMode readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown ScoreMode ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(QueryRescoreMode.class); } @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(this.ordinal()); + out.writeEnum(this); } public static QueryRescoreMode fromString(String scoreMode) { @@ -111,4 +107,4 @@ public static QueryRescoreMode fromString(String scoreMode) { public String toString() { return name().toLowerCase(Locale.ROOT); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/elasticsearch/search/sort/ScriptSortBuilder.java b/core/src/main/java/org/elasticsearch/search/sort/ScriptSortBuilder.java index 2901e05f05191..b136dd7798968 100644 --- a/core/src/main/java/org/elasticsearch/search/sort/ScriptSortBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/sort/ScriptSortBuilder.java @@ -350,18 +350,14 @@ public enum ScriptSortType implements Writeable { @Override public void writeTo(final StreamOutput out) throws IOException { - out.writeVInt(ordinal()); + out.writeEnum(this); } /** * Read from a stream. */ static ScriptSortType readFromStream(final StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown ScriptSortType ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(ScriptSortType.class); } public static ScriptSortType fromString(final String str) { diff --git a/core/src/main/java/org/elasticsearch/search/sort/SortMode.java b/core/src/main/java/org/elasticsearch/search/sort/SortMode.java index 21495798a89f9..07b5bfa98c23f 100644 --- a/core/src/main/java/org/elasticsearch/search/sort/SortMode.java +++ b/core/src/main/java/org/elasticsearch/search/sort/SortMode.java @@ -52,15 +52,11 @@ public enum SortMode implements Writeable { @Override public void writeTo(final StreamOutput out) throws IOException { - out.writeVInt(ordinal()); + out.writeEnum(this); } public static SortMode readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown SortMode ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(SortMode.class); } public static SortMode fromString(final String str) { @@ -85,4 +81,4 @@ public static SortMode fromString(final String str) { public String toString() { return name().toLowerCase(Locale.ROOT); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/elasticsearch/search/sort/SortOrder.java b/core/src/main/java/org/elasticsearch/search/sort/SortOrder.java index cd0a3bb6d4642..fbcb7b4288e31 100644 --- a/core/src/main/java/org/elasticsearch/search/sort/SortOrder.java +++ b/core/src/main/java/org/elasticsearch/search/sort/SortOrder.java @@ -52,16 +52,12 @@ public String toString() { }; static SortOrder readFromStream(StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown SortOrder ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(SortOrder.class); } @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(this.ordinal()); + out.writeEnum(this); } public static SortOrder fromString(String op) { diff --git a/core/src/main/java/org/elasticsearch/search/suggest/SortBy.java b/core/src/main/java/org/elasticsearch/search/suggest/SortBy.java index 3cd19c5c2fb22..328fc4e8218ed 100644 --- a/core/src/main/java/org/elasticsearch/search/suggest/SortBy.java +++ b/core/src/main/java/org/elasticsearch/search/suggest/SortBy.java @@ -38,15 +38,11 @@ public enum SortBy implements Writeable { @Override public void writeTo(final StreamOutput out) throws IOException { - out.writeVInt(ordinal()); + out.writeEnum(this); } public static SortBy readFromStream(final StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown SortBy ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(SortBy.class); } public static SortBy resolve(final String str) { diff --git a/core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestionBuilder.java b/core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestionBuilder.java index 72fd41dc5b49f..f701ff3642624 100644 --- a/core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestionBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestionBuilder.java @@ -511,15 +511,11 @@ public org.apache.lucene.search.spell.SuggestMode toLucene() { @Override public void writeTo(final StreamOutput out) throws IOException { - out.writeVInt(ordinal()); + out.writeEnum(this); } public static SuggestMode readFromStream(final StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown SuggestMode ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(SuggestMode.class); } public static SuggestMode resolve(final String str) { @@ -571,15 +567,11 @@ public StringDistance toLucene() { @Override public void writeTo(final StreamOutput out) throws IOException { - out.writeVInt(ordinal()); + out.writeEnum(this); } public static StringDistanceImpl readFromStream(final StreamInput in) throws IOException { - int ordinal = in.readVInt(); - if (ordinal < 0 || ordinal >= values().length) { - throw new IOException("Unknown StringDistanceImpl ordinal [" + ordinal + "]"); - } - return values()[ordinal]; + return in.readEnum(StringDistanceImpl.class); } public static StringDistanceImpl resolve(final String str) { diff --git a/core/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java b/core/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java index a8b065343c974..b67000e2b2313 100644 --- a/core/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java +++ b/core/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java @@ -812,4 +812,34 @@ public void testVLong() throws IOException { StreamInput in = new BytesArray(Base64.getDecoder().decode("////////////AQAAAAAAAA==")).streamInput(); assertEquals(-1, in.readVLong()); } + + public enum TestEnum { + ONE, + TWO, + THREE + } + + public void testEnum() throws IOException { + TestEnum value = randomFrom(TestEnum.values()); + BytesStreamOutput output = new BytesStreamOutput(); + output.writeEnum(value); + StreamInput input = output.bytes().streamInput(); + assertEquals(value, input.readEnum(TestEnum.class)); + assertEquals(0, input.available()); + } + + public void testInvalidEnum() throws IOException { + BytesStreamOutput output = new BytesStreamOutput(); + int randomNumber = randomInt(); + boolean validEnum = randomNumber >= 0 && randomNumber < TestEnum.values().length; + output.writeVInt(randomNumber); + StreamInput input = output.bytes().streamInput(); + if (validEnum) { + assertEquals(TestEnum.values()[randomNumber], input.readEnum(TestEnum.class)); + } else { + IOException ex = expectThrows(IOException.class, () -> input.readEnum(TestEnum.class)); + assertEquals("Unknown TestEnum ordinal [" + randomNumber + "]", ex.getMessage()); + } + assertEquals(0, input.available()); + } } From 9bc7e210a03a128dd8e7283305ff50b594d945ff Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 4 May 2017 13:11:09 -0400 Subject: [PATCH 208/619] Test: Move flag to painless tests (#24494) The `-XX:-OmitStackTraceInFastThrow` flag is only required by Painless's tests so we'll only set it there. This is much simpler. --- .../org/elasticsearch/gradle/BuildPlugin.groovy | 13 +------------ modules/lang-painless/build.gradle | 6 ++++-- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy index 3acf865f7bc47..b3c2f4faef8e8 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy @@ -468,18 +468,7 @@ class BuildPlugin implements Plugin { File heapdumpDir = new File(project.buildDir, 'heapdump') heapdumpDir.mkdirs() jvmArg '-XX:HeapDumpPath=' + heapdumpDir - /* - * We only want to append -XX:-OmitStackTraceInFastThrow if a flag for OmitStackTraceInFastThrow is not already included in - * tests.jvm.argline. - */ - final String testsJvmArgline = System.getProperty('tests.jvm.argline') - if (testsJvmArgline == null) { - argLine '-XX:-OmitStackTraceInFastThrow' - } else if (testsJvmArgline.indexOf("OmitStackTraceInFastThrow") < 0) { - argLine testsJvmArgline.trim() + ' ' + '-XX:-OmitStackTraceInFastThrow' - } else { - argLine testsJvmArgline.trim() - } + argLine System.getProperty('tests.jvm.argline') // we use './temp' since this is per JVM and tests are forbidden from writing to CWD systemProperty 'java.io.tmpdir', './temp' diff --git a/modules/lang-painless/build.gradle b/modules/lang-painless/build.gradle index 31b41261b3a77..85f609d7757bc 100644 --- a/modules/lang-painless/build.gradle +++ b/modules/lang-painless/build.gradle @@ -33,6 +33,10 @@ dependencyLicenses { mapping from: /asm-.*/, to: 'asm' } +test { + jvmArg '-XX:-OmitStackTraceInFastThrow' +} + integTestCluster { setting 'script.max_compilations_per_minute', '1000' } @@ -146,5 +150,3 @@ task regen { } } } - - From 9f431543fc56b9b824d2c273c0a751b5fcee4a7a Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 4 May 2017 17:30:54 -0400 Subject: [PATCH 209/619] CONSOLEify inner hits docs Rewrites most of the snippets in the `innert_hits` docs to be complete examples and enables `VIEW IN CONSOLE`, `COPY AS CURL`, and automatic testing of the snippets. --- docs/build.gradle | 1 - .../search/request/inner-hits.asciidoc | 424 ++++++++++++++---- 2 files changed, 333 insertions(+), 92 deletions(-) diff --git a/docs/build.gradle b/docs/build.gradle index 699cda15872ae..204b9abb1b997 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -59,7 +59,6 @@ buildRestTests.expectedUnconvertedCandidates = [ 'reference/modules/cross-cluster-search.asciidoc', // this is hard to test since we need 2 clusters -- maybe we can trick it into referencing itself... 'reference/search/field-stats.asciidoc', 'reference/search/profile.asciidoc', - 'reference/search/request/inner-hits.asciidoc', ] integTestCluster { diff --git a/docs/reference/search/request/inner-hits.asciidoc b/docs/reference/search/request/inner-hits.asciidoc index 1b118a419ed22..900426bee9b8f 100644 --- a/docs/reference/search/request/inner-hits.asciidoc +++ b/docs/reference/search/request/inner-hits.asciidoc @@ -22,6 +22,7 @@ The structure looks like this: } } -------------------------------------------------- +// NOTCONSOLE If `inner_hits` is defined on a query that supports it then each search hit will contain an `inner_hits` json object with the following structure: @@ -52,6 +53,7 @@ If `inner_hits` is defined on a query that supports it then each search hit will ... ] -------------------------------------------------- +// NOTCONSOLE ==== Options @@ -80,22 +82,50 @@ Inner hits also supports the following per document features: The nested `inner_hits` can be used to include nested inner objects as inner hits to a search hit. -The example below assumes that there is a nested object field defined with the name `comments`: - [source,js] -------------------------------------------------- +PUT test { - "query" : { - "nested" : { - "path" : "comments", - "query" : { - "match" : {"comments.message" : "[actual query]"} - }, - "inner_hits" : {} <1> + "mappings": { + "doc": { + "properties": { + "comments": { + "type": "nested" } + } + } + } +} + +PUT test/doc/1?refresh +{ + "title": "Test title", + "comments": [ + { + "author": "kimchy", + "text": "comment text" + }, + { + "author": "nik9000", + "text": "words words words" } + ] +} + +POST test/_search +{ + "query": { + "nested": { + "path": "comments", + "query": { + "match": {"comments.text" : "words"} + }, + "inner_hits": {} <1> + } + } } -------------------------------------------------- +// CONSOLE <1> The inner hit definition in the nested query. No other options need to be defined. @@ -103,35 +133,46 @@ An example of a response snippet that could be generated from the above search r [source,js] -------------------------------------------------- -... -"hits": { - ... - "hits": [ - { - "_index": "my-index", - "_type": "question", +{ + ..., + "hits": { + "total": 1, + "max_score": 0.9651416, + "hits": [ + { + "_index": "test", + "_type": "doc", "_id": "1", + "_score": 0.9651416, "_source": ..., "inner_hits": { - "comments": { <1> - "hits": { - "total": ..., - "hits": [ - { - "_nested": { - "field": "comments", - "offset": 2 - }, - "_source": ... - }, - ... - ] - } - } + "comments": { <1> + "hits": { + "total": 1, + "max_score": 0.9651416, + "hits": [ + { + "_nested": { + "field": "comments", + "offset": 1 + }, + "_score": 0.9651416, + "_source": { + "author": "nik9000", + "text": "words words words" + } + } + ] + } + } } - }, - ... + } + ] + } +} -------------------------------------------------- +// TESTRESPONSE[s/"_source": \.\.\./"_source": $body.hits.hits.0._source/] +// TESTRESPONSE[s/\.\.\./"timed_out": false, "took": $body.took, "_shards": $body._shards/] <1> The name used in the inner hit definition in the search request. A custom key can be used via the `name` option. @@ -156,46 +197,111 @@ its `_source` field. To include the source of just the nested document, the sour the relevant bit for the nested document is included as source in the inner hit. Doing this for each matching nested document has an impact on the time it takes to execute the entire search request, especially when `size` and the inner hits' `size` are set higher than the default. To avoid the relative expensive source extraction for nested inner hits, one can disable -including the source and solely rely on stored fields. - -Enabled stored field for fields under the nested object field in your mapping: +including the source and solely rely on stored fields. Like this: [source,js] -------------------------------------------------- +PUT test { - "properties": { - "comment": { - "type": "comments", - "properties" : { - "message" : { - "type" : "text", - "store" : true + "mappings": { + "doc": { + "properties": { + "comments": { + "type": "nested", + "properties": { + "text": { + "type": "text", + "store": true } } } + } + } + } +} + +PUT test/doc/1?refresh +{ + "title": "Test title", + "comments": [ + { + "author": "kimchy", + "text": "comment text" + }, + { + "author": "nik9000", + "text": "words words words" } + ] +} + +POST test/_search +{ + "query": { + "nested": { + "path": "comments", + "query": { + "match": {"comments.text" : "words"} + }, + "inner_hits": { + "_source" : false, + "stored_fields" : ["comments.text"] + } + } + } } -------------------------------------------------- +// CONSOLE + +//// -Disable including source and include specific stored fields in the inner hits definition: +Response not included in text but tested for completeness sake. [source,js] -------------------------------------------------- { - "query" : { - "nested" : { - "path" : "comments", - "query" : { - "match" : {"comments.message" : "[actual query]"} - }, - "inner_hits" : { - "_source" : false, - "stored_fields" : ["comments.text"] + ..., + "hits": { + "total": 1, + "max_score": 0.9651416, + "hits": [ + { + "_index": "test", + "_type": "doc", + "_id": "1", + "_score": 0.9651416, + "_source": ..., + "inner_hits": { + "comments": { <1> + "hits": { + "total": 1, + "max_score": 0.9651416, + "hits": [ + { + "_nested": { + "field": "comments", + "offset": 1 + }, + "_score": 0.9651416, + "fields": { + "comments.text": [ + "words words words" + ] + } + } + ] } + } } - } + } + ] + } } -------------------------------------------------- +// TESTRESPONSE[s/"_source": \.\.\./"_source": $body.hits.hits.0._source/] +// TESTRESPONSE[s/\.\.\./"timed_out": false, "took": $body.took, "_shards": $body._shards/] + +//// [[hierarchical-nested-inner-hits]] ==== Hierarchical levels of nested object fields and inner hits. @@ -206,16 +312,113 @@ with the root hits then the following path can be defined: [source,js] -------------------------------------------------- +PUT test { - "query" : { - "nested" : { - "path" : "comments.votes", - "query" : { ... }, - "inner_hits" : {} + "mappings": { + "doc": { + "properties": { + "comments": { + "type": "nested", + "properties": { + "message": { + "type": "text", + "store": true + }, + "votes": { + "type": "nested" + } + } + } } } + } +} + +PUT test/doc/1?refresh +{ + "title": "Test title", + "comments": [ + { + "author": "kimchy", + "text": "comment text", + "votes": [] + }, + { + "author": "nik9000", + "text": "words words words", + "votes": [ + {"value": 1 , "voter": "kimchy"}, + {"value": -1, "voter": "other"} + ] + } + ] +} + +POST test/_search +{ + "query": { + "nested": { + "path": "comments.votes", + "query": { + "match": { + "comments.votes.voter": "kimchy" + } + }, + "inner_hits" : {} + } + } } -------------------------------------------------- +// CONSOLE + +Which would look like: + +[source,js] +-------------------------------------------------- +{ + ..., + "hits": { + "total": 1, + "max_score": 0.6931472, + "hits": [ + { + "_index": "test", + "_type": "doc", + "_id": "1", + "_score": 0.6931472, + "_source": ..., + "inner_hits": { + "comments.votes": { <1> + "hits": { + "total": 1, + "max_score": 0.6931472, + "hits": [ + { + "_nested": { + "field": "comments", + "offset": 1, + "_nested": { + "field": "votes", + "offset": 0 + } + }, + "_score": 0.6931472, + "_source": { + "value": 1, + "voter": "kimchy" + } + } + ] + } + } + } + } + ] + } +} +-------------------------------------------------- +// TESTRESPONSE[s/"_source": \.\.\./"_source": $body.hits.hits.0._source/] +// TESTRESPONSE[s/\.\.\./"timed_out": false, "took": $body.took, "_shards": $body._shards/] This indirect referencing is only supported for nested inner hits. @@ -224,22 +427,49 @@ This indirect referencing is only supported for nested inner hits. The parent/child `inner_hits` can be used to include parent or child -The examples below assumes that there is a `_parent` field mapping in the `comment` type: - [source,js] -------------------------------------------------- +PUT test { - "query" : { - "has_child" : { - "type" : "comment", - "query" : { - "match" : {"message" : "[actual query]"} - }, - "inner_hits" : {} <1> + "settings": { + "mapping.single_type": false + }, + "mappings": { + "my_parent": {}, + "my_child": { + "_parent": { + "type": "my_parent" + } + } + } +} + +PUT test/my_parent/1?refresh +{ + "test": "test" +} + +PUT test/my_child/1?parent=1&refresh +{ + "test": "test" +} + +POST test/_search +{ + "query": { + "has_child": { + "type": "my_child", + "query": { + "match": { + "test": "test" } + }, + "inner_hits": {} <1> } + } } -------------------------------------------------- +// CONSOLE <1> The inner hit definition like in the nested example. @@ -247,30 +477,42 @@ An example of a response snippet that could be generated from the above search r [source,js] -------------------------------------------------- -... -"hits": { - ... - "hits": [ - { - "_index": "my-index", - "_type": "question", +{ + ..., + "hits": { + "total": 1, + "max_score": 1.0, + "hits": [ + { + "_index": "test", + "_type": "my_parent", "_id": "1", + "_score": 1.0, "_source": ..., "inner_hits": { - "comment": { - "hits": { - "total": ..., - "hits": [ - { - "_type": "comment", - "_id": "5", - "_source": ... - }, - ... - ] - } - } + "my_child": { + "hits": { + "total": 1, + "max_score": 0.18232156, + "hits": [ + { + "_type": "my_child", + "_id": "1", + "_score": 0.18232156, + "_routing": "1", + "_parent": "1", + "_source": { + "test": "test" + } + } + ] + } + } } - }, - ... --------------------------------------------------- \ No newline at end of file + } + ] + } +} +-------------------------------------------------- +// TESTRESPONSE[s/"_source": \.\.\./"_source": $body.hits.hits.0._source/] +// TESTRESPONSE[s/\.\.\./"timed_out": false, "took": $body.took, "_shards": $body._shards/] From 559bec23cca3ad70aa251f163834d18b6bbd2686 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 4 May 2017 17:57:25 -0400 Subject: [PATCH 210/619] Docs: rewrite the docs/README file I originally wrote this file when we first added snippets testing and a lot has changed. We've grown quite fond of the `// TESTRESPONSE[s/foo/bar/]` construct, for example, but the docs discouraged its use. Relates to #18160 --- docs/README.asciidoc | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/docs/README.asciidoc b/docs/README.asciidoc index 9365f877eb5c5..90602d76ffa01 100644 --- a/docs/README.asciidoc +++ b/docs/README.asciidoc @@ -4,24 +4,28 @@ Elasticsearch documentation build process. See: https://github.com/elastic/docs Snippets marked with `// CONSOLE` are automatically annotated with "VIEW IN -CONSOLE" in the documentation and are automatically tested by the command -`gradle :docs:check`. To test just the docs from a single page, use e.g. -`gradle :docs:check -Dtests.method=*rollover*`. +CONSOLE" and "COPY AS CURL" in the documentation and are automatically tested +by the command `gradle :docs:check`. To test just the docs from a single page, +use e.g. `gradle :docs:check -Dtests.method=*rollover*`. -By default `// CONSOLE` snippet runs as its own isolated -test. You can manipulate the test execution in the following ways: +By default each `// CONSOLE` snippet runs as its own isolated test. You can +manipulate the test execution in the following ways: * `// TEST`: Explicitly marks a snippet as a test. Snippets marked this way -are tests even if they don't have `// CONSOLE`. - * `// TEST[s/foo/bar/]`: Replace `foo` with `bar` in the test. This should be - used sparingly because it makes the test "lie". Sometimes, though, you can use - it to make the tests more clear. +are tests even if they don't have `// CONSOLE` but usually `// TEST` is used +for its modifiers: + * `// TEST[s/foo/bar/]`: Replace `foo` with `bar` in the generated test. This + should be used sparingly because it makes the snippet "lie". Sometimes, + though, you can use it to make the snippet more clear more clear. Keep in + mind the that if there are multiple substitutions then they are applied in + the order that they are defined. * `// TEST[catch:foo]`: Used to expect errors in the requests. Replace `foo` with `request` to expect a 400 error, for example. If the snippet contains multiple requests then only the last request will expect the error. * `// TEST[continued]`: Continue the test started in the last snippet. Between - tests the nodes are cleaned: indexes are removed, etc. This will prevent that. - This is really useful when you have text and snippets that work together to + tests the nodes are cleaned: indexes are removed, etc. This prevents that + from happening between snippets because the two snippets are a single test. + This is most useful when you have text and snippets that work together to tell the story of some use case because it merges the snippets (and thus the use case) into one big test. * `// TEST[skip:reason]`: Skip this test. Replace `reason` with the actual @@ -38,8 +42,11 @@ are tests even if they don't have `// CONSOLE`. * `// TESTRESPONSE`: Matches this snippet against the body of the response of the last test. If the response is JSON then order is ignored. If you add `// TEST[continued]` to the snippet after `// TESTRESPONSE` it will continue - in the same test, allowing you to interleve requests with responses to check. - * `// TESTRESPONSE[s/foo/bar/]`: Substitutions. See `// TEST[s/foo/bar]`. + in the same test, allowing you to interleave requests with responses to check. + * `// TESTRESPONSE[s/foo/bar/]`: Substitutions. See `// TEST[s/foo/bar]` for + how it works. These are much more common than `// TEST[s/foo/bar]` because + they are useful for eliding portions of the response that are not pertinent + to the documentation. * `// TESTRESPONSE[_cat]`: Add substitutions for testing `_cat` responses. Use this after all other substitutions so it doesn't make other substitutions difficult. From a01f8462260c3e985660ffab063726c0ff6b69bf Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 4 May 2017 21:01:14 -0400 Subject: [PATCH 211/619] CONSOLEify a few more docs Adds CONSOLE to cross-cluster-search docs but skips them for testing because we don't have a second cluster set up. This gets us the `VIEW IN CONSOLE` and `COPY AS CURL` links and makes sure that they are valid yaml (not json, technically) but doesn't get testing. Which is better than we had before. Adds CONSOLE to the dynamic templates docs and ingest-node docs. The ingest-node docs contain a *ton* of non-console snippets. We might want to convert them to full examples later, but that can be a separate thing. Relates to #18160 --- docs/build.gradle | 3 - docs/reference/ingest/ingest-node.asciidoc | 110 +++++++++++------- .../mapping/dynamic/templates.asciidoc | 7 +- .../modules/cross-cluster-search.asciidoc | 9 +- 4 files changed, 83 insertions(+), 46 deletions(-) diff --git a/docs/build.gradle b/docs/build.gradle index 204b9abb1b997..ef53bd135aba4 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -54,9 +54,6 @@ buildRestTests.expectedUnconvertedCandidates = [ 'reference/indices/recovery.asciidoc', 'reference/indices/segments.asciidoc', 'reference/indices/shard-stores.asciidoc', - 'reference/ingest/ingest-node.asciidoc', - 'reference/mapping/dynamic/templates.asciidoc', - 'reference/modules/cross-cluster-search.asciidoc', // this is hard to test since we need 2 clusters -- maybe we can trick it into referencing itself... 'reference/search/field-stats.asciidoc', 'reference/search/profile.asciidoc', ] diff --git a/docs/reference/ingest/ingest-node.asciidoc b/docs/reference/ingest/ingest-node.asciidoc index f195ee1f2fd75..723d7205cfc63 100644 --- a/docs/reference/ingest/ingest-node.asciidoc +++ b/docs/reference/ingest/ingest-node.asciidoc @@ -12,6 +12,7 @@ and a list of `processors`: "processors" : [ ... ] } -------------------------------------------------- +// NOTCONSOLE The `description` is a special field to store a helpful description of what the pipeline does. @@ -126,6 +127,7 @@ using `filter_path` to limit the response to just the `version`: -------------------------------------------------- GET /_ingest/pipeline/my-pipeline-id?filter_path=*.version -------------------------------------------------- +// CONSOLE // TEST[continued] This should give a small response that makes it both easy and inexpensive to parse: @@ -209,6 +211,7 @@ POST _ingest/pipeline/_simulate ] } -------------------------------------------------- +// NOTCONSOLE Here is the structure of a simulate request against an existing pipeline: @@ -223,7 +226,7 @@ POST _ingest/pipeline/my-pipeline-id/_simulate ] } -------------------------------------------------- - +// NOTCONSOLE Here is an example of a simulate request with a pipeline defined in the request and its response: @@ -275,42 +278,36 @@ Response: { "doc": { "_id": "id", - "_ttl": null, - "_parent": null, "_index": "index", - "_routing": null, "_type": "type", - "_timestamp": null, "_source": { "field2": "_value", "foo": "bar" }, "_ingest": { - "timestamp": "2016-01-04T23:53:27.186+0000" + "timestamp": "2017-05-04T22:30:03.187Z" } } }, { "doc": { "_id": "id", - "_ttl": null, - "_parent": null, "_index": "index", - "_routing": null, "_type": "type", - "_timestamp": null, "_source": { "field2": "_value", "foo": "rab" }, "_ingest": { - "timestamp": "2016-01-04T23:53:27.186+0000" + "timestamp": "2017-05-04T22:30:03.188Z" } } } ] } -------------------------------------------------- +// TESTRESPONSE[s/"2017-05-04T22:30:03.187Z"/$body.docs.0.doc._ingest.timestamp/] +// TESTRESPONSE[s/"2017-05-04T22:30:03.188Z"/$body.docs.1.doc._ingest.timestamp/] [[ingest-verbose-param]] ==== Viewing Verbose Results @@ -374,41 +371,31 @@ Response: { "processor_results": [ { - "tag": "processor[set]-0", "doc": { "_id": "id", - "_ttl": null, - "_parent": null, "_index": "index", - "_routing": null, "_type": "type", - "_timestamp": null, "_source": { "field2": "_value2", "foo": "bar" }, "_ingest": { - "timestamp": "2016-01-05T00:02:51.383+0000" + "timestamp": "2017-05-04T22:46:09.674Z" } } }, { - "tag": "processor[set]-1", "doc": { "_id": "id", - "_ttl": null, - "_parent": null, "_index": "index", - "_routing": null, "_type": "type", - "_timestamp": null, "_source": { "field3": "_value3", "field2": "_value2", "foo": "bar" }, "_ingest": { - "timestamp": "2016-01-05T00:02:51.383+0000" + "timestamp": "2017-05-04T22:46:09.675Z" } } } @@ -417,41 +404,31 @@ Response: { "processor_results": [ { - "tag": "processor[set]-0", "doc": { "_id": "id", - "_ttl": null, - "_parent": null, "_index": "index", - "_routing": null, "_type": "type", - "_timestamp": null, "_source": { "field2": "_value2", "foo": "rab" }, "_ingest": { - "timestamp": "2016-01-05T00:02:51.384+0000" + "timestamp": "2017-05-04T22:46:09.676Z" } } }, { - "tag": "processor[set]-1", "doc": { "_id": "id", - "_ttl": null, - "_parent": null, "_index": "index", - "_routing": null, "_type": "type", - "_timestamp": null, "_source": { "field3": "_value3", "field2": "_value2", "foo": "rab" }, "_ingest": { - "timestamp": "2016-01-05T00:02:51.384+0000" + "timestamp": "2017-05-04T22:46:09.677Z" } } } @@ -460,6 +437,10 @@ Response: ] } -------------------------------------------------- +// TESTRESPONSE[s/"2017-05-04T22:46:09.674Z"/$body.docs.0.processor_results.0.doc._ingest.timestamp/] +// TESTRESPONSE[s/"2017-05-04T22:46:09.675Z"/$body.docs.0.processor_results.1.doc._ingest.timestamp/] +// TESTRESPONSE[s/"2017-05-04T22:46:09.676Z"/$body.docs.1.processor_results.0.doc._ingest.timestamp/] +// TESTRESPONSE[s/"2017-05-04T22:46:09.677Z"/$body.docs.1.processor_results.1.doc._ingest.timestamp/] [[accessing-data-in-pipelines]] == Accessing Data in Pipelines @@ -482,6 +463,7 @@ their name. For example: } } -------------------------------------------------- +// NOTCONSOLE On top of this, fields from the source are always accessible via the `_source` prefix: @@ -494,6 +476,7 @@ On top of this, fields from the source are always accessible via the `_source` p } } -------------------------------------------------- +// NOTCONSOLE [float] [[accessing-metadata-fields]] @@ -513,6 +496,7 @@ The following example sets the `_id` metadata field of a document to `1`: } } -------------------------------------------------- +// NOTCONSOLE The following metadata fields are accessible by a processor: `_index`, `_type`, `_id`, `_routing`, `_parent`. @@ -538,6 +522,7 @@ The following example adds a field with the name `received`. The value is the in } } -------------------------------------------------- +// NOTCONSOLE Unlike Elasticsearch metadata fields, the ingest metadata field name `_ingest` can be used as a valid field name in the source of a document. Use `_source._ingest` to refer to the field in the source document. Otherwise, `_ingest` @@ -562,6 +547,7 @@ the values of `field_a` and `field_b`. } } -------------------------------------------------- +// NOTCONSOLE The following example uses the value of the `geoip.country_iso_code` field in the source to set the index that the document will be indexed into: @@ -575,6 +561,7 @@ to set the index that the document will be indexed into: } } -------------------------------------------------- +// NOTCONSOLE [[handling-failure-in-pipelines]] == Handling Failures in Pipelines @@ -620,6 +607,7 @@ Elasticsearch. ] } -------------------------------------------------- +// NOTCONSOLE The following example defines an `on_failure` block on a whole pipeline to change the index to which failed documents get sent. @@ -639,6 +627,7 @@ the index to which failed documents get sent. ] } -------------------------------------------------- +// NOTCONSOLE Alternatively instead of defining behaviour in case of processor failure, it is also possible to ignore a failure and continue with the next processor by specifying the `ignore_failure` setting. @@ -661,6 +650,7 @@ continues to execute, which in this case means that the pipeline does nothing. ] } -------------------------------------------------- +// NOTCONSOLE The `ignore_failure` can be set on any processor and defaults to `false`. @@ -699,6 +689,7 @@ metadata field to provide the error message. ] } -------------------------------------------------- +// NOTCONSOLE [[ingest-processors]] == Processors @@ -713,6 +704,7 @@ All processors are defined in the following way within a pipeline definition: } } -------------------------------------------------- +// NOTCONSOLE Each processor defines its own configuration parameters, but all processors have the ability to declare `tag` and `on_failure` fields. These fields are optional. @@ -765,6 +757,7 @@ Accepts a single value or an array of values. } } -------------------------------------------------- +// NOTCONSOLE [[convert-processor]] === Convert Processor @@ -802,6 +795,7 @@ such a case, `target_field` will still be updated with the unconverted field val } } -------------------------------------------------- +// NOTCONSOLE [[date-processor]] === Date Processor @@ -842,6 +836,7 @@ Here is an example that adds the parsed date to the `timestamp` field based on t ] } -------------------------------------------------- +// NOTCONSOLE [[date-index-name-processor]] === Date Index Name Processor @@ -1011,6 +1006,7 @@ to the requester. } } -------------------------------------------------- +// NOTCONSOLE [[foreach-processor]] === Foreach Processor @@ -1059,6 +1055,7 @@ Assume the following document: "values" : ["foo", "bar", "baz"] } -------------------------------------------------- +// NOTCONSOLE When this `foreach` processor operates on this sample document: @@ -1075,6 +1072,7 @@ When this `foreach` processor operates on this sample document: } } -------------------------------------------------- +// NOTCONSOLE Then the document will look like this after preprocessing: @@ -1084,6 +1082,7 @@ Then the document will look like this after preprocessing: "values" : ["FOO", "BAR", "BAZ"] } -------------------------------------------------- +// NOTCONSOLE Let's take a look at another example: @@ -1102,6 +1101,7 @@ Let's take a look at another example: ] } -------------------------------------------------- +// NOTCONSOLE In this case, the `id` field needs to be removed, so the following `foreach` processor is used: @@ -1119,6 +1119,7 @@ so the following `foreach` processor is used: } } -------------------------------------------------- +// NOTCONSOLE After preprocessing the result is: @@ -1135,6 +1136,7 @@ After preprocessing the result is: ] } -------------------------------------------------- +// NOTCONSOLE The wrapped processor can have a `on_failure` definition. For example, the `id` field may not exist on all person objects. @@ -1162,6 +1164,7 @@ block to send the document to the 'failure_index' index for later inspection: } } -------------------------------------------------- +// NOTCONSOLE In this example, if the `remove` processor does fail, then the array elements that have been processed thus far will @@ -1210,7 +1213,7 @@ The `TYPE` is the type you wish to cast your named field. `int` and `float` are For example, you might want to match the following text: -[source,js] +[source,txt] -------------------------------------------------- 3.44 55.3.244.1 -------------------------------------------------- @@ -1218,7 +1221,7 @@ For example, you might want to match the following text: You may know that the message in the example is a number followed by an IP address. You can match this text by using the following Grok expression. -[source,js] +[source,txt] -------------------------------------------------- %{NUMBER:duration} %{IP:client} -------------------------------------------------- @@ -1247,10 +1250,11 @@ a document. "message": "55.3.244.1 GET /index.html 15824 0.043" } -------------------------------------------------- +// NOTCONSOLE The pattern for this could be: -[source,js] +[source,txt] -------------------------------------------------- %{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration} -------------------------------------------------- @@ -1271,6 +1275,7 @@ Here is an example pipeline for processing the above document by using Grok: ] } -------------------------------------------------- +// NOTCONSOLE This pipeline will insert these named captures as new fields within the document, like so: @@ -1285,6 +1290,7 @@ This pipeline will insert these named captures as new fields within the document "duration": "0.043" } -------------------------------------------------- +// NOTCONSOLE [[custom-patterns]] ==== Custom Patterns and Pattern Files @@ -1313,6 +1319,7 @@ Here is an example of a pipeline specifying custom pattern definitions: ] } -------------------------------------------------- +// NOTCONSOLE [[trace-match]] ==== Providing Multiple Match Patterns @@ -1472,6 +1479,7 @@ If the field is not a string, the processor will throw an exception. } } -------------------------------------------------- +// NOTCONSOLE [[join-processor]] === Join Processor @@ -1496,6 +1504,7 @@ Throws an error when the field is not an array. } } -------------------------------------------------- +// NOTCONSOLE [[json-processor]] === JSON Processor @@ -1522,6 +1531,7 @@ Suppose you provide this configuration of the `json` processor: } } -------------------------------------------------- +// NOTCONSOLE If the following document is processed: @@ -1531,6 +1541,7 @@ If the following document is processed: "string_source": "{\"foo\": 2000}" } -------------------------------------------------- +// NOTCONSOLE after the `json` processor operates on it, it will look like: @@ -1543,6 +1554,7 @@ after the `json` processor operates on it, it will look like: } } -------------------------------------------------- +// NOTCONSOLE If the following configuration is provided, omitting the optional `target_field` setting: [source,js] @@ -1553,6 +1565,7 @@ If the following configuration is provided, omitting the optional `target_field` } } -------------------------------------------------- +// NOTCONSOLE then after the `json` processor operates on this document: @@ -1562,6 +1575,7 @@ then after the `json` processor operates on this document: "source_and_target": "{\"foo\": 2000}" } -------------------------------------------------- +// NOTCONSOLE it will look like: @@ -1573,8 +1587,9 @@ it will look like: } } -------------------------------------------------- +// NOTCONSOLE -This illustrates that, unless it is explicitly named in the processor configuration, the `target_field` +This illustrates that, unless it is explicitly named in the processor configuration, the `target_field` is the same field provided in the required `field` configuration. [[kv-processor]] @@ -1594,6 +1609,7 @@ For example, if you have a log message which contains `ip=1.2.3.4 error=REFUSED` } } -------------------------------------------------- +// NOTCONSOLE [[kv-options]] .Kv Options @@ -1630,6 +1646,7 @@ Converts a string to its lowercase equivalent. } } -------------------------------------------------- +// NOTCONSOLE [[remove-processor]] === Remove Processor @@ -1651,6 +1668,7 @@ Removes an existing field. If the field doesn't exist, an exception will be thro } } -------------------------------------------------- +// NOTCONSOLE [[rename-processor]] === Rename Processor @@ -1675,6 +1693,7 @@ Renames an existing field. If the field doesn't exist or the new name is already } } -------------------------------------------------- +// NOTCONSOLE [[script-processor]] === Script Processor @@ -1718,6 +1737,7 @@ numeric fields `field_a` and `field_b` multiplied by the parameter param_c: } } -------------------------------------------------- +// NOTCONSOLE [[set-processor]] @@ -1744,6 +1764,7 @@ its value will be replaced with the provided one. } } -------------------------------------------------- +// NOTCONSOLE [[split-processor]] === Split Processor @@ -1768,6 +1789,7 @@ Splits a field into an array using a separator character. Only works on string f } } -------------------------------------------------- +// NOTCONSOLE <1> Treat all consecutive whitespace characters as a single separator [[sort-processor]] @@ -1794,6 +1816,7 @@ Throws an error when the field is not an array. } } -------------------------------------------------- +// NOTCONSOLE [[trim-processor]] === Trim Processor @@ -1818,6 +1841,7 @@ NOTE: This only works on leading and trailing whitespace. } } -------------------------------------------------- +// NOTCONSOLE [[uppercase-processor]] === Uppercase Processor @@ -1840,6 +1864,7 @@ Converts a string to its uppercase equivalent. } } -------------------------------------------------- +// NOTCONSOLE [[dot-expand-processor]] === Dot Expander Processor @@ -1865,6 +1890,7 @@ Otherwise these <> can't be accessed by any } } -------------------------------------------------- +// NOTCONSOLE For example the dot expand processor would turn this document: @@ -1874,6 +1900,7 @@ For example the dot expand processor would turn this document: "foo.bar" : "value" } -------------------------------------------------- +// NOTCONSOLE into: @@ -1885,6 +1912,7 @@ into: } } -------------------------------------------------- +// NOTCONSOLE If there is already a `bar` field nested under `foo` then this processor merges the the `foo.bar` field into it. If the field is @@ -1901,6 +1929,7 @@ For example, the following document: } } -------------------------------------------------- +// NOTCONSOLE is transformed by the `dot_expander` processor into: @@ -1912,6 +1941,7 @@ is transformed by the `dot_expander` processor into: } } -------------------------------------------------- +// NOTCONSOLE If any field outside of the leaf field conflicts with a pre-existing field of the same name, then that field needs to be renamed first. @@ -1925,6 +1955,7 @@ Consider the following document: "foo.bar": "value2" } -------------------------------------------------- +// NOTCONSOLE Then the the `foo` needs to be renamed first before the `dot_expander` processor is applied. So in order for the `foo.bar` field to properly @@ -1949,6 +1980,7 @@ pipeline should be used: ] } -------------------------------------------------- +// NOTCONSOLE The reason for this is that Ingest doesn't know how to automatically cast a scalar field to an object field. diff --git a/docs/reference/mapping/dynamic/templates.asciidoc b/docs/reference/mapping/dynamic/templates.asciidoc index eb9eb1a60d90e..90fc5de5c4544 100644 --- a/docs/reference/mapping/dynamic/templates.asciidoc +++ b/docs/reference/mapping/dynamic/templates.asciidoc @@ -32,6 +32,7 @@ Dynamic templates are specified as an array of named objects: ... ] -------------------------------------------------- +// NOTCONSOLE <1> The template name can be any string value. <2> The match conditions can include any of : `match_mapping_type`, `match`, `match_pattern`, `unmatch`, `path_match`, `path_unmatch`. <3> The mapping that the matched field should use. @@ -94,7 +95,6 @@ PUT my_index/my_type/1 "my_integer": 5, <1> "my_string": "Some string" <2> } - -------------------------------------------------- // CONSOLE <1> The `my_integer` field is mapped as an `integer`. @@ -156,6 +156,7 @@ instead of simple wildcards, for instance: "match_pattern": "regex", "match": "^profit_\d+$" -------------------------------------------------- +// NOTCONSOLE [[path-match-unmatch]] ==== `path_match` and `path_unmatch` @@ -282,6 +283,7 @@ PUT my_index } } -------------------------------------------------- +// CONSOLE ===== `text`-only mappings for strings @@ -311,6 +313,7 @@ PUT my_index } } -------------------------------------------------- +// CONSOLE ===== Disabled norms @@ -345,6 +348,7 @@ PUT my_index } } -------------------------------------------------- +// CONSOLE The sub `keyword` field appears in this template to be consistent with the default rules of dynamic mappings. Of course if you do not need them because @@ -388,6 +392,7 @@ PUT my_index } } -------------------------------------------------- +// CONSOLE <1> Like the default dynamic mapping rules, doubles are mapped as floats, which are usually accurate enough, yet require half the disk space. diff --git a/docs/reference/modules/cross-cluster-search.asciidoc b/docs/reference/modules/cross-cluster-search.asciidoc index a91a56dd27074..c4e7d4d3b48c5 100644 --- a/docs/reference/modules/cross-cluster-search.asciidoc +++ b/docs/reference/modules/cross-cluster-search.asciidoc @@ -105,6 +105,8 @@ POST /cluster_one:twitter/tweet/_search "match_all": {} } -------------------------------------------------- +// CONSOLE +// TEST[skip:we don't have two clusters set up during docs testing] In contrast to the `tribe` feature cross cluster search can also search indices with the same name on different clusters: @@ -116,6 +118,8 @@ POST /cluster_one:twitter,twitter/tweet/_search "match_all": {} } -------------------------------------------------- +// CONSOLE +// TEST[skip:we don't have two clusters set up during docs testing] Search results are disambiguated the same way as the indices are disambiguated in the request. Even if index names are identical these indices will be treated as different indices when results are merged. All results retrieved from a @@ -124,7 +128,7 @@ will be prefixed with their remote cluster name: [source,js] -------------------------------------------------- - { +{ "took" : 89, "timed_out" : false, "_shards" : { @@ -162,6 +166,7 @@ will be prefixed with their remote cluster name: } } -------------------------------------------------- +// TESTRESPONSE [float] === Cross cluster search settings @@ -188,5 +193,3 @@ will be prefixed with their remote cluster name: to `false` (defaults to `true`) to prevent certain nodes from connecting to remote clusters. Cross-cluster search requests must be sent to a node that is allowed to act as a cross-cluster client. - - From 61d5eddbd680f5b8e3f8a1e54805d6cbd2660575 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Thu, 4 May 2017 21:03:52 -0400 Subject: [PATCH 212/619] Fix typo in comment in IndexShardTestCase This commit fixes a silly typo in IndexShardTestCase.java. --- .../java/org/elasticsearch/index/shard/IndexShardTestCase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java index 95013ee964999..80a0e9486f1ae 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java @@ -239,7 +239,7 @@ protected IndexShard newShard(ShardRouting routing, IndexMetaData indexMetaData, @Nullable EngineFactory engineFactory, IndexingOperationListener... listeners) throws IOException { - // add node id as name to settings for popper logging + // add node id as name to settings for proper logging final ShardId shardId = routing.shardId(); final NodeEnvironment.NodePath nodePath = new NodeEnvironment.NodePath(createTempDir()); ShardPath shardPath = new ShardPath(false, nodePath.resolve(shardId), nodePath.resolve(shardId), shardId); From 035494fa175187fe57a11cca9f6b6e5b4fbc54ad Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Fri, 5 May 2017 00:49:18 -0400 Subject: [PATCH 213/619] Remove obsolete JVM options from build We start the test JVMs with various options. There are two that we should remove, they do not make sense. - we require Java 8 yet there was a conditional build option for Java 7 - we do not set MaxDirectMemorySize in our default JVM options, we should not in the test JVMs either Relates #24501 --- .../main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy | 5 ----- 1 file changed, 5 deletions(-) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy index b3c2f4faef8e8..64ebba7578c4f 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy @@ -459,11 +459,6 @@ class BuildPlugin implements Plugin { // TODO: why are we not passing maxmemory to junit4? jvmArg '-Xmx' + System.getProperty('tests.heap.size', '512m') jvmArg '-Xms' + System.getProperty('tests.heap.size', '512m') - if (JavaVersion.current().isJava7()) { - // some tests need a large permgen, but that only exists on java 7 - jvmArg '-XX:MaxPermSize=128m' - } - jvmArg '-XX:MaxDirectMemorySize=512m' jvmArg '-XX:+HeapDumpOnOutOfMemoryError' File heapdumpDir = new File(project.buildDir, 'heapdump') heapdumpDir.mkdirs() From c8712e9531f55e84899be0f65fe435ae09f6a6a5 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Fri, 5 May 2017 08:39:18 +0200 Subject: [PATCH 214/619] Limit AllocationService dependency injection hack (#24479) Changes the scope of the AllocationService dependency injection hack so that it is at least contained to the AllocationService and does not leak into the Discovery world. --- .../routing/allocation/Allocators.java | 4 +-- .../elasticsearch/cluster/ClusterModule.java | 25 +++++++++++-------- .../routing/allocation/AllocationService.java | 18 +++++++++---- .../elasticsearch/discovery/Discovery.java | 7 ------ .../discovery/DiscoveryModule.java | 8 +++--- .../discovery/single/SingleNodeDiscovery.java | 6 ----- .../discovery/zen/ZenDiscovery.java | 18 +++++-------- .../gateway/GatewayAllocator.java | 18 ++++++++----- .../java/org/elasticsearch/node/Node.java | 14 +++++------ .../plugins/DiscoveryPlugin.java | 4 ++- .../cluster/ClusterModuleTests.java | 11 ++++---- .../allocation/BalanceConfigurationTests.java | 2 +- .../discovery/DiscoveryModuleTests.java | 6 +++-- .../discovery/zen/ZenDiscoveryUnitTests.java | 2 +- .../org/elasticsearch/test/NoopDiscovery.java | 5 ---- .../azure/classic/AzureDiscoveryPlugin.java | 6 +++-- .../discovery/ec2/Ec2DiscoveryPlugin.java | 6 +++-- .../discovery/gce/GceDiscoveryPlugin.java | 6 +++-- .../cluster/ESAllocationTestCase.java | 2 +- .../test/discovery/TestZenDiscovery.java | 11 +++++--- .../test/gateway/NoopGatewayAllocator.java | 2 +- .../test/gateway/TestGatewayAllocator.java | 2 +- 22 files changed, 95 insertions(+), 88 deletions(-) diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/routing/allocation/Allocators.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/routing/allocation/Allocators.java index 4d8f7cfeaac99..591fa400d18da 100644 --- a/benchmarks/src/main/java/org/elasticsearch/benchmark/routing/allocation/Allocators.java +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/routing/allocation/Allocators.java @@ -36,8 +36,6 @@ import org.elasticsearch.gateway.GatewayAllocator; import java.lang.reflect.InvocationTargetException; -import java.net.InetAddress; -import java.net.UnknownHostException; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -49,7 +47,7 @@ private static class NoopGatewayAllocator extends GatewayAllocator { public static final NoopGatewayAllocator INSTANCE = new NoopGatewayAllocator(); protected NoopGatewayAllocator() { - super(Settings.EMPTY, null, null); + super(Settings.EMPTY); } @Override diff --git a/core/src/main/java/org/elasticsearch/cluster/ClusterModule.java b/core/src/main/java/org/elasticsearch/cluster/ClusterModule.java index 716472c76f0e2..603952101659d 100644 --- a/core/src/main/java/org/elasticsearch/cluster/ClusterModule.java +++ b/core/src/main/java/org/elasticsearch/cluster/ClusterModule.java @@ -58,9 +58,7 @@ import org.elasticsearch.common.ParseField; import org.elasticsearch.common.inject.AbstractModule; import org.elasticsearch.common.io.stream.NamedWriteable; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry; -import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Setting; @@ -92,19 +90,22 @@ public class ClusterModule extends AbstractModule { public static final Setting SHARDS_ALLOCATOR_TYPE_SETTING = new Setting<>("cluster.routing.allocation.type", BALANCED_ALLOCATOR, Function.identity(), Property.NodeScope); - private final Settings settings; private final ClusterService clusterService; private final IndexNameExpressionResolver indexNameExpressionResolver; + private final AllocationDeciders allocationDeciders; + private final AllocationService allocationService; // pkg private for tests - final Collection allocationDeciders; + final Collection deciderList; final ShardsAllocator shardsAllocator; - public ClusterModule(Settings settings, ClusterService clusterService, List clusterPlugins) { - this.settings = settings; - this.allocationDeciders = createAllocationDeciders(settings, clusterService.getClusterSettings(), clusterPlugins); + public ClusterModule(Settings settings, ClusterService clusterService, List clusterPlugins, + ClusterInfoService clusterInfoService) { + this.deciderList = createAllocationDeciders(settings, clusterService.getClusterSettings(), clusterPlugins); + this.allocationDeciders = new AllocationDeciders(settings, deciderList); this.shardsAllocator = createShardsAllocator(settings, clusterService.getClusterSettings(), clusterPlugins); this.clusterService = clusterService; - indexNameExpressionResolver = new IndexNameExpressionResolver(settings); + this.indexNameExpressionResolver = new IndexNameExpressionResolver(settings); + this.allocationService = new AllocationService(settings, allocationDeciders, shardsAllocator, clusterInfoService); } @@ -213,10 +214,14 @@ private static ShardsAllocator createShardsAllocator(Settings settings, ClusterS "ShardsAllocator factory for [" + allocatorName + "] returned null"); } + public AllocationService getAllocationService() { + return allocationService; + } + @Override protected void configure() { bind(GatewayAllocator.class).asEagerSingleton(); - bind(AllocationService.class).asEagerSingleton(); + bind(AllocationService.class).toInstance(allocationService); bind(ClusterService.class).toInstance(clusterService); bind(NodeConnectionsService.class).asEagerSingleton(); bind(MetaDataCreateIndexService.class).asEagerSingleton(); @@ -233,7 +238,7 @@ protected void configure() { bind(NodeMappingRefreshAction.class).asEagerSingleton(); bind(MappingUpdatedAction.class).asEagerSingleton(); bind(TaskResultsService.class).asEagerSingleton(); - bind(AllocationDeciders.class).toInstance(new AllocationDeciders(settings, allocationDeciders)); + bind(AllocationDeciders.class).toInstance(allocationDeciders); bind(ShardsAllocator.class).toInstance(shardsAllocator); } } diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocationService.java b/core/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocationService.java index 8974c8a4a9a0b..6b0f8bfba2af4 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocationService.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocationService.java @@ -37,7 +37,6 @@ import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders; import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.component.AbstractComponent; -import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.gateway.GatewayAllocator; @@ -61,20 +60,29 @@ public class AllocationService extends AbstractComponent { private final AllocationDeciders allocationDeciders; - private final GatewayAllocator gatewayAllocator; + private GatewayAllocator gatewayAllocator; private final ShardsAllocator shardsAllocator; private final ClusterInfoService clusterInfoService; - @Inject - public AllocationService(Settings settings, AllocationDeciders allocationDeciders, GatewayAllocator gatewayAllocator, + public AllocationService(Settings settings, AllocationDeciders allocationDeciders, + GatewayAllocator gatewayAllocator, + ShardsAllocator shardsAllocator, ClusterInfoService clusterInfoService) { + this(settings, allocationDeciders, shardsAllocator, clusterInfoService); + setGatewayAllocator(gatewayAllocator); + } + + public AllocationService(Settings settings, AllocationDeciders allocationDeciders, ShardsAllocator shardsAllocator, ClusterInfoService clusterInfoService) { super(settings); this.allocationDeciders = allocationDeciders; - this.gatewayAllocator = gatewayAllocator; this.shardsAllocator = shardsAllocator; this.clusterInfoService = clusterInfoService; } + public void setGatewayAllocator(GatewayAllocator gatewayAllocator) { + this.gatewayAllocator = gatewayAllocator; + } + /** * Applies the started shards. Note, only initializing ShardRouting instances that exist in the routing table should be * provided as parameter and no duplicates should be contained. diff --git a/core/src/main/java/org/elasticsearch/discovery/Discovery.java b/core/src/main/java/org/elasticsearch/discovery/Discovery.java index 4c28b51bc4f16..7f68f417fcb73 100644 --- a/core/src/main/java/org/elasticsearch/discovery/Discovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/Discovery.java @@ -23,7 +23,6 @@ import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.common.io.stream.StreamInput; @@ -37,12 +36,6 @@ */ public interface Discovery extends LifecycleComponent { - /** - * Another hack to solve dep injection problem..., note, this will be called before - * any start is called. - */ - void setAllocationService(AllocationService allocationService); - /** * Publish all the changes to the cluster from the master (can be called just by the master). The publish * process should apply this state to the master as well! diff --git a/core/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java b/core/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java index 43fccab1a284a..b2367c6e95377 100644 --- a/core/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java +++ b/core/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java @@ -19,6 +19,7 @@ package org.elasticsearch.discovery; +import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterApplier; import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -58,7 +59,8 @@ public class DiscoveryModule { public DiscoveryModule(Settings settings, ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, NetworkService networkService, MasterService masterService, - ClusterApplier clusterApplier, ClusterSettings clusterSettings, List plugins) { + ClusterApplier clusterApplier, ClusterSettings clusterSettings, List plugins, + AllocationService allocationService) { final UnicastHostsProvider hostsProvider; Map> hostProviders = new HashMap<>(); @@ -83,12 +85,12 @@ public DiscoveryModule(Settings settings, ThreadPool threadPool, TransportServic Map> discoveryTypes = new HashMap<>(); discoveryTypes.put("zen", () -> new ZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, masterService, clusterApplier, - clusterSettings, hostsProvider)); + clusterSettings, hostsProvider, allocationService)); discoveryTypes.put("tribe", () -> new TribeDiscovery(settings, transportService, clusterApplier)); discoveryTypes.put("single-node", () -> new SingleNodeDiscovery(settings, transportService, clusterApplier)); for (DiscoveryPlugin plugin : plugins) { plugin.getDiscoveryTypes(threadPool, transportService, namedWriteableRegistry, - masterService, clusterApplier, clusterSettings, hostsProvider).entrySet().forEach(entry -> { + masterService, clusterApplier, clusterSettings, hostsProvider, allocationService).entrySet().forEach(entry -> { if (discoveryTypes.put(entry.getKey(), entry.getValue()) != null) { throw new IllegalArgumentException("Cannot register discovery type [" + entry.getKey() + "] twice"); } diff --git a/core/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java index d7275e7be91da..11526961797c7 100644 --- a/core/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java @@ -27,7 +27,6 @@ import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterApplier; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.settings.Settings; @@ -59,11 +58,6 @@ public SingleNodeDiscovery(final Settings settings, final TransportService trans this.clusterApplier = clusterApplier; } - @Override - public void setAllocationService(final AllocationService allocationService) { - - } - @Override public synchronized void publish(final ClusterChangedEvent event, final AckListener ackListener) { diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java index fe94cea597dbb..d08b148554b4b 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java @@ -109,7 +109,6 @@ public class ZenDiscovery extends AbstractLifecycleComponent implements Discover private final TransportService transportService; private final MasterService masterService; - private AllocationService allocationService; private final ClusterName clusterName; private final DiscoverySettings discoverySettings; protected final ZenPing zenPing; // protected to allow tests access @@ -140,9 +139,8 @@ public class ZenDiscovery extends AbstractLifecycleComponent implements Discover private final JoinThreadControl joinThreadControl; - // must initialized in doStart(), when we have the allocationService set - private volatile NodeJoinController nodeJoinController; - private volatile NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; + private final NodeJoinController nodeJoinController; + private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; private final ClusterApplier clusterApplier; private final AtomicReference state; // last committed cluster state @@ -151,7 +149,7 @@ public class ZenDiscovery extends AbstractLifecycleComponent implements Discover public ZenDiscovery(Settings settings, ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, MasterService masterService, ClusterApplier clusterApplier, - ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { + ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider, AllocationService allocationService) { super(settings); this.masterService = masterService; this.clusterApplier = clusterApplier; @@ -213,6 +211,9 @@ public ZenDiscovery(Settings settings, ThreadPool threadPool, TransportService t this.membership = new MembershipAction(settings, transportService, new MembershipListener()); this.joinThreadControl = new JoinThreadControl(); + this.nodeJoinController = new NodeJoinController(masterService, allocationService, electMaster, settings); + this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, electMaster, this::submitRejoin, logger); + transportService.registerRequestHandler( DISCOVERY_REJOIN_ACTION_NAME, RejoinClusterRequest::new, ThreadPool.Names.SAME, new RejoinClusterRequestHandler()); } @@ -223,11 +224,6 @@ protected ZenPing newZenPing(Settings settings, ThreadPool threadPool, Transport return new UnicastZenPing(settings, threadPool, transportService, hostsProvider); } - @Override - public void setAllocationService(AllocationService allocationService) { - this.allocationService = allocationService; - } - @Override protected void doStart() { DiscoveryNode localNode = transportService.getLocalNode(); @@ -239,8 +235,6 @@ protected void doStart() { joinThreadControl.start(); } zenPing.start(this); - this.nodeJoinController = new NodeJoinController(masterService, allocationService, electMaster, settings); - this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, electMaster, this::submitRejoin, logger); } @Override diff --git a/core/src/main/java/org/elasticsearch/gateway/GatewayAllocator.java b/core/src/main/java/org/elasticsearch/gateway/GatewayAllocator.java index 69f7af6eef507..c616716b86a97 100644 --- a/core/src/main/java/org/elasticsearch/gateway/GatewayAllocator.java +++ b/core/src/main/java/org/elasticsearch/gateway/GatewayAllocator.java @@ -43,7 +43,7 @@ public class GatewayAllocator extends AbstractComponent { - private RoutingService routingService; + private final RoutingService routingService; private final PrimaryShardAllocator primaryShardAllocator; private final ReplicaShardAllocator replicaShardAllocator; @@ -52,14 +52,12 @@ public class GatewayAllocator extends AbstractComponent { private final ConcurrentMap> asyncFetchStore = ConcurrentCollections.newConcurrentMap(); @Inject - public GatewayAllocator(Settings settings, final TransportNodesListGatewayStartedShards startedAction, final TransportNodesListShardStoreMetaData storeAction) { + public GatewayAllocator(Settings settings, ClusterService clusterService, RoutingService routingService, + TransportNodesListGatewayStartedShards startedAction, TransportNodesListShardStoreMetaData storeAction) { super(settings); + this.routingService = routingService; this.primaryShardAllocator = new InternalPrimaryShardAllocator(settings, startedAction); this.replicaShardAllocator = new InternalReplicaShardAllocator(settings, storeAction); - } - - public void setReallocation(final ClusterService clusterService, final RoutingService routingService) { - this.routingService = routingService; clusterService.addStateApplier(event -> { boolean cleanCache = false; DiscoveryNode localNode = event.state().nodes().getLocalNode(); @@ -79,6 +77,14 @@ public void setReallocation(final ClusterService clusterService, final RoutingSe }); } + // for tests + protected GatewayAllocator(Settings settings) { + super(settings); + this.routingService = null; + this.primaryShardAllocator = null; + this.replicaShardAllocator = null; + } + public int getNumberOfInFlightFetch() { int count = 0; for (AsyncShardFetch fetch : asyncFetchStarted.values()) { diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index 9c933b1da708c..28d2f42f592cd 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -49,7 +49,6 @@ import org.elasticsearch.cluster.metadata.MetaDataIndexUpgradeService; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.RoutingService; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.StopWatch; import org.elasticsearch.common.SuppressForbidden; @@ -352,7 +351,7 @@ protected Node(final Environment environment, Collection final MonitorService monitorService = new MonitorService(settings, nodeEnvironment, threadPool, clusterInfoService); modules.add(new NodeModule(this, monitorService)); ClusterModule clusterModule = new ClusterModule(settings, clusterService, - pluginsService.filterPlugins(ClusterPlugin.class)); + pluginsService.filterPlugins(ClusterPlugin.class), clusterInfoService); modules.add(clusterModule); IndicesModule indicesModule = new IndicesModule(pluginsService.filterPlugins(MapperPlugin.class)); modules.add(indicesModule); @@ -437,7 +436,8 @@ protected Node(final Environment environment, Collection final DiscoveryModule discoveryModule = new DiscoveryModule(this.settings, threadPool, transportService, namedWriteableRegistry, networkService, clusterService.getMasterService(), clusterService.getClusterApplierService(), - clusterService.getClusterSettings(), pluginsService.filterPlugins(DiscoveryPlugin.class)); + clusterService.getClusterSettings(), pluginsService.filterPlugins(DiscoveryPlugin.class), + clusterModule.getAllocationService()); NodeService nodeService = new NodeService(settings, threadPool, monitorService, discoveryModule.getDiscovery(), transportService, indicesService, pluginsService, circuitBreakerService, scriptModule.getScriptService(), httpServerTransport, ingestService, clusterService, settingsModule.getSettingsFilter()); @@ -488,6 +488,9 @@ protected Node(final Environment environment, Collection ); injector = modules.createInjector(); + // TODO hack around circular dependencies problems in AllocationService + clusterModule.getAllocationService().setGatewayAllocator(injector.getInstance(GatewayAllocator.class)); + List pluginLifecycleComponents = pluginComponents.stream() .filter(p -> p instanceof LifecycleComponent) .map(p -> (LifecycleComponent) p).collect(Collectors.toList()); @@ -644,8 +647,6 @@ public Node start() throws NodeValidationException { Logger logger = Loggers.getLogger(Node.class, NODE_NAME_SETTING.get(settings)); logger.info("starting ..."); - // hack around dependency injection problem (for now...) - injector.getInstance(Discovery.class).setAllocationService(injector.getInstance(AllocationService.class)); pluginLifecycleComponents.forEach(LifecycleComponent::start); injector.getInstance(MappingUpdatedAction.class).setClient(client); @@ -663,9 +664,6 @@ public Node start() throws NodeValidationException { nodeConnectionsService.start(); clusterService.setNodeConnectionsService(nodeConnectionsService); - // TODO hack around circular dependencies problems - injector.getInstance(GatewayAllocator.class).setReallocation(clusterService, injector.getInstance(RoutingService.class)); - injector.getInstance(ResourceWatcherService.class).start(); injector.getInstance(GatewayService.class).start(); Discovery discovery = injector.getInstance(Discovery.class); diff --git a/core/src/main/java/org/elasticsearch/plugins/DiscoveryPlugin.java b/core/src/main/java/org/elasticsearch/plugins/DiscoveryPlugin.java index 0569547b82240..c3af5593cd7c4 100644 --- a/core/src/main/java/org/elasticsearch/plugins/DiscoveryPlugin.java +++ b/core/src/main/java/org/elasticsearch/plugins/DiscoveryPlugin.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.function.Supplier; +import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterApplier; import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -68,7 +69,8 @@ default Map> getDiscoveryTypes(ThreadPool threadPool MasterService masterService, ClusterApplier clusterApplier, ClusterSettings clusterSettings, - UnicastHostsProvider hostsProvider) { + UnicastHostsProvider hostsProvider, + AllocationService allocationService) { return Collections.emptyMap(); } diff --git a/core/src/test/java/org/elasticsearch/cluster/ClusterModuleTests.java b/core/src/test/java/org/elasticsearch/cluster/ClusterModuleTests.java index 9e45b0d38280e..67d04c3c235f4 100644 --- a/core/src/test/java/org/elasticsearch/cluster/ClusterModuleTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/ClusterModuleTests.java @@ -57,6 +57,7 @@ import java.util.function.Supplier; public class ClusterModuleTests extends ModuleTestCase { + private ClusterInfoService clusterInfoService = EmptyClusterInfoService.INSTANCE; private ClusterService clusterService = new ClusterService(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), null); static class FakeAllocationDecider extends AllocationDecider { @@ -114,7 +115,7 @@ public void testRegisterAllocationDeciderDuplicate() { public Collection createAllocationDeciders(Settings settings, ClusterSettings clusterSettings) { return Collections.singletonList(new EnableAllocationDecider(settings, clusterSettings)); } - }))); + }), clusterInfoService)); assertEquals(e.getMessage(), "Cannot specify allocation decider [" + EnableAllocationDecider.class.getName() + "] twice"); } @@ -126,8 +127,8 @@ public void testRegisterAllocationDecider() { public Collection createAllocationDeciders(Settings settings, ClusterSettings clusterSettings) { return Collections.singletonList(new FakeAllocationDecider(settings)); } - })); - assertTrue(module.allocationDeciders.stream().anyMatch(d -> d.getClass().equals(FakeAllocationDecider.class))); + }), clusterInfoService); + assertTrue(module.deciderList.stream().anyMatch(d -> d.getClass().equals(FakeAllocationDecider.class))); } private ClusterModule newClusterModuleWithShardsAllocator(Settings settings, String name, Supplier supplier) { @@ -138,7 +139,7 @@ public Map> getShardsAllocators(Settings setti return Collections.singletonMap(name, supplier); } } - )); + ), clusterInfoService); } public void testRegisterShardsAllocator() { @@ -156,7 +157,7 @@ public void testRegisterShardsAllocatorAlreadyRegistered() { public void testUnknownShardsAllocator() { Settings settings = Settings.builder().put(ClusterModule.SHARDS_ALLOCATOR_TYPE_SETTING.getKey(), "dne").build(); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> - new ClusterModule(settings, clusterService, Collections.emptyList())); + new ClusterModule(settings, clusterService, Collections.emptyList(), clusterInfoService)); assertEquals("Unknown ShardsAllocator [dne]", e.getMessage()); } diff --git a/core/src/test/java/org/elasticsearch/cluster/routing/allocation/BalanceConfigurationTests.java b/core/src/test/java/org/elasticsearch/cluster/routing/allocation/BalanceConfigurationTests.java index cfca80b78cac7..5e400d95e4b02 100644 --- a/core/src/test/java/org/elasticsearch/cluster/routing/allocation/BalanceConfigurationTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/routing/allocation/BalanceConfigurationTests.java @@ -391,7 +391,7 @@ public ShardAllocationDecision decideShardAllocation(ShardRouting shard, Routing private class NoopGatewayAllocator extends GatewayAllocator { NoopGatewayAllocator() { - super(Settings.EMPTY, null, null); + super(Settings.EMPTY); } @Override diff --git a/core/src/test/java/org/elasticsearch/discovery/DiscoveryModuleTests.java b/core/src/test/java/org/elasticsearch/discovery/DiscoveryModuleTests.java index 9460261e547f7..39a9dbff959c6 100644 --- a/core/src/test/java/org/elasticsearch/discovery/DiscoveryModuleTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/DiscoveryModuleTests.java @@ -20,6 +20,7 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.Version; +import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterApplier; import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -71,7 +72,8 @@ public interface DummyDiscoveryPlugin extends DiscoveryPlugin { default Map> getDiscoveryTypes(ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, MasterService masterService, ClusterApplier clusterApplier, - ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { + ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider, + AllocationService allocationService) { return impl(); } } @@ -93,7 +95,7 @@ public void clearDummyServices() throws IOException { private DiscoveryModule newModule(Settings settings, List plugins) { return new DiscoveryModule(settings, threadPool, transportService, namedWriteableRegistry, null, masterService, - clusterApplier, clusterSettings, plugins); + clusterApplier, clusterSettings, plugins, null); } public void testDefaults() { diff --git a/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java b/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java index 0d0d391663d0b..d0c138954ab82 100644 --- a/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java @@ -299,7 +299,7 @@ private ZenDiscovery buildZenDiscovery(Settings settings, TransportService servi ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); ZenDiscovery zenDiscovery = new ZenDiscovery(settings, threadPool, service, new NamedWriteableRegistry(ClusterModule.getNamedWriteables()), masterService, (source, clusterStateSupplier, listener) -> listener.clusterStateProcessed(source, clusterStateSupplier.get(), clusterStateSupplier.get()), - clusterSettings, Collections::emptyList); + clusterSettings, Collections::emptyList, null); zenDiscovery.start(); return zenDiscovery; } diff --git a/core/src/test/java/org/elasticsearch/test/NoopDiscovery.java b/core/src/test/java/org/elasticsearch/test/NoopDiscovery.java index 1a661b509a2bb..5b33fb1c8deb3 100644 --- a/core/src/test/java/org/elasticsearch/test/NoopDiscovery.java +++ b/core/src/test/java/org/elasticsearch/test/NoopDiscovery.java @@ -28,11 +28,6 @@ public class NoopDiscovery implements Discovery { - @Override - public void setAllocationService(AllocationService allocationService) { - - } - @Override public void publish(ClusterChangedEvent clusterChangedEvent, AckListener ackListener) { diff --git a/plugins/discovery-azure-classic/src/main/java/org/elasticsearch/plugin/discovery/azure/classic/AzureDiscoveryPlugin.java b/plugins/discovery-azure-classic/src/main/java/org/elasticsearch/plugin/discovery/azure/classic/AzureDiscoveryPlugin.java index 6bff34a667c19..0240781c1aa52 100644 --- a/plugins/discovery-azure-classic/src/main/java/org/elasticsearch/plugin/discovery/azure/classic/AzureDiscoveryPlugin.java +++ b/plugins/discovery-azure-classic/src/main/java/org/elasticsearch/plugin/discovery/azure/classic/AzureDiscoveryPlugin.java @@ -22,6 +22,7 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.cloud.azure.classic.management.AzureComputeService; import org.elasticsearch.cloud.azure.classic.management.AzureComputeServiceImpl; +import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterApplier; import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -76,11 +77,12 @@ public Map> getZenHostsProviders(Transpor public Map> getDiscoveryTypes(ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, MasterService masterService, ClusterApplier clusterApplier, - ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { + ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider, + AllocationService allocationService) { // this is for backcompat with pre 5.1, where users would set discovery.type to use ec2 hosts provider return Collections.singletonMap(AZURE, () -> new ZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, masterService, clusterApplier, - clusterSettings, hostsProvider)); + clusterSettings, hostsProvider, allocationService)); } @Override diff --git a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java index 3c80d0eda0623..3280368631be8 100644 --- a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java +++ b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java @@ -24,6 +24,7 @@ import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.SetOnce; import org.elasticsearch.SpecialPermission; +import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterApplier; import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -98,11 +99,12 @@ public Ec2DiscoveryPlugin(Settings settings) { public Map> getDiscoveryTypes(ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, MasterService masterService, ClusterApplier clusterApplier, - ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { + ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider, + AllocationService allocationService) { // this is for backcompat with pre 5.1, where users would set discovery.type to use ec2 hosts provider return Collections.singletonMap(EC2, () -> new ZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, masterService, clusterApplier, - clusterSettings, hostsProvider)); + clusterSettings, hostsProvider, allocationService)); } @Override diff --git a/plugins/discovery-gce/src/main/java/org/elasticsearch/plugin/discovery/gce/GceDiscoveryPlugin.java b/plugins/discovery-gce/src/main/java/org/elasticsearch/plugin/discovery/gce/GceDiscoveryPlugin.java index 44fb5c8d15ec8..d20b5eaef05b2 100644 --- a/plugins/discovery-gce/src/main/java/org/elasticsearch/plugin/discovery/gce/GceDiscoveryPlugin.java +++ b/plugins/discovery-gce/src/main/java/org/elasticsearch/plugin/discovery/gce/GceDiscoveryPlugin.java @@ -29,6 +29,7 @@ import org.elasticsearch.cloud.gce.GceMetadataService; import org.elasticsearch.cloud.gce.network.GceNameResolver; import org.elasticsearch.cloud.gce.util.Access; +import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterApplier; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.logging.DeprecationLogger; @@ -86,11 +87,12 @@ public GceDiscoveryPlugin(Settings settings) { public Map> getDiscoveryTypes(ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, MasterService masterService, ClusterApplier clusterApplier, - ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { + ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider, + AllocationService allocationService) { // this is for backcompat with pre 5.1, where users would set discovery.type to use ec2 hosts provider return Collections.singletonMap(GCE, () -> new ZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, masterService, clusterApplier, - clusterSettings, hostsProvider)); + clusterSettings, hostsProvider, allocationService)); } @Override diff --git a/test/framework/src/main/java/org/elasticsearch/cluster/ESAllocationTestCase.java b/test/framework/src/main/java/org/elasticsearch/cluster/ESAllocationTestCase.java index 02f8896be4d37..e1205ba846b36 100644 --- a/test/framework/src/main/java/org/elasticsearch/cluster/ESAllocationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/cluster/ESAllocationTestCase.java @@ -205,7 +205,7 @@ protected long currentNanoTime() { protected static class DelayedShardsMockGatewayAllocator extends GatewayAllocator { public DelayedShardsMockGatewayAllocator() { - super(Settings.EMPTY, null, null); + super(Settings.EMPTY); } @Override diff --git a/test/framework/src/main/java/org/elasticsearch/test/discovery/TestZenDiscovery.java b/test/framework/src/main/java/org/elasticsearch/test/discovery/TestZenDiscovery.java index cb4b8e098aeda..af1bbc94d27ab 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/discovery/TestZenDiscovery.java +++ b/test/framework/src/main/java/org/elasticsearch/test/discovery/TestZenDiscovery.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.function.Supplier; +import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterApplier; import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -59,10 +60,11 @@ public TestPlugin(Settings settings) { public Map> getDiscoveryTypes(ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, MasterService masterService, ClusterApplier clusterApplier, - ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { + ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider, + AllocationService allocationService) { return Collections.singletonMap("test-zen", () -> new TestZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, masterService, - clusterApplier, clusterSettings, hostsProvider)); + clusterApplier, clusterSettings, hostsProvider, allocationService)); } @Override @@ -78,9 +80,10 @@ public Settings additionalSettings() { private TestZenDiscovery(Settings settings, ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, MasterService masterService, - ClusterApplier clusterApplier, ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider) { + ClusterApplier clusterApplier, ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider, + AllocationService allocationService) { super(settings, threadPool, transportService, namedWriteableRegistry, masterService, clusterApplier, clusterSettings, - hostsProvider); + hostsProvider, allocationService); } @Override diff --git a/test/framework/src/main/java/org/elasticsearch/test/gateway/NoopGatewayAllocator.java b/test/framework/src/main/java/org/elasticsearch/test/gateway/NoopGatewayAllocator.java index b2b41b31461e3..d3e05d36f6ea5 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/gateway/NoopGatewayAllocator.java +++ b/test/framework/src/main/java/org/elasticsearch/test/gateway/NoopGatewayAllocator.java @@ -35,7 +35,7 @@ public class NoopGatewayAllocator extends GatewayAllocator { public static final NoopGatewayAllocator INSTANCE = new NoopGatewayAllocator(); protected NoopGatewayAllocator() { - super(Settings.EMPTY, null, null); + super(Settings.EMPTY); } @Override diff --git a/test/framework/src/main/java/org/elasticsearch/test/gateway/TestGatewayAllocator.java b/test/framework/src/main/java/org/elasticsearch/test/gateway/TestGatewayAllocator.java index f8c8c4694e59d..2bbf2ce4c2caf 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/gateway/TestGatewayAllocator.java +++ b/test/framework/src/main/java/org/elasticsearch/test/gateway/TestGatewayAllocator.java @@ -96,7 +96,7 @@ protected boolean hasInitiatedFetching(ShardRouting shard) { }; public TestGatewayAllocator() { - super(Settings.EMPTY, null, null); + super(Settings.EMPTY); } @Override From 6b67e0bf2fb3e27279dff90ba76fa7c93bb4c828 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Fri, 5 May 2017 09:34:12 +0200 Subject: [PATCH 215/619] Include all aliases including non-filtering in `_search_shards` response (#24489) `_search_shards`API today only returns aliases names if there is an alias filter associated with one of them. Now it can be useful to see which aliases have been expanded for an index given the index expressions. This change also includes non-filtering aliases even without a filtering alias being present. --- .../shards/ClusterSearchShardsResponse.java | 13 ++-- .../TransportClusterSearchShardsAction.java | 6 +- .../metadata/IndexNameExpressionResolver.java | 60 +++++++++++-------- .../IndexNameExpressionResolverTests.java | 14 +++++ docs/reference/search/search-shards.asciidoc | 4 +- .../test/search_shards/10_basic.yaml | 31 +++++++++- 6 files changed, 92 insertions(+), 36 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsResponse.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsResponse.java index 3ee5f56ebdc75..c2fb90434e57a 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsResponse.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsResponse.java @@ -29,6 +29,7 @@ import org.elasticsearch.search.internal.AliasFilter; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -117,10 +118,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws String index = entry.getKey(); builder.startObject(index); AliasFilter aliasFilter = entry.getValue(); - if (aliasFilter.getAliases().length > 0) { - builder.array("aliases", aliasFilter.getAliases()); - builder.field("filter"); - aliasFilter.getQueryBuilder().toXContent(builder, params); + String[] aliases = aliasFilter.getAliases(); + if (aliases.length > 0) { + Arrays.sort(aliases); // we want consistent ordering here and these values might be generated from a set / map + builder.array("aliases", aliases); + if (aliasFilter.getQueryBuilder() != null) { // might be null if we include non-filtering aliases + builder.field("filter"); + aliasFilter.getQueryBuilder().toXContent(builder, params); + } } builder.endObject(); } diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java index 8825a42676812..20ed69ae5a92f 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java @@ -83,8 +83,10 @@ protected void masterOperation(final ClusterSearchShardsRequest request, final C Map> routingMap = indexNameExpressionResolver.resolveSearchRouting(state, request.routing(), request.indices()); Map indicesAndFilters = new HashMap<>(); for (String index : concreteIndices) { - AliasFilter aliasFilter = indicesService.buildAliasFilter(clusterState, index, request.indices()); - indicesAndFilters.put(index, aliasFilter); + final AliasFilter aliasFilter = indicesService.buildAliasFilter(clusterState, index, request.indices()); + final String[] aliases = indexNameExpressionResolver.indexAliases(clusterState, index, aliasMetadata -> true, true, + request.indices()); + indicesAndFilters.put(index, new AliasFilter(aliasFilter.getQueryBuilder(), aliases)); } Set nodeIds = new HashSet<>(); diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java b/core/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java index 168fe2ad7f291..d4c6ec587db78 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java @@ -48,6 +48,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; public class IndexNameExpressionResolver extends AbstractComponent { @@ -268,8 +269,19 @@ public String resolveDateMathExpression(String dateExpression) { * the index itself - null is returned. Returns null if no filtering is required. */ public String[] filteringAliases(ClusterState state, String index, String... expressions) { + return indexAliases(state, index, AliasMetaData::filteringRequired, false, expressions); + } + + /** + * Iterates through the list of indices and selects the effective list of required aliases for the + * given index. + *

Only aliases where the given predicate tests successfully are returned. If the indices list contains a non-required reference to + * the index itself - null is returned. Returns null if no filtering is required. + */ + public String[] indexAliases(ClusterState state, String index, Predicate requiredAlias, boolean skipIdentity, + String... expressions) { // expand the aliases wildcard - List resolvedExpressions = expressions != null ? Arrays.asList(expressions) : Collections.emptyList(); + List resolvedExpressions = expressions != null ? Arrays.asList(expressions) : Collections.emptyList(); Context context = new Context(state, IndicesOptions.lenientExpandOpen(), true); for (ExpressionResolver expressionResolver : expressionResolvers) { resolvedExpressions = expressionResolver.resolve(context, resolvedExpressions); @@ -278,54 +290,50 @@ public String[] filteringAliases(ClusterState state, String index, String... exp if (isAllIndices(resolvedExpressions)) { return null; } + final IndexMetaData indexMetaData = state.metaData().getIndices().get(index); + if (indexMetaData == null) { + // Shouldn't happen + throw new IndexNotFoundException(index); + } // optimize for the most common single index/alias scenario if (resolvedExpressions.size() == 1) { String alias = resolvedExpressions.get(0); - IndexMetaData indexMetaData = state.metaData().getIndices().get(index); - if (indexMetaData == null) { - // Shouldn't happen - throw new IndexNotFoundException(index); - } + AliasMetaData aliasMetaData = indexMetaData.getAliases().get(alias); - boolean filteringRequired = aliasMetaData != null && aliasMetaData.filteringRequired(); - if (!filteringRequired) { + if (aliasMetaData == null || requiredAlias.test(aliasMetaData) == false) { return null; } return new String[]{alias}; } - List filteringAliases = null; + List aliases = null; for (String alias : resolvedExpressions) { if (alias.equals(index)) { - return null; - } - - IndexMetaData indexMetaData = state.metaData().getIndices().get(index); - if (indexMetaData == null) { - // Shouldn't happen - throw new IndexNotFoundException(index); + if (skipIdentity) { + continue; + } else { + return null; + } } - AliasMetaData aliasMetaData = indexMetaData.getAliases().get(alias); // Check that this is an alias for the current index // Otherwise - skip it if (aliasMetaData != null) { - boolean filteringRequired = aliasMetaData.filteringRequired(); - if (filteringRequired) { - // If filtering required - add it to the list of filters - if (filteringAliases == null) { - filteringAliases = new ArrayList<>(); + if (requiredAlias.test(aliasMetaData)) { + // If required - add it to the list of aliases + if (aliases == null) { + aliases = new ArrayList<>(); } - filteringAliases.add(alias); + aliases.add(alias); } else { - // If not, we have a non filtering alias for this index - no filtering needed + // If not, we have a non required alias for this index - no futher checking needed return null; } } } - if (filteringAliases == null) { + if (aliases == null) { return null; } - return filteringAliases.toArray(new String[filteringAliases.size()]); + return aliases.toArray(new String[aliases.size()]); } /** diff --git a/core/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java b/core/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java index b68f3735c0a97..7d3ca04e5a849 100644 --- a/core/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java @@ -33,6 +33,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.function.Predicate; import static org.elasticsearch.common.util.set.Sets.newHashSet; import static org.hamcrest.Matchers.arrayContaining; @@ -956,4 +957,17 @@ public void testFilteringAliases() { strings = indexNameExpressionResolver.filteringAliases(state, "test-0", "test-*,alias-*"); assertNull(strings); } + + public void testIndexAliases() { + MetaData.Builder mdBuilder = MetaData.builder() + .put(indexBuilder("test-0").state(State.OPEN) + .putAlias(AliasMetaData.builder("test-alias-0").filter("{ \"term\": \"foo\"}")) + .putAlias(AliasMetaData.builder("test-alias-1").filter("{ \"term\": \"foo\"}")) + .putAlias(AliasMetaData.builder("test-alias-non-filtering")) + ); + ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build(); + String[] strings = indexNameExpressionResolver.indexAliases(state, "test-0", x -> true, true, "test-*"); + Arrays.sort(strings); + assertArrayEquals(new String[] {"test-alias-0", "test-alias-1", "test-alias-non-filtering"}, strings); + } } diff --git a/docs/reference/search/search-shards.asciidoc b/docs/reference/search/search-shards.asciidoc index 1515a182d4256..b20117bb75dbd 100644 --- a/docs/reference/search/search-shards.asciidoc +++ b/docs/reference/search/search-shards.asciidoc @@ -4,7 +4,7 @@ The search shards api returns the indices and shards that a search request would be executed against. This can give useful feedback for working out issues or planning optimizations with routing and shard preferences. When filtered aliases -are used, the filter is returned as part of the `indices` section. +are used, the filter is returned as part of the `indices` section [5.1.0] Added in 5.1.0. The `index` may be a single value, or comma-separated. @@ -165,4 +165,4 @@ routing values have been specified. `local`:: A boolean value whether to read the cluster state locally in order to determine where shards are allocated instead of using the Master node's - cluster state. + cluster state. \ No newline at end of file diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml index 42189883b1bab..d2a53a4416ad9 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml @@ -14,7 +14,7 @@ --- "Search shards aliases with and without filters": - skip: - version: " - 5.0.99" + version: " - 5.99.99" # temporarily disabled reason: indices section was added in 5.1.0 - do: @@ -49,7 +49,7 @@ - match: { shards.0.0.index: test_index } - is_true: indices.test_index - is_false: indices.test_index.filter - - is_false: indices.test_index.aliases + - match: { indices.test_index.aliases: [test_alias_no_filter]} - do: search_shards: @@ -78,3 +78,30 @@ - match: { indices.test_index.filter.bool.adjust_pure_negative: true} - lte: { indices.test_index.filter.bool.boost: 1.0 } - gte: { indices.test_index.filter.bool.boost: 1.0 } + + - do: + search_shards: + index: "test*" + + - length: { shards: 1 } + - match: { shards.0.0.index: test_index } + - match: { indices.test_index.aliases: [test_alias_filter_1, test_alias_filter_2, test_alias_no_filter]} + - is_false: indices.test_index.filter + + - do: + search_shards: + index: ["test_alias_filter_1","test_alias_no_filter"] + + - length: { shards: 1 } + - match: { shards.0.0.index: test_index } + - match: { indices.test_index.aliases: [test_alias_filter_1, test_alias_no_filter]} + - is_false: indices.test_index.filter + + - do: + search_shards: + index: ["test_alias_no_filter"] + + - length: { shards: 1 } + - match: { shards.0.0.index: test_index } + - match: { indices.test_index.aliases: [test_alias_no_filter]} + - is_false: indices.test_index.filter From 6e970db5337d488e67ab73b25fb68d39835ca850 Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Fri, 5 May 2017 11:08:55 +0200 Subject: [PATCH 216/619] Fixed chunking of breaking changes docs --- docs/reference/migration/migrate_6_0/docs.asciidoc | 4 ++-- docs/reference/migration/migrate_6_0/indices.asciidoc | 8 ++++---- docs/reference/migration/migrate_6_0/mappings.asciidoc | 4 ++-- docs/reference/migration/migrate_6_0/rest.asciidoc | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/reference/migration/migrate_6_0/docs.asciidoc b/docs/reference/migration/migrate_6_0/docs.asciidoc index b7a08ea8930da..9b4ad82a32d9c 100644 --- a/docs/reference/migration/migrate_6_0/docs.asciidoc +++ b/docs/reference/migration/migrate_6_0/docs.asciidoc @@ -1,11 +1,11 @@ [[breaking_60_docs_changes]] === Document API changes -==== version type 'force' removed +==== version type `force` removed Document modification operations may no longer specify the `version_type` of `force` to override any previous version checks. -==== <> no longer support versions +==== <> no longer support versions Adding a `version` to an upsert request is no longer supported. diff --git a/docs/reference/migration/migrate_6_0/indices.asciidoc b/docs/reference/migration/migrate_6_0/indices.asciidoc index a1d1ffd578d32..0a05fd55139a4 100644 --- a/docs/reference/migration/migrate_6_0/indices.asciidoc +++ b/docs/reference/migration/migrate_6_0/indices.asciidoc @@ -1,7 +1,7 @@ [[breaking_60_indices_changes]] -=== Templates changes +=== Indices changes -==== `template` is now `index_patterns` +==== Index templates use `index_patterns` instead of `template` Previously templates expressed the indices that they should match using a glob style pattern in the `template` field. They should now use the `index_patterns` @@ -29,7 +29,7 @@ PUT _template/template_2 // CONSOLE -=== Shadow Replicas have been removed +==== Shadow Replicas have been removed Shadow replicas don't see enough usage, and have been removed. This includes the following settings: @@ -38,7 +38,7 @@ following settings: - `index.shadow_replicas` - `node.add_lock_id_to_custom_path` -=== Open/Close index API allows wildcard expressions that match no indices by default +==== Open/Close index API allows wildcard expressions that match no indices by default The default value of the `allow_no_indices` option for the Open/Close index API has been changed from `false` to `true` so it is aligned with the behaviour of the diff --git a/docs/reference/migration/migrate_6_0/mappings.asciidoc b/docs/reference/migration/migrate_6_0/mappings.asciidoc index 754f4fe726611..e85b31d97ff06 100644 --- a/docs/reference/migration/migrate_6_0/mappings.asciidoc +++ b/docs/reference/migration/migrate_6_0/mappings.asciidoc @@ -4,7 +4,7 @@ ==== Coercion of boolean fields Previously, Elasticsearch recognized the strings `true`, `false`, `on`, `off`, `yes`, `no`, `0`, `1` as booleans. Elasticsearch 6.0 -recognizes only `true` and `false` as boolean and will throw an error otherwise. For backwards compatibility purposes, during the 6.x +recognizes only the strings `true` and `false` as booleans and will throw an error otherwise. For backwards compatibility purposes, during the 6.x series the previous coercion rules will continue to work on pre-6.0 indices. This means that you do not need to change affected existing mappings immediately. However, it is not possible to create new indices from existing index templates that violate the strict `boolean` coercion rules. @@ -20,7 +20,7 @@ created with Elasticsearch version 6.0 or later. ==== The `include_in_all` mapping parameter is now disallowed -Since the `_all` field is now disabled by default and cannot be configured for +Since the ++_all++ field is now disabled by default and cannot be configured for indices created with Elasticsearch 6.0 or later, the `include_in_all` setting is now disallowed for these indices' mappings. diff --git a/docs/reference/migration/migrate_6_0/rest.asciidoc b/docs/reference/migration/migrate_6_0/rest.asciidoc index 5ef09a15bff11..cfd8a9511a2ee 100644 --- a/docs/reference/migration/migrate_6_0/rest.asciidoc +++ b/docs/reference/migration/migrate_6_0/rest.asciidoc @@ -48,7 +48,7 @@ Refresh requests that are broadcast to multiple shards that can have one or more shards fail during the request now return a 500 response instead of a 200 response in the event there is at least one failure. -=== Delete by Query API requires an explicit query +==== Delete by Query API requires an explicit query In previous versions of Elasticsearch, delete by query requests without an explicit query were accepted, match_all was used as the default query and all documents were deleted From 03267e03da5facdef138a082e9762fb8a04c00e1 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Fri, 5 May 2017 11:56:03 +0200 Subject: [PATCH 217/619] Fix NPE if field caps request has a field that exists not in all indices (#24504) If a field caps request contains a field name that doesn't exist in all indices the response will be partial and we hide an NPE. The NPE is now fixed but we still have the problem that we don't pass on errors on the shard level to the user. This will be fixed in a followup. --- ...TransportFieldCapabilitiesIndexAction.java | 6 ++- .../test/field_caps/10_basic.yaml | 42 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java b/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java index e7db685b4a882..b9e6f56b6d7ad 100644 --- a/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java +++ b/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java @@ -80,8 +80,10 @@ protected FieldCapabilitiesIndexResponse shardOperation(final FieldCapabilitiesI Map responseMap = new HashMap<>(); for (String field : fieldNames) { MappedFieldType ft = mapperService.fullName(field); - FieldCapabilities fieldCap = new FieldCapabilities(field, ft.typeName(), ft.isSearchable(), ft.isAggregatable()); - responseMap.put(field, fieldCap); + if (ft != null) { + FieldCapabilities fieldCap = new FieldCapabilities(field, ft.typeName(), ft.isSearchable(), ft.isAggregatable()); + responseMap.put(field, fieldCap); + } } return new FieldCapabilitiesIndexResponse(shardId.getIndexName(), responseMap); } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml index cef72b6e3fe43..e6dd5a2c71f6a 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml @@ -165,3 +165,45 @@ setup: - match: {fields.number.long.indices: ["test3"]} - is_false: fields.number.long.non_searchable_indices - is_false: fields.number.long.non_aggregatable_indices + +--- +"Mix in non-existing field field caps": + - skip: + version: " - 5.4.0" + reason: this bug has been fixed in 5.4.0 + + - do: + field_caps: + index: 'test1,test2,test3' + fields: [text, keyword, no_such_field, number, geo] + + - match: {fields.text.text.searchable: true} + - match: {fields.text.text.aggregatable: false} + - is_false: fields.text.text.indices + - is_false: fields.text.text.non_searchable_indices + - is_false: fields.text.text.non_aggregatable_indices + - match: {fields.keyword.keyword.searchable: true} + - match: {fields.keyword.keyword.aggregatable: true} + - is_false: fields.text.keyword.indices + - is_false: fields.text.keyword.non_searchable_indices + - is_false: fields.text.keyword.non_aggregatable_indices + - match: {fields.number.double.searchable: true} + - match: {fields.number.double.aggregatable: true} + - match: {fields.number.double.indices: ["test1", "test2"]} + - is_false: fields.number.double.non_searchable_indices + - is_false: fields.number.double.non_aggregatable_indices + - match: {fields.number.long.searchable: true} + - match: {fields.number.long.aggregatable: true} + - match: {fields.number.long.indices: ["test3"]} + - is_false: fields.number.long.non_searchable_indices + - is_false: fields.number.long.non_aggregatable_indices + - match: {fields.geo.geo_point.searchable: true} + - match: {fields.geo.geo_point.aggregatable: true} + - match: {fields.geo.geo_point.indices: ["test1", "test2"]} + - is_false: fields.geo.geo_point.non_searchable_indices + - is_false: fields.geo.geo_point.non_aggregatable_indices + - match: {fields.geo.keyword.searchable: true} + - match: {fields.geo.keyword.aggregatable: true} + - match: {fields.geo.keyword.indices: ["test3"]} + - is_false: fields.geo.keyword.non_searchable_indices + - is_false: fields.geo.keyword.on_aggregatable_indices From 8055b14f2eb16e0b8b697655006355f3bf243e87 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Fri, 5 May 2017 12:08:00 +0200 Subject: [PATCH 218/619] Temporarily disable tests --- .../main/resources/rest-api-spec/test/field_caps/10_basic.yaml | 2 +- .../resources/rest-api-spec/test/search_shards/10_basic.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml index e6dd5a2c71f6a..2568f401a5ea5 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml @@ -169,7 +169,7 @@ setup: --- "Mix in non-existing field field caps": - skip: - version: " - 5.4.0" + version: " - 6.0.0" # temporarily disabled reason: this bug has been fixed in 5.4.0 - do: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml index d2a53a4416ad9..ef01f3b7b44e4 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml @@ -14,7 +14,7 @@ --- "Search shards aliases with and without filters": - skip: - version: " - 5.99.99" # temporarily disabled + version: " - 6.0.0" # temporarily disabled reason: indices section was added in 5.1.0 - do: From c9aecbb8a5a911861fbbcc1083a8f39453b34386 Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Fri, 5 May 2017 11:52:57 +0200 Subject: [PATCH 219/619] Added removal of JavaScript and Python to breaking changes --- docs/reference/migration/migrate_6_0/scripting.asciidoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/migration/migrate_6_0/scripting.asciidoc b/docs/reference/migration/migrate_6_0/scripting.asciidoc index 6740200922327..075678b3ab3f7 100644 --- a/docs/reference/migration/migrate_6_0/scripting.asciidoc +++ b/docs/reference/migration/migrate_6_0/scripting.asciidoc @@ -1,10 +1,10 @@ [[breaking_60_scripting_changes]] === Scripting changes -==== Groovy language removed +==== Groovy, JavaScript, and Python languages removed -The groovy scripting language was deprecated in elasticsearch 5.0 and is now removed. -Use painless instead. +The Groovy, JavaScript, and Python scripting languages were deprecated in +elasticsearch 5.0 and have now been removed. Use painless instead. ==== Date fields now return dates From 017411929664bba1d01ee5420d4316939b7c5509 Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Fri, 5 May 2017 12:39:41 +0200 Subject: [PATCH 220/619] Added release notes for 6.0.0-alpha1 --- docs/Versions.asciidoc | 6 +- docs/reference/index.asciidoc | 4 +- docs/reference/release-notes.asciidoc | 8 +- .../release-notes/6.0.0-alpha1-5x.asciidoc | 1096 +++++++++++++++++ .../release-notes/6.0.0-alpha1.asciidoc | 309 +++++ 5 files changed, 1416 insertions(+), 7 deletions(-) create mode 100644 docs/reference/release-notes/6.0.0-alpha1-5x.asciidoc create mode 100644 docs/reference/release-notes/6.0.0-alpha1.asciidoc diff --git a/docs/Versions.asciidoc b/docs/Versions.asciidoc index 12e606ba78a11..bbd5f3bce07ac 100644 --- a/docs/Versions.asciidoc +++ b/docs/Versions.asciidoc @@ -1,9 +1,9 @@ :version: 6.0.0-alpha1 :major-version: 6.x -:lucene_version: 6.4.0 -:lucene_version_path: 6_4_0 +:lucene_version: 7.0.0-SNAPSHOT +:lucene_version_path: 7_0_0 :branch: master -:jdk: 1.8.0_73 +:jdk: 1.8.0_131 ////////// release-state can be: released | prerelease | unreleased diff --git a/docs/reference/index.asciidoc b/docs/reference/index.asciidoc index 2a4d7852a2f79..cb97bcf6d9f1f 100644 --- a/docs/reference/index.asciidoc +++ b/docs/reference/index.asciidoc @@ -42,9 +42,7 @@ include::testing.asciidoc[] include::glossary.asciidoc[] -////// - include::release-notes.asciidoc[] -////// +include::release-notes.asciidoc[] include::painless-api-reference.asciidoc[] diff --git a/docs/reference/release-notes.asciidoc b/docs/reference/release-notes.asciidoc index 267525b1b3cb4..909d2cb6d1c23 100644 --- a/docs/reference/release-notes.asciidoc +++ b/docs/reference/release-notes.asciidoc @@ -3,5 +3,11 @@ [partintro] -- -This section will summarize the changes in released versions. +This section summarizes the changes in each release. + +* <> +* <> + -- +include::release-notes/6.0.0-alpha1.asciidoc[] +include::release-notes/6.0.0-alpha1-5x.asciidoc[] diff --git a/docs/reference/release-notes/6.0.0-alpha1-5x.asciidoc b/docs/reference/release-notes/6.0.0-alpha1-5x.asciidoc new file mode 100644 index 0000000000000..699260fd0c8d2 --- /dev/null +++ b/docs/reference/release-notes/6.0.0-alpha1-5x.asciidoc @@ -0,0 +1,1096 @@ +[[release-notes-6.0.0-alpha1-5x]] +== 6.0.0-alpha1 Release Notes (Changes previously released in 5.x) + +The changes listed below were first released in the 5.x series. Changes +released for the first time in Elasticsearch 6.0.0-alpha1 are listed in +<>. + +[[breaking-6.0.0-alpha1-5x]] +[float] +=== Breaking changes + +Aliases:: +* Validate alias names the same as index names {pull}20771[#20771] (issue: {issue}20748[#20748]) + +CRUD:: +* Fixed naming inconsistency for fields/stored_fields in the APIs {pull}20166[#20166] (issues: {issue}18943[#18943], {issue}20155[#20155]) + +Core:: +* Add system call filter bootstrap check {pull}21940[#21940] +* Remove ignore system bootstrap checks {pull}20511[#20511] + +Internal:: +* `_flush` should block by default {pull}20597[#20597] (issue: {issue}20569[#20569]) + +Packaging:: +* Rename service.bat to elasticsearch-service.bat {pull}20496[#20496] (issue: {issue}17528[#17528]) + +Plugin Lang Painless:: +* Remove all date 'now' methods from Painless {pull}20766[#20766] (issue: {issue}20762[#20762]) + +Query DSL:: +* Fix name of `enabled_position_increments` {pull}22895[#22895] + +REST:: +* Change separator for shards preference {pull}20786[#20786] (issues: {issue}20722[#20722], {issue}20769[#20769]) + +Search:: +* Remove DFS_QUERY_AND_FETCH as a search type {pull}22787[#22787] + +Settings:: +* Remove support for default settings {pull}24093[#24093] (issues: {issue}23981[#23981], {issue}24052[#24052], {issue}24074[#24074]) + + + +[[breaking-java-6.0.0-alpha1-5x]] +[float] +=== Breaking Java changes + +Aggregations:: +* Move getProperty method out of MultiBucketsAggregation.Bucket interface {pull}23988[#23988] +* Remove getProperty method from Aggregations interface and impl {pull}23972[#23972] +* Move getProperty method out of Aggregation interface {pull}23949[#23949] + +Allocation:: +* Cluster Explain API uses the allocation process to explain shard allocation decisions {pull}22182[#22182] (issues: {issue}20347[#20347], {issue}20634[#20634], {issue}21103[#21103], {issue}21662[#21662], {issue}21691[#21691]) + +Cluster:: +* Remove PROTO-based custom cluster state components {pull}22336[#22336] (issue: {issue}21868[#21868]) + +Core:: +* Remove ability to plug-in TransportService {pull}20505[#20505] + +Discovery:: +* Remove pluggability of ElectMasterService {pull}21031[#21031] + +Exceptions:: +* Remove `IndexTemplateAlreadyExistsException` and `IndexShardAlreadyExistsException` {pull}21539[#21539] (issue: {issue}21494[#21494]) +* Replace IndexAlreadyExistsException with ResourceAlreadyExistsException {pull}21494[#21494] + +Ingest:: +* Change type of ingest doc meta-data field 'TIMESTAMP' to `Date` {pull}22234[#22234] (issue: {issue}22074[#22074]) + +Internal:: +* Replace SearchExtRegistry with namedObject {pull}22492[#22492] +* Replace Suggesters with namedObject {pull}22491[#22491] +* Consolidate the last easy parser construction {pull}22095[#22095] +* Introduce XContentParser#namedObject {pull}22003[#22003] +* Pass executor name to request interceptor to support async intercept calls {pull}21089[#21089] +* Remove TransportService#registerRequestHandler leniency {pull}20469[#20469] (issue: {issue}20468[#20468]) + +Java API:: +* Fold InternalSearchHits and friends into their interfaces {pull}23042[#23042] + +Network:: +* Remove HttpServer and HttpServerAdapter in favor of a simple dispatch method {pull}22636[#22636] (issue: {issue}18482[#18482]) +* Unguice Transport and friends {pull}20526[#20526] + +Plugins:: +* Deguice rest handlers {pull}22575[#22575] +* Plugins: Replace Rest filters with RestHandler wrapper {pull}21905[#21905] +* Plugins: Remove support for onModule {pull}21416[#21416] +* Cleanup sub fetch phase extension point {pull}20382[#20382] + +Query DSL:: +* Resolve index names in indices_boost {pull}21393[#21393] (issue: {issue}4756[#4756]) + +Scripting:: +* Refactor ScriptType to be a Top-Level Class {pull}21136[#21136] + +Search:: +* Remove QUERY_AND_FETCH search type {pull}22996[#22996] +* Cluster search shards improvements: expose ShardId, adjust visibility of some members {pull}21752[#21752] + + + +[[deprecation-6.0.0-alpha1-5x]] +[float] +=== Deprecations + +Java API:: +* Add BulkProcessor methods with XContentType parameter {pull}23078[#23078] (issue: {issue}22691[#22691]) +* Deprecate and remove "minimumNumberShouldMatch" in BoolQueryBuilder {pull}22403[#22403] + +Plugin Repository S3:: +* S3 Repository: Deprecate remaining `repositories.s3.*` settings {pull}24144[#24144] (issue: {issue}24143[#24143]) +* Deprecate specifying credentials through env vars, sys props, and remove profile files {pull}22567[#22567] (issues: {issue}21041[#21041], {issue}22479[#22479]) + +Query DSL:: +* Add deprecation logging message for 'fuzzy' query {pull}20993[#20993] (issue: {issue}15760[#15760]) + +REST:: +* Optionally require a valid content type for all rest requests with content {pull}22691[#22691] (issue: {issue}19388[#19388]) + +Scripting:: +* Change Namespace for Stored Script to Only Use Id {pull}22206[#22206] + +Shadow Replicas:: +* Add a deprecation notice to shadow replicas {pull}22647[#22647] (issue: {issue}22024[#22024]) + +Stats:: +* Deprecate _field_stats endpoint {pull}23914[#23914] + + + +[[feature-6.0.0-alpha1-5x]] +[float] +=== New features + +Aggregations:: +* Initial version of an adjacency matrix using the Filters aggregation {pull}22239[#22239] (issue: {issue}22169[#22169]) + +Analysis:: +* Adds pattern keyword marker filter support {pull}23600[#23600] (issue: {issue}4877[#4877]) +* Expose WordDelimiterGraphTokenFilter {pull}23327[#23327] (issue: {issue}23104[#23104]) +* Synonym Graph Support (LUCENE-6664) {pull}21517[#21517] +* Expose Lucenes Ukrainian analyzer {pull}21176[#21176] (issue: {issue}19433[#19433]) + +CAT API:: +* Provides a cat api endpoint for templates. {pull}20545[#20545] (issue: {issue}20467[#20467]) + +CRUD:: +* Allow an index to be partitioned with custom routing {pull}22274[#22274] (issue: {issue}21585[#21585]) + +Highlighting:: +* Integrate UnifiedHighlighter {pull}21621[#21621] (issue: {issue}21376[#21376]) + +Index APIs:: +* Add FieldCapabilities (_field_caps) API {pull}23007[#23007] (issue: {issue}22438[#22438]) + +Ingest:: +* introduce KV Processor in Ingest Node {pull}22272[#22272] (issue: {issue}22222[#22222]) + +Mapping:: +* Add the ability to set a normalizer on keyword fields. {pull}21919[#21919] (issue: {issue}18064[#18064]) +* Add RangeFieldMapper for numeric and date range types {pull}21002[#21002] (issue: {issue}20999[#20999]) + +Plugin Discovery File:: +* File-based discovery plugin {pull}20394[#20394] (issue: {issue}20323[#20323]) + +Query DSL:: +* Add "all fields" execution mode to simple_query_string query {pull}21341[#21341] (issues: {issue}19784[#19784], {issue}20925[#20925]) +* Add support for `quote_field_suffix` to `simple_query_string`. {pull}21060[#21060] (issue: {issue}18641[#18641]) +* Add "all field" execution mode to query_string query {pull}20925[#20925] (issue: {issue}19784[#19784]) + +Reindex API:: +* Add automatic parallelization support to reindex and friends {pull}20767[#20767] (issue: {issue}20624[#20624]) + +Search:: +* Introduce incremental reduction of TopDocs {pull}23946[#23946] +* Add federated cross cluster search capabilities {pull}22502[#22502] (issue: {issue}21473[#21473]) +* Add field collapsing for search request {pull}22337[#22337] (issue: {issue}21833[#21833]) + +Settings:: +* Add infrastructure for elasticsearch keystore {pull}22335[#22335] + +Similarities:: +* Adds boolean similarity to Elasticsearch {pull}23637[#23637] (issue: {issue}6731[#6731]) + + + +[[enhancement-6.0.0-alpha1-5x]] +[float] +=== Enhancements + +Aggregations:: +* Add `count` to rest output of `geo_centroid` {pull}24387[#24387] (issue: {issue}24366[#24366]) +* Allow scripted metric agg to access `_score` {pull}24295[#24295] +* Add BucketMetricValue interface {pull}24188[#24188] +* Move aggs CommonFields and TYPED_KEYS_DELIMITER from InternalAggregation to Aggregation {pull}23987[#23987] +* Use ParseField for aggs CommonFields rather than String {pull}23717[#23717] +* Share XContent rendering code in terms aggs {pull}23680[#23680] +* Add unit tests for ParentToChildAggregator {pull}23305[#23305] (issue: {issue}22278[#22278]) +* First step towards incremental reduction of query responses {pull}23253[#23253] +* `value_type` is useful regardless of scripting. {pull}22160[#22160] (issue: {issue}20163[#20163]) +* Support for partitioning set of terms {pull}21626[#21626] (issue: {issue}21487[#21487]) +* Rescorer should be applied in the TopHits aggregation {pull}20978[#20978] (issue: {issue}19317[#19317]) + +Aliases:: +* Handle multiple aliases in _cat/aliases api {pull}23698[#23698] (issue: {issue}23661[#23661]) + +Allocation:: +* Trigger replica recovery restarts by master when primary relocation completes {pull}23926[#23926] (issue: {issue}23904[#23904]) +* Makes the same_shard host dynamically updatable {pull}23397[#23397] (issue: {issue}22992[#22992]) +* Include stale replica shard info when explaining an unassigned primary {pull}22826[#22826] +* Adds setting level to allocation decider explanations {pull}22268[#22268] (issue: {issue}21771[#21771]) +* Improves allocation decider decision explanation messages {pull}21771[#21771] +* Prepares allocator decision objects for use with the allocation explain API {pull}21691[#21691] +* Balance step in BalancedShardsAllocator for a single shard {pull}21103[#21103] +* Process more expensive allocation deciders last {pull}20724[#20724] (issue: {issue}12815[#12815]) +* Separates decision making from decision application in BalancedShardsAllocator {pull}20634[#20634] + +Analysis:: +* Support Keyword type in Analyze API {pull}23161[#23161] +* Expose FlattenGraphTokenFilter {pull}22643[#22643] +* Analyze API Position Length Support {pull}22574[#22574] +* Remove AnalysisService and reduce it to a simple name to analyzer mapping {pull}20627[#20627] (issues: {issue}19827[#19827], {issue}19828[#19828]) + +CAT API:: +* Adding built-in sorting capability to _cat apis. {pull}20658[#20658] (issue: {issue}16975[#16975]) +* Add health status parameter to cat indices API {pull}20393[#20393] + +CRUD:: +* Use correct block levels for TRA subclasses {pull}22224[#22224] +* Make index and delete operation execute as a single bulk item {pull}21964[#21964] + +Cache:: +* Do not cache term queries. {pull}21566[#21566] (issues: {issue}16031[#16031], {issue}20116[#20116]) +* Parse alias filters on the coordinating node {pull}20916[#20916] + +Circuit Breakers:: +* Closing a ReleasableBytesStreamOutput closes the underlying BigArray {pull}23941[#23941] +* Add used memory amount to CircuitBreakingException message (#22521) {pull}22693[#22693] (issue: {issue}22521[#22521]) +* Cluster Settings Updates should not trigger circuit breakers. {pull}20827[#20827] + +Cluster:: +* Extract a common base class to allow services to listen to remote cluster config updates {pull}24367[#24367] +* Prevent nodes from joining if newer indices exist in the cluster {pull}23843[#23843] +* Connect to new nodes concurrently {pull}22984[#22984] (issue: {issue}22828[#22828]) +* Keep NodeConnectionsService in sync with current nodes in the cluster state {pull}22509[#22509] +* Add a generic way of checking version before serializing custom cluster object {pull}22376[#22376] (issue: {issue}22313[#22313]) +* Add validation for supported index version on node join, restore, upgrade & open index {pull}21830[#21830] (issue: {issue}21670[#21670]) +* Let ClusterStateObserver only hold onto state that's needed for change detection {pull}21631[#21631] (issue: {issue}21568[#21568]) +* Cache successful shard deletion checks {pull}21438[#21438] +* Remove mutable status field from cluster state {pull}21379[#21379] +* Skip shard management code when updating cluster state on client/tribe nodes {pull}20731[#20731] +* Add clusterUUID to RestMainAction output {pull}20503[#20503] + +Core:: +* Regex upgrades {pull}24316[#24316] (issue: {issue}24226[#24226]) +* Detect remnants of path.data/default.path.data bug {pull}24099[#24099] (issues: {issue}23981[#23981], {issue}24052[#24052], {issue}24074[#24074], {issue}24093[#24093]) +* Await termination after shutting down executors {pull}23889[#23889] +* Add early-access check {pull}23743[#23743] (issue: {issue}23668[#23668]) +* Adapter action future should restore interrupts {pull}23618[#23618] (issue: {issue}23617[#23617]) +* Disable bootstrap checks for single-node discovery {pull}23598[#23598] (issues: {issue}23585[#23585], {issue}23595[#23595]) +* Enable explicitly enforcing bootstrap checks {pull}23585[#23585] (issue: {issue}21864[#21864]) +* Add equals/hashcode method to ReplicationResponse {pull}23215[#23215] +* Simplify ElasticsearchException rendering as a XContent {pull}22611[#22611] +* Remove setLocalNode from ClusterService and TransportService {pull}22608[#22608] +* Rename bootstrap.seccomp to bootstrap.system_call_filter {pull}22226[#22226] (issue: {issue}21940[#21940]) +* Cleanup random stats serialization code {pull}22223[#22223] +* Avoid corruption when deserializing booleans {pull}22152[#22152] +* Reduce memory pressure when sending large terms queries. {pull}21776[#21776] +* Install a security manager on startup {pull}21716[#21716] +* Log node ID on startup {pull}21673[#21673] +* Ensure source filtering automatons are only compiled once {pull}20857[#20857] (issue: {issue}20839[#20839]) +* Improve scheduling fairness when batching cluster state changes with equal priority {pull}20775[#20775] (issue: {issue}20768[#20768]) +* Add production warning for pre-release builds {pull}20674[#20674] +* Add serial collector bootstrap check {pull}20558[#20558] +* Do not log full bootstrap checks exception {pull}19989[#19989] + +Dates:: +* Improve error handling for epoch format parser with time zone (#22621) {pull}23689[#23689] + +Discovery:: +* Introduce single-node discovery {pull}23595[#23595] +* UnicastZenPing shouldn't ping the address of the local node {pull}23567[#23567] +* MasterFaultDetection can start after the initial cluster state has been processed {pull}23037[#23037] (issue: {issue}22828[#22828]) +* Simplify Unicast Zen Ping {pull}22277[#22277] (issues: {issue}19370[#19370], {issue}21739[#21739], {issue}22120[#22120], {issue}22194[#22194]) +* Prefer joining node with conflicting transport address when becoming master {pull}22134[#22134] (issues: {issue}22049[#22049], {issue}22120[#22120]) + +Engine:: +* Engine: store maxUnsafeAutoIdTimestamp in commit {pull}24149[#24149] +* Replace EngineClosedException with AlreadyClosedExcpetion {pull}22631[#22631] + +Exceptions:: +* Add BWC layer for Exceptions {pull}21694[#21694] (issue: {issue}21656[#21656]) + +Geo:: +* Optimize geo-distance sorting. {pull}20596[#20596] (issue: {issue}20450[#20450]) + +Highlighting:: +* Add support for fragment_length in the unified highlighter {pull}23431[#23431] +* Add BreakIteratorBoundaryScanner support {pull}23248[#23248] + +Index APIs:: +* Open and close index to honour allow_no_indices option {pull}24222[#24222] (issue: {issue}24031[#24031]) +* Wildcard cluster names for cross cluster search {pull}23985[#23985] (issue: {issue}23893[#23893]) +* Indexing: Add shard id to indexing operation listener {pull}22606[#22606] +* Better error when can't auto create index {pull}22488[#22488] (issues: {issue}21448[#21448], {issue}22435[#22435]) +* Add date-math support to `_rollover` {pull}20709[#20709] + +Ingest:: +* Lazy load the geoip databases {pull}23337[#23337] +* add `ignore_missing` flag to ingest plugins {pull}22273[#22273] +* Added ability to remove pipelines via wildcards (#22149) {pull}22191[#22191] (issue: {issue}22149[#22149]) +* Enables the ability to inject serialized json fields into root of document {pull}22179[#22179] (issue: {issue}21898[#21898]) +* compile ScriptProcessor inline scripts when creating ingest pipelines {pull}21858[#21858] (issue: {issue}21842[#21842]) +* add `ignore_missing` option to SplitProcessor {pull}20982[#20982] (issues: {issue}19995[#19995], {issue}20840[#20840]) +* add ignore_missing option to convert,trim,lowercase,uppercase,grok,rename {pull}20194[#20194] (issue: {issue}19995[#19995]) +* introduce the JSON Processor {pull}20128[#20128] (issue: {issue}20052[#20052]) + +Internal:: +* Log JVM arguments on startup {pull}24451[#24451] +* Move RemoteClusterService into TransportService {pull}24424[#24424] +* Enum related performance additions. {pull}24274[#24274] (issue: {issue}24226[#24226]) +* Add a dedicated TransportRemoteInfoAction for consistency {pull}24040[#24040] (issue: {issue}23969[#23969]) +* Simplify sorted top docs merging in SearchPhaseController {pull}23881[#23881] +* Synchronized CollapseTopFieldDocs with lucenes relatives {pull}23854[#23854] +* Cleanup SearchPhaseController interface {pull}23844[#23844] +* Do not create String instances in 'Strings' methods accepting StringBuilder {pull}22907[#22907] +* Improve connection closing in `RemoteClusterConnection` {pull}22804[#22804] (issue: {issue}22803[#22803]) +* Remove some more usages of ParseFieldMatcher {pull}22437[#22437] (issues: {issue}19552[#19552], {issue}22130[#22130]) +* Remove some more usages of ParseFieldMatcher {pull}22398[#22398] (issues: {issue}19552[#19552], {issue}22130[#22130]) +* Remove some more usages of ParseFieldMatcher {pull}22395[#22395] (issues: {issue}19552[#19552], {issue}22130[#22130]) +* Remove some ParseFieldMatcher usages {pull}22389[#22389] (issues: {issue}19552[#19552], {issue}22130[#22130]) +* Introduce ToXContentObject interface {pull}22387[#22387] (issue: {issue}16347[#16347]) +* Add infrastructure to manage network connections outside of Transport/TransportService {pull}22194[#22194] +* Replace strict parsing mode with response headers assertions {pull}22130[#22130] (issues: {issue}11859[#11859], {issue}19552[#19552], {issue}20993[#20993]) +* Start using `ObjectParser` for aggs. {pull}22048[#22048] (issue: {issue}22009[#22009]) +* Don't output null source node in RecoveryFailedException {pull}21963[#21963] +* ClusterService should expose "applied" cluster states (i.e., remove ClusterStateStatus) {pull}21817[#21817] +* Rename ClusterState#lookupPrototypeSafe to `lookupPrototype` and remove "unsafe" unused variant {pull}21686[#21686] +* ShardActiveResponseHandler shouldn't hold to an entire cluster state {pull}21470[#21470] (issue: {issue}21394[#21394]) +* Remove unused ClusterService dependency from SearchPhaseController {pull}21421[#21421] +* Remove special case in case no action filters are registered {pull}21251[#21251] +* Use TimveValue instead of long for CacheBuilder methods {pull}20887[#20887] +* Remove SearchContext#current and all it's threadlocals {pull}20778[#20778] (issue: {issue}19341[#19341]) +* Remove poor-mans compression in InternalSearchHit and friends {pull}20472[#20472] + +Java API:: +* Added types options to DeleteByQueryRequest {pull}23265[#23265] (issue: {issue}21984[#21984]) +* prevent NPE when trying to uncompress a null BytesReference {pull}22386[#22386] + +Java High Level REST Client:: +* Add utility method to parse named XContent objects with typed prefix {pull}24240[#24240] (issue: {issue}22965[#22965]) +* Convert suggestion response parsing to use NamedXContentRegistry {pull}23355[#23355] +* UpdateRequest implements ToXContent {pull}23289[#23289] +* Add javadoc for DocWriteResponse.Builders {pull}23267[#23267] +* Expose WriteRequest.RefreshPolicy string representation {pull}23106[#23106] +* Use `typed_keys` parameter to prefix suggester names by type in search responses {pull}23080[#23080] (issue: {issue}22965[#22965]) +* Add parsing from xContent to MainResponse {pull}22934[#22934] +* Parse elasticsearch exception's root causes {pull}22924[#22924] +* Add parsing method to BytesRestResponse's error {pull}22873[#22873] +* Add parsing methods to BulkItemResponse {pull}22859[#22859] +* Add parsing method for ElasticsearchException.generateFailureXContent() {pull}22815[#22815] +* Add parsing method for ElasticsearchException.generateThrowableXContent() {pull}22783[#22783] +* Add parsing methods for UpdateResponse {pull}22586[#22586] +* Add parsing from xContent to InternalSearchHit and InternalSearchHits {pull}22429[#22429] +* Add fromxcontent methods to index response {pull}22229[#22229] +* Add fromXContent() methods for ReplicationResponse {pull}22196[#22196] (issue: {issue}22082[#22082]) +* Add parsing method for ElasticsearchException {pull}22143[#22143] +* Add fromXContent method to GetResponse {pull}22082[#22082] + +Java REST Client:: +* move ignore parameter support from yaml test client to low level rest client {pull}22637[#22637] +* Warn log deprecation warnings received from server {pull}21895[#21895] +* Support Preemptive Authentication with RestClient {pull}21336[#21336] +* Provide error message when rest request path is null {pull}21233[#21233] (issue: {issue}21232[#21232]) + +Logging:: +* Log deleting indices at info level {pull}22627[#22627] (issue: {issue}22605[#22605]) +* Expose logs base path {pull}22625[#22625] +* Log failure to connect to node at info instead of debug {pull}21809[#21809] (issue: {issue}6468[#6468]) +* Truncate log messages from the end {pull}21609[#21609] (issue: {issue}21602[#21602]) +* Ensure logging is initialized in CLI tools {pull}20575[#20575] +* Give useful error message if log config is missing {pull}20493[#20493] +* Complete Elasticsearch logger names {pull}20457[#20457] (issue: {issue}20326[#20326]) +* Logging shutdown hack {pull}20389[#20389] (issue: {issue}20304[#20304]) +* Disable console logging {pull}20387[#20387] +* Warn on not enough masters during election {pull}20063[#20063] (issue: {issue}8362[#8362]) + +Mapping:: +* Only allow one type on 6.0 indices {pull}24317[#24317] (issue: {issue}15613[#15613]) +* token_count type : add an option to count tokens (fix #23227) {pull}24175[#24175] (issue: {issue}23227[#23227]) +* Atomic mapping updates across types {pull}22220[#22220] +* Only update DocumentMapper if field type changes {pull}22165[#22165] +* Better error message when _parent isn't an object {pull}21987[#21987] +* Create the QueryShardContext lazily in DocumentMapperParser. {pull}21287[#21287] + +Nested Docs:: +* Avoid adding unnecessary nested filters when ranges are used. {pull}23427[#23427] + +Network:: +* Set available processors for Netty {pull}24420[#24420] (issue: {issue}6224[#6224]) +* Adjust default Netty receive predictor size to 64k {pull}23542[#23542] (issue: {issue}23185[#23185]) +* Keep the pipeline handler queue small initially {pull}23335[#23335] +* Set network receive predictor size to 32kb {pull}23284[#23284] (issue: {issue}23185[#23185]) +* TransportService.connectToNode should validate remote node ID {pull}22828[#22828] (issue: {issue}22194[#22194]) +* Disable the Netty recycler {pull}22452[#22452] (issues: {issue}22189[#22189], {issue}22360[#22360], {issue}22406[#22406], {issue}5904[#5904]) +* Tell Netty not to be unsafe in transport client {pull}22284[#22284] +* Introduce a low level protocol handshake {pull}22094[#22094] +* Detach handshake from connect to node {pull}22037[#22037] +* Reduce number of connections per node depending on the nodes role {pull}21849[#21849] +* Add a connect timeout to the ConnectionProfile to allow per node connect timeouts {pull}21847[#21847] (issue: {issue}19719[#19719]) +* Grant Netty permission to read system somaxconn {pull}21840[#21840] +* Remove connectToNodeLight and replace it with a connection profile {pull}21799[#21799] +* Lazy resolve unicast hosts {pull}21630[#21630] (issues: {issue}14441[#14441], {issue}16412[#16412]) +* Fix handler name on message not fully read {pull}21478[#21478] +* Handle rejected pings on shutdown gracefully {pull}20842[#20842] +* Network: Allow to listen on virtual interfaces. {pull}19568[#19568] (issues: {issue}17473[#17473], {issue}19537[#19537]) + +Packaging:: +* Introduce Java version check {pull}23194[#23194] (issue: {issue}21102[#21102]) +* Improve the out-of-the-box experience {pull}21920[#21920] (issues: {issue}18317[#18317], {issue}21783[#21783]) +* Add empty plugins dir for archive distributions {pull}21204[#21204] (issue: {issue}20342[#20342]) +* Make explicit missing settings for Windows service {pull}21200[#21200] (issue: {issue}18317[#18317]) +* Change permissions on config files {pull}20966[#20966] +* Add quiet option to disable console logging {pull}20422[#20422] (issues: {issue}15315[#15315], {issue}16159[#16159], {issue}17220[#17220]) + +Percolator:: +* Allowing range queries with now ranges inside percolator queries {pull}23921[#23921] (issue: {issue}23859[#23859]) +* Add term extraction support for MultiPhraseQuery {pull}23176[#23176] + +Plugin Discovery EC2:: +* Settings: Migrate ec2 discovery sensitive settings to elasticsearch keystore {pull}23961[#23961] (issue: {issue}22475[#22475]) +* Add support for ca-central-1 region to EC2 and S3 plugins {pull}22458[#22458] (issue: {issue}22454[#22454]) +* Support for eu-west-2 (London) cloud-aws plugin {pull}22308[#22308] (issue: {issue}22306[#22306]) +* Add us-east-2 AWS region {pull}21961[#21961] (issue: {issue}21881[#21881]) +* Add setting to set read timeout for EC2 discovery and S3 repository plugins {pull}21956[#21956] (issue: {issue}19078[#19078]) + +Plugin Ingest GeoIp:: +* Cache results of geoip lookups {pull}22231[#22231] (issue: {issue}22074[#22074]) + +Plugin Lang Painless:: +* Allow painless to load stored fields {pull}24290[#24290] +* Start on custom whitelists for Painless {pull}23563[#23563] +* Fix Painless's implementation of interfaces returning primitives {pull}23298[#23298] (issue: {issue}22983[#22983]) +* Allow painless to implement more interfaces {pull}22983[#22983] +* Generate reference links for painless API {pull}22775[#22775] +* Painless: Add augmentation to String for base 64 {pull}22665[#22665] (issue: {issue}22648[#22648]) +* Improve painless's ScriptException generation {pull}21762[#21762] (issue: {issue}21733[#21733]) +* Add Debug.explain to painless {pull}21723[#21723] (issue: {issue}20263[#20263]) +* Implement the ?: operator in painless {pull}21506[#21506] +* In painless suggest a long constant if int won't do {pull}21415[#21415] (issue: {issue}21313[#21313]) +* Support decimal constants with trailing [dD] in painless {pull}21412[#21412] (issue: {issue}21116[#21116]) +* Implement reading from null safe dereferences {pull}21239[#21239] +* Painless negative offsets {pull}21080[#21080] (issue: {issue}20870[#20870]) +* Remove more equivalents of the now method from the Painless whitelist. {pull}21047[#21047] +* Disable regexes by default in painless {pull}20427[#20427] (issue: {issue}20397[#20397]) + +Plugin Repository Azure:: +* Add Backoff policy to azure repository {pull}23387[#23387] (issue: {issue}22728[#22728]) + +Plugin Repository S3:: +* Removes the retry mechanism from the S3 blob store {pull}23952[#23952] (issue: {issue}22845[#22845]) +* S3 Repository: Eagerly load static settings {pull}23910[#23910] +* S3 repository: Add named configurations {pull}22762[#22762] (issues: {issue}22479[#22479], {issue}22520[#22520]) +* Make the default S3 buffer size depend on the available memory. {pull}21299[#21299] + +Plugins:: +* Plugins: Add support for platform specific plugins {pull}24265[#24265] +* Plugins: Remove leniency for missing plugins dir {pull}24173[#24173] +* Modify permissions dialog for plugins {pull}23742[#23742] +* Plugins: Add plugin cli specific exit codes {pull}23599[#23599] (issue: {issue}15295[#15295]) +* Plugins: Output better error message when existing plugin is incompatible {pull}23562[#23562] (issue: {issue}20691[#20691]) +* Add the ability to define search response listeners in search plugin {pull}22682[#22682] +* Pass ThreadContext to transport interceptors to allow header modification {pull}22618[#22618] (issue: {issue}22585[#22585]) +* Provide helpful error message if a plugin exists {pull}22305[#22305] (issue: {issue}22084[#22084]) +* Add shutdown hook for closing CLI commands {pull}22126[#22126] (issue: {issue}22111[#22111]) +* Allow plugins to install bootstrap checks {pull}22110[#22110] +* Clarify that plugins can be closed {pull}21669[#21669] +* Plugins: Convert custom discovery to pull based plugin {pull}21398[#21398] +* Removing plugin that isn't installed shouldn't trigger usage information {pull}21272[#21272] (issue: {issue}21250[#21250]) +* Remove pluggability of ZenPing {pull}21049[#21049] +* Make UnicastHostsProvider extension pull based {pull}21036[#21036] +* Revert "Display plugins versions" {pull}20807[#20807] (issues: {issue}18683[#18683], {issue}20668[#20668]) +* Provide error message when plugin id is missing {pull}20660[#20660] + +Query DSL:: +* Make it possible to validate a query on all shards instead of a single random shard {pull}23697[#23697] (issue: {issue}18254[#18254]) +* QueryString and SimpleQueryString Graph Support {pull}22541[#22541] +* Additional Graph Support in Match Query {pull}22503[#22503] (issue: {issue}22490[#22490]) +* RangeQuery WITHIN case now normalises query {pull}22431[#22431] (issue: {issue}22412[#22412]) +* Un-deprecate fuzzy query {pull}22088[#22088] (issue: {issue}15760[#15760]) +* support numeric bounds with decimal parts for long/integer/short/byte datatypes {pull}21972[#21972] (issue: {issue}21600[#21600]) +* Using ObjectParser in MatchAllQueryBuilder and IdsQueryBuilder {pull}21273[#21273] +* Expose splitOnWhitespace in `Query String Query` {pull}20965[#20965] (issue: {issue}20841[#20841]) +* Throw error if query element doesn't end with END_OBJECT {pull}20528[#20528] (issue: {issue}20515[#20515]) +* Remove `lowercase_expanded_terms` and `locale` from query-parser options. {pull}20208[#20208] (issue: {issue}9978[#9978]) + +REST:: +* Allow passing single scrollID in clear scroll API body {pull}24242[#24242] (issue: {issue}24233[#24233]) +* Validate top-level keys when parsing mget requests {pull}23746[#23746] (issue: {issue}23720[#23720]) +* Cluster stats should not render empty http/transport types {pull}23735[#23735] +* Add parameter to prefix aggs name with type in search responses {pull}22965[#22965] +* Add a REST spec for the create API {pull}20924[#20924] +* Add response params to REST params did you mean {pull}20753[#20753] (issues: {issue}20722[#20722], {issue}20747[#20747]) +* Add did you mean to strict REST params {pull}20747[#20747] (issue: {issue}20722[#20722]) + +Reindex API:: +* Increase visibility of doExecute so it can be used directly {pull}22614[#22614] +* Improve error message when reindex-from-remote gets bad json {pull}22536[#22536] (issue: {issue}22330[#22330]) +* Reindex: Better error message for pipeline in wrong place {pull}21985[#21985] +* Timeout improvements for rest client and reindex {pull}21741[#21741] (issue: {issue}21707[#21707]) +* Add "simple match" support for reindex-from-remote whitelist {pull}21004[#21004] +* Make reindex-from-remote ignore unknown fields {pull}20591[#20591] (issue: {issue}20504[#20504]) + +Scripting:: +* Expose multi-valued dates to scripts and document painless's date functions {pull}22875[#22875] (issue: {issue}22162[#22162]) +* Wrap VerifyError in ScriptException {pull}21769[#21769] +* Log ScriptException's xcontent if file script compilation fails {pull}21767[#21767] (issue: {issue}21733[#21733]) +* Support binary field type in script values {pull}21484[#21484] (issue: {issue}14469[#14469]) +* Mustache: Add {{#url}}{{/url}} function to URL encode strings {pull}20838[#20838] +* Expose `ctx._now` in update scripts {pull}20835[#20835] (issue: {issue}17895[#17895]) + +Search:: +* Remove leniency when merging fetched hits in a search response phase {pull}24158[#24158] +* Set shard count limit to unlimited {pull}24012[#24012] +* Streamline shard index availability in all SearchPhaseResults {pull}23788[#23788] +* Search took time should use a relative clock {pull}23662[#23662] +* Prevent negative `from` parameter in SearchSourceBuilder {pull}23358[#23358] (issue: {issue}23324[#23324]) +* Remove unnecessary result sorting in SearchPhaseController {pull}23321[#23321] +* Expose `batched_reduce_size` via `_search` {pull}23288[#23288] (issue: {issue}23253[#23253]) +* Adding fromXContent to Suggest and Suggestion class {pull}23226[#23226] (issue: {issue}23202[#23202]) +* Adding fromXContent to Suggestion.Entry and subclasses {pull}23202[#23202] +* Add CollapseSearchPhase as a successor for the FetchSearchPhase {pull}23165[#23165] +* Integrate IndexOrDocValuesQuery. {pull}23119[#23119] +* Detach SearchPhases from AbstractSearchAsyncAction {pull}23118[#23118] +* Fix GraphQuery expectation after Lucene upgrade to 6.5 {pull}23117[#23117] (issue: {issue}23102[#23102]) +* Nested queries should avoid adding unnecessary filters when possible. {pull}23079[#23079] (issue: {issue}20797[#20797]) +* Add xcontent parsing to completion suggestion option {pull}23071[#23071] +* Add xcontent parsing to suggestion options {pull}23018[#23018] +* Separate reduce (aggs, suggest and profile) from merging fetched hits {pull}23017[#23017] +* Add a setting to disable remote cluster connections on a node {pull}23005[#23005] +* First step towards separating individual search phases {pull}22802[#22802] +* Add parsing from xContent to SearchProfileShardResults and nested classes {pull}22649[#22649] +* Move SearchTransportService and SearchPhaseController creation outside of TransportSearchAction constructor {pull}21754[#21754] +* Don't carry ShardRouting around when not needed in AbstractSearchAsyncAction {pull}21753[#21753] +* ShardSearchRequest to take ShardId constructor argument rather than the whole ShardRouting {pull}21750[#21750] +* Use index uuid as key in the alias filter map rather than the index name {pull}21749[#21749] +* Add indices and filter information to search shards api output {pull}21738[#21738] (issue: {issue}20916[#20916]) +* remove pointless catch exception in TransportSearchAction {pull}21689[#21689] +* Optimize query with types filter in the URL (t/t/_search) {pull}20979[#20979] +* Makes search action cancelable by task management API {pull}20405[#20405] + +Search Templates:: +* Add profile and explain parameters to template API {pull}20451[#20451] + +Settings:: +* Add secure file setting to keystore {pull}24001[#24001] +* Add a setting which specifies a list of setting {pull}23883[#23883] +* Add a property to mark setting as final {pull}23872[#23872] +* Remove obsolete index setting `index.version.minimum_compatible`. {pull}23593[#23593] +* Provide a method to retrieve a closeable char[] from a SecureString {pull}23389[#23389] +* Update indices settings api to support CBOR and SMILE format {pull}23309[#23309] (issues: {issue}23242[#23242], {issue}23245[#23245]) +* Improve setting deprecation message {pull}23156[#23156] (issue: {issue}22849[#22849]) +* Add secure settings validation on startup {pull}22894[#22894] +* Allow comma delimited array settings to have a space after each entry {pull}22591[#22591] (issue: {issue}22297[#22297]) +* Allow affix settings to be dynamic / updatable {pull}22526[#22526] +* Allow affix settings to delegate to actual settings {pull}22523[#22523] +* Make s3 repository sensitive settings use secure settings {pull}22479[#22479] +* Speed up filter and prefix settings operations {pull}22249[#22249] +* Add precise logging on unknown or invalid settings {pull}20951[#20951] (issue: {issue}20946[#20946]) + +Snapshot/Restore:: +* Ensure every repository has an incompatible-snapshots blob {pull}24403[#24403] (issue: {issue}22267[#22267]) +* Change snapshot status error to use generic SnapshotException {pull}24355[#24355] (issue: {issue}24225[#24225]) +* Duplicate snapshot name throws InvalidSnapshotNameException {pull}22921[#22921] (issue: {issue}18228[#18228]) +* Fixes retrieval of the latest snapshot index blob {pull}22700[#22700] +* Use general cluster state batching mechanism for snapshot state updates {pull}22528[#22528] (issue: {issue}14899[#14899]) +* Synchronize snapshot deletions on the cluster state {pull}22313[#22313] (issue: {issue}19957[#19957]) +* Abort snapshots on a node that leaves the cluster {pull}21084[#21084] (issue: {issue}20876[#20876]) + +Stats:: +* Show JVM arguments {pull}24450[#24450] +* Add cross-cluster search remote cluster info API {pull}23969[#23969] (issue: {issue}23925[#23925]) +* Add geo_point to FieldStats {pull}21947[#21947] (issue: {issue}20707[#20707]) +* Include unindexed field in FieldStats response {pull}21821[#21821] (issue: {issue}21952[#21952]) +* Remove load average leniency {pull}21380[#21380] +* Strengthen handling of unavailable cgroup stats {pull}21094[#21094] (issue: {issue}21029[#21029]) +* Add basic cgroup CPU metrics {pull}21029[#21029] + +Suggesters:: +* Provide informative error message in case of unknown suggestion context. {pull}24241[#24241] +* Allow different data types for category in Context suggester {pull}23491[#23491] (issue: {issue}22358[#22358]) + +Task Manager:: +* Allow task to be unregistered by ClusterStateApplier {pull}23931[#23931] +* Limit IndexRequest toString() length {pull}22832[#22832] +* Improve the error message if task and node isn't found {pull}22062[#22062] (issue: {issue}22027[#22027]) +* Add descriptions to create snapshot and restore snapshot tasks. {pull}21901[#21901] (issue: {issue}21768[#21768]) +* Add proper descriptions to reindex, update-by-query and delete-by-query tasks. {pull}21841[#21841] (issue: {issue}21768[#21768]) +* Add search task descriptions {pull}21740[#21740] + +Tribe Node:: +* Add support for merging custom meta data in tribe node {pull}21552[#21552] (issues: {issue}20544[#20544], {issue}20791[#20791], {issue}9372[#9372]) + + + +[[bug-6.0.0-alpha1-5x]] +[float] +=== Bug fixes + +Aggregations:: +* InternalPercentilesBucket should not rely on ordered percents array {pull}24336[#24336] (issue: {issue}24331[#24331]) +* Align behavior HDR percentiles iterator with percentile() method {pull}24206[#24206] +* The `filter` and `significant_terms` aggregations should parse the `filter` as a filter, not a query. {pull}23797[#23797] +* Completion suggestion should also consider text if prefix/regex is missing {pull}23451[#23451] (issue: {issue}23340[#23340]) +* Fixes the per term error in the terms aggregation {pull}23399[#23399] +* Fixes terms error count for multiple reduce phases {pull}23291[#23291] (issue: {issue}23286[#23286]) +* Fix scaled_float numeric type in aggregations {pull}22351[#22351] (issue: {issue}22350[#22350]) +* Allow terms aggregations on pure boolean scripts. {pull}22201[#22201] (issue: {issue}20941[#20941]) +* Fix numeric terms aggregations with includes/excludes and minDocCount=0 {pull}22141[#22141] (issue: {issue}22140[#22140]) +* Fix `missing` on aggs on `boolean` fields. {pull}22135[#22135] (issue: {issue}22009[#22009]) +* IP range masks exclude the maximum address of the range. {pull}22018[#22018] (issue: {issue}22005[#22005]) +* Fix `other_bucket` on the `filters` agg to be enabled if a key is set. {pull}21994[#21994] (issue: {issue}21951[#21951]) +* Rewrite Queries/Filter in FilterAggregationBuilder and ensure client usage marks query as non-cachable {pull}21303[#21303] (issue: {issue}21301[#21301]) +* Percentiles bucket fails for 100th percentile {pull}21218[#21218] +* Thread safety for scripted significance heuristics {pull}21113[#21113] (issue: {issue}18120[#18120]) +* `ip_range` aggregation should accept null bounds. {pull}21043[#21043] (issue: {issue}21006[#21006]) +* Fixes bug preventing script sort working on top_hits aggregation {pull}21023[#21023] (issue: {issue}21022[#21022]) +* Fixed writeable name from range to geo_distance {pull}20860[#20860] +* Fix date_range aggregation to not cache if now is used {pull}20740[#20740] +* The `top_hits` aggregation should compile scripts only once. {pull}20738[#20738] + +Allocation:: +* Cannot force allocate primary to a node where the shard already exists {pull}22031[#22031] (issue: {issue}22021[#22021]) +* Promote shadow replica to primary when initializing primary fails {pull}22021[#22021] +* Trim in-sync allocations set only when it grows {pull}21976[#21976] (issue: {issue}21719[#21719]) +* Allow master to assign primary shard to node that has shard store locked during shard state fetching {pull}21656[#21656] (issue: {issue}19416[#19416]) +* Keep a shadow replicas' allocation id when it is promoted to primary {pull}20863[#20863] (issue: {issue}20650[#20650]) +* IndicesClusterStateService should clean local started when re-assigns an initializing shard with the same aid {pull}20687[#20687] +* IndexRoutingTable.initializeEmpty shouldn't override supplied primary RecoverySource {pull}20638[#20638] (issue: {issue}20637[#20637]) +* Update incoming recoveries stats when shadow replica is reinitialized {pull}20612[#20612] +* `index.routing.allocation.initial_recovery` limits replica allocation {pull}20589[#20589] + +Analysis:: +* AsciiFoldingFilter's multi-term component should never preserve the original token. {pull}21982[#21982] +* Pre-built analysis factories do not implement MultiTermAware correctly. {pull}21981[#21981] +* Can load non-PreBuiltTokenFilter in Analyze API {pull}20396[#20396] +* Named analyzer should close the analyzer that it wraps {pull}20197[#20197] + +Bulk:: +* Reject empty IDs {pull}24118[#24118] (issue: {issue}24116[#24116]) + +CAT API:: +* Consume `full_id` request parameter early {pull}21270[#21270] (issue: {issue}21266[#21266]) + +CRUD:: +* Reject external versioning and explicit version numbers on create {pull}21998[#21998] +* MultiGet should not fail entirely if alias resolves to many indices {pull}20858[#20858] (issue: {issue}20845[#20845]) +* Fixed date math expression support in multi get requests. {pull}20659[#20659] (issue: {issue}17957[#17957]) + +Cache:: +* Invalidate cached query results if query timed out {pull}22807[#22807] (issue: {issue}22789[#22789]) +* Fix the request cache keys to not hold references to the SearchContext. {pull}21284[#21284] +* Prevent requests that use scripts or now() from being cached {pull}20750[#20750] (issue: {issue}20645[#20645]) + +Circuit Breakers:: +* ClusterState publishing shouldn't trigger circuit breakers {pull}20986[#20986] (issues: {issue}20827[#20827], {issue}20960[#20960]) + +Cluster:: +* Don't set local node on cluster state used for node join validation {pull}23311[#23311] (issues: {issue}21830[#21830], {issue}3[#3], {issue}4[#4], {issue}6[#6], {issue}9[#9]) +* Allow a cluster state applier to create an observer and wait for a better state {pull}23132[#23132] (issue: {issue}21817[#21817]) +* Cluster allocation explain to never return empty response body {pull}23054[#23054] +* IndicesService handles all exceptions during index deletion {pull}22433[#22433] +* Remove cluster update task when task times out {pull}21578[#21578] (issue: {issue}21568[#21568]) + +Core:: +* Check for default.path.data included in path.data {pull}24285[#24285] (issue: {issue}24283[#24283]) +* Improve performance of extracting warning value {pull}24114[#24114] (issue: {issue}24018[#24018]) +* Reject duplicate settings on the command line {pull}24053[#24053] +* Restrict build info loading to ES jar, not any jar {pull}24049[#24049] (issue: {issue}21955[#21955]) +* Streamline foreign stored context restore and allow to perserve response headers {pull}22677[#22677] (issue: {issue}22647[#22647]) +* Support negative numbers in readVLong {pull}22314[#22314] +* Add a StreamInput#readArraySize method that ensures sane array sizes {pull}21697[#21697] +* Use a buffer to do character to byte conversion in StreamOutput#writeString {pull}21680[#21680] (issue: {issue}21660[#21660]) +* Fix ShardInfo#toString {pull}21319[#21319] +* Protect BytesStreamOutput against overflows of the current number of written bytes. {pull}21174[#21174] (issue: {issue}21159[#21159]) +* Return target index name even if _rollover conditions are not met {pull}21138[#21138] +* .es_temp_file remains after system crash, causing it not to start again {pull}21007[#21007] (issue: {issue}20992[#20992]) +* StoreStatsCache should also ignore AccessDeniedException when checking file size {pull}20790[#20790] (issue: {issue}17580[#17580]) + +Dates:: +* Fix time zone rounding edge case for DST overlaps {pull}21550[#21550] (issue: {issue}20833[#20833]) + +Discovery:: +* ZenDiscovery - only validate min_master_nodes values if local node is master {pull}23915[#23915] (issue: {issue}23695[#23695]) +* Close InputStream when receiving cluster state in PublishClusterStateAction {pull}22711[#22711] +* Do not reply to pings from another cluster {pull}21894[#21894] (issue: {issue}21874[#21874]) +* Add current cluster state version to zen pings and use them in master election {pull}20384[#20384] (issue: {issue}20348[#20348]) + +Engine:: +* Close and flush refresh listeners on shard close {pull}22342[#22342] +* Die with dignity on the Lucene layer {pull}21721[#21721] (issue: {issue}19272[#19272]) +* Fix `InternalEngine#isThrottled` to not always return `false`. {pull}21592[#21592] +* Retrying replication requests on replica doesn't call `onRetry` {pull}21189[#21189] (issue: {issue}20211[#20211]) +* Take refresh IOExceptions into account when catching ACE in InternalEngine {pull}20546[#20546] (issue: {issue}19975[#19975]) + +Exceptions:: +* Stop returning "es." internal exception headers as http response headers {pull}22703[#22703] (issue: {issue}17593[#17593]) +* Fixing shard recovery error message to report the number of docs correctly for each node {pull}22515[#22515] (issue: {issue}21893[#21893]) + +Highlighting:: +* Fix FiltersFunctionScoreQuery highlighting {pull}21827[#21827] +* Fix highlighting on a stored keyword field {pull}21645[#21645] (issue: {issue}21636[#21636]) +* Fix highlighting of MultiTermQuery within a FunctionScoreQuery {pull}20400[#20400] (issue: {issue}20392[#20392]) + +Index APIs:: +* Fixes restore of a shrunken index when initial recovery node is gone {pull}24322[#24322] (issue: {issue}24257[#24257]) +* Honor update request timeout {pull}23825[#23825] +* Ensure shrunk indices carry over version information from its source {pull}22469[#22469] (issue: {issue}22373[#22373]) +* Validate the `_rollover` target index name early to also fail if dry_run=true {pull}21330[#21330] (issue: {issue}21149[#21149]) +* Only negate index expression on all indices with preceding wildcard {pull}20898[#20898] (issues: {issue}19800[#19800], {issue}20033[#20033]) +* Fix IndexNotFoundException in multi index search request. {pull}20188[#20188] (issue: {issue}3839[#3839]) + +Index Templates:: +* Fix integer overflows when dealing with templates. {pull}21628[#21628] (issue: {issue}21622[#21622]) + +Ingest:: +* Improve missing ingest processor error {pull}23379[#23379] (issue: {issue}23392[#23392]) +* update _ingest.timestamp to use new ZonedDateTime {pull}23174[#23174] (issue: {issue}23168[#23168]) +* fix date-processor to a new default year for every new pipeline execution {pull}22601[#22601] (issue: {issue}22547[#22547]) +* fix index out of bounds error in KV Processor {pull}22288[#22288] (issue: {issue}22272[#22272]) +* Fixes GrokProcessor's ignorance of named-captures with same name. {pull}22131[#22131] (issue: {issue}22117[#22117]) +* fix trace_match behavior for when there is only one grok pattern {pull}21413[#21413] (issue: {issue}21371[#21371]) +* Stored scripts and ingest node configurations should be included into a snapshot {pull}21227[#21227] (issue: {issue}21184[#21184]) +* make painless the default scripting language for ScriptProcessor {pull}20981[#20981] (issue: {issue}20943[#20943]) +* no null values in ingest configuration error messages {pull}20616[#20616] +* JSON Processor was not properly added {pull}20613[#20613] + +Inner Hits:: +* Replace NestedChildrenQuery with ParentChildrenBlockJoinQuery {pull}24016[#24016] (issue: {issue}24009[#24009]) +* Changed DisMaxQueryBuilder to extract inner hits from leaf queries {pull}23512[#23512] (issue: {issue}23482[#23482]) +* Inner hits and ignore unmapped {pull}21693[#21693] (issue: {issue}21620[#21620]) +* Skip adding a parent field to nested documents. {pull}21522[#21522] (issue: {issue}21503[#21503]) + +Internal:: +* Add infrastructure to mark contexts as system contexts {pull}23830[#23830] +* Always restore the ThreadContext for operations delayed due to a block {pull}23349[#23349] +* Index creation and setting update may not return deprecation logging {pull}22702[#22702] +* Rethrow ExecutionException from the loader to concurrent callers of Cache#computeIfAbsent {pull}21549[#21549] +* Restore thread's original context before returning to the ThreadPool {pull}21411[#21411] +* Fix NPE in SearchContext.toString() {pull}21069[#21069] +* Prevent AbstractArrays from release bytes more than once {pull}20819[#20819] +* Source filtering should treat dots in field names as sub objects. {pull}20736[#20736] (issue: {issue}20719[#20719]) +* IndicesAliasesRequest should not implement CompositeIndicesRequest {pull}20726[#20726] +* Ensure elasticsearch doesn't start with unuspported indices {pull}20514[#20514] (issue: {issue}20512[#20512]) + +Java API:: +* Don't output empty ext object in SearchSourceBuilder#toXContent {pull}22093[#22093] (issue: {issue}20969[#20969]) +* Transport client: Fix remove address to actually work {pull}21743[#21743] +* Add a HostFailureListener to notify client code if a node got disconnected {pull}21709[#21709] (issue: {issue}21424[#21424]) +* Fix InternalSearchHit#hasSource to return the proper boolean value {pull}21441[#21441] (issue: {issue}21419[#21419]) +* Null checked for source when calling sourceRef {pull}21431[#21431] (issue: {issue}19279[#19279]) +* ClusterAdminClient.prepareDeletePipeline method should accept pipeline id to delete {pull}21228[#21228] +* fix IndexResponse#toString to print out shards info {pull}20562[#20562] + +Java High Level REST Client:: +* Correctly parse BulkItemResponse.Failure's status {pull}23432[#23432] + +Java REST Client:: +* Make buffer limit configurable in HeapBufferedConsumerFactory {pull}23970[#23970] (issue: {issue}23958[#23958]) +* RestClient asynchronous execution should not throw exceptions {pull}23307[#23307] +* Don't use null charset in RequestLogger {pull}22197[#22197] (issue: {issue}22190[#22190]) +* Rest client: don't reuse the same HttpAsyncResponseConsumer across multiple retries {pull}21378[#21378] + +Logging:: +* Do not prematurely shutdown Log4j {pull}21519[#21519] (issue: {issue}21514[#21514]) +* Assert status logger does not warn on Log4j usage {pull}21339[#21339] +* Fix logger names for Netty {pull}21223[#21223] (issue: {issue}20457[#20457]) +* Fix logger when you can not create an azure storage client {pull}20670[#20670] (issues: {issue}20633[#20633], {issue}20669[#20669]) +* Avoid unnecessary creation of prefix loggers {pull}20571[#20571] (issue: {issue}20570[#20570]) +* Fix logging hierarchy configs {pull}20463[#20463] +* Fix prefix logging {pull}20429[#20429] + +Mapping:: +* Preserve response headers when creating an index {pull}23950[#23950] (issue: {issue}23947[#23947]) +* Improves disabled fielddata error message {pull}23841[#23841] (issue: {issue}22768[#22768]) +* Fix MapperService StackOverflowError {pull}23605[#23605] (issue: {issue}23604[#23604]) +* Fix NPE with scaled floats stats when field is not indexed {pull}23528[#23528] (issue: {issue}23487[#23487]) +* Range types causing `GetFieldMappingsIndexRequest` to fail due to `NullPointerException` in `RangeFieldMapper.doXContentBody` when `include_defaults=true` is on the query string {pull}22925[#22925] +* Disallow introducing illegal object mappings (double '..') {pull}22891[#22891] (issue: {issue}22794[#22794]) +* The `_all` default mapper is not completely configured. {pull}22236[#22236] +* Fix MapperService.allEnabled(). {pull}22227[#22227] +* Dynamic `date` fields should use the `format` that was used to detect it is a date. {pull}22174[#22174] (issue: {issue}9410[#9410]) +* Sub-fields should not accept `include_in_all` parameter {pull}21971[#21971] (issue: {issue}21710[#21710]) +* Mappings: Fix get mapping when no indexes exist to not fail in response generation {pull}21924[#21924] (issue: {issue}21916[#21916]) +* Fail to index fields with dots in field names when one of the intermediate objects is nested. {pull}21787[#21787] (issue: {issue}21726[#21726]) +* Uncommitted mapping updates should not efect existing indices {pull}21306[#21306] (issue: {issue}21189[#21189]) + +Nested Docs:: +* Fix bug in query builder rewrite that ignores the ignore_unmapped option {pull}22456[#22456] + +Network:: +* Respect promises on pipelined responses {pull}23317[#23317] (issues: {issue}23310[#23310], {issue}23322[#23322]) +* Ensure that releasing listener is called {pull}23310[#23310] +* Pass `forceExecution` flag to transport interceptor {pull}22739[#22739] +* Ensure new connections won't be opened if transport is closed or closing {pull}22589[#22589] (issue: {issue}22554[#22554]) +* Prevent open channel leaks if handshake times out or is interrupted {pull}22554[#22554] +* Execute low level handshake in #openConnection {pull}22440[#22440] +* Handle connection close / reset events gracefully during handshake {pull}22178[#22178] +* Do not lose host information when pinging {pull}21939[#21939] (issue: {issue}21828[#21828]) +* DiscoveryNode and TransportAddress should preserve host information {pull}21828[#21828] +* Die with dignity on the network layer {pull}21720[#21720] (issue: {issue}19272[#19272]) +* Fix connection close header handling {pull}20956[#20956] (issue: {issue}20938[#20938]) +* Ensure port range is readable in the exception message {pull}20893[#20893] +* Prevent double release in TcpTransport if send listener throws an exception {pull}20880[#20880] + +Packaging:: +* Fall back to non-atomic move when removing plugins {pull}23548[#23548] (issue: {issue}35[#35]) +* Another fix for handling of paths on Windows {pull}22132[#22132] (issue: {issue}21921[#21921]) +* Fix handling of spaces in Windows paths {pull}21921[#21921] (issues: {issue}20809[#20809], {issue}21525[#21525]) +* Add option to skip kernel parameters on install {pull}21899[#21899] (issue: {issue}21877[#21877]) +* Set vm.max_map_count on systemd package install {pull}21507[#21507] +* Export ES_JVM_OPTIONS for SysV init {pull}21445[#21445] (issue: {issue}21255[#21255]) +* Debian: configure start-stop-daemon to not go into background {pull}21343[#21343] (issues: {issue}12716[#12716], {issue}21300[#21300]) +* Generate POM files with non-wildcard excludes {pull}21234[#21234] (issue: {issue}21170[#21170]) +* [Packaging] Do not remove scripts directory on upgrade {pull}20452[#20452] +* [Package] Remove bin/lib/modules directories on RPM uninstall/upgrade {pull}20448[#20448] + +Parent/Child:: +* Add null check in case of orphan child document {pull}22772[#22772] (issue: {issue}22770[#22770]) + +Percolator:: +* Fix memory leak when percolator uses bitset or field data cache {pull}24115[#24115] (issue: {issue}24108[#24108]) +* Fix NPE in percolator's 'now' range check for percolator queries with range queries {pull}22356[#22356] (issue: {issue}22355[#22355]) + +Plugin Analysis Stempel:: +* Fix thread safety of Stempel's token filter factory {pull}22610[#22610] (issue: {issue}21911[#21911]) + +Plugin Discovery EC2:: +* Fix ec2 discovery when used with IAM profiles. {pull}21048[#21048] (issue: {issue}21039[#21039]) + +Plugin Ingest GeoIp:: +* [ingest-geoip] update geoip to not include null-valued results from {pull}20455[#20455] + +Plugin Lang Painless:: +* painless: Fix method references to ctor with the new LambdaBootstrap and cleanup code {pull}24406[#24406] +* Fix Painless Lambdas for Java 9 {pull}24070[#24070] (issue: {issue}23473[#23473]) +* Fix painless's regex lexer and error messages {pull}23634[#23634] +* Replace Painless's Cast with casting strategies {pull}23369[#23369] +* Fix Bad Casts In Painless {pull}23282[#23282] (issue: {issue}23238[#23238]) +* Don't allow casting from void to def in painless {pull}22969[#22969] (issue: {issue}22908[#22908]) +* Fix def invoked qualified method refs {pull}22918[#22918] +* Whitelist some ScriptDocValues in painless {pull}22600[#22600] (issue: {issue}22584[#22584]) +* Update Painless Loop Counter to be Higher {pull}22560[#22560] (issue: {issue}22508[#22508]) +* Fix some issues with painless's strings {pull}22393[#22393] (issue: {issue}22372[#22372]) +* Test fix for def equals in Painless {pull}21945[#21945] (issue: {issue}21801[#21801]) +* Fix a VerifyError bug in Painless {pull}21765[#21765] +* Fix Lambdas in Painless to be Able to Use Top-Level Variables Such as params and doc {pull}21635[#21635] (issues: {issue}20869[#20869], {issue}21479[#21479]) +* Fix String Concatenation Bug In Painless {pull}20623[#20623] + +Plugin Repository Azure:: +* Azure blob store's readBlob() method first checks if the blob exists {pull}23483[#23483] (issue: {issue}23480[#23480]) +* Fixes default chunk size for Azure repositories {pull}22577[#22577] (issue: {issue}22513[#22513]) +* readonly on azure repository must be taken into account {pull}22055[#22055] (issues: {issue}22007[#22007], {issue}22053[#22053]) + +Plugin Repository S3:: +* Handle BlobPath's trailing separator case. Add test cases to BlobPathTests.java {pull}23091[#23091] +* Fixes leading forward slash in S3 repository base_path {pull}20861[#20861] + +Plugins:: +* Fix delete of plugin directory on remove plugin {pull}24266[#24266] (issue: {issue}24252[#24252]) +* Use a marker file when removing a plugin {pull}24252[#24252] (issue: {issue}24231[#24231]) +* Remove hidden file leniency from plugin service {pull}23982[#23982] (issue: {issue}12465[#12465]) +* Add check for null pluginName in remove command {pull}22930[#22930] (issue: {issue}22922[#22922]) +* Use sysprop like with es.path.home to pass conf dir {pull}18870[#18870] (issue: {issue}18689[#18689]) + +Query DSL:: +* FuzzyQueryBuilder should error when parsing array of values {pull}23762[#23762] (issue: {issue}23759[#23759]) +* Fix parsing for `max_determinized_states` {pull}22749[#22749] (issue: {issue}22722[#22722]) +* Fix script score function that combines _score and weight {pull}22713[#22713] (issue: {issue}21483[#21483]) +* Fixes date range query using epoch with timezone {pull}21542[#21542] (issue: {issue}21501[#21501]) +* Allow overriding all-field leniency when `lenient` option is specified {pull}21504[#21504] (issues: {issue}20925[#20925], {issue}21341[#21341]) +* Max score should be updated when a rescorer is used {pull}20977[#20977] (issue: {issue}20651[#20651]) +* Fixes MultiMatchQuery so that it doesn't provide a null context {pull}20882[#20882] +* Fix silently accepting malformed queries {pull}20515[#20515] (issue: {issue}20500[#20500]) +* Fix match_phrase_prefix query with single term on _all field {pull}20471[#20471] (issue: {issue}20470[#20470]) + +REST:: +* [API] change wait_for_completion default according to docs {pull}23672[#23672] +* Deprecate request_cache for clear-cache {pull}23638[#23638] (issue: {issue}22748[#22748]) +* HTTP transport stashes the ThreadContext instead of the RestController {pull}23456[#23456] +* Fix date format in warning headers {pull}23418[#23418] (issue: {issue}23275[#23275]) +* Align REST specs for HEAD requests {pull}23313[#23313] (issue: {issue}21125[#21125]) +* Correct warning header to be compliant {pull}23275[#23275] (issue: {issue}22986[#22986]) +* Fix get HEAD requests {pull}23186[#23186] (issue: {issue}21125[#21125]) +* Handle bad HTTP requests {pull}23153[#23153] (issue: {issue}23034[#23034]) +* Fix get source HEAD requests {pull}23151[#23151] (issue: {issue}21125[#21125]) +* Properly encode location header {pull}23133[#23133] (issues: {issue}21057[#21057], {issue}23115[#23115]) +* Fix template HEAD requests {pull}23130[#23130] (issue: {issue}21125[#21125]) +* Fix index HEAD requests {pull}23112[#23112] (issue: {issue}21125[#21125]) +* Fix alias HEAD requests {pull}23094[#23094] (issue: {issue}21125[#21125]) +* Strict level parsing for indices stats {pull}21577[#21577] (issue: {issue}21024[#21024]) +* The routing query string param is supported by mget but was missing from the rest spec {pull}21357[#21357] +* fix thread_pool_patterns path variable definition {pull}21332[#21332] +* Read indices options in indices upgrade API {pull}21281[#21281] (issue: {issue}21099[#21099]) +* ensure the XContentBuilder is always closed in RestBuilderListener {pull}21124[#21124] +* Add correct Content-Length on HEAD requests {pull}21123[#21123] (issue: {issue}21077[#21077]) +* Make sure HEAD / has 0 Content-Length {pull}21077[#21077] (issue: {issue}21075[#21075]) +* Adds percent-encoding for Location headers {pull}21057[#21057] (issue: {issue}21016[#21016]) +* Whitelist node stats indices level parameter {pull}21024[#21024] (issue: {issue}20722[#20722]) +* Remove lenient URL parameter parsing {pull}20722[#20722] (issue: {issue}14719[#14719]) +* XContentBuilder: Avoid building self-referencing objects {pull}20550[#20550] (issues: {issue}19475[#19475], {issue}20540[#20540]) + +Recovery:: +* Provide target allocation id as part of start recovery request {pull}24333[#24333] (issue: {issue}24167[#24167]) +* Fix primary relocation for shadow replicas {pull}22474[#22474] (issue: {issue}20300[#20300]) +* Don't close store under CancellableThreads {pull}22434[#22434] (issue: {issue}22325[#22325]) +* Use a fresh recovery id when retrying recoveries {pull}22325[#22325] (issue: {issue}22043[#22043]) +* Allow flush/force_merge/upgrade on shard marked as relocated {pull}22078[#22078] (issue: {issue}22043[#22043]) +* Fix concurrency issues between cancelling a relocation and marking shard as relocated {pull}20443[#20443] + +Reindex API:: +* Fix throttled reindex_from_remote {pull}23953[#23953] (issues: {issue}23828[#23828], {issue}23945[#23945]) +* Fix reindex with a remote source on a version before 2.0.0 {pull}23805[#23805] +* Make reindex wait for cleanup before responding {pull}23677[#23677] (issue: {issue}23653[#23653]) +* Reindex: do not log when can't clear old scroll {pull}22942[#22942] (issue: {issue}22937[#22937]) +* Fix reindex-from-remote from <2.0 {pull}22931[#22931] (issue: {issue}22893[#22893]) +* Fix reindex from remote clearing scroll {pull}22525[#22525] (issue: {issue}22514[#22514]) +* Fix source filtering in reindex-from-remote {pull}22514[#22514] (issue: {issue}22507[#22507]) +* Remove content type detection from reindex-from-remote {pull}22504[#22504] (issue: {issue}22329[#22329]) +* Don't close rest client from its callback {pull}22061[#22061] (issue: {issue}22027[#22027]) +* Keep context during reindex's retries {pull}21941[#21941] +* Ignore IllegalArgumentException with assertVersionSerializable {pull}21409[#21409] (issues: {issue}20767[#20767], {issue}21350[#21350]) +* Bump reindex-from-remote's buffer to 200mb {pull}21222[#21222] (issue: {issue}21185[#21185]) +* Fix reindex-from-remote for parent/child from <2.0 {pull}21070[#21070] (issue: {issue}21044[#21044]) + +Scripting:: +* Convert script/template objects to json format internally {pull}23308[#23308] (issue: {issue}23245[#23245]) +* Script: Fix value of `ctx._now` to be current epoch time in milliseconds {pull}23175[#23175] (issue: {issue}23169[#23169]) +* Expose `ip` fields as strings in scripts. {pull}21997[#21997] (issue: {issue}21977[#21977]) +* Add support for booleans in scripts {pull}20950[#20950] (issue: {issue}20949[#20949]) +* Native scripts should be created once per index, not per segment. {pull}20609[#20609] + +Search:: +* Cross Cluster Search: propagate original indices per cluster {pull}24328[#24328] +* Query string default field {pull}24214[#24214] +* Speed up parsing of large `terms` queries. {pull}24210[#24210] +* IndicesQueryCache should delegate the scorerSupplier method. {pull}24209[#24209] +* Disable graph analysis at query time for shingle and cjk filters producing tokens of different size {pull}23920[#23920] (issue: {issue}23918[#23918]) +* Fix cross-cluster remote node gateway attributes {pull}23863[#23863] +* Use a fixed seed for computing term hashCode in TermsSliceQuery {pull}23795[#23795] +* Honor max concurrent searches in multi-search {pull}23538[#23538] (issue: {issue}23527[#23527]) +* Avoid stack overflow in multi-search {pull}23527[#23527] (issue: {issue}23523[#23523]) +* Fix query_string_query to transform "foo:*" in an exists query on the field name {pull}23433[#23433] (issue: {issue}23356[#23356]) +* Factor out filling of TopDocs in SearchPhaseController {pull}23380[#23380] (issues: {issue}19356[#19356], {issue}23357[#23357]) +* Replace blocking calls in ExpandCollapseSearchResponseListener by asynchronous requests {pull}23053[#23053] (issue: {issue}23048[#23048]) +* Ensure fixed serialization order of InnerHitBuilder {pull}22820[#22820] (issue: {issue}22808[#22808]) +* Improve concurrency of ShardCoreKeyMap. {pull}22316[#22316] +* Make `-0` compare less than `+0` consistently. {pull}22173[#22173] (issue: {issue}22167[#22167]) +* Fix boost_mode propagation when the function score query builder is rewritten {pull}22172[#22172] (issue: {issue}22138[#22138]) +* FiltersAggregationBuilder: rewriting filter queries, the same way as in FilterAggregationBuilder {pull}22076[#22076] +* Fix cross_fields type on multi_match query with synonyms {pull}21638[#21638] (issue: {issue}21633[#21633]) +* Fix match_phrase_prefix on boosted fields {pull}21623[#21623] (issue: {issue}21613[#21613]) +* Respect default search timeout {pull}21599[#21599] (issues: {issue}12211[#12211], {issue}21595[#21595]) +* Remove LateParsingQuery to prevent timestamp access after context is frozen {pull}21328[#21328] (issue: {issue}21295[#21295]) +* Make range queries round up upper bounds again. {pull}20582[#20582] (issues: {issue}20579[#20579], {issue}8889[#8889]) +* Throw error when trying to fetch fields from source and source is disabled {pull}20424[#20424] (issues: {issue}20093[#20093], {issue}20408[#20408]) + +Search Templates:: +* No longer add illegal content type option to stored search templates {pull}24251[#24251] (issue: {issue}24227[#24227]) +* SearchTemplateRequest to implement CompositeIndicesRequest {pull}21865[#21865] (issue: {issue}21747[#21747]) + +Settings:: +* Do not set path.data in environment if not set {pull}24132[#24132] (issue: {issue}24099[#24099]) +* Correct handling of default and array settings {pull}24074[#24074] (issues: {issue}23981[#23981], {issue}24052[#24052]) +* Fix merge scheduler config settings {pull}23391[#23391] +* Settings: Fix keystore cli prompting for yes/no to handle console returning null {pull}23320[#23320] +* Expose `search.highlight.term_vector_multi_value` as a node level setting {pull}22999[#22999] +* NPE when no setting name passed to elasticsearch-keystore {pull}22609[#22609] +* Handle spaces in `action.auto_create_index` gracefully {pull}21790[#21790] (issue: {issue}21449[#21449]) +* Fix settings diff generation for affix and group settings {pull}21788[#21788] +* Don't reset non-dynamic settings unless explicitly requested {pull}21646[#21646] (issue: {issue}21593[#21593]) +* Fix Setting.timeValue() method {pull}20696[#20696] (issue: {issue}20662[#20662]) +* Add a hard limit for `index.number_of_shard` {pull}20682[#20682] +* Include complex settings in settings requests {pull}20622[#20622] + +Snapshot/Restore:: +* Fixes maintaining the shards a snapshot is waiting on {pull}24289[#24289] +* Fixes snapshot status on failed snapshots {pull}23833[#23833] (issue: {issue}23716[#23716]) +* Fixes snapshot deletion handling on in-progress snapshot failure {pull}23703[#23703] (issue: {issue}23663[#23663]) +* Prioritize listing index-N blobs over index.latest in reading snapshots {pull}23333[#23333] +* Gracefully handles pre 2.x compressed snapshots {pull}22267[#22267] +* URLRepository should throw NoSuchFileException to correctly adhere to readBlob contract {pull}22069[#22069] (issue: {issue}22004[#22004]) +* Fixes shard level snapshot metadata loading when index-N file is missing {pull}21813[#21813] +* Ensures cleanup of temporary index-* generational blobs during snapshotting {pull}21469[#21469] (issue: {issue}21462[#21462]) +* Fixes get snapshot duplicates when asking for _all {pull}21340[#21340] (issue: {issue}21335[#21335]) + +Stats:: +* Avoid overflow when computing total FS stats {pull}23641[#23641] +* Handle existence of cgroup version 2 hierarchy {pull}23493[#23493] (issue: {issue}23486[#23486]) +* Handle long overflow when adding paths' totals {pull}23293[#23293] (issue: {issue}23093[#23093]) +* Fix control group pattern {pull}23219[#23219] (issue: {issue}23218[#23218]) +* Fix total disk bytes returning negative value {pull}23093[#23093] +* Implement stats for geo_point and geo_shape field {pull}22391[#22391] (issue: {issue}22384[#22384]) +* Use reader for doc stats {pull}22317[#22317] (issue: {issue}22285[#22285]) +* Avoid NPE in NodeService#stats if HTTP is disabled {pull}22060[#22060] (issue: {issue}22058[#22058]) +* Add support for "include_segment_file_sizes" in indices stats REST handler {pull}21879[#21879] (issue: {issue}21878[#21878]) +* Remove output_uuid parameter from cluster stats {pull}21020[#21020] (issue: {issue}20722[#20722]) +* Fix FieldStats deserialization of `ip` field {pull}20522[#20522] (issue: {issue}20516[#20516]) + +Task Manager:: +* Task Management: Make TaskInfo parsing forwards compatible {pull}24073[#24073] (issue: {issue}23250[#23250]) +* Fix hanging cancelling task with no children {pull}22796[#22796] +* Fix broken TaskInfo.toString() {pull}22698[#22698] (issue: {issue}22387[#22387]) +* Task cancellation command should wait for all child nodes to receive cancellation request before returning {pull}21397[#21397] (issue: {issue}21126[#21126]) + +Term Vectors:: +* Fix _termvectors with preference to not hit NPE {pull}21959[#21959] +* Return correct term statistics when a field is not found in a shard {pull}21922[#21922] (issue: {issue}21906[#21906]) + +Tribe Node:: +* Add socket permissions for tribe nodes {pull}21546[#21546] (issues: {issue}16392[#16392], {issue}21122[#21122]) + + + +[[regression-6.0.0-alpha1-5x]] +[float] +=== Regressions + +Bulk:: +* Fix _bulk response when it can't create an index {pull}24048[#24048] (issues: {issue}22488[#22488], {issue}24028[#24028]) + +Core:: +* Source filtering: only accept array items if the previous include pattern matches {pull}22593[#22593] (issue: {issue}22557[#22557]) + +Highlighting:: +* Handle SynonymQuery extraction for the FastVectorHighlighter {pull}20829[#20829] (issue: {issue}20781[#20781]) + +Logging:: +* Restores the original default format of search slow log {pull}21770[#21770] (issue: {issue}21711[#21711]) + +Plugin Discovery EC2:: +* Fix ec2 discovery when used with IAM profiles. {pull}21042[#21042] (issue: {issue}21039[#21039]) + +Plugin Repository S3:: +* Fix s3 repository when used with IAM profiles {pull}21058[#21058] (issue: {issue}21048[#21048]) + +Plugins:: +* Plugins: Add back user agent when downloading plugins {pull}20872[#20872] + +Search:: +* Handle specialized term queries in MappedFieldType.extractTerm(Query) {pull}21889[#21889] (issue: {issue}21882[#21882]) + + + +[[upgrade-6.0.0-alpha1-5x]] +[float] +=== Upgrades + +Aggregations:: +* Upgrade HDRHistogram to 2.1.9 {pull}23254[#23254] (issue: {issue}23239[#23239]) + +Core:: +* Upgrade to Lucene 6.5.0 {pull}23750[#23750] +* Upgrade from JNA 4.2.2 to JNA 4.4.0 {pull}23636[#23636] +* Upgrade to lucene-6.5.0-snapshot-d00c5ca {pull}23385[#23385] +* Upgrade to lucene-6.5.0-snapshot-f919485. {pull}23087[#23087] +* Upgrade to Lucene 6.4.0 {pull}22724[#22724] +* Update Jackson to 2.8.6 {pull}22596[#22596] (issue: {issue}22266[#22266]) +* Upgrade to lucene-6.4.0-snapshot-084f7a0. {pull}22413[#22413] +* Upgrade to lucene-6.4.0-snapshot-ec38570 {pull}21853[#21853] +* Upgrade to lucene-6.3.0. {pull}21464[#21464] + +Dates:: +* Update Joda Time to version 2.9.5 {pull}21468[#21468] (issues: {issue}20911[#20911], {issue}332[#332], {issue}373[#373], {issue}378[#378], {issue}379[#379], {issue}386[#386], {issue}394[#394], {issue}396[#396], {issue}397[#397], {issue}404[#404], {issue}69[#69]) + +Internal:: +* Upgrade to Lucene 6.4.1. {pull}22978[#22978] + +Logging:: +* Upgrade to Log4j 2.8.2 {pull}23995[#23995] +* Upgrade Log4j 2 to version 2.7 {pull}20805[#20805] (issue: {issue}20304[#20304]) + +Network:: +* Upgrade Netty to 4.1.10.Final {pull}24414[#24414] +* Upgrade to Netty 4.1.9 {pull}23540[#23540] (issues: {issue}23172[#23172], {issue}6308[#6308], {issue}6374[#6374]) +* Upgrade to Netty 4.1.8 {pull}23055[#23055] +* Upgrade to Netty 4.1.7 {pull}22587[#22587] +* Upgrade to Netty 4.1.6 {pull}21051[#21051] + +Plugin Repository Azure:: +* Update to Azure Storage 5.0.0 {pull}23517[#23517] (issue: {issue}23448[#23448]) + diff --git a/docs/reference/release-notes/6.0.0-alpha1.asciidoc b/docs/reference/release-notes/6.0.0-alpha1.asciidoc new file mode 100644 index 0000000000000..d66d3ba0ed110 --- /dev/null +++ b/docs/reference/release-notes/6.0.0-alpha1.asciidoc @@ -0,0 +1,309 @@ +[[release-notes-6.0.0-alpha1]] +== 6.0.0-alpha1 Release Notes + +The changes listed below have been released for the first time in Elasticsearch 6.0.0-alpha1. Changes in this release which were first released in the 5.x series are listed in <>. + + +Also see <>. + +[[breaking-6.0.0-alpha1]] +[float] +=== Breaking changes + +Allocation:: +* Remove `cluster.routing.allocation.snapshot.relocation_enabled` setting {pull}20994[#20994] + +Analysis:: +* Removing query-string parameters in `_analyze` API {pull}20704[#20704] (issue: {issue}20246[#20246]) + +CAT API:: +* Write -1 on unbounded queue in cat thread pool {pull}21342[#21342] (issue: {issue}21187[#21187]) + +CRUD:: +* Disallow `VersionType.FORCE` for GetRequest {pull}21079[#21079] (issue: {issue}20995[#20995]) +* Disallow `VersionType.FORCE` versioning for 6.x indices {pull}20995[#20995] (issue: {issue}20377[#20377]) + +Cluster:: +* No longer allow cluster name in data path {pull}20433[#20433] (issue: {issue}20391[#20391]) + +Core:: +* Make boolean conversion strict {pull}22200[#22200] +* Remove the `default` store type. {pull}21616[#21616] +* Remove store throttling. {pull}21573[#21573] + +Geo:: +* Remove deprecated geo search features {pull}22876[#22876] +* Reduce GeoDistance Insanity {pull}19846[#19846] + +Index APIs:: +* Remove support for controversial `ignore_unavailable` and `allow_no_indices` from indices exists api {pull}20712[#20712] + +Index Templates:: +* Allows multiple patterns to be specified for index templates {pull}21009[#21009] (issue: {issue}20690[#20690]) + +Java API:: +* Enforce Content-Type requirement on the rest layer and remove deprecated methods {pull}23146[#23146] (issue: {issue}19388[#19388]) + +Mapping:: +* Disallow `include_in_all` for 6.0+ indices {pull}22970[#22970] (issue: {issue}22923[#22923]) +* Disable _all by default, disallow configuring _all on 6.0+ indices {pull}22144[#22144] (issues: {issue}19784[#19784], {issue}20925[#20925], {issue}21341[#21341]) +* Throw an exception on unrecognized "match_mapping_type" {pull}22090[#22090] (issue: {issue}17285[#17285]) + +NOT CLASSIFIED:: +* Add note to docs on duplicate keys in config {pull}24022[#24022] (issue: {issue}24006[#24006]) + +Network:: +* Remove blocking TCP clients and servers {pull}22639[#22639] +* Remove `modules/transport_netty_3` in favor of `netty_4` {pull}21590[#21590] +* Remove LocalTransport in favor of MockTcpTransport {pull}20695[#20695] + +Packaging:: +* Remove customization of ES_USER and ES_GROUP {pull}23989[#23989] (issue: {issue}23848[#23848]) + +Percolator:: +* Remove deprecated percolate and mpercolate apis {pull}22331[#22331] + +Plugin Delete By Query:: +* Require explicit query in _delete_by_query API {pull}23632[#23632] (issue: {issue}23629[#23629]) + +Plugin Discovery EC2:: +* Ec2 Discovery: Cleanup deprecated settings {pull}24150[#24150] +* Discovery EC2: Remove region setting {pull}23991[#23991] (issue: {issue}22758[#22758]) +* AWS Plugins: Remove signer type setting {pull}23984[#23984] (issue: {issue}22599[#22599]) + +Plugin Lang JS:: +* Remove lang-python and lang-javascript {pull}20734[#20734] (issue: {issue}20698[#20698]) + +Plugin Mapper Attachment:: +* Remove mapper attachments plugin {pull}20416[#20416] (issue: {issue}18837[#18837]) + +Plugin Repository Azure:: +* Remove global `repositories.azure` settings {pull}23262[#23262] (issues: {issue}22800[#22800], {issue}22856[#22856]) +* Remove auto creation of container for azure repository {pull}22858[#22858] (issue: {issue}22857[#22857]) + +Plugin Repository S3:: +* S3 Repository: Cleanup deprecated settings {pull}24097[#24097] +* S3 Repository: Remove region setting {pull}22853[#22853] (issue: {issue}22758[#22758]) +* S3 Repository: Remove bucket auto create {pull}22846[#22846] (issue: {issue}22761[#22761]) +* S3 Repository: Remove env var and sysprop credentials support {pull}22842[#22842] + +Query DSL:: +* Remove deprecated `minimum_number_should_match` in BoolQueryBuilder {pull}22416[#22416] +* Remove support for empty queries {pull}22092[#22092] (issue: {issue}17624[#17624]) +* Remove deprecated query names: in, geo_bbox, mlt, fuzzy_match and match_fuzzy {pull}21852[#21852] +* The `terms` query should always map to a Lucene `TermsQuery`. {pull}21786[#21786] +* Be strict when parsing values searching for booleans {pull}21555[#21555] (issue: {issue}21545[#21545]) +* Remove collect payloads parameter {pull}20385[#20385] + +REST:: +* Remove ldjson support and document ndjson for bulk/msearch {pull}23049[#23049] (issue: {issue}23025[#23025]) +* Enable strict duplicate checks for all XContent types {pull}22225[#22225] (issues: {issue}19614[#19614], {issue}22073[#22073]) +* Enable strict duplicate checks for JSON content {pull}22073[#22073] (issue: {issue}19614[#19614]) +* Remove lenient stats parsing {pull}21417[#21417] (issues: {issue}20722[#20722], {issue}21410[#21410]) +* Remove allow unquoted JSON {pull}20388[#20388] (issues: {issue}17674[#17674], {issue}17801[#17801]) +* Remove FORCE version_type {pull}20377[#20377] (issue: {issue}19769[#19769]) + +Scripting:: +* Make dates be ReadableDateTimes in scripts {pull}22948[#22948] (issue: {issue}22875[#22875]) +* Remove groovy scripting language {pull}21607[#21607] + +Search:: +* ProfileResult and CollectorResult should print machine readable timing information {pull}22561[#22561] +* Remove indices query {pull}21837[#21837] (issue: {issue}17710[#17710]) +* Remove ignored type parameter in search_shards api {pull}21688[#21688] + +Sequence IDs:: +* Change certain replica failures not to fail the replica shard {pull}22874[#22874] (issue: {issue}10708[#10708]) + +Shadow Replicas:: +* Remove shadow replicas {pull}23906[#23906] (issue: {issue}22024[#22024]) + + + +[[breaking-java-6.0.0-alpha1]] +[float] +=== Breaking Java changes + +Network:: +* Simplify TransportAddress {pull}20798[#20798] + + + +[[deprecation-6.0.0-alpha1]] +[float] +=== Deprecations + +Index Templates:: +* Restore deprecation warning for invalid match_mapping_type values {pull}22304[#22304] + +Internal:: +* Deprecate XContentType auto detection methods in XContentFactory {pull}22181[#22181] (issue: {issue}19388[#19388]) + + + +[[feature-6.0.0-alpha1]] +[float] +=== New features + +Core:: +* Enable index-time sorting {pull}24055[#24055] (issue: {issue}6720[#6720]) + + + +[[enhancement-6.0.0-alpha1]] +[float] +=== Enhancements + +Aggregations:: +* Agg builder accessibility fixes {pull}24323[#24323] +* Remove support for the include/pattern syntax. {pull}23141[#23141] (issue: {issue}22933[#22933]) +* Promote longs to doubles when a terms agg mixes decimal and non-decimal numbers {pull}22449[#22449] (issue: {issue}22232[#22232]) + +Analysis:: +* Match- and MultiMatchQueryBuilder should only allow setting analyzer on string values {pull}23684[#23684] (issue: {issue}21665[#21665]) + +Bulk:: +* Simplify bulk request execution {pull}20109[#20109] + +CRUD:: +* Added validation for upsert request {pull}24282[#24282] (issue: {issue}16671[#16671]) + +Cluster:: +* Separate publishing from applying cluster states {pull}24236[#24236] +* Adds cluster state size to /_cluster/state response {pull}23440[#23440] (issue: {issue}3415[#3415]) + +Core:: +* Remove connect SocketPermissions from core {pull}22797[#22797] +* Add repository-url module and move URLRepository {pull}22752[#22752] (issue: {issue}22116[#22116]) +* Remove accept SocketPermissions from core {pull}22622[#22622] (issue: {issue}22116[#22116]) +* Move IfConfig.logIfNecessary call into bootstrap {pull}22455[#22455] (issue: {issue}22116[#22116]) +* Remove artificial default processors limit {pull}20874[#20874] (issue: {issue}20828[#20828]) +* Simplify write failure handling {pull}19105[#19105] (issue: {issue}20109[#20109]) + +Engine:: +* Fill missing sequence IDs up to max sequence ID when recovering from store {pull}24238[#24238] (issue: {issue}10708[#10708]) +* Use sequence numbers to identify out of order delivery in replicas & recovery {pull}24060[#24060] (issue: {issue}10708[#10708]) +* Add replica ops with version conflict to translog {pull}22626[#22626] +* Clarify global checkpoint recovery {pull}21934[#21934] (issue: {issue}21254[#21254]) + +Internal:: +* Try to convince the JVM not to lose stacktraces {pull}24426[#24426] (issue: {issue}24376[#24376]) +* Make document write requests immutable {pull}23038[#23038] + +Java High Level REST Client:: +* Add info method to High Level Rest client {pull}23350[#23350] +* Add support for named xcontent parsers to high level REST client {pull}23328[#23328] +* Add BulkRequest support to High Level Rest client {pull}23312[#23312] +* Add UpdateRequest support to High Level Rest client {pull}23266[#23266] +* Add delete API to the High Level Rest Client {pull}23187[#23187] +* Add Index API to High Level Rest Client {pull}23040[#23040] +* Add get/exists method to RestHighLevelClient {pull}22706[#22706] +* Add fromxcontent methods to delete response {pull}22680[#22680] (issue: {issue}22229[#22229]) +* Add parsing from xContent to SearchResponse {pull}22533[#22533] +* Add REST high level client gradle submodule and first simple method {pull}22371[#22371] + +Java REST Client:: +* Wrap rest httpclient with doPrivileged blocks {pull}22603[#22603] (issue: {issue}22116[#22116]) + +Mapping:: +* Date detection should not rely on a hardcoded set of characters. {pull}22171[#22171] (issue: {issue}1694[#1694]) + +Network:: +* Isolate SocketPermissions to Netty {pull}23057[#23057] +* Wrap netty accept/connect ops with doPrivileged {pull}22572[#22572] (issue: {issue}22116[#22116]) +* Replace Socket, ServerSocket, and HttpServer usages in tests with mocksocket versions {pull}22287[#22287] (issue: {issue}22116[#22116]) + +Plugin Discovery EC2:: +* Read ec2 discovery address from aws instance tags {pull}22743[#22743] (issue: {issue}22566[#22566]) + +Plugin Repository HDFS:: +* Add doPrivilege blocks for socket connect ops in repository-hdfs {pull}22793[#22793] (issue: {issue}22116[#22116]) + +Plugins:: +* Add doPrivilege blocks for socket connect operations in plugins {pull}22534[#22534] (issue: {issue}22116[#22116]) + +Recovery:: +* Peer Recovery: remove maxUnsafeAutoIdTimestamp hand off {pull}24243[#24243] (issue: {issue}24149[#24149]) +* Introduce sequence-number-based recovery {pull}22484[#22484] (issue: {issue}10708[#10708]) + +Search:: +* Add parsing from xContent to Suggest {pull}22903[#22903] +* Add parsing from xContent to ShardSearchFailure {pull}22699[#22699] + +Sequence IDs:: +* Add primary term to doc write response {pull}24171[#24171] (issue: {issue}10708[#10708]) +* Preserve multiple translog generations {pull}24015[#24015] (issue: {issue}10708[#10708]) +* Introduce translog generation rolling {pull}23606[#23606] (issue: {issue}10708[#10708]) +* Replicate write failures {pull}23314[#23314] +* Introduce sequence-number-aware translog {pull}22822[#22822] (issue: {issue}10708[#10708]) +* Introduce translog no-op {pull}22291[#22291] (issue: {issue}10708[#10708]) +* Tighten sequence numbers recovery {pull}22212[#22212] (issue: {issue}10708[#10708]) +* Add BWC layer to seq no infra and enable BWC tests {pull}22185[#22185] (issue: {issue}21670[#21670]) +* Add internal _primary_term doc values field, fix _seq_no indexing {pull}21637[#21637] (issues: {issue}10708[#10708], {issue}21480[#21480]) +* Add global checkpoint to translog checkpoints {pull}21254[#21254] +* Sequence numbers commit data for Lucene uses Iterable interface {pull}20793[#20793] (issue: {issue}10708[#10708]) +* Simplify GlobalCheckpointService and properly hook it for cluster state updates {pull}20720[#20720] + +Stats:: +* Expose disk usage estimates in nodes stats {pull}22081[#22081] (issue: {issue}8686[#8686]) + +Store:: +* Remote support for lucene versions without checksums {pull}24021[#24021] + +Suggesters:: +* Remove deprecated _suggest endpoint {pull}22203[#22203] (issue: {issue}20305[#20305]) + +Task Manager:: +* Add descriptions to bulk tasks {pull}22059[#22059] (issue: {issue}21768[#21768]) + + + +[[bug-6.0.0-alpha1]] +[float] +=== Bug fixes + +Ingest:: +* Remove support for Visio and potm files {pull}22079[#22079] (issue: {issue}22077[#22077]) + +Inner Hits:: +* If size / offset are out of bounds just do a plain count {pull}20556[#20556] (issue: {issue}20501[#20501]) + +Internal:: +* Fix handling of document failure exception in InternalEngine {pull}22718[#22718] + +Plugin Ingest Attachment:: +* Add missing mime4j library {pull}22764[#22764] (issue: {issue}22077[#22077]) + +Plugin Repository S3:: +* Wrap getCredentials() in a doPrivileged() block {pull}23297[#23297] (issues: {issue}22534[#22534], {issue}23271[#23271]) + +Sequence IDs:: +* Avoid losing ops in file-based recovery {pull}22945[#22945] (issue: {issue}22484[#22484]) + +Snapshot/Restore:: +* Keep snapshot restore state and routing table in sync {pull}20836[#20836] (issue: {issue}19774[#19774]) + +Translog:: +* Fix Translog.Delete serialization for sequence numbers {pull}22543[#22543] + + + +[[regression-6.0.0-alpha1]] +[float] +=== Regressions + +Bulk:: +* Only re-parse operation if a mapping update was needed {pull}23832[#23832] (issue: {issue}23665[#23665]) + + + +[[upgrade-6.0.0-alpha1]] +[float] +=== Upgrades + +Core:: +* Upgrade to a Lucene 7 snapshot {pull}24089[#24089] (issues: {issue}23966[#23966], {issue}24086[#24086], {issue}24087[#24087], {issue}24088[#24088]) + +Plugin Ingest Attachment:: +* Update to Tika 1.14 {pull}21591[#21591] (issue: {issue}20390[#20390]) + From 5bfb98ade4d1692093ac47fe882ecef633529f8c Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Fri, 5 May 2017 16:02:26 +0200 Subject: [PATCH 221/619] [TEST] Reenable disabled tests for _field_caps and _search_shards (#24505) --- .../resources/rest-api-spec/test/field_caps/10_basic.yaml | 4 ++-- .../resources/rest-api-spec/test/search_shards/10_basic.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml index 2568f401a5ea5..f22afb91169dd 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml @@ -169,8 +169,8 @@ setup: --- "Mix in non-existing field field caps": - skip: - version: " - 6.0.0" # temporarily disabled - reason: this bug has been fixed in 5.4.0 + version: " - 5.4.0" + reason: "#24504 fixed a bug in this API in 5.4.1" - do: field_caps: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml index ef01f3b7b44e4..c2d341e3439a8 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml @@ -14,8 +14,8 @@ --- "Search shards aliases with and without filters": - skip: - version: " - 6.0.0" # temporarily disabled - reason: indices section was added in 5.1.0 + version: " - 5.4.0" + reason: "#24489 fixed a bug that not all aliases where added in 5.4.1 - indices section was added in 5.1.0" - do: indices.create: From 9f08a553d960cd3253d85ee8b98a9ef22804d192 Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Fri, 5 May 2017 16:06:48 +0200 Subject: [PATCH 222/619] Fixed docs syntax for for-in loop in painless --- docs/reference/modules/scripting/painless-syntax.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/modules/scripting/painless-syntax.asciidoc b/docs/reference/modules/scripting/painless-syntax.asciidoc index e3a6ed24bc0c7..15656d72f2ca8 100644 --- a/docs/reference/modules/scripting/painless-syntax.asciidoc +++ b/docs/reference/modules/scripting/painless-syntax.asciidoc @@ -207,7 +207,7 @@ In addition to Java's `enhanced for` loop, the `for in` syntax from groovy can a [source,painless] --------------------------------------------------------- -for (item : list) { +for (def item : list) { ... } --------------------------------------------------------- From e3766d28288ed2e0f3cd098e7a6b8c22de3a0fa6 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Fri, 5 May 2017 16:40:04 +0200 Subject: [PATCH 223/619] Expand cross cluster search indices for search requests to the concrete index or to it's aliases (#24502) This change will expand the shard level request to the actual concrete index or to the aliases that expanded to the concrete index to ensure shard level requests won't see wildcard expressions as their original indices --- .../action/search/TransportSearchAction.java | 13 ++++++++----- .../org/elasticsearch/common/util/ArrayUtils.java | 1 - .../action/search/TransportSearchActionTests.java | 15 +++++++++------ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 9c8d1a879b13d..f65597a966bc2 100644 --- a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -220,11 +220,6 @@ static BiFunction processRemoteShards(Map processRemoteShards(Map { diff --git a/core/src/main/java/org/elasticsearch/common/util/ArrayUtils.java b/core/src/main/java/org/elasticsearch/common/util/ArrayUtils.java index de23663b3e9c8..20c12d564da62 100644 --- a/core/src/main/java/org/elasticsearch/common/util/ArrayUtils.java +++ b/core/src/main/java/org/elasticsearch/common/util/ArrayUtils.java @@ -84,5 +84,4 @@ public static T[] concat(T[] one, T[] other, Class clazz) { System.arraycopy(other, 0, target, one.length, other.length); return target; } - } diff --git a/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java b/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java index f34f4313fd6df..7491eda8fd7e4 100644 --- a/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java @@ -64,7 +64,6 @@ public void tearDown() throws Exception { ThreadPool.terminate(threadPool, 10, TimeUnit.SECONDS); } - public void testMergeShardsIterators() throws IOException { List localShardIterators = new ArrayList<>(); { @@ -159,7 +158,8 @@ public void testProcessRemoteShards() throws IOException { new DiscoveryNode("node2", buildNewFakeTransportAddress(), Version.CURRENT) }; Map indicesAndAliases = new HashMap<>(); - indicesAndAliases.put("foo", new AliasFilter(new TermsQueryBuilder("foo", "bar"), Strings.EMPTY_ARRAY)); + indicesAndAliases.put("foo", new AliasFilter(new TermsQueryBuilder("foo", "bar"), "some_alias_for_foo", + "some_other_foo_alias")); indicesAndAliases.put("bar", new AliasFilter(new MatchAllQueryBuilder(), Strings.EMPTY_ARRAY)); ClusterSearchShardsGroup[] groups = new ClusterSearchShardsGroup[] { new ClusterSearchShardsGroup(new ShardId("foo", "foo_id", 0), @@ -180,7 +180,9 @@ public void testProcessRemoteShards() throws IOException { new ClusterSearchShardsGroup(new ShardId("xyz", "xyz_id", 0), new ShardRouting[] {TestShardRouting.newShardRouting("xyz", 0, "node3", true, ShardRoutingState.STARTED)}) }; - searchShardsResponseMap.put("test_cluster_2", new ClusterSearchShardsResponse(groups2, nodes2, null)); + Map filter = new HashMap<>(); + filter.put("xyz", new AliasFilter(null, "some_alias_for_xyz")); + searchShardsResponseMap.put("test_cluster_2", new ClusterSearchShardsResponse(groups2, nodes2, filter)); Map remoteIndicesByCluster = new HashMap<>(); remoteIndicesByCluster.put("test_cluster_1", @@ -193,7 +195,8 @@ public void testProcessRemoteShards() throws IOException { assertEquals(4, iteratorList.size()); for (SearchShardIterator iterator : iteratorList) { if (iterator.shardId().getIndexName().endsWith("foo")) { - assertArrayEquals(new String[]{"fo*", "ba*"}, iterator.getOriginalIndices().indices()); + assertArrayEquals(new String[]{"some_alias_for_foo", "some_other_foo_alias"}, + iterator.getOriginalIndices().indices()); assertTrue(iterator.shardId().getId() == 0 || iterator.shardId().getId() == 1); assertEquals("test_cluster_1:foo", iterator.shardId().getIndexName()); ShardRouting shardRouting = iterator.nextOrNull(); @@ -204,7 +207,7 @@ public void testProcessRemoteShards() throws IOException { assertEquals(shardRouting.getIndexName(), "foo"); assertNull(iterator.nextOrNull()); } else if (iterator.shardId().getIndexName().endsWith("bar")) { - assertArrayEquals(new String[]{"fo*", "ba*"}, iterator.getOriginalIndices().indices()); + assertArrayEquals(new String[]{"bar"}, iterator.getOriginalIndices().indices()); assertEquals(0, iterator.shardId().getId()); assertEquals("test_cluster_1:bar", iterator.shardId().getIndexName()); ShardRouting shardRouting = iterator.nextOrNull(); @@ -215,7 +218,7 @@ public void testProcessRemoteShards() throws IOException { assertEquals(shardRouting.getIndexName(), "bar"); assertNull(iterator.nextOrNull()); } else if (iterator.shardId().getIndexName().endsWith("xyz")) { - assertArrayEquals(new String[]{"x*"}, iterator.getOriginalIndices().indices()); + assertArrayEquals(new String[]{"some_alias_for_xyz"}, iterator.getOriginalIndices().indices()); assertEquals(0, iterator.shardId().getId()); assertEquals("test_cluster_2:xyz", iterator.shardId().getIndexName()); ShardRouting shardRouting = iterator.nextOrNull(); From 0c4eb0a02940139ca26ccc45523c03ba26c7d75b Mon Sep 17 00:00:00 2001 From: Nicholas Knize Date: Tue, 2 May 2017 09:56:52 -0500 Subject: [PATCH 224/619] Add new ip_range field type This commit adds support for indexing and searching a new ip_range field type. Both IPv4 and IPv6 formats are supported. Tests are updated and docs are added. --- .../index/mapper/RangeFieldMapper.java | 236 ++++++++++-------- .../index/mapper/RangeFieldMapperTests.java | 44 +++- .../index/mapper/RangeFieldTypeTests.java | 28 ++- docs/reference/mapping/types/range.asciidoc | 2 + .../release-notes/6.0.0-alpha1.asciidoc | 1 + 5 files changed, 201 insertions(+), 110 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/mapper/RangeFieldMapper.java b/core/src/main/java/org/elasticsearch/index/mapper/RangeFieldMapper.java index 9a271916ac19f..257792b8b093e 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/RangeFieldMapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/RangeFieldMapper.java @@ -22,6 +22,8 @@ import org.apache.lucene.document.DoubleRange; import org.apache.lucene.document.FloatRange; import org.apache.lucene.document.IntRange; +import org.apache.lucene.document.InetAddressPoint; +import org.apache.lucene.document.InetAddressRange; import org.apache.lucene.document.LongRange; import org.apache.lucene.document.StoredField; import org.apache.lucene.index.IndexOptions; @@ -29,12 +31,12 @@ import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.Query; import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.NumericUtils; import org.elasticsearch.common.Explicit; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.joda.DateMathParser; import org.elasticsearch.common.joda.FormatDateTimeFormatter; +import org.elasticsearch.common.network.InetAddresses; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.LocaleUtils; @@ -45,6 +47,7 @@ import org.joda.time.DateTimeZone; import java.io.IOException; +import java.net.InetAddress; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -341,8 +344,8 @@ protected void parseCreateField(ParseContext context, List field RangeFieldType fieldType = fieldType(); RangeType rangeType = fieldType.rangeType; String fieldName = null; - Number from = rangeType.minValue(); - Number to = rangeType.maxValue(); + Object from = rangeType.minValue(); + Object to = rangeType.maxValue(); boolean includeFrom = DEFAULT_INCLUDE_LOWER; boolean includeTo = DEFAULT_INCLUDE_UPPER; XContentParser.Token token; @@ -427,10 +430,72 @@ && fieldType().dateTimeFormatter().locale() != Locale.ROOT))) { /** Enum defining the type of range */ public enum RangeType { + IP("ip_range") { + @Override + public Field getRangeField(String name, Range r) { + return new InetAddressRange(name, (InetAddress)r.from, (InetAddress)r.to); + } + @Override + public InetAddress parseFrom(RangeFieldType fieldType, XContentParser parser, boolean coerce, boolean included) + throws IOException { + InetAddress address = InetAddresses.forString(parser.text()); + return included ? address : nextUp(address); + } + @Override + public InetAddress parseTo(RangeFieldType fieldType, XContentParser parser, boolean coerce, boolean included) + throws IOException { + InetAddress address = InetAddresses.forString(parser.text()); + return included ? address : nextDown(address); + } + @Override + public InetAddress parse(Object value, boolean coerce) { + return value instanceof InetAddress ? (InetAddress) value : InetAddresses.forString((String) value); + } + @Override + public InetAddress minValue() { + return InetAddressPoint.MIN_VALUE; + } + @Override + public InetAddress maxValue() { + return InetAddressPoint.MAX_VALUE; + } + @Override + public InetAddress nextUp(Object value) { + return InetAddressPoint.nextUp((InetAddress)value); + } + @Override + public InetAddress nextDown(Object value) { + return InetAddressPoint.nextDown((InetAddress)value); + } + @Override + public Query withinQuery(String field, Object from, Object to, boolean includeLower, boolean includeUpper) { + InetAddress lower = (InetAddress)from; + InetAddress upper = (InetAddress)to; + return InetAddressRange.newWithinQuery(field, + includeLower ? lower : nextUp(lower), includeUpper ? upper : nextDown(upper)); + } + @Override + public Query containsQuery(String field, Object from, Object to, boolean includeLower, boolean includeUpper) { + InetAddress lower = (InetAddress)from; + InetAddress upper = (InetAddress)to; + return InetAddressRange.newContainsQuery(field, + includeLower ? lower : nextUp(lower), includeUpper ? upper : nextDown(upper)); + } + @Override + public Query intersectsQuery(String field, Object from, Object to, boolean includeLower, boolean includeUpper) { + InetAddress lower = (InetAddress)from; + InetAddress upper = (InetAddress)to; + return InetAddressRange.newIntersectsQuery(field, + includeLower ? lower : nextUp(lower), includeUpper ? upper : nextDown(upper)); + } + public String toString(InetAddress address) { + return InetAddresses.toAddrString(address); + } + }, DATE("date_range", NumberType.LONG) { @Override public Field getRangeField(String name, Range r) { - return new LongRange(name, new long[] {r.from.longValue()}, new long[] {r.to.longValue()}); + return new LongRange(name, new long[] {((Number)r.from).longValue()}, new long[] {((Number)r.to).longValue()}); } private Number parse(DateMathParser dateMathParser, String dateStr) { return dateMathParser.parse(dateStr, () -> {throw new IllegalArgumentException("now is not used at indexing time");}); @@ -456,16 +521,12 @@ public Long maxValue() { return Long.MAX_VALUE; } @Override - public Number nextUp(Number value) { - return LONG.nextUp(value); + public Long nextUp(Object value) { + return (long) LONG.nextUp(value); } @Override - public Number nextDown(Number value) { - return LONG.nextDown(value); - } - @Override - public byte[] getBytes(Range r) { - return LONG.getBytes(r); + public Long nextDown(Object value) { + return (long) LONG.nextDown(value); } @Override public Query rangeQuery(String field, Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @@ -484,15 +545,15 @@ public Query rangeQuery(String field, Object lowerTerm, Object upperTerm, boolea return super.rangeQuery(field, low, high, includeLower, includeUpper, relation, zone, dateMathParser, context); } @Override - public Query withinQuery(String field, Number from, Number to, boolean includeLower, boolean includeUpper) { + public Query withinQuery(String field, Object from, Object to, boolean includeLower, boolean includeUpper) { return LONG.withinQuery(field, from, to, includeLower, includeUpper); } @Override - public Query containsQuery(String field, Number from, Number to, boolean includeLower, boolean includeUpper) { + public Query containsQuery(String field, Object from, Object to, boolean includeLower, boolean includeUpper) { return LONG.containsQuery(field, from, to, includeLower, includeUpper); } @Override - public Query intersectsQuery(String field, Number from, Number to, boolean includeLower, boolean includeUpper) { + public Query intersectsQuery(String field, Object from, Object to, boolean includeLower, boolean includeUpper) { return LONG.intersectsQuery(field, from, to, includeLower, includeUpper); } }, @@ -507,38 +568,31 @@ public Float maxValue() { return Float.POSITIVE_INFINITY; } @Override - public Float nextUp(Number value) { - return Math.nextUp(value.floatValue()); + public Float nextUp(Object value) { + return Math.nextUp(((Number)value).floatValue()); } @Override - public Float nextDown(Number value) { - return Math.nextDown(value.floatValue()); + public Float nextDown(Object value) { + return Math.nextDown(((Number)value).floatValue()); } @Override public Field getRangeField(String name, Range r) { - return new FloatRange(name, new float[] {r.from.floatValue()}, new float[] {r.to.floatValue()}); + return new FloatRange(name, new float[] {((Number)r.from).floatValue()}, new float[] {((Number)r.to).floatValue()}); } @Override - public byte[] getBytes(Range r) { - byte[] b = new byte[Float.BYTES*2]; - NumericUtils.intToSortableBytes(NumericUtils.floatToSortableInt(r.from.floatValue()), b, 0); - NumericUtils.intToSortableBytes(NumericUtils.floatToSortableInt(r.to.floatValue()), b, Float.BYTES); - return b; - } - @Override - public Query withinQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) { + public Query withinQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) { return FloatRange.newWithinQuery(field, new float[] {includeFrom ? (Float)from : Math.nextUp((Float)from)}, new float[] {includeTo ? (Float)to : Math.nextDown((Float)to)}); } @Override - public Query containsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) { + public Query containsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) { return FloatRange.newContainsQuery(field, new float[] {includeFrom ? (Float)from : Math.nextUp((Float)from)}, new float[] {includeTo ? (Float)to : Math.nextDown((Float)to)}); } @Override - public Query intersectsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) { + public Query intersectsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) { return FloatRange.newIntersectsQuery(field, new float[] {includeFrom ? (Float)from : Math.nextUp((Float)from)}, new float[] {includeTo ? (Float)to : Math.nextDown((Float)to)}); @@ -554,38 +608,31 @@ public Double maxValue() { return Double.POSITIVE_INFINITY; } @Override - public Double nextUp(Number value) { - return Math.nextUp(value.doubleValue()); + public Double nextUp(Object value) { + return Math.nextUp(((Number)value).doubleValue()); } @Override - public Double nextDown(Number value) { - return Math.nextDown(value.doubleValue()); + public Double nextDown(Object value) { + return Math.nextDown(((Number)value).doubleValue()); } @Override public Field getRangeField(String name, Range r) { - return new DoubleRange(name, new double[] {r.from.doubleValue()}, new double[] {r.to.doubleValue()}); + return new DoubleRange(name, new double[] {((Number)r.from).doubleValue()}, new double[] {((Number)r.to).doubleValue()}); } @Override - public byte[] getBytes(Range r) { - byte[] b = new byte[Double.BYTES*2]; - NumericUtils.longToSortableBytes(NumericUtils.doubleToSortableLong(r.from.doubleValue()), b, 0); - NumericUtils.longToSortableBytes(NumericUtils.doubleToSortableLong(r.to.doubleValue()), b, Double.BYTES); - return b; - } - @Override - public Query withinQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) { + public Query withinQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) { return DoubleRange.newWithinQuery(field, new double[] {includeFrom ? (Double)from : Math.nextUp((Double)from)}, new double[] {includeTo ? (Double)to : Math.nextDown((Double)to)}); } @Override - public Query containsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) { + public Query containsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) { return DoubleRange.newContainsQuery(field, new double[] {includeFrom ? (Double)from : Math.nextUp((Double)from)}, new double[] {includeTo ? (Double)to : Math.nextDown((Double)to)}); } @Override - public Query intersectsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) { + public Query intersectsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) { return DoubleRange.newIntersectsQuery(field, new double[] {includeFrom ? (Double)from : Math.nextUp((Double)from)}, new double[] {includeTo ? (Double)to : Math.nextDown((Double)to)}); @@ -603,36 +650,29 @@ public Integer maxValue() { return Integer.MAX_VALUE; } @Override - public Integer nextUp(Number value) { - return value.intValue() + 1; + public Integer nextUp(Object value) { + return ((Number)value).intValue() + 1; } @Override - public Integer nextDown(Number value) { - return value.intValue() - 1; + public Integer nextDown(Object value) { + return ((Number)value).intValue() - 1; } @Override public Field getRangeField(String name, Range r) { - return new IntRange(name, new int[] {r.from.intValue()}, new int[] {r.to.intValue()}); - } - @Override - public byte[] getBytes(Range r) { - byte[] b = new byte[Integer.BYTES*2]; - NumericUtils.intToSortableBytes(r.from.intValue(), b, 0); - NumericUtils.intToSortableBytes(r.to.intValue(), b, Integer.BYTES); - return b; + return new IntRange(name, new int[] {((Number)r.from).intValue()}, new int[] {((Number)r.to).intValue()}); } @Override - public Query withinQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) { + public Query withinQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) { return IntRange.newWithinQuery(field, new int[] {(Integer)from + (includeFrom ? 0 : 1)}, new int[] {(Integer)to - (includeTo ? 0 : 1)}); } @Override - public Query containsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) { + public Query containsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) { return IntRange.newContainsQuery(field, new int[] {(Integer)from + (includeFrom ? 0 : 1)}, new int[] {(Integer)to - (includeTo ? 0 : 1)}); } @Override - public Query intersectsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) { + public Query intersectsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) { return IntRange.newIntersectsQuery(field, new int[] {(Integer)from + (includeFrom ? 0 : 1)}, new int[] {(Integer)to - (includeTo ? 0 : 1)}); } @@ -647,43 +687,40 @@ public Long maxValue() { return Long.MAX_VALUE; } @Override - public Long nextUp(Number value) { - return value.longValue() + 1; + public Long nextUp(Object value) { + return ((Number)value).longValue() + 1; } @Override - public Long nextDown(Number value) { - return value.longValue() - 1; + public Long nextDown(Object value) { + return ((Number)value).longValue() - 1; } @Override public Field getRangeField(String name, Range r) { - return new LongRange(name, new long[] {r.from.longValue()}, new long[] {r.to.longValue()}); - } - @Override - public byte[] getBytes(Range r) { - byte[] b = new byte[Long.BYTES*2]; - long from = r.from == null ? Long.MIN_VALUE : r.from.longValue(); - long to = r.to == null ? Long.MAX_VALUE : r.to.longValue(); - NumericUtils.longToSortableBytes(from, b, 0); - NumericUtils.longToSortableBytes(to, b, Long.BYTES); - return b; + return new LongRange(name, new long[] {((Number)r.from).longValue()}, + new long[] {((Number)r.to).longValue()}); } @Override - public Query withinQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) { + public Query withinQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) { return LongRange.newWithinQuery(field, new long[] {(Long)from + (includeFrom ? 0 : 1)}, new long[] {(Long)to - (includeTo ? 0 : 1)}); } @Override - public Query containsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) { + public Query containsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) { return LongRange.newContainsQuery(field, new long[] {(Long)from + (includeFrom ? 0 : 1)}, new long[] {(Long)to - (includeTo ? 0 : 1)}); } @Override - public Query intersectsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo) { + public Query intersectsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo) { return LongRange.newIntersectsQuery(field, new long[] {(Long)from + (includeFrom ? 0 : 1)}, new long[] {(Long)to - (includeTo ? 0 : 1)}); } }; + RangeType(String name) { + this.name = name; + this.numberType = null; + } + RangeType(String name, NumberType type) { this.name = name; this.numberType = type; @@ -694,7 +731,6 @@ public final String typeName() { return name; } - protected abstract byte[] getBytes(Range range); public abstract Field getRangeField(String name, Range range); public List createFields(String name, Range range, boolean indexed, boolean docValued, boolean stored) { assert range != null : "range cannot be null when creating fields"; @@ -709,29 +745,31 @@ public List createFields(String name, Range range, boolean index return fields; } /** parses from value. rounds according to included flag */ - public Number parseFrom(RangeFieldType fieldType, XContentParser parser, boolean coerce, boolean included) throws IOException { + public Object parseFrom(RangeFieldType fieldType, XContentParser parser, boolean coerce, boolean included) throws IOException { Number value = numberType.parse(parser, coerce); - return included ? value : nextUp(value); + return included ? value : (Number)nextUp(value); } /** parses to value. rounds according to included flag */ - public Number parseTo(RangeFieldType fieldType, XContentParser parser, boolean coerce, boolean included) throws IOException { + public Object parseTo(RangeFieldType fieldType, XContentParser parser, boolean coerce, boolean included) throws IOException { Number value = numberType.parse(parser, coerce); - return included ? value : nextDown(value); + return included ? value : (Number)nextDown(value); } - public abstract Number minValue(); - public abstract Number maxValue(); - public abstract Number nextUp(Number value); - public abstract Number nextDown(Number value); - public abstract Query withinQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo); - public abstract Query containsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo); - public abstract Query intersectsQuery(String field, Number from, Number to, boolean includeFrom, boolean includeTo); - + public abstract Object minValue(); + public abstract Object maxValue(); + public abstract Object nextUp(Object value); + public abstract Object nextDown(Object value); + public abstract Query withinQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo); + public abstract Query containsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo); + public abstract Query intersectsQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo); + public Object parse(Object value, boolean coerce) { + return numberType.parse(value, coerce); + } public Query rangeQuery(String field, Object from, Object to, boolean includeFrom, boolean includeTo, ShapeRelation relation, @Nullable DateTimeZone timeZone, @Nullable DateMathParser dateMathParser, QueryShardContext context) { - Number lower = from == null ? minValue() : numberType.parse(from, false); - Number upper = to == null ? maxValue() : numberType.parse(to, false); + Object lower = from == null ? minValue() : parse(from, false); + Object upper = to == null ? maxValue() : parse(to, false); if (relation == ShapeRelation.WITHIN) { return withinQuery(field, lower, upper, includeFrom, includeTo); } else if (relation == ShapeRelation.CONTAINS) { @@ -747,12 +785,12 @@ public Query rangeQuery(String field, Object from, Object to, boolean includeFro /** Class defining a range */ public static class Range { RangeType type; - private Number from; - private Number to; + private Object from; + private Object to; private boolean includeFrom; private boolean includeTo; - public Range(RangeType type, Number from, Number to, boolean includeFrom, boolean includeTo) { + public Range(RangeType type, Object from, Object to, boolean includeFrom, boolean includeTo) { this.type = type; this.from = from; this.to = to; @@ -764,9 +802,11 @@ public Range(RangeType type, Number from, Number to, boolean includeFrom, boolea public String toString() { StringBuilder sb = new StringBuilder(); sb.append(includeFrom ? '[' : '('); - sb.append(includeFrom || from.equals(type.minValue()) ? from : type.nextDown(from)); - sb.append(':'); - sb.append(includeTo || to.equals(type.maxValue()) ? to : type.nextUp(to)); + Object f = includeFrom || from.equals(type.minValue()) ? from : type.nextDown(from); + Object t = includeTo || to.equals(type.maxValue()) ? to : type.nextUp(to); + sb.append(type == RangeType.IP ? InetAddresses.toAddrString((InetAddress)f) : f.toString()); + sb.append(" : "); + sb.append(type == RangeType.IP ? InetAddresses.toAddrString((InetAddress)t) : t.toString()); sb.append(includeTo ? ']' : ')'); return sb.toString(); } diff --git a/core/src/test/java/org/elasticsearch/index/mapper/RangeFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/RangeFieldMapperTests.java index 18a771bb46716..a6fbfc44a56c1 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/RangeFieldMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/RangeFieldMapperTests.java @@ -18,14 +18,17 @@ */ package org.elasticsearch.index.mapper; +import org.apache.lucene.document.InetAddressPoint; import org.apache.lucene.index.IndexableField; import org.elasticsearch.common.compress.CompressedXContent; +import org.elasticsearch.common.network.InetAddresses; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; +import java.net.InetAddress; import java.util.Arrays; import java.util.HashSet; import java.util.Locale; @@ -40,6 +43,8 @@ public class RangeFieldMapperTests extends AbstractNumericFieldMapperTestCase { private static String FROM_DATE = "2016-10-31"; private static String TO_DATE = "2016-11-01 20:00:00"; + private static String FROM_IP = "::ffff:c0a8:107"; + private static String TO_IP = "2001:db8::"; private static int FROM = 5; private static String FROM_STR = FROM + ""; private static int TO = 10; @@ -48,12 +53,14 @@ public class RangeFieldMapperTests extends AbstractNumericFieldMapperTestCase { @Override protected void setTypeList() { - TYPES = new HashSet<>(Arrays.asList("date_range", "float_range", "double_range", "integer_range", "long_range")); + TYPES = new HashSet<>(Arrays.asList("date_range", "ip_range", "float_range", "double_range", "integer_range", "long_range")); } private Object getFrom(String type) { if (type.equals("date_range")) { return FROM_DATE; + } else if (type.equals("ip_range")) { + return FROM_IP; } return random().nextBoolean() ? FROM : FROM_STR; } @@ -69,13 +76,17 @@ private String getToField() { private Object getTo(String type) { if (type.equals("date_range")) { return TO_DATE; + } else if (type.equals("ip_range")) { + return TO_IP; } return random().nextBoolean() ? TO : TO_STR; } - private Number getMax(String type) { + private Object getMax(String type) { if (type.equals("date_range") || type.equals("long_range")) { return Long.MAX_VALUE; + } else if (type.equals("ip_range")) { + return InetAddressPoint.MAX_VALUE; } else if (type.equals("integer_range")) { return Integer.MAX_VALUE; } else if (type.equals("float_range")) { @@ -189,7 +200,14 @@ protected void doTestStore(String type) throws Exception { assertEquals(2, pointField.fieldType().pointDimensionCount()); IndexableField storedField = fields[1]; assertTrue(storedField.fieldType().stored()); - assertThat(storedField.stringValue(), containsString(type.equals("date_range") ? "1477872000000" : "5")); + String strVal = "5"; + if (type.equals("date_range")) { + strVal = "1477872000000"; + } else if (type.equals("ip_range")) { + strVal = InetAddresses.toAddrString(InetAddresses.forString("192.168.1.7")) + " : " + + InetAddresses.toAddrString(InetAddresses.forString("2001:db8:0:0:0:0:0:0")); + } + assertThat(storedField.stringValue(), containsString(strVal)); } @Override @@ -234,7 +252,8 @@ public void doTestCoerce(String type) throws IOException { .endObject().bytes(), XContentType.JSON)); MapperParsingException e = expectThrows(MapperParsingException.class, runnable); - assertThat(e.getCause().getMessage(), anyOf(containsString("passed as String"), containsString("failed to parse date"))); + assertThat(e.getCause().getMessage(), anyOf(containsString("passed as String"), + containsString("failed to parse date"), containsString("is not an IP string literal"))); } @Override @@ -261,7 +280,8 @@ protected void doTestNullValue(String type) throws IOException { assertEquals(2, doc.rootDoc().getFields("field").length); IndexableField[] fields = doc.rootDoc().getFields("field"); IndexableField storedField = fields[1]; - assertThat(storedField.stringValue(), containsString(type.equals("date_range") ? Long.MAX_VALUE+"" : getMax(type)+"")); + String expected = type.equals("ip_range") ? InetAddresses.toAddrString((InetAddress)getMax(type)) : getMax(type) +""; + assertThat(storedField.stringValue(), containsString(expected)); // test null max value doc = mapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder() @@ -280,8 +300,14 @@ protected void doTestNullValue(String type) throws IOException { assertFalse(pointField.fieldType().stored()); storedField = fields[1]; assertTrue(storedField.fieldType().stored()); - assertThat(storedField.stringValue(), containsString(type.equals("date_range") ? "1477872000000" : "5")); - assertThat(storedField.stringValue(), containsString(getMax(type) + "")); + String strVal = "5"; + if (type.equals("date_range")) { + strVal = "1477872000000"; + } else if (type.equals("ip_range")) { + strVal = InetAddresses.toAddrString(InetAddresses.forString("192.168.1.7")) + " : " + + InetAddresses.toAddrString(InetAddresses.forString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")); + } + assertThat(storedField.stringValue(), containsString(strVal)); } public void testNoBounds() throws Exception { @@ -316,8 +342,8 @@ public void doTestNoBounds(String type) throws IOException { assertFalse(pointField.fieldType().stored()); IndexableField storedField = fields[1]; assertTrue(storedField.fieldType().stored()); - assertThat(storedField.stringValue(), containsString(type.equals("date_range") ? Long.MAX_VALUE+"" : getMax(type)+"")); - assertThat(storedField.stringValue(), containsString(getMax(type) + "")); + String expected = type.equals("ip_range") ? InetAddresses.toAddrString((InetAddress)getMax(type)) : getMax(type) +""; + assertThat(storedField.stringValue(), containsString(expected)); } public void testIllegalArguments() throws Exception { diff --git a/core/src/test/java/org/elasticsearch/index/mapper/RangeFieldTypeTests.java b/core/src/test/java/org/elasticsearch/index/mapper/RangeFieldTypeTests.java index b3d7db23c3826..015509d4a7363 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/RangeFieldTypeTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/RangeFieldTypeTests.java @@ -21,6 +21,8 @@ import com.carrotsearch.randomizedtesting.generators.RandomPicks; import org.apache.lucene.document.DoubleRange; import org.apache.lucene.document.FloatRange; +import org.apache.lucene.document.InetAddressPoint; +import org.apache.lucene.document.InetAddressRange; import org.apache.lucene.document.IntRange; import org.apache.lucene.document.LongRange; import org.apache.lucene.index.IndexOptions; @@ -37,6 +39,7 @@ import org.joda.time.DateTime; import org.junit.Before; +import java.net.InetAddress; import java.util.Locale; public class RangeFieldTypeTests extends FieldTypeTestCase { @@ -100,6 +103,8 @@ private Query getExpectedRangeQuery(ShapeRelation relation, Object from, Object return getLongRangeQuery(relation, (long)from, (long)to, includeLower, includeUpper); case DOUBLE: return getDoubleRangeQuery(relation, (double)from, (double)to, includeLower, includeUpper); + case IP: + return getInetAddressRangeQuery(relation, (InetAddress)from, (InetAddress)to, includeLower, includeUpper); default: return getFloatRangeQuery(relation, (float)from, (float)to, includeLower, includeUpper); } @@ -142,7 +147,8 @@ private Query getFloatRangeQuery(ShapeRelation relation, float from, float to, b return FloatRange.newIntersectsQuery(FIELDNAME, lower, upper); } - private Query getDoubleRangeQuery(ShapeRelation relation, double from, double to, boolean includeLower, boolean includeUpper) { + private Query getDoubleRangeQuery(ShapeRelation relation, double from, double to, boolean includeLower, + boolean includeUpper) { double[] lower = new double[] {includeLower ? from : Math.nextUp(from)}; double[] upper = new double[] {includeUpper ? to : Math.nextDown(to)}; if (relation == ShapeRelation.WITHIN) { @@ -153,7 +159,19 @@ private Query getDoubleRangeQuery(ShapeRelation relation, double from, double to return DoubleRange.newIntersectsQuery(FIELDNAME, lower, upper); } - private Object nextFrom() { + private Query getInetAddressRangeQuery(ShapeRelation relation, InetAddress from, InetAddress to, boolean includeLower, + boolean includeUpper) { + InetAddress lower = includeLower ? from : InetAddressPoint.nextUp(from); + InetAddress upper = includeUpper ? to : InetAddressPoint.nextDown(to); + if (relation == ShapeRelation.WITHIN) { + return InetAddressRange.newWithinQuery(FIELDNAME, lower, upper); + } else if (relation == ShapeRelation.CONTAINS) { + return InetAddressRange.newContainsQuery(FIELDNAME, lower, upper); + } + return InetAddressRange.newIntersectsQuery(FIELDNAME, lower, upper); + } + + private Object nextFrom() throws Exception { switch (type) { case INTEGER: return (int)(random().nextInt() * 0.5 - DISTANCE); @@ -163,12 +181,14 @@ private Object nextFrom() { return (long)(random().nextLong() * 0.5 - DISTANCE); case FLOAT: return (float)(random().nextFloat() * 0.5 - DISTANCE); + case IP: + return InetAddress.getByName("::ffff:c0a8:107"); default: return random().nextDouble() * 0.5 - DISTANCE; } } - private Object nextTo(Object from) { + private Object nextTo(Object from) throws Exception { switch (type) { case INTEGER: return (Integer)from + DISTANCE; @@ -178,6 +198,8 @@ private Object nextTo(Object from) { return (Long)from + DISTANCE; case DOUBLE: return (Double)from + DISTANCE; + case IP: + return InetAddress.getByName("2001:db8::"); default: return (Float)from + DISTANCE; } diff --git a/docs/reference/mapping/types/range.asciidoc b/docs/reference/mapping/types/range.asciidoc index d4bf76b0cef44..4ac7ec03f61ea 100644 --- a/docs/reference/mapping/types/range.asciidoc +++ b/docs/reference/mapping/types/range.asciidoc @@ -9,6 +9,8 @@ The following range types are supported: `long_range`:: A range of signed 64-bit integers with a minimum value of +-2^63^+ and maximum of +2^63^-1+. `double_range`:: A range of double-precision 64-bit IEEE 754 floating point values. `date_range`:: A range of date values represented as unsigned 64-bit integer milliseconds elapsed since system epoch. +`ip_range` :: A range of ip values supporting either https://en.wikipedia.org/wiki/IPv4[IPv4] or + https://en.wikipedia.org/wiki/IPv6[IPv6] (or mixed) addresses. Below is an example of configuring a mapping with various range fields followed by an example that indexes several range types. diff --git a/docs/reference/release-notes/6.0.0-alpha1.asciidoc b/docs/reference/release-notes/6.0.0-alpha1.asciidoc index d66d3ba0ed110..3af5dc49df823 100644 --- a/docs/reference/release-notes/6.0.0-alpha1.asciidoc +++ b/docs/reference/release-notes/6.0.0-alpha1.asciidoc @@ -147,6 +147,7 @@ Internal:: Core:: * Enable index-time sorting {pull}24055[#24055] (issue: {issue}6720[#6720]) +* Add new ip_range field type {pull}24433[#24433] From 0b36fb052c10eea5b248bfab71e5caffaf51ef92 Mon Sep 17 00:00:00 2001 From: Anupam Date: Fri, 5 May 2017 21:03:32 +0530 Subject: [PATCH 225/619] Update completion-suggest.asciidoc (#24506) --- docs/reference/search/suggesters/completion-suggest.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/search/suggesters/completion-suggest.asciidoc b/docs/reference/search/suggesters/completion-suggest.asciidoc index 6bf17a4ac39fc..3f21a18de72b4 100644 --- a/docs/reference/search/suggesters/completion-suggest.asciidoc +++ b/docs/reference/search/suggesters/completion-suggest.asciidoc @@ -130,7 +130,7 @@ PUT music/song/1?refresh // TEST[continued] You can use the following shorthand form. Note that you can not specify -a weight with suggestion(s). +a weight with suggestion(s) in the shorthand form. [source,js] -------------------------------------------------- From 7bd2abe48af5a651b54bcd5bcb41c88e29390be0 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Fri, 5 May 2017 20:00:39 +0200 Subject: [PATCH 226/619] Change Terms.Bucket to an interface (#24492) This commit changes the Terms.Bucket abstract class to an interface, so that it's easier for the Java High Level Rest Client to provide its own implementation. In its current state, the Terms.Bucket abstract class inherits from InternalMultiBucketAggregation.InternalBucket which forces subclasses to implement Writeable and exposes a public getProperty() method that relies on InternalAggregation. This two points make it difficult for the Java High Level Rest Client to implement the Terms and Terms.Bucket correctly. This is also different from other MultiBucketsAggregation like Range which are pure interfaces. Changing Terms.Bucket to an interface causes a method clashes for the `getBuckets()` method in InternalTerms. This is because: - InternalTerms implements Terms which declared a `List getBuckets()` method - InternalTerms extends InternalMultiBucketAggregation which declares a `List getBuckets()` method - both overrides the MultiBucketsAggregation `List getBuckets()` method There was no clashes before this change because Terms.Bucket extends InternalBucket and conformed to both declaration. With Terms.Bucket now an interface, the getBuckets() method in the Terms interface is changed to avoid method clash. This is a breaking change in the Java API but it's a straightforward change and the Terms multi bucket aggregation interface is also more coherent with the other Range, Histogram, Filters, AdjacencyMatrix etc that all return a `List`. --- .../bucket/terms/DoubleTerms.java | 2 +- .../GlobalOrdinalsStringTermsAggregator.java | 2 +- .../bucket/terms/InternalMappedTerms.java | 3 +- .../bucket/terms/InternalTerms.java | 16 +++----- .../aggregations/bucket/terms/LongTerms.java | 4 +- .../bucket/terms/StringTerms.java | 2 +- .../aggregations/bucket/terms/Terms.java | 38 +++---------------- .../bucket/terms/UnmappedTerms.java | 2 +- .../search/aggregations/CombiIT.java | 4 +- .../search/aggregations/EquivalenceIT.java | 6 +-- .../bucket/DiversifiedSamplerIT.java | 11 +++--- .../aggregations/bucket/DoubleTermsIT.java | 4 +- .../aggregations/bucket/LongTermsIT.java | 4 +- .../aggregations/bucket/MinDocCountIT.java | 4 +- .../search/aggregations/bucket/SamplerIT.java | 5 ++- .../aggregations/bucket/ShardSizeTermsIT.java | 32 ++++++++-------- .../SignificantTermsSignificanceScoreIT.java | 2 +- .../aggregations/bucket/StringTermsIT.java | 8 ++-- .../bucket/TermsDocCountErrorIT.java | 10 ++--- .../bucket/terms/TermsAggregatorTests.java | 2 +- .../search/aggregations/metrics/AvgIT.java | 2 +- .../aggregations/metrics/ExtendedStatsIT.java | 2 +- .../aggregations/metrics/GeoBoundsIT.java | 2 +- .../metrics/HDRPercentileRanksIT.java | 2 +- .../metrics/HDRPercentilesIT.java | 2 +- .../search/aggregations/metrics/MaxIT.java | 2 +- .../search/aggregations/metrics/MinIT.java | 2 +- .../search/aggregations/metrics/StatsIT.java | 2 +- .../search/aggregations/metrics/SumIT.java | 2 +- .../metrics/TDigestPercentileRanksIT.java | 2 +- .../metrics/TDigestPercentilesIT.java | 2 +- .../aggregations/metrics/TopHitsIT.java | 8 ++-- .../aggregations/pipeline/AvgBucketIT.java | 12 +++--- .../pipeline/ExtendedStatsBucketIT.java | 12 +++--- .../aggregations/pipeline/MaxBucketIT.java | 12 +++--- .../aggregations/pipeline/MinBucketIT.java | 12 +++--- .../pipeline/PercentilesBucketIT.java | 18 ++++----- .../aggregations/pipeline/StatsBucketIT.java | 12 +++--- .../aggregations/pipeline/SumBucketIT.java | 12 +++--- 39 files changed, 125 insertions(+), 156 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java index ae18cb59d9523..e4c7906f21586 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java @@ -76,7 +76,7 @@ public Number getKeyAsNumber() { } @Override - int compareTerm(Terms.Bucket other) { + public int compareTerm(Terms.Bucket other) { return Double.compare(term, ((Number) other.getKey()).doubleValue()); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/GlobalOrdinalsStringTermsAggregator.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/GlobalOrdinalsStringTermsAggregator.java index 5ef96d19257cc..f315d915f0dc6 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/GlobalOrdinalsStringTermsAggregator.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/GlobalOrdinalsStringTermsAggregator.java @@ -218,7 +218,7 @@ static class OrdBucket extends InternalTerms.Bucket { } @Override - int compareTerm(Terms.Bucket other) { + public int compareTerm(Terms.Bucket other) { return Long.compare(globalOrd, ((OrdBucket) other).globalOrd); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalMappedTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalMappedTerms.java index 21c9c461d0f07..57c80c5fb40f0 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalMappedTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalMappedTerms.java @@ -26,6 +26,7 @@ import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.io.IOException; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -101,7 +102,7 @@ public long getSumOfOtherDocCounts() { } @Override - public List getBucketsInternal() { + public List getBuckets() { return buckets; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTerms.java index 0fb4ceea33a4e..3834f9a65be53 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTerms.java @@ -40,15 +40,13 @@ import java.util.Map; import java.util.Objects; -import static java.util.Collections.unmodifiableList; - public abstract class InternalTerms, B extends InternalTerms.Bucket> extends InternalMultiBucketAggregation implements Terms, ToXContent { protected static final ParseField DOC_COUNT_ERROR_UPPER_BOUND_FIELD_NAME = new ParseField("doc_count_error_upper_bound"); protected static final ParseField SUM_OF_OTHER_DOC_COUNTS = new ParseField("sum_other_doc_count"); - public abstract static class Bucket> extends Terms.Bucket { + public abstract static class Bucket> extends InternalMultiBucketAggregation.InternalBucket implements Terms.Bucket { /** * Reads a bucket. Should be a constructor reference. @@ -212,11 +210,7 @@ protected final void doWriteTo(StreamOutput out) throws IOException { protected abstract void writeTermTypeInfoTo(StreamOutput out) throws IOException; @Override - public final List getBuckets() { - return unmodifiableList(getBucketsInternal()); - } - - protected abstract List getBucketsInternal(); + public abstract List getBuckets(); @Override public abstract B getBucketByKey(String term); @@ -244,7 +238,7 @@ public InternalAggregation doReduce(List aggregations, Redu } otherDocCount += terms.getSumOfOtherDocCounts(); final long thisAggDocCountError; - if (terms.getBucketsInternal().size() < getShardSize() || InternalOrder.isTermOrder(order)) { + if (terms.getBuckets().size() < getShardSize() || InternalOrder.isTermOrder(order)) { thisAggDocCountError = 0; } else if (InternalOrder.isCountDesc(this.order)) { if (terms.getDocCountError() > 0) { @@ -254,7 +248,7 @@ public InternalAggregation doReduce(List aggregations, Redu } else { // otherwise use the doc count of the last term in the // aggregation - thisAggDocCountError = terms.getBucketsInternal().get(terms.getBucketsInternal().size() - 1).docCount; + thisAggDocCountError = terms.getBuckets().get(terms.getBuckets().size() - 1).docCount; } } else { thisAggDocCountError = -1; @@ -267,7 +261,7 @@ public InternalAggregation doReduce(List aggregations, Redu } } setDocCountError(thisAggDocCountError); - for (B bucket : terms.getBucketsInternal()) { + for (B bucket : terms.getBuckets()) { // If there is already a doc count error for this bucket // subtract this aggs doc count error from it to make the // new value for the bucket. This then means that when the diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTerms.java index 98aa4825ee7a3..0de13a4d98f6b 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTerms.java @@ -76,7 +76,7 @@ public Number getKeyAsNumber() { } @Override - int compareTerm(Terms.Bucket other) { + public int compareTerm(Terms.Bucket other) { return Long.compare(term, ((Number) other.getKey()).longValue()); } @@ -161,7 +161,7 @@ public InternalAggregation doReduce(List aggregations, Redu * Converts a {@link LongTerms} into a {@link DoubleTerms}, returning the value of the specified long terms as doubles. */ static DoubleTerms convertLongTermsToDouble(LongTerms longTerms, DocValueFormat decimalFormat) { - List buckets = longTerms.getBuckets(); + List buckets = longTerms.getBuckets(); List newBuckets = new ArrayList<>(); for (Terms.Bucket bucket : buckets) { newBuckets.add(new DoubleTerms.Bucket(bucket.getKeyAsNumber().doubleValue(), diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/StringTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/StringTerms.java index b48c443fac93a..049d996c08c2e 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/StringTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/StringTerms.java @@ -75,7 +75,7 @@ public String getKeyAsString() { } @Override - int compareTerm(Terms.Bucket other) { + public int compareTerm(Terms.Bucket other) { return termBytes.compareTo(((Bucket) other).termBytes); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/Terms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/Terms.java index e4ff4cd394400..166ece4e1122d 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/Terms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/Terms.java @@ -20,7 +20,6 @@ import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.search.aggregations.Aggregator; -import org.elasticsearch.search.aggregations.InternalMultiBucketAggregation; import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import java.util.Arrays; @@ -33,50 +32,23 @@ */ public interface Terms extends MultiBucketsAggregation { - enum ValueType { - - STRING(org.elasticsearch.search.aggregations.support.ValueType.STRING), - LONG(org.elasticsearch.search.aggregations.support.ValueType.LONG), - DOUBLE(org.elasticsearch.search.aggregations.support.ValueType.DOUBLE); - - final org.elasticsearch.search.aggregations.support.ValueType scriptValueType; - - ValueType(org.elasticsearch.search.aggregations.support.ValueType scriptValueType) { - this.scriptValueType = scriptValueType; - } - - static ValueType resolveType(String type) { - if ("string".equals(type)) { - return STRING; - } - if ("double".equals(type) || "float".equals(type)) { - return DOUBLE; - } - if ("long".equals(type) || "integer".equals(type) || "short".equals(type) || "byte".equals(type)) { - return LONG; - } - return null; - } - } - /** * A bucket that is associated with a single term */ - abstract class Bucket extends InternalMultiBucketAggregation.InternalBucket { - - public abstract Number getKeyAsNumber(); + interface Bucket extends MultiBucketsAggregation.Bucket { - abstract int compareTerm(Terms.Bucket other); + Number getKeyAsNumber(); - public abstract long getDocCountError(); + int compareTerm(Terms.Bucket other); + long getDocCountError(); } /** * Return the sorted list of the buckets in this terms aggregation. */ @Override - List getBuckets(); + List getBuckets(); /** * Get the bucket for the given term, or null if there is no such bucket. diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/UnmappedTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/UnmappedTerms.java index bdc95b2e87e96..40cbacd37e698 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/UnmappedTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/UnmappedTerms.java @@ -127,7 +127,7 @@ public long getSumOfOtherDocCounts() { } @Override - protected List getBucketsInternal() { + public List getBuckets() { return emptyList(); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/CombiIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/CombiIT.java index 7517e98ff277f..c38552ae25410 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/CombiIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/CombiIT.java @@ -30,7 +30,7 @@ import org.elasticsearch.test.ESIntegTestCase; import org.hamcrest.Matchers; -import java.util.Collection; +import java.util.List; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; @@ -95,7 +95,7 @@ public void testMultipleAggsOnSameField_WithDifferentRequiredValueSourceType() t Terms terms = aggs.get("values"); assertNotNull(terms); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(values.size())); for (Terms.Bucket bucket : buckets) { values.remove(((Number) bucket.getKey()).intValue()); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/EquivalenceIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/EquivalenceIT.java index 00f82dfbe4423..6c08c1697244f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/EquivalenceIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/EquivalenceIT.java @@ -485,10 +485,10 @@ public void testReduce() throws Exception { } private void assertEquals(Terms t1, Terms t2) { - List t1Buckets = t1.getBuckets(); - List t2Buckets = t1.getBuckets(); + List t1Buckets = t1.getBuckets(); + List t2Buckets = t1.getBuckets(); assertEquals(t1Buckets.size(), t2Buckets.size()); - for (Iterator it1 = t1Buckets.iterator(), it2 = t2Buckets.iterator(); it1.hasNext(); ) { + for (Iterator it1 = t1Buckets.iterator(), it2 = t2Buckets.iterator(); it1.hasNext(); ) { final Terms.Bucket b1 = it1.next(); final Terms.Bucket b2 = it2.next(); assertEquals(b1.getDocCount(), b2.getDocCount()); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java index 9af28806fef6c..c8803b7e790e1 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java @@ -32,6 +32,7 @@ import org.elasticsearch.test.ESIntegTestCase; import java.util.Collection; +import java.util.List; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_SHARDS; @@ -108,7 +109,7 @@ public void testIssue10719() throws Exception { ).execute().actionGet(); assertSearchResponse(response); Terms genres = response.getAggregations().get("genres"); - Collection genreBuckets = genres.getBuckets(); + Collection genreBuckets = genres.getBuckets(); // For this test to be useful we need >1 genre bucket to compare assertThat(genreBuckets.size(), greaterThan(1)); double lastMaxPrice = asc ? Double.MIN_VALUE : Double.MAX_VALUE; @@ -141,7 +142,7 @@ public void testSimpleDiversity() throws Exception { assertSearchResponse(response); Sampler sample = response.getAggregations().get("sample"); Terms authors = sample.getAggregations().get("authors"); - Collection testBuckets = authors.getBuckets(); + List testBuckets = authors.getBuckets(); for (Terms.Bucket testBucket : testBuckets) { assertThat(testBucket.getDocCount(), lessThanOrEqualTo((long) NUM_SHARDS * MAX_DOCS_PER_AUTHOR)); @@ -162,11 +163,11 @@ public void testNestedDiversity() throws Exception { .addAggregation(rootTerms).execute().actionGet(); assertSearchResponse(response); Terms genres = response.getAggregations().get("genres"); - Collection genreBuckets = genres.getBuckets(); + List genreBuckets = genres.getBuckets(); for (Terms.Bucket genreBucket : genreBuckets) { Sampler sample = genreBucket.getAggregations().get("sample"); Terms authors = sample.getAggregations().get("authors"); - Collection testBuckets = authors.getBuckets(); + List testBuckets = authors.getBuckets(); for (Terms.Bucket testBucket : testBuckets) { assertThat(testBucket.getDocCount(), lessThanOrEqualTo((long) NUM_SHARDS * MAX_DOCS_PER_AUTHOR)); @@ -195,7 +196,7 @@ public void testNestedSamples() throws Exception { Sampler sample = genreSample.getAggregations().get("sample"); Terms genres = sample.getAggregations().get("genres"); - Collection testBuckets = genres.getBuckets(); + List testBuckets = genres.getBuckets(); for (Terms.Bucket testBucket : testBuckets) { assertThat(testBucket.getDocCount(), lessThanOrEqualTo((long) NUM_SHARDS * MAX_DOCS_PER_GENRE)); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java index cf28541121c12..ca106721fcc99 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java @@ -842,7 +842,7 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws assertThat(tags.getName(), equalTo("num_tags")); assertThat(tags.getBuckets().size(), equalTo(2)); - Iterator iters = tags.getBuckets().iterator(); + Iterator iters = tags.getBuckets().iterator(); Terms.Bucket tag = iters.next(); assertThat(tag, notNullValue()); @@ -883,7 +883,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels( assertThat(tags.getName(), equalTo("tags")); assertThat(tags.getBuckets().size(), equalTo(2)); - Iterator iters = tags.getBuckets().iterator(); + Iterator iters = tags.getBuckets().iterator(); // the max for "1" is 2 // the max for "0" is 4 diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java index 6793768a91fe2..a54dc3e2f5edf 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java @@ -854,7 +854,7 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws assertThat(tags.getName(), equalTo("num_tags")); assertThat(tags.getBuckets().size(), equalTo(2)); - Iterator iters = tags.getBuckets().iterator(); + Iterator iters = tags.getBuckets().iterator(); Terms.Bucket tag = iters.next(); assertThat(tag, notNullValue()); @@ -893,7 +893,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels( assertThat(tags.getName(), equalTo("tags")); assertThat(tags.getBuckets().size(), equalTo(2)); - Iterator iters = tags.getBuckets().iterator(); + Iterator iters = tags.getBuckets().iterator(); // the max for "1" is 2 // the max for "0" is 4 diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java index 7307b756e3f1f..e1e8f1ba660cd 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java @@ -161,8 +161,8 @@ TermsAggregationBuilder apply(TermsAggregationBuilder builder, String field) { // check that terms2 is a subset of terms1 private void assertSubset(Terms terms1, Terms terms2, long minDocCount, int size, String include) { final Matcher matcher = include == null ? null : Pattern.compile(include).matcher("");; - final Iterator it1 = terms1.getBuckets().iterator(); - final Iterator it2 = terms2.getBuckets().iterator(); + final Iterator it1 = terms1.getBuckets().iterator(); + final Iterator it2 = terms2.getBuckets().iterator(); int size2 = 0; while (it1.hasNext()) { final Terms.Bucket bucket1 = it1.next(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java index f87b98bc87813..ebd078de67402 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java @@ -31,6 +31,7 @@ import org.elasticsearch.test.ESIntegTestCase; import java.util.Collection; +import java.util.List; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_SHARDS; @@ -104,7 +105,7 @@ public void testIssue10719() throws Exception { ).execute().actionGet(); assertSearchResponse(response); Terms genres = response.getAggregations().get("genres"); - Collection genreBuckets = genres.getBuckets(); + List genreBuckets = genres.getBuckets(); // For this test to be useful we need >1 genre bucket to compare assertThat(genreBuckets.size(), greaterThan(1)); double lastMaxPrice = asc ? Double.MIN_VALUE : Double.MAX_VALUE; @@ -130,7 +131,7 @@ public void testSimpleSampler() throws Exception { assertSearchResponse(response); Sampler sample = response.getAggregations().get("sample"); Terms authors = sample.getAggregations().get("authors"); - Collection testBuckets = authors.getBuckets(); + List testBuckets = authors.getBuckets(); long maxBooksPerAuthor = 0; for (Terms.Bucket testBucket : testBuckets) { diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTermsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTermsIT.java index ecc16b85f1312..748c5f886f669 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTermsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTermsIT.java @@ -22,8 +22,8 @@ import org.elasticsearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; @@ -43,7 +43,7 @@ public void testNoShardSizeString() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); Map expected = new HashMap<>(); expected.put("1", 8L); @@ -66,7 +66,7 @@ public void testShardSizeEqualsSizeString() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); Map expected = new HashMap<>(); expected.put("1", 8L); @@ -90,7 +90,7 @@ public void testWithShardSizeString() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); // we still only return 3 entries (based on the 'size' param) Map expected = new HashMap<>(); expected.put("1", 8L); @@ -114,7 +114,7 @@ public void testWithShardSizeStringSingleShard() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); // we still only return 3 entries (based on the 'size' param) Map expected = new HashMap<>(); expected.put("1", 5L); @@ -137,7 +137,7 @@ public void testNoShardSizeTermOrderString() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); Map expected = new HashMap<>(); expected.put("1", 8L); @@ -160,7 +160,7 @@ public void testNoShardSizeLong() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); Map expected = new HashMap<>(); expected.put(1, 8L); @@ -183,7 +183,7 @@ public void testShardSizeEqualsSizeLong() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); Map expected = new HashMap<>(); expected.put(1, 8L); @@ -206,7 +206,7 @@ public void testWithShardSizeLong() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); // we still only return 3 entries (based on the 'size' param) Map expected = new HashMap<>(); expected.put(1, 8L); @@ -230,7 +230,7 @@ public void testWithShardSizeLongSingleShard() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); // we still only return 3 entries (based on the 'size' param) Map expected = new HashMap<>(); expected.put(1, 5L); @@ -253,7 +253,7 @@ public void testNoShardSizeTermOrderLong() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); Map expected = new HashMap<>(); expected.put(1, 8L); @@ -276,7 +276,7 @@ public void testNoShardSizeDouble() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); Map expected = new HashMap<>(); expected.put(1, 8L); @@ -299,7 +299,7 @@ public void testShardSizeEqualsSizeDouble() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); Map expected = new HashMap<>(); expected.put(1, 8L); @@ -322,7 +322,7 @@ public void testWithShardSizeDouble() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); Map expected = new HashMap<>(); expected.put(1, 8L); @@ -345,7 +345,7 @@ public void testWithShardSizeDoubleSingleShard() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); Map expected = new HashMap<>(); expected.put(1, 5L); @@ -368,7 +368,7 @@ public void testNoShardSizeTermOrderDouble() throws Exception { .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); - Collection buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(3)); Map expected = new HashMap<>(); expected.put(1, 8L); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java index 7fc20908e81e9..09cec9a458fce 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java @@ -438,7 +438,7 @@ public void testScoresEqualForPositiveAndNegative(SignificanceHeuristic heuristi assertSearchResponse(response); StringTerms classes = response.getAggregations().get("class"); assertThat(classes.getBuckets().size(), equalTo(2)); - Iterator classBuckets = classes.getBuckets().iterator(); + Iterator classBuckets = classes.getBuckets().iterator(); Aggregations aggregations = classBuckets.next().getAggregations(); SignificantTerms sigTerms = aggregations.get("mySignificantTerms"); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/StringTermsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/StringTermsIT.java index b5dbcd9085a86..df69cfcfa9383 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/StringTermsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/StringTermsIT.java @@ -1012,7 +1012,7 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws assertThat(tags.getName(), equalTo("tags")); assertThat(tags.getBuckets().size(), equalTo(2)); - Iterator iters = tags.getBuckets().iterator(); + Iterator iters = tags.getBuckets().iterator(); Terms.Bucket tag = iters.next(); assertThat(tag, notNullValue()); @@ -1054,7 +1054,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels( assertThat(tags.getName(), equalTo("tags")); assertThat(tags.getBuckets().size(), equalTo(2)); - Iterator iters = tags.getBuckets().iterator(); + Iterator iters = tags.getBuckets().iterator(); // the max for "more" is 2 // the max for "less" is 4 @@ -1117,7 +1117,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevelsS assertThat(tags.getName(), equalTo("tags")); assertThat(tags.getBuckets().size(), equalTo(2)); - Iterator iters = tags.getBuckets().iterator(); + Iterator iters = tags.getBuckets().iterator(); // the max for "more" is 2 // the max for "less" is 4 @@ -1180,7 +1180,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevelsS assertThat(tags.getName(), equalTo("tags")); assertThat(tags.getBuckets().size(), equalTo(2)); - Iterator iters = tags.getBuckets().iterator(); + Iterator iters = tags.getBuckets().iterator(); // the max for "more" is 2 // the max for "less" is 4 diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java index 9d5ca3afc549b..9ed32ca2e7b34 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java @@ -177,7 +177,7 @@ private void assertDocCountErrorWithinBounds(int size, SearchResponse accurateRe assertThat(testTerms, notNullValue()); assertThat(testTerms.getName(), equalTo("terms")); assertThat(testTerms.getDocCountError(), greaterThanOrEqualTo(0L)); - Collection testBuckets = testTerms.getBuckets(); + List testBuckets = testTerms.getBuckets(); assertThat(testBuckets.size(), lessThanOrEqualTo(size)); assertThat(accurateTerms.getBuckets().size(), greaterThanOrEqualTo(testBuckets.size())); @@ -211,7 +211,7 @@ private void assertNoDocCountError(int size, SearchResponse accurateResponse, Se assertThat(testTerms, notNullValue()); assertThat(testTerms.getName(), equalTo("terms")); assertThat(testTerms.getDocCountError(), equalTo(0L)); - Collection testBuckets = testTerms.getBuckets(); + List testBuckets = testTerms.getBuckets(); assertThat(testBuckets.size(), lessThanOrEqualTo(size)); assertThat(accurateTerms.getBuckets().size(), greaterThanOrEqualTo(testBuckets.size())); @@ -229,7 +229,7 @@ private void assertNoDocCountErrorSingleResponse(int size, SearchResponse testRe assertThat(testTerms, notNullValue()); assertThat(testTerms.getName(), equalTo("terms")); assertThat(testTerms.getDocCountError(), equalTo(0L)); - Collection testBuckets = testTerms.getBuckets(); + List testBuckets = testTerms.getBuckets(); assertThat(testBuckets.size(), lessThanOrEqualTo(size)); for (Terms.Bucket testBucket : testBuckets) { @@ -248,7 +248,7 @@ private void assertUnboundedDocCountError(int size, SearchResponse accurateRespo assertThat(testTerms, notNullValue()); assertThat(testTerms.getName(), equalTo("terms")); assertThat(testTerms.getDocCountError(),anyOf(equalTo(-1L), equalTo(0L))); - Collection testBuckets = testTerms.getBuckets(); + List testBuckets = testTerms.getBuckets(); assertThat(testBuckets.size(), lessThanOrEqualTo(size)); assertThat(accurateTerms.getBuckets().size(), greaterThanOrEqualTo(testBuckets.size())); @@ -988,7 +988,7 @@ public void testFixedDocs() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getDocCountError(), equalTo(46L)); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets, notNullValue()); assertThat(buckets.size(), equalTo(5)); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java index f2977fd769205..1648d8ede9fdf 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java @@ -136,7 +136,7 @@ public void testMixLongAndDouble() throws Exception { InternalAggregation mergedAggs = internalAgg.doReduce(aggs, ctx); assertTrue(mergedAggs instanceof DoubleTerms); long expected = numLongs + numDoubles; - List buckets = ((DoubleTerms) mergedAggs).getBuckets(); + List buckets = ((DoubleTerms) mergedAggs).getBuckets(); assertEquals(4, buckets.size()); assertEquals("1.0", buckets.get(0).getKeyAsString()); assertEquals(expected, buckets.get(0).getDocCount()); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AvgIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AvgIT.java index 2e2aa9657d839..b7739d6c816a1 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AvgIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AvgIT.java @@ -334,7 +334,7 @@ public void testOrderByEmptyAggregation() throws Exception { Terms terms = searchResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets, notNullValue()); assertThat(buckets.size(), equalTo(10)); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java index 7de7bb0f31596..3903dd8b0bc76 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java @@ -604,7 +604,7 @@ public void testOrderByEmptyAggregation() throws Exception { Terms terms = searchResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets, notNullValue()); assertThat(buckets.size(), equalTo(10)); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/GeoBoundsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/GeoBoundsIT.java index e4444571a31b4..1a97cb49164a4 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/GeoBoundsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/GeoBoundsIT.java @@ -233,7 +233,7 @@ public void testSingleValuedFieldAsSubAggToHighCardTermsAgg() { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(10)); for (int i = 0; i < 10; i++) { Bucket bucket = buckets.get(i); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java index 0aef20bca6d67..5b56e6b7efbf2 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java @@ -515,7 +515,7 @@ public void testOrderByEmptyAggregation() throws Exception { Terms terms = searchResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets, notNullValue()); assertThat(buckets.size(), equalTo(10)); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java index d3531da9250f5..56fb14402ad5d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java @@ -506,7 +506,7 @@ public void testOrderByEmptyAggregation() throws Exception { Terms terms = searchResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets, notNullValue()); assertThat(buckets.size(), equalTo(10)); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MaxIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MaxIT.java index 5c9e14c3453ad..03eb9a092372a 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MaxIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MaxIT.java @@ -336,7 +336,7 @@ public void testOrderByEmptyAggregation() throws Exception { Terms terms = searchResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets, notNullValue()); assertThat(buckets.size(), equalTo(10)); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MinIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MinIT.java index 2b517b64fa1a1..cba2ba9eb97c8 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MinIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MinIT.java @@ -348,7 +348,7 @@ public void testOrderByEmptyAggregation() throws Exception { Terms terms = searchResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets, notNullValue()); assertThat(buckets.size(), equalTo(10)); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java index 85db61663072a..9231f09396307 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java @@ -455,7 +455,7 @@ public void testOrderByEmptyAggregation() throws Exception { Terms terms = searchResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets, notNullValue()); assertThat(buckets.size(), equalTo(10)); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java index 1591e6df9313d..16d345c7b842d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java @@ -333,7 +333,7 @@ public void testOrderByEmptyAggregation() throws Exception { Terms terms = searchResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets, notNullValue()); assertThat(buckets.size(), equalTo(10)); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java index 057731e275a08..f1943747ceb43 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java @@ -466,7 +466,7 @@ public void testOrderByEmptyAggregation() throws Exception { Terms terms = searchResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets, notNullValue()); assertThat(buckets.size(), equalTo(10)); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java index d81ca004b97b4..2589e9977a6c9 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java @@ -451,7 +451,7 @@ public void testOrderByEmptyAggregation() throws Exception { Terms terms = searchResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets, notNullValue()); assertThat(buckets.size(), equalTo(10)); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java index aeef44d62726d..e9b9ae20407da 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java @@ -519,9 +519,9 @@ public void testSortByBucket() throws Exception { SearchHits hits = topHits.getHits(); assertThat(hits.getTotalHits(), equalTo(10L)); assertThat(hits.getHits().length, equalTo(3)); - assertThat((Long) hits.getAt(0).getSortValues()[0], equalTo(higestSortValue)); - assertThat((Long) hits.getAt(1).getSortValues()[0], equalTo(higestSortValue - 1)); - assertThat((Long) hits.getAt(2).getSortValues()[0], equalTo(higestSortValue - 2)); + assertThat(hits.getAt(0).getSortValues()[0], equalTo(higestSortValue)); + assertThat(hits.getAt(1).getSortValues()[0], equalTo(higestSortValue - 1)); + assertThat(hits.getAt(2).getSortValues()[0], equalTo(higestSortValue - 2)); Max max = bucket.getAggregations().get("max_sort"); assertThat(max.getValue(), equalTo(((Long) higestSortValue).doubleValue())); higestSortValue -= 10; @@ -544,7 +544,7 @@ public void testFieldCollapsing() throws Exception { assertThat(terms.getName(), equalTo("terms")); assertThat(terms.getBuckets().size(), equalTo(3)); - Iterator bucketIterator = terms.getBuckets().iterator(); + Iterator bucketIterator = terms.getBuckets().iterator(); Terms.Bucket bucket = bucketIterator.next(); assertThat(key(bucket), equalTo("b")); TopHits topHits = bucket.getAggregations().get("hits"); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/AvgBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/AvgBucketIT.java index d62e93fa5e9ac..4f6ff0e32eda8 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/AvgBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/AvgBucketIT.java @@ -140,7 +140,7 @@ public void testDocCountAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -182,7 +182,7 @@ public void testMetricTopLevel() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(interval)); double bucketSum = 0; @@ -223,7 +223,7 @@ public void testMetricAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -277,7 +277,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -322,7 +322,7 @@ public void testNoBuckets() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(0)); InternalSimpleValue avgBucketValue = response.getAggregations().get("avg_bucket"); @@ -349,7 +349,7 @@ public void testNested() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); double aggTermsSum = 0; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java index d4310e581c026..607124ecb159d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java @@ -211,7 +211,7 @@ public void testDocCountAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -262,7 +262,7 @@ public void testMetricTopLevel() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(interval)); double bucketSum = 0; @@ -312,7 +312,7 @@ public void testMetricAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -375,7 +375,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -429,7 +429,7 @@ public void testNoBuckets() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(0)); ExtendedStatsBucket extendedStatsBucketValue = response.getAggregations().get("extended_stats_bucket"); @@ -487,7 +487,7 @@ public void testNested() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); double aggTermsSum = 0; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MaxBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MaxBucketIT.java index aba941ebb4b6a..632f11f7ec73d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MaxBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MaxBucketIT.java @@ -149,7 +149,7 @@ public void testDocCountAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -196,7 +196,7 @@ public void testMetricTopLevel() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(interval)); List maxKeys = new ArrayList<>(); @@ -242,7 +242,7 @@ public void testMetricAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -348,7 +348,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -397,7 +397,7 @@ public void testNoBuckets() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(0)); InternalBucketMetricValue maxBucketValue = response.getAggregations().get("max_bucket"); @@ -425,7 +425,7 @@ public void testNested() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); List maxTermsKeys = new ArrayList<>(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MinBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MinBucketIT.java index cbd6824b3a4a3..04fdd0c313354 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MinBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MinBucketIT.java @@ -146,7 +146,7 @@ public void testDocCountAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -193,7 +193,7 @@ public void testMetricTopLevel() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(interval)); List minKeys = new ArrayList<>(); @@ -239,7 +239,7 @@ public void testMetricAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -298,7 +298,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -347,7 +347,7 @@ public void testNoBuckets() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(0)); InternalBucketMetricValue minBucketValue = response.getAggregations().get("min_bucket"); @@ -375,7 +375,7 @@ public void testNested() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); List minTermsKeys = new ArrayList<>(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java index c582c76bd8c92..e23e5441431eb 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java @@ -145,7 +145,7 @@ public void testDocCountAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -187,7 +187,7 @@ public void testMetricTopLevel() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(interval)); double[] values = new double[interval]; @@ -220,7 +220,7 @@ public void testMetricTopLevelDefaultPercents() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(interval)); double[] values = new double[interval]; @@ -261,7 +261,7 @@ public void testMetricAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -316,7 +316,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -361,7 +361,7 @@ public void testNoBuckets() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(0)); PercentilesBucket percentilesBucketValue = response.getAggregations().get("percentiles_bucket"); @@ -384,7 +384,7 @@ public void testWrongPercents() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(0)); PercentilesBucket percentilesBucketValue = response.getAggregations().get("percentiles_bucket"); @@ -479,7 +479,7 @@ public void testNested() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); double[] values = new double[termsBuckets.size()]; @@ -539,7 +539,7 @@ public void testNestedWithDecimal() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); double[] values = new double[termsBuckets.size()]; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/StatsBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/StatsBucketIT.java index c38dc99bdf939..231005f1b5b62 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/StatsBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/StatsBucketIT.java @@ -147,7 +147,7 @@ public void testDocCountAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -195,7 +195,7 @@ public void testMetricTopLevel() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(interval)); double bucketSum = 0; @@ -242,7 +242,7 @@ public void testMetricAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -302,7 +302,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -353,7 +353,7 @@ public void testNoBuckets() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(0)); StatsBucket statsBucketValue = response.getAggregations().get("stats_bucket"); @@ -380,7 +380,7 @@ public void testNested() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); double aggTermsSum = 0; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/SumBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/SumBucketIT.java index 09582430046f1..048dfac8648d4 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/SumBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/SumBucketIT.java @@ -137,7 +137,7 @@ public void testDocCountAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -176,7 +176,7 @@ public void testMetricTopLevel() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(interval)); double bucketSum = 0; @@ -214,7 +214,7 @@ public void testMetricAsSubAgg() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -265,7 +265,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); for (int i = 0; i < interval; ++i) { @@ -307,7 +307,7 @@ public void testNoBuckets() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); + List buckets = terms.getBuckets(); assertThat(buckets.size(), equalTo(0)); InternalSimpleValue sumBucketValue = response.getAggregations().get("sum_bucket"); @@ -334,7 +334,7 @@ public void testNested() throws Exception { Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); - List termsBuckets = terms.getBuckets(); + List termsBuckets = terms.getBuckets(); assertThat(termsBuckets.size(), equalTo(interval)); double aggTermsSum = 0; From 4e5537d9edf0ff7eb1064c5dd18ccdd0ff75e316 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Fri, 5 May 2017 15:56:09 -0400 Subject: [PATCH 227/619] Add workaround so path.data can be set in run task Previously you could set the system property tests.es.path.data and start the run task with a custom data directory. A change in core to detect duplicate settings broke this. That change should stay, but we can work around this easily by only setting path.data to the data directory if tests.es.path.data is not set. --- .../groovy/org/elasticsearch/gradle/test/NodeInfo.groovy | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/NodeInfo.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/NodeInfo.groovy index 59c65c684acee..c8617d9a36022 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/NodeInfo.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/NodeInfo.groovy @@ -161,7 +161,10 @@ class NodeInfo { } } env.put('ES_JVM_OPTIONS', new File(confDir, 'jvm.options')) - args.addAll("-E", "path.conf=${confDir}", "-E", "path.data=${-> dataDir.toString()}") + args.addAll("-E", "path.conf=${confDir}") + if (!System.properties.containsKey("tests.es.path.data")) { + args.addAll("-E", "path.data=${-> dataDir.toString()}") + } if (Os.isFamily(Os.FAMILY_WINDOWS)) { args.add('"') // end the entire command, quoted } From 4027c9da7b1b998c1c2faffb16b91336191a6062 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Fri, 5 May 2017 18:37:09 -0400 Subject: [PATCH 228/619] TimeValue#parseTimeValue author is bad, feels bad I stumbled on this code today and I hated it; I wrote it. I did not like that you could not tell at a glance whether or not the method parameters were correct. This commit fixes it. Relates #24522 --- .../elasticsearch/common/unit/TimeValue.java | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java b/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java index 4c3344eb9d880..0f6eabed1e3de 100644 --- a/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java +++ b/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java @@ -254,7 +254,7 @@ public String format(PeriodType type) { * Returns a {@link String} representation of the current {@link TimeValue}. * * Note that this method might produce fractional time values (ex 1.6m) which cannot be - * parsed by method like {@link TimeValue#parse(String, String, int)}. + * parsed by method like {@link TimeValue#parse(String, String, String)}. */ @Override public String toString() { @@ -326,22 +326,20 @@ public static TimeValue parseTimeValue(String sValue, TimeValue defaultValue, St } final String normalized = sValue.toLowerCase(Locale.ROOT).trim(); if (normalized.endsWith("nanos")) { - return new TimeValue(parse(sValue, normalized, 5), TimeUnit.NANOSECONDS); + return new TimeValue(parse(sValue, normalized, "nanos"), TimeUnit.NANOSECONDS); } else if (normalized.endsWith("micros")) { - return new TimeValue(parse(sValue, normalized, 6), TimeUnit.MICROSECONDS); + return new TimeValue(parse(sValue, normalized, "micros"), TimeUnit.MICROSECONDS); } else if (normalized.endsWith("ms")) { - return new TimeValue(parse(sValue, normalized, 2), TimeUnit.MILLISECONDS); + return new TimeValue(parse(sValue, normalized, "ms"), TimeUnit.MILLISECONDS); } else if (normalized.endsWith("s")) { - return new TimeValue(parse(sValue, normalized, 1), TimeUnit.SECONDS); + return new TimeValue(parse(sValue, normalized, "s"), TimeUnit.SECONDS); } else if (sValue.endsWith("m")) { - // parsing minutes should be case sensitive as `M` is generally - // accepted to mean months not minutes. This is the only case where - // the upper and lower case forms indicate different time units - return new TimeValue(parse(sValue, normalized, 1), TimeUnit.MINUTES); + // parsing minutes should be case-sensitive as 'M' means "months", not "minutes"; this is the only special case. + return new TimeValue(parse(sValue, normalized, "m"), TimeUnit.MINUTES); } else if (normalized.endsWith("h")) { - return new TimeValue(parse(sValue, normalized, 1), TimeUnit.HOURS); + return new TimeValue(parse(sValue, normalized, "h"), TimeUnit.HOURS); } else if (normalized.endsWith("d")) { - return new TimeValue(parse(sValue, normalized, 1), TimeUnit.DAYS); + return new TimeValue(parse(sValue, normalized, "d"), TimeUnit.DAYS); } else if (normalized.matches("-0*1")) { return TimeValue.MINUS_ONE; } else if (normalized.matches("0+")) { @@ -355,8 +353,8 @@ public static TimeValue parseTimeValue(String sValue, TimeValue defaultValue, St } } - private static long parse(final String initialInput, final String normalized, final int suffixLength) { - final String s = normalized.substring(0, normalized.length() - suffixLength).trim(); + private static long parse(final String initialInput, final String normalized, final String suffix) { + final String s = normalized.substring(0, normalized.length() - suffix.length()).trim(); try { return Long.parseLong(s); } catch (final NumberFormatException e) { From 13c17c75b530b5b110304960f24d7896d7b1c73c Mon Sep 17 00:00:00 2001 From: Koen De Groote Date: Sat, 6 May 2017 06:28:53 +0200 Subject: [PATCH 229/619] Remove unneeded empty string concatentation This commit removes concatenation by empty string in places where it is simply not needed to obtain a string representation. Relates #24411 --- .../main/java/org/elasticsearch/index/engine/Segment.java | 2 +- .../main/java/org/elasticsearch/index/shard/IndexShard.java | 2 +- .../java/org/elasticsearch/repositories/RepositoryData.java | 2 +- .../java/org/elasticsearch/search/lookup/LeafDocLookup.java | 2 +- .../org/elasticsearch/search/lookup/LeafFieldsLookup.java | 2 +- .../org/elasticsearch/snapshots/SnapshotShardsService.java | 2 +- .../main/java/org/elasticsearch/transport/TcpTransport.java | 2 +- .../src/main/java/org/elasticsearch/painless/DefMath.java | 6 ++++-- .../main/java/org/elasticsearch/painless/node/EBinary.java | 2 +- .../aggregations/metrics/AbstractNumericTestCase.java | 6 +++--- .../elasticsearch/test/disruption/NetworkDisruption.java | 2 +- .../elasticsearch/test/disruption/SingleNodeDisruption.java | 2 +- 12 files changed, 17 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/engine/Segment.java b/core/src/main/java/org/elasticsearch/index/engine/Segment.java index 565ed9f1d83f5..4de85d0020d9f 100644 --- a/core/src/main/java/org/elasticsearch/index/engine/Segment.java +++ b/core/src/main/java/org/elasticsearch/index/engine/Segment.java @@ -284,7 +284,7 @@ void writeSegmentSort(StreamOutput out, Sort sort) throws IOException { out.writeBoolean(((SortedNumericSortField) field).getSelector() == SortedNumericSelector.Type.MAX); out.writeBoolean(field.getReverse()); } else { - throw new IOException("invalid index sort field:" + field + ""); + throw new IOException("invalid index sort field:" + field); } } } diff --git a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java index 5e004a4759c2d..146900542a07f 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -399,7 +399,7 @@ public void updateRoutingEntry(ShardRouting newRouting) throws IOException { currentRouting = this.shardRouting; if (!newRouting.shardId().equals(shardId())) { - throw new IllegalArgumentException("Trying to set a routing entry with shardId " + newRouting.shardId() + " on a shard with shardId " + shardId() + ""); + throw new IllegalArgumentException("Trying to set a routing entry with shardId " + newRouting.shardId() + " on a shard with shardId " + shardId()); } if ((currentRouting == null || newRouting.isSameAllocation(currentRouting)) == false) { throw new IllegalArgumentException("Trying to set a routing entry with a different allocation. Current " + currentRouting + ", new " + newRouting); diff --git a/core/src/main/java/org/elasticsearch/repositories/RepositoryData.java b/core/src/main/java/org/elasticsearch/repositories/RepositoryData.java index f823da0f2e5bb..ee4a64cac87dc 100644 --- a/core/src/main/java/org/elasticsearch/repositories/RepositoryData.java +++ b/core/src/main/java/org/elasticsearch/repositories/RepositoryData.java @@ -210,7 +210,7 @@ public RepositoryData addIncompatibleSnapshots(final List incompatib public Set getSnapshots(final IndexId indexId) { Set snapshotIds = indexSnapshots.get(indexId); if (snapshotIds == null) { - throw new IllegalArgumentException("unknown snapshot index " + indexId + ""); + throw new IllegalArgumentException("unknown snapshot index " + indexId); } return snapshotIds; } diff --git a/core/src/main/java/org/elasticsearch/search/lookup/LeafDocLookup.java b/core/src/main/java/org/elasticsearch/search/lookup/LeafDocLookup.java index f02cf14f4ba26..5c75c4a6b1d81 100644 --- a/core/src/main/java/org/elasticsearch/search/lookup/LeafDocLookup.java +++ b/core/src/main/java/org/elasticsearch/search/lookup/LeafDocLookup.java @@ -76,7 +76,7 @@ public ScriptDocValues get(Object key) { if (scriptValues == null) { final MappedFieldType fieldType = mapperService.fullName(fieldName); if (fieldType == null) { - throw new IllegalArgumentException("No field found for [" + fieldName + "] in mapping with types " + Arrays.toString(types) + ""); + throw new IllegalArgumentException("No field found for [" + fieldName + "] in mapping with types " + Arrays.toString(types)); } // load fielddata on behalf of the script: otherwise it would need additional permissions // to deal with pagedbytes/ramusagestimator/etc diff --git a/core/src/main/java/org/elasticsearch/search/lookup/LeafFieldsLookup.java b/core/src/main/java/org/elasticsearch/search/lookup/LeafFieldsLookup.java index 374fe7189a87f..2d1c855b7c328 100644 --- a/core/src/main/java/org/elasticsearch/search/lookup/LeafFieldsLookup.java +++ b/core/src/main/java/org/elasticsearch/search/lookup/LeafFieldsLookup.java @@ -135,7 +135,7 @@ private FieldLookup loadFieldData(String name) { if (data == null) { MappedFieldType fieldType = mapperService.fullName(name); if (fieldType == null) { - throw new IllegalArgumentException("No field found for [" + name + "] in mapping with types " + Arrays.toString(types) + ""); + throw new IllegalArgumentException("No field found for [" + name + "] in mapping with types " + Arrays.toString(types)); } data = new FieldLookup(fieldType); cachedFieldData.put(name, data); diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotShardsService.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotShardsService.java index 6b42d17869449..7cbe10fad3b57 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotShardsService.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotShardsService.java @@ -497,7 +497,7 @@ public ShardSnapshotStatus status() { @Override public String toString() { - return "" + snapshot + ", shardId [" + shardId + "], status [" + status.state() + "]"; + return snapshot + ", shardId [" + shardId + "], status [" + status.state() + "]"; } } diff --git a/core/src/main/java/org/elasticsearch/transport/TcpTransport.java b/core/src/main/java/org/elasticsearch/transport/TcpTransport.java index cbdc0dfa1785c..30a18e05611bf 100644 --- a/core/src/main/java/org/elasticsearch/transport/TcpTransport.java +++ b/core/src/main/java/org/elasticsearch/transport/TcpTransport.java @@ -684,7 +684,7 @@ protected void bindServer(final String name, final Settings settings) { try { hostAddresses = networkService.resolveBindHostAddresses(bindHosts); } catch (IOException e) { - throw new BindTransportException("Failed to resolve host " + Arrays.toString(bindHosts) + "", e); + throw new BindTransportException("Failed to resolve host " + Arrays.toString(bindHosts), e); } if (logger.isDebugEnabled()) { String[] addresses = new String[hostAddresses.length]; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java index f89b1429f386b..6628484660699 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java @@ -379,8 +379,10 @@ private static boolean add(boolean a, boolean b) { } private static Object add(Object left, Object right) { - if (left instanceof String || right instanceof String) { - return "" + left + right; + if (left instanceof String) { + return (String) left + right; + } else if (right instanceof String) { + return left + (String) right; } else if (left instanceof Number) { if (right instanceof Number) { if (left instanceof Double || right instanceof Double) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBinary.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBinary.java index b762a1e114b9c..72748d7069e28 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBinary.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBinary.java @@ -291,7 +291,7 @@ private void analyzeAdd(Locals variables) { } else if (sort == Sort.DOUBLE) { constant = (double)left.constant + (double)right.constant; } else if (sort == Sort.STRING) { - constant = "" + left.constant + right.constant; + constant = left.constant.toString() + right.constant.toString(); } else { throw createError(new IllegalStateException("Illegal tree structure.")); } diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractNumericTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractNumericTestCase.java index 29457603c1cee..5ce4de82c2a56 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractNumericTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractNumericTestCase.java @@ -39,7 +39,7 @@ public void setupSuiteScopeCluster() throws Exception { final int numDocs = 10; for (int i = 0; i < numDocs; i++) { // TODO randomize the size and the params in here? - builders.add(client().prepareIndex("idx", "type", ""+i).setSource(jsonBuilder() + builders.add(client().prepareIndex("idx", "type", String.valueOf(i)).setSource(jsonBuilder() .startObject() .field("value", i+1) .startArray("values").value(i+2).value(i+3).endArray() @@ -58,7 +58,7 @@ public void setupSuiteScopeCluster() throws Exception { prepareCreate("empty_bucket_idx").addMapping("type", "value", "type=integer").execute().actionGet(); builders = new ArrayList<>(); for (int i = 0; i < 2; i++) { - builders.add(client().prepareIndex("empty_bucket_idx", "type", ""+i).setSource(jsonBuilder() + builders.add(client().prepareIndex("empty_bucket_idx", "type", String.valueOf(i)).setSource(jsonBuilder() .startObject() .field("value", i*2) .endObject())); @@ -96,4 +96,4 @@ public void setupSuiteScopeCluster() throws Exception { public abstract void testScriptMultiValuedWithParams() throws Exception; public abstract void testOrderByEmptyAggregation() throws Exception; -} \ No newline at end of file +} diff --git a/test/framework/src/main/java/org/elasticsearch/test/disruption/NetworkDisruption.java b/test/framework/src/main/java/org/elasticsearch/test/disruption/NetworkDisruption.java index f3168b94a9471..5d731b094d1da 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/disruption/NetworkDisruption.java +++ b/test/framework/src/main/java/org/elasticsearch/test/disruption/NetworkDisruption.java @@ -110,7 +110,7 @@ public static void ensureFullyConnectedCluster(InternalTestCluster cluster) { protected void ensureNodeCount(InternalTestCluster cluster) { assertFalse("cluster failed to form after disruption was healed", cluster.client().admin().cluster().prepareHealth() - .setWaitForNodes("" + cluster.size()) + .setWaitForNodes(String.valueOf(cluster.size())) .setWaitForNoRelocatingShards(true) .get().isTimedOut()); } diff --git a/test/framework/src/main/java/org/elasticsearch/test/disruption/SingleNodeDisruption.java b/test/framework/src/main/java/org/elasticsearch/test/disruption/SingleNodeDisruption.java index 862e18d7aca81..a22994cfa9bc4 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/disruption/SingleNodeDisruption.java +++ b/test/framework/src/main/java/org/elasticsearch/test/disruption/SingleNodeDisruption.java @@ -84,7 +84,7 @@ public synchronized void testClusterClosed() { protected void ensureNodeCount(InternalTestCluster cluster) { assertFalse("cluster failed to form after disruption was healed", cluster.client().admin().cluster().prepareHealth() - .setWaitForNodes("" + cluster.size()) + .setWaitForNodes(String.valueOf(cluster.size())) .setWaitForNoRelocatingShards(true) .get().isTimedOut()); } From 3e485c2ca5c2be247adb8e383a3d9e2c8e7e20e4 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Sat, 6 May 2017 13:25:10 -0400 Subject: [PATCH 230/619] Fix plugin installation permissions When installing plugin permissions, we try to set the permissions on all installed files ourselves because a umask from the user could violate everything needed to get the permissions right. Sadly, directories were not handled correctly at all and so we were still left with broken installations with umasks like 0077. This commit fixes this issue, adds a thorough unit test for the situation, and most importantly, adds a test that sets the umask before installing the plugin. Relates #24527 --- distribution/build.gradle | 1 + .../plugins/InstallPluginCommand.java | 20 ++--- .../plugins/InstallPluginCommandTests.java | 77 +++++++++++++------ .../tests/module_and_plugin_test_cases.bash | 4 + .../resources/packaging/utils/plugins.bash | 19 +++-- 5 files changed, 84 insertions(+), 37 deletions(-) diff --git a/distribution/build.gradle b/distribution/build.gradle index 75b40d57b1730..b078008250e74 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -249,6 +249,7 @@ configure(distributions.findAll { ['zip', 'tar', 'integ-test-zip'].contains(it.n } into('') { from { + dirMode 0755 plugins.getParent() } } diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java index dd7e39f7c797c..1e44d0522d3cf 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java @@ -554,20 +554,20 @@ private void install(Terminal terminal, boolean isBatch, Path tmpRoot, Environme Files.move(tmpRoot, destination, StandardCopyOption.ATOMIC_MOVE); Files.walkFileTree(destination, new SimpleFileVisitor() { @Override - public FileVisitResult visitFile(Path pluginFile, BasicFileAttributes attrs) throws IOException { - if (Files.isDirectory(pluginFile)) { - setFileAttributes(pluginFile, PLUGIN_DIR_PERMS); + public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { + if ("bin".equals(file.getParent().getFileName().toString())) { + setFileAttributes(file, BIN_FILES_PERMS); } else { - // There can also be "bin" directories under the plugin directory, storing native code executables - Path parentDir = pluginFile.getParent().getFileName(); - if ("bin".equals(parentDir.toString())) { - setFileAttributes(pluginFile, BIN_FILES_PERMS); - } else { - setFileAttributes(pluginFile, PLUGIN_FILES_PERMS); - } + setFileAttributes(file, PLUGIN_FILES_PERMS); } return FileVisitResult.CONTINUE; } + + @Override + public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException { + setFileAttributes(dir, PLUGIN_DIR_PERMS); + return FileVisitResult.CONTINUE; + } }); terminal.println("-> Installed " + info.getName()); diff --git a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java index 57dfec308073c..67ccfd210a076 100644 --- a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java +++ b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java @@ -469,32 +469,65 @@ public void testBinPermissions() throws Exception { } } - public void testPlatformBinPermissions() throws Exception { + public void testPluginPermissions() throws Exception { assumeTrue("posix filesystem", isPosix); - Tuple env = createEnv(fs, temp); - Path pluginDir = createPluginDir(temp); - Path platformDir = pluginDir.resolve("platform"); - Path platformNameDir = platformDir.resolve("linux-x86_64"); - Path platformBinDir = platformNameDir.resolve("bin"); + + final Tuple env = createEnv(fs, temp); + final Path pluginDir = createPluginDir(temp); + final Path resourcesDir = pluginDir.resolve("resources"); + final Path platformDir = pluginDir.resolve("platform"); + final Path platformNameDir = platformDir.resolve("linux-x86_64"); + final Path platformBinDir = platformNameDir.resolve("bin"); Files.createDirectories(platformBinDir); - Path programFile = Files.createFile(platformBinDir.resolve("someprogram")); - // a file created with Files.createFile() should not have execute permissions - Set sourcePerms = Files.getPosixFilePermissions(programFile); - assertFalse(sourcePerms.contains(PosixFilePermission.OWNER_EXECUTE)); - assertFalse(sourcePerms.contains(PosixFilePermission.GROUP_EXECUTE)); - assertFalse(sourcePerms.contains(PosixFilePermission.OTHERS_EXECUTE)); - String pluginZip = createPluginUrl("fake", pluginDir); + + Files.createFile(pluginDir.resolve("fake-" + Version.CURRENT.toString() + ".jar")); + Files.createFile(platformBinDir.resolve("fake_executable")); + Files.createDirectory(resourcesDir); + Files.createFile(resourcesDir.resolve("resource")); + + final String pluginZip = createPluginUrl("fake", pluginDir); + installPlugin(pluginZip, env.v1()); assertPlugin("fake", pluginDir, env.v2()); - // check that the installed program has execute permissions, even though the one added to the plugin didn't - Path installedPlatformBinDir = env.v2().pluginsFile().resolve("fake").resolve("platform").resolve("linux-x86_64").resolve("bin"); - assertTrue(Files.isDirectory(installedPlatformBinDir)); - Path installedProgramFile = installedPlatformBinDir.resolve("someprogram"); - assertTrue(Files.isRegularFile(installedProgramFile)); - Set installedPerms = Files.getPosixFilePermissions(installedProgramFile); - assertTrue(installedPerms.contains(PosixFilePermission.OWNER_EXECUTE)); - assertTrue(installedPerms.contains(PosixFilePermission.GROUP_EXECUTE)); - assertTrue(installedPerms.contains(PosixFilePermission.OTHERS_EXECUTE)); + + final Path fake = env.v2().pluginsFile().resolve("fake"); + final Path resources = fake.resolve("resources"); + final Path platform = fake.resolve("platform"); + final Path platformName = platform.resolve("linux-x86_64"); + final Path bin = platformName.resolve("bin"); + assert755(fake); + assert644(fake.resolve("fake-" + Version.CURRENT + ".jar")); + assert755(resources); + assert644(resources.resolve("resource")); + assert755(platform); + assert755(platformName); + assert755(bin.resolve("fake_executable")); + } + + private void assert644(final Path path) throws IOException { + final Set permissions = Files.getPosixFilePermissions(path); + assertTrue(permissions.contains(PosixFilePermission.OWNER_READ)); + assertTrue(permissions.contains(PosixFilePermission.OWNER_WRITE)); + assertFalse(permissions.contains(PosixFilePermission.OWNER_EXECUTE)); + assertTrue(permissions.contains(PosixFilePermission.GROUP_READ)); + assertFalse(permissions.contains(PosixFilePermission.GROUP_WRITE)); + assertFalse(permissions.contains(PosixFilePermission.GROUP_EXECUTE)); + assertTrue(permissions.contains(PosixFilePermission.OTHERS_READ)); + assertFalse(permissions.contains(PosixFilePermission.OTHERS_WRITE)); + assertFalse(permissions.contains(PosixFilePermission.OTHERS_EXECUTE)); + } + + private void assert755(final Path path) throws IOException { + final Set permissions = Files.getPosixFilePermissions(path); + assertTrue(permissions.contains(PosixFilePermission.OWNER_READ)); + assertTrue(permissions.contains(PosixFilePermission.OWNER_WRITE)); + assertTrue(permissions.contains(PosixFilePermission.OWNER_EXECUTE)); + assertTrue(permissions.contains(PosixFilePermission.GROUP_READ)); + assertFalse(permissions.contains(PosixFilePermission.GROUP_WRITE)); + assertTrue(permissions.contains(PosixFilePermission.GROUP_EXECUTE)); + assertTrue(permissions.contains(PosixFilePermission.OTHERS_READ)); + assertFalse(permissions.contains(PosixFilePermission.OTHERS_WRITE)); + assertTrue(permissions.contains(PosixFilePermission.OTHERS_EXECUTE)); } public void testConfig() throws Exception { diff --git a/qa/vagrant/src/test/resources/packaging/tests/module_and_plugin_test_cases.bash b/qa/vagrant/src/test/resources/packaging/tests/module_and_plugin_test_cases.bash index 31b9ca4f3fd34..42b6d7bf0394f 100644 --- a/qa/vagrant/src/test/resources/packaging/tests/module_and_plugin_test_cases.bash +++ b/qa/vagrant/src/test/resources/packaging/tests/module_and_plugin_test_cases.bash @@ -475,3 +475,7 @@ fi # restore ES_JAVA_OPTS export ES_JAVA_OPTS=$es_java_opts } + +@test "[$GROUP] test umask" { + install_jvm_example $(readlink -m jvm-example-*.zip) 0077 +} diff --git a/qa/vagrant/src/test/resources/packaging/utils/plugins.bash b/qa/vagrant/src/test/resources/packaging/utils/plugins.bash index d6a4f389fd009..f8c40d2742036 100644 --- a/qa/vagrant/src/test/resources/packaging/utils/plugins.bash +++ b/qa/vagrant/src/test/resources/packaging/utils/plugins.bash @@ -34,10 +34,15 @@ install_plugin() { local name=$1 local path="$2" + local umask="$3" assert_file_exist "$path" - sudo -E -u $ESPLUGIN_COMMAND_USER "$ESHOME/bin/elasticsearch-plugin" install -batch "file://$path" + if [ -z "$umask" ]; then + sudo -E -u $ESPLUGIN_COMMAND_USER "$ESHOME/bin/elasticsearch-plugin" install -batch "file://$path" + else + sudo -E -u $ESPLUGIN_COMMAND_USER bash -c "umask $umask && \"$ESHOME/bin/elasticsearch-plugin\" install -batch \"file://$path\"" + fi assert_file_exist "$ESPLUGINS/$name" assert_file_exist "$ESPLUGINS/$name/plugin-descriptor.properties" @@ -56,7 +61,7 @@ install_plugin() { install_jvm_plugin() { local name=$1 local path="$2" - install_plugin $name "$path" + install_plugin $name "$path" $3 assert_file_exist "$ESPLUGINS/$name/$name"*".jar" } @@ -82,12 +87,16 @@ remove_plugin() { # placements for non-site plugins. install_jvm_example() { local relativePath=${1:-$(readlink -m jvm-example-*.zip)} - install_jvm_plugin jvm-example "$relativePath" + install_jvm_plugin jvm-example "$relativePath" $2 - #owner group and permissions vary depending on how es was installed - #just make sure that everything is the same as the parent bin dir, which was properly set up during install bin_user=$(find "$ESHOME/bin" -maxdepth 0 -printf "%u") bin_owner=$(find "$ESHOME/bin" -maxdepth 0 -printf "%g") + + assert_file "$ESHOME/plugins/jvm-example" d $bin_user $bin_owner 755 + assert_file "$ESHOME/plugins/jvm-example/jvm-example-$(cat version).jar" f $bin_user $bin_owner 644 + + #owner group and permissions vary depending on how es was installed + #just make sure that everything is the same as the parent bin dir, which was properly set up during install assert_file "$ESHOME/bin/jvm-example" d $bin_user $bin_owner 755 assert_file "$ESHOME/bin/jvm-example/test" f $bin_user $bin_owner 755 From e875f7f72e15b7700a9dddfe9643fb1eb3220fcf Mon Sep 17 00:00:00 2001 From: javanna Date: Mon, 8 May 2017 10:36:18 +0200 Subject: [PATCH 231/619] remove duplicated import in AppendProcessor --- .../java/org/elasticsearch/ingest/common/AppendProcessor.java | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/AppendProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/AppendProcessor.java index 7bc6a17152a32..ddcb4c4bb95f0 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/AppendProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/AppendProcessor.java @@ -21,7 +21,6 @@ import org.elasticsearch.ingest.AbstractProcessor; import org.elasticsearch.ingest.ConfigurationUtils; -import org.elasticsearch.ingest.ConfigurationUtils; import org.elasticsearch.ingest.IngestDocument; import org.elasticsearch.ingest.Processor; import org.elasticsearch.ingest.TemplateService; From 59dd4d288a0b7e004abd72dac920349cf60b6cd7 Mon Sep 17 00:00:00 2001 From: glefloch Date: Fri, 7 Apr 2017 16:25:51 +0200 Subject: [PATCH 232/619] documentation of preserve existing settings --- docs/reference/indices/update-settings.asciidoc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/reference/indices/update-settings.asciidoc b/docs/reference/indices/update-settings.asciidoc index 2ca42c71e8d7c..15d0cdaaead96 100644 --- a/docs/reference/indices/update-settings.asciidoc +++ b/docs/reference/indices/update-settings.asciidoc @@ -4,8 +4,8 @@ Change specific index level settings in real time. The REST endpoint is `/_settings` (to update all indices) or -`{index}/_settings` to update one (or more) indices settings. The body -of the request includes the updated settings, for example: +`{index}/_settings` to update one (or more) indices settings. +The body of the request includes the updated settings, for example: [source,js] -------------------------------------------------- @@ -21,6 +21,8 @@ PUT /twitter/_settings The list of per-index settings which can be updated dynamically on live indices can be found in <>. +To preserve existing settings from being updated, the `preserve_existing` +request parameter can be set to `true`. [float] [[bulk]] From bbdaf113d40cc96fb41e7ebcb87c5ff4eecd3c36 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 8 May 2017 06:38:28 -0400 Subject: [PATCH 233/619] Remove gap skipping when opening engine Today when opening the engine we skip gaps in the history, advancing the local checkpoint until it is equal to the maximum sequence number contained in the commit. This allows history to advance, but it leaves gaps. A previous change filled these gaps when recovering from store, but since we were skipping the gaps while opening the engine, this change had no effect. This commit removes the gap skipping when opening the engine allowing the gap filling to do its job. Relates #24535 --- .../elasticsearch/index/engine/Engine.java | 2 +- .../index/engine/InternalEngine.java | 32 ++++------- .../index/shard/StoreRecovery.java | 2 +- .../index/engine/InternalEngineTests.java | 55 ++++++++++++------- 4 files changed, 49 insertions(+), 42 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/engine/Engine.java b/core/src/main/java/org/elasticsearch/index/engine/Engine.java index 755d9db68b027..31e0d1e422acf 100644 --- a/core/src/main/java/org/elasticsearch/index/engine/Engine.java +++ b/core/src/main/java/org/elasticsearch/index/engine/Engine.java @@ -1421,7 +1421,7 @@ public interface Warmer { * @param primaryTerm the shards primary term this engine was created for * @return the number of no-ops added */ - public abstract int fillSequenceNumberHistory(long primaryTerm) throws IOException; + public abstract int fillSeqNoGaps(long primaryTerm) throws IOException; /** * Performs recovery from the transaction log. diff --git a/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java b/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java index 5d494ef7ff705..9bfbb1467a20f 100644 --- a/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java +++ b/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java @@ -177,15 +177,6 @@ public InternalEngine(EngineConfig engineConfig) throws EngineException { logger.trace("recovered [{}]", seqNoStats); seqNoService = sequenceNumberService(shardId, engineConfig.getIndexSettings(), seqNoStats); updateMaxUnsafeAutoIdTimestampFromWriter(writer); - // norelease - /* - * We have no guarantees that all operations above the local checkpoint are in the Lucene commit or the translog. This means - * that we there might be operations greater than the local checkpoint that will not be replayed. Here we force the local - * checkpoint to the maximum sequence number in the commit (at the potential expense of correctness). - */ - while (seqNoService().getLocalCheckpoint() < seqNoService().getMaxSeqNo()) { - seqNoService().markSeqNoAsCompleted(seqNoService().getLocalCheckpoint() + 1); - } indexWriter = writer; translog = openTranslog(engineConfig, writer, () -> seqNoService().getGlobalCheckpoint()); assert translog.getGeneration() != null; @@ -226,21 +217,20 @@ public InternalEngine(EngineConfig engineConfig) throws EngineException { } @Override - public int fillSequenceNumberHistory(long primaryTerm) throws IOException { - try (ReleasableLock lock = writeLock.acquire()) { + public int fillSeqNoGaps(long primaryTerm) throws IOException { + try (ReleasableLock ignored = writeLock.acquire()) { ensureOpen(); - final long localCheckpoint = seqNoService.getLocalCheckpoint(); - final long maxSeqId = seqNoService.getMaxSeqNo(); + final long localCheckpoint = seqNoService().getLocalCheckpoint(); + final long maxSeqNo = seqNoService().getMaxSeqNo(); int numNoOpsAdded = 0; - for (long seqNo = localCheckpoint + 1; seqNo <= maxSeqId; - // the local checkpoint might have been advanced so we are leap-frogging - // to the next seq ID we need to process and create a noop for - seqNo = seqNoService.getLocalCheckpoint()+1) { - final NoOp noOp = new NoOp(seqNo, primaryTerm, Operation.Origin.PRIMARY, System.nanoTime(), "filling up seqNo history"); - innerNoOp(noOp); + for ( + long seqNo = localCheckpoint + 1; + seqNo <= maxSeqNo; + seqNo = seqNoService().getLocalCheckpoint() + 1 /* the local checkpoint might have advanced so we leap-frog */) { + innerNoOp(new NoOp(seqNo, primaryTerm, Operation.Origin.PRIMARY, System.nanoTime(), "filling gaps")); numNoOpsAdded++; - assert seqNo <= seqNoService.getLocalCheckpoint() : "localCheckpoint didn't advanced used to be " + seqNo + " now it's on:" - + seqNoService.getLocalCheckpoint(); + assert seqNo <= seqNoService().getLocalCheckpoint() + : "local checkpoint did not advance; was [" + seqNo + "], now [" + seqNoService().getLocalCheckpoint() + "]"; } return numNoOpsAdded; diff --git a/core/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java b/core/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java index 5d5e17c19291b..b2e9416564059 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java +++ b/core/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java @@ -365,7 +365,7 @@ private void internalRecoverFromStore(IndexShard indexShard) throws IndexShardRe } indexShard.performTranslogRecovery(indexShouldExists); assert indexShard.shardRouting.primary() : "only primary shards can recover from store"; - indexShard.getEngine().fillSequenceNumberHistory(indexShard.getPrimaryTerm()); + indexShard.getEngine().fillSeqNoGaps(indexShard.getPrimaryTerm()); } indexShard.finalizeRecovery(); indexShard.postRecovery("post recovery from shard_store"); diff --git a/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java b/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java index f1b981e14d220..2a611adb7635f 100644 --- a/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java +++ b/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java @@ -114,7 +114,6 @@ import org.elasticsearch.index.mapper.RootObjectMapper; import org.elasticsearch.index.mapper.SeqNoFieldMapper; import org.elasticsearch.index.mapper.SourceFieldMapper; -import org.elasticsearch.index.mapper.SourceToParse; import org.elasticsearch.index.mapper.Uid; import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.seqno.SequenceNumbers; @@ -3580,6 +3579,8 @@ public void testSequenceNumberAdvancesToMaxSeqOnEngineOpenOnPrimary() throws Bro try (Engine recoveringEngine = new InternalEngine(copy(initialEngine.config(), EngineConfig.OpenMode.OPEN_INDEX_AND_TRANSLOG))) { + recoveringEngine.recoverFromTranslog(); + recoveringEngine.fillSeqNoGaps(2); assertThat(recoveringEngine.seqNoService().getLocalCheckpoint(), greaterThanOrEqualTo((long) (docs - 1))); } } @@ -3618,6 +3619,8 @@ public void testSequenceNumberAdvancesToMaxSeqNoOnEngineOpenOnReplica() throws I try (Engine recoveringEngine = new InternalEngine(copy(initialEngine.config(), EngineConfig.OpenMode.OPEN_INDEX_AND_TRANSLOG))) { + recoveringEngine.recoverFromTranslog(); + recoveringEngine.fillSeqNoGaps(1); assertThat(recoveringEngine.seqNoService().getLocalCheckpoint(), greaterThanOrEqualTo((long) (3 * (docs - 1) + 2 - 1))); } } @@ -3719,21 +3722,35 @@ public long generateSeqNo() { throw new UnsupportedOperationException(); } }; - noOpEngine = createEngine(defaultSettings, store, primaryTranslogDir, newMergePolicy(), null, () -> seqNoService); + noOpEngine = new InternalEngine(copy(engine.config(), EngineConfig.OpenMode.OPEN_INDEX_AND_TRANSLOG)) { + @Override + public SequenceNumbersService seqNoService() { + return seqNoService; + } + }; + noOpEngine.recoverFromTranslog(); final long primaryTerm = randomNonNegativeLong(); + final int gapsFilled = noOpEngine.fillSeqNoGaps(primaryTerm); final String reason = randomAlphaOfLength(16); noOpEngine.noOp( - new Engine.NoOp( - maxSeqNo + 1, - primaryTerm, - randomFrom(PRIMARY, REPLICA, PEER_RECOVERY, LOCAL_TRANSLOG_RECOVERY), - System.nanoTime(), - reason)); + new Engine.NoOp( + maxSeqNo + 1, + primaryTerm, + randomFrom(PRIMARY, REPLICA, PEER_RECOVERY, LOCAL_TRANSLOG_RECOVERY), + System.nanoTime(), + reason)); assertThat(noOpEngine.seqNoService().getLocalCheckpoint(), equalTo((long) (maxSeqNo + 1))); - assertThat(noOpEngine.getTranslog().totalOperations(), equalTo(1)); - final Translog.Operation op = noOpEngine.getTranslog().newSnapshot().next(); - assertThat(op, instanceOf(Translog.NoOp.class)); - final Translog.NoOp noOp = (Translog.NoOp) op; + assertThat(noOpEngine.getTranslog().totalOperations(), equalTo(1 + gapsFilled)); + // skip to the op that we added to the translog + Translog.Operation op; + Translog.Operation last = null; + final Translog.Snapshot snapshot = noOpEngine.getTranslog().newSnapshot(); + while ((op = snapshot.next()) != null) { + last = op; + } + assertNotNull(last); + assertThat(last, instanceOf(Translog.NoOp.class)); + final Translog.NoOp noOp = (Translog.NoOp) last; assertThat(noOp.seqNo(), equalTo((long) (maxSeqNo + 1))); assertThat(noOp.primaryTerm(), equalTo(primaryTerm)); assertThat(noOp.reason(), equalTo(reason)); @@ -3846,7 +3863,7 @@ public void testFillUpSequenceIdGapsOnRecovery() throws IOException { for (int i = 0; i < docs; i++) { final String docId = Integer.toString(i); final ParsedDocument doc = - testParsedDocument(docId, "test", null, testDocumentWithTextField(), SOURCE, null); + testParsedDocument(docId, "test", null, testDocumentWithTextField(), SOURCE, null); Engine.Index primaryResponse = indexForDoc(doc); Engine.IndexResult indexResult = engine.index(primaryResponse); if (randomBoolean()) { @@ -3864,8 +3881,8 @@ public void testFillUpSequenceIdGapsOnRecovery() throws IOException { boolean flushed = false; Engine recoveringEngine = null; try { - assertEquals(docs-1, engine.seqNoService().getMaxSeqNo()); - assertEquals(docs-1, engine.seqNoService().getLocalCheckpoint()); + assertEquals(docs - 1, engine.seqNoService().getMaxSeqNo()); + assertEquals(docs - 1, engine.seqNoService().getLocalCheckpoint()); assertEquals(maxSeqIDOnReplica, replicaEngine.seqNoService().getMaxSeqNo()); assertEquals(checkpointOnReplica, replicaEngine.seqNoService().getLocalCheckpoint()); recoveringEngine = new InternalEngine(copy(replicaEngine.config(), EngineConfig.OpenMode.OPEN_INDEX_AND_TRANSLOG)); @@ -3873,13 +3890,13 @@ public void testFillUpSequenceIdGapsOnRecovery() throws IOException { recoveringEngine.recoverFromTranslog(); assertEquals(maxSeqIDOnReplica, recoveringEngine.seqNoService().getMaxSeqNo()); assertEquals(checkpointOnReplica, recoveringEngine.seqNoService().getLocalCheckpoint()); - assertEquals((maxSeqIDOnReplica+1) - numDocsOnReplica, recoveringEngine.fillSequenceNumberHistory(2)); + assertEquals((maxSeqIDOnReplica + 1) - numDocsOnReplica, recoveringEngine.fillSeqNoGaps(2)); // now snapshot the tlog and ensure the primary term is updated Translog.Snapshot snapshot = recoveringEngine.getTranslog().newSnapshot(); - assertTrue((maxSeqIDOnReplica+1) - numDocsOnReplica <= snapshot.totalOperations()); + assertTrue((maxSeqIDOnReplica + 1) - numDocsOnReplica <= snapshot.totalOperations()); Translog.Operation operation; - while((operation = snapshot.next()) != null) { + while ((operation = snapshot.next()) != null) { if (operation.opType() == Translog.Operation.Type.NO_OP) { assertEquals(2, operation.primaryTerm()); } else { @@ -3905,7 +3922,7 @@ public void testFillUpSequenceIdGapsOnRecovery() throws IOException { recoveringEngine.recoverFromTranslog(); assertEquals(maxSeqIDOnReplica, recoveringEngine.seqNoService().getMaxSeqNo()); assertEquals(maxSeqIDOnReplica, recoveringEngine.seqNoService().getLocalCheckpoint()); - assertEquals(0, recoveringEngine.fillSequenceNumberHistory(3)); + assertEquals(0, recoveringEngine.fillSeqNoGaps(3)); assertEquals(maxSeqIDOnReplica, recoveringEngine.seqNoService().getMaxSeqNo()); assertEquals(maxSeqIDOnReplica, recoveringEngine.seqNoService().getLocalCheckpoint()); } finally { From f41ddb36075f05fa5298f2c35b94982dc29839ae Mon Sep 17 00:00:00 2001 From: Hendrik Muhs Date: Mon, 8 May 2017 13:02:27 +0200 Subject: [PATCH 234/619] Move MockLogAppender to elasticsearch test (#24542) In order to make MockLogAppender (utility to test logging) available outside of es-core move MockLogAppender from test core-tests to test framework. As package names do not change, no need to change clients. --- .../src/main}/java/org/elasticsearch/test/MockLogAppender.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {core/src/test => test/framework/src/main}/java/org/elasticsearch/test/MockLogAppender.java (100%) diff --git a/core/src/test/java/org/elasticsearch/test/MockLogAppender.java b/test/framework/src/main/java/org/elasticsearch/test/MockLogAppender.java similarity index 100% rename from core/src/test/java/org/elasticsearch/test/MockLogAppender.java rename to test/framework/src/main/java/org/elasticsearch/test/MockLogAppender.java From 7e437f23663d66d4592b86a001a45eebff17b66f Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Mon, 8 May 2017 07:34:36 -0400 Subject: [PATCH 235/619] Document work-around for jar hell in idea_rt.jar file (#24523) --- CONTRIBUTING.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8775e1464d068..0192ab13a5557 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -101,7 +101,11 @@ IntelliJ users can automatically configure their IDE: `gradle idea` then `File->New Project From Existing Sources`. Point to the root of the source directory, select `Import project from external model->Gradle`, enable -`Use auto-import`. +`Use auto-import`. Additionally, in order to run tests directly from +IDEA 2017.1 and above it is required to disable IDEA run launcher, +which can be achieved by adding `-Didea.no.launcher=true` +[JVM option](https://intellij-support.jetbrains.com/hc/en-us/articles/206544869-Configuring-JVM-options-and-platform-properties) + The Elasticsearch codebase makes heavy use of Java `assert`s and the test runner requires that assertions be enabled within the JVM. This From 0ec30eb8e03220140127616ab254ecd54d3b8026 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 8 May 2017 09:49:14 -0400 Subject: [PATCH 236/619] Fix cache expire after access This commit fixes a bug in the cache expire after access implementation. The bug is this: if you construct a cache with an expire after access of T, put a key, and then touch the key at some time t > T, the act of getting the key would update the access time for the entry before checking if the entry was expired. There are situations in which expire after access would be honored (e.g., if the cache needs to prune the LRU list to keep the cache under a certain weight, or a manual refresh was called) but this behavior is otherwise broken. Relates #24546 --- .../org/elasticsearch/common/cache/Cache.java | 43 ++++++++++--------- .../common/cache/CacheTests.java | 22 ++++++++++ 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/common/cache/Cache.java b/core/src/main/java/org/elasticsearch/common/cache/Cache.java index 2297df67655d9..df30123c35b42 100644 --- a/core/src/main/java/org/elasticsearch/common/cache/Cache.java +++ b/core/src/main/java/org/elasticsearch/common/cache/Cache.java @@ -34,6 +34,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.BiFunction; +import java.util.function.Predicate; import java.util.function.ToLongBiFunction; /** @@ -193,33 +194,35 @@ private static class CacheSegment { SegmentStats segmentStats = new SegmentStats(); /** - * get an entry from the segment + * get an entry from the segment; expired entries will be returned as null but not removed from the cache until the LRU list is + * pruned or a manual {@link Cache#refresh()} is performed * - * @param key the key of the entry to get from the cache - * @param now the access time of this entry + * @param key the key of the entry to get from the cache + * @param now the access time of this entry + * @param isExpired test if the entry is expired * @return the entry if there was one, otherwise null */ - Entry get(K key, long now) { + Entry get(K key, long now, Predicate> isExpired) { CompletableFuture> future; Entry entry = null; try (ReleasableLock ignored = readLock.acquire()) { future = map.get(key); } if (future != null) { - try { - entry = future.handle((ok, ex) -> { - if (ok != null) { - segmentStats.hit(); - ok.accessTime = now; - return ok; - } else { - segmentStats.miss(); - return null; - } - }).get(); - } catch (ExecutionException | InterruptedException e) { - throw new IllegalStateException(e); - } + try { + entry = future.handle((ok, ex) -> { + if (ok != null && !isExpired.test(ok)) { + segmentStats.hit(); + ok.accessTime = now; + return ok; + } else { + segmentStats.miss(); + return null; + } + }).get(); + } catch (ExecutionException | InterruptedException e) { + throw new IllegalStateException(e); + } } else { segmentStats.miss(); @@ -332,8 +335,8 @@ public V get(K key) { private V get(K key, long now) { CacheSegment segment = getCacheSegment(key); - Entry entry = segment.get(key, now); - if (entry == null || isExpired(entry, now)) { + Entry entry = segment.get(key, now, e -> isExpired(e, now)); + if (entry == null) { return null; } else { promote(entry, now); diff --git a/core/src/test/java/org/elasticsearch/common/cache/CacheTests.java b/core/src/test/java/org/elasticsearch/common/cache/CacheTests.java index 71fe7b262a699..7dbaba02897c6 100644 --- a/core/src/test/java/org/elasticsearch/common/cache/CacheTests.java +++ b/core/src/test/java/org/elasticsearch/common/cache/CacheTests.java @@ -257,6 +257,28 @@ protected long now() { } } + public void testSimpleExpireAfterAccess() { + AtomicLong now = new AtomicLong(); + Cache cache = new Cache() { + @Override + protected long now() { + return now.get(); + } + }; + cache.setExpireAfterAccessNanos(1); + now.set(0); + for (int i = 0; i < numberOfEntries; i++) { + cache.put(i, Integer.toString(i)); + } + for (int i = 0; i < numberOfEntries; i++) { + assertEquals(cache.get(i), Integer.toString(i)); + } + now.set(2); + for(int i = 0; i < numberOfEntries; i++) { + assertNull(cache.get(i)); + } + } + public void testExpirationAfterWrite() { AtomicLong now = new AtomicLong(); Cache cache = new Cache() { From 1907c46689d62b60b7b67e0a3b0fdfd0ec3397ca Mon Sep 17 00:00:00 2001 From: Lee Hinman Date: Fri, 21 Apr 2017 15:28:52 -0600 Subject: [PATCH 237/619] Refactor UpdateHelper into unit-testable pieces This starts breaking up the `UpdateHelper.prepare` method so that each piece can be individually unit tested. No actual functionality has changed. Note however, that I did add a TODO about `ctx.op` leniency, which I'd love to remove as a separate PR if desired. --- .../resources/checkstyle_suppressions.xml | 1 - .../action/update/UpdateHelper.java | 346 +++++++++++++----- .../action/update/UpdateRequestTests.java | 158 ++++++++ 3 files changed, 403 insertions(+), 102 deletions(-) diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 82c0ce3b77c86..4b43b766d38f1 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -177,7 +177,6 @@ - diff --git a/core/src/main/java/org/elasticsearch/action/update/UpdateHelper.java b/core/src/main/java/org/elasticsearch/action/update/UpdateHelper.java index 09fc0bccbf35e..6ea50df6b5e94 100644 --- a/core/src/main/java/org/elasticsearch/action/update/UpdateHelper.java +++ b/core/src/main/java/org/elasticsearch/action/update/UpdateHelper.java @@ -19,6 +19,7 @@ package org.elasticsearch.action.update; +import org.apache.logging.log4j.Logger; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.DocWriteResponse; import org.elasticsearch.action.delete.DeleteRequest; @@ -79,11 +80,56 @@ public Result prepare(UpdateRequest request, IndexShard indexShard, LongSupplier } /** - * Prepares an update request by converting it into an index or delete request or an update response (no action). + * Prepares an update request by converting it into an index or delete request or an update response (no action, in the event of a + * noop). */ @SuppressWarnings("unchecked") protected Result prepare(ShardId shardId, UpdateRequest request, final GetResult getResult, LongSupplier nowInMillis) { - if (!getResult.isExists()) { + if (getResult.isExists() == false) { + // If the document didn't exist, execute the update request as an upsert + return prepareUpsert(shardId, request, getResult, nowInMillis); + } else if (getResult.internalSourceRef() == null) { + // no source, we can't do anything, throw a failure... + throw new DocumentSourceMissingException(shardId, request.type(), request.id()); + } else if (request.script() == null && request.doc() != null) { + // The request has no script, it is a new doc that should be merged with the old document + return prepareUpdateIndexRequest(shardId, request, getResult, request.detectNoop()); + } else { + // The request has a script (or empty script), execute the script and prepare a new index request + return prepareUpdateScriptRequest(shardId, request, getResult, nowInMillis); + } + } + + /** + * Execute a scripted upsert, where there is an existing upsert document and a script to be executed. The script is executed and a new + * Tuple of operation and updated {@code _source} is returned. + */ + Tuple> executeScriptedUpsert(IndexRequest upsert, Script script, LongSupplier nowInMillis) { + Map upsertDoc = upsert.sourceAsMap(); + Map ctx = new HashMap<>(3); + // Tell the script that this is a create and not an update + ctx.put(ContextFields.OP, UpdateOpType.CREATE.toString()); + ctx.put(ContextFields.SOURCE, upsertDoc); + ctx.put(ContextFields.NOW, nowInMillis.getAsLong()); + ctx = executeScript(script, ctx); + + UpdateOpType operation = UpdateOpType.lenientFromString((String) ctx.get(ContextFields.OP), logger, script.getIdOrCode()); + Map newSource = (Map) ctx.get(ContextFields.SOURCE); + + if (operation != UpdateOpType.CREATE && operation != UpdateOpType.NONE) { + // Only valid options for an upsert script are "create" (the default) or "none", meaning abort upsert + logger.warn("Invalid upsert operation [{}] for script [{}], doing nothing...", operation, script.getIdOrCode()); + operation = UpdateOpType.NONE; + } + + return new Tuple<>(operation, newSource); + } + + /** + * Prepare the request for upsert, executing the upsert script if present, and returning a {@code Result} containing a new + * {@code IndexRequest} to be executed on the primary and replicas. + */ + Result prepareUpsert(ShardId shardId, UpdateRequest request, final GetResult getResult, LongSupplier nowInMillis) { if (request.upsertRequest() == null && !request.docAsUpsert()) { throw new DocumentMissingException(shardId, request.type(), request.id()); } @@ -91,123 +137,164 @@ protected Result prepare(ShardId shardId, UpdateRequest request, final GetResult if (request.scriptedUpsert() && request.script() != null) { // Run the script to perform the create logic IndexRequest upsert = request.upsertRequest(); - Map upsertDoc = upsert.sourceAsMap(); - Map ctx = new HashMap<>(2); - // Tell the script that this is a create and not an update - ctx.put("op", "create"); - ctx.put("_source", upsertDoc); - ctx.put("_now", nowInMillis.getAsLong()); - ctx = executeScript(request.script, ctx); - - //Allow the script to abort the create by setting "op" to "none" - String scriptOpChoice = (String) ctx.get("op"); - - // Only valid options for an upsert script are "create" - // (the default) or "none", meaning abort upsert - if (!"create".equals(scriptOpChoice)) { - if (!"none".equals(scriptOpChoice)) { - logger.warn("Used upsert operation [{}] for script [{}], doing nothing...", scriptOpChoice, - request.script.getIdOrCode()); - } - UpdateResponse update = new UpdateResponse(shardId, getResult.getType(), getResult.getId(), - getResult.getVersion(), DocWriteResponse.Result.NOOP); - update.setGetResult(getResult); - return new Result(update, DocWriteResponse.Result.NOOP, upsertDoc, XContentType.JSON); + Tuple> upsertResult = executeScriptedUpsert(upsert, request.script, nowInMillis); + switch (upsertResult.v1()) { + case CREATE: + // Update the index request with the new "_source" + indexRequest.source(upsertResult.v2()); + break; + case NONE: + UpdateResponse update = new UpdateResponse(shardId, getResult.getType(), getResult.getId(), + getResult.getVersion(), DocWriteResponse.Result.NOOP); + update.setGetResult(getResult); + return new Result(update, DocWriteResponse.Result.NOOP, upsertResult.v2(), XContentType.JSON); + default: + // It's fine to throw an exception here, the leniency is handled/logged by `executeScriptedUpsert` + throw new IllegalArgumentException("unknown upsert operation, got: " + upsertResult.v1()); } - indexRequest.source((Map) ctx.get("_source")); } - indexRequest.index(request.index()).type(request.type()).id(request.id()) + indexRequest.index(request.index()) + .type(request.type()).id(request.id()).setRefreshPolicy(request.getRefreshPolicy()).routing(request.routing()) + .parent(request.parent()).timeout(request.timeout()).waitForActiveShards(request.waitForActiveShards()) // it has to be a "create!" - .create(true) - .setRefreshPolicy(request.getRefreshPolicy()) - .routing(request.routing()) - .parent(request.parent()) - .timeout(request.timeout()) - .waitForActiveShards(request.waitForActiveShards()); + .create(true); + if (request.versionType() != VersionType.INTERNAL) { // in all but the internal versioning mode, we want to create the new document using the given version. indexRequest.version(request.version()).versionType(request.versionType()); } - return new Result(indexRequest, DocWriteResponse.Result.CREATED, null, null); - } - long updateVersion = getResult.getVersion(); + return new Result(indexRequest, DocWriteResponse.Result.CREATED, null, null); + } + /** + * Calculate the version to use for the update request, using either the existing version if internal versioning is used, or the get + * result document's version if the version type is "FORCE". + */ + static long calculateUpdateVersion(UpdateRequest request, GetResult getResult) { if (request.versionType() != VersionType.INTERNAL) { assert request.versionType() == VersionType.FORCE; - updateVersion = request.version(); // remember, match_any is excluded by the conflict test + return request.version(); // remember, match_any is excluded by the conflict test + } else { + return getResult.getVersion(); } + } - if (getResult.internalSourceRef() == null) { - // no source, we can't do nothing, through a failure... - throw new DocumentSourceMissingException(shardId, request.type(), request.id()); + /** + * Calculate a routing value to be used, either the included index request's routing, or retrieved document's routing when defined. + */ + @Nullable + static String calculateRouting(GetResult getResult, @Nullable IndexRequest updateIndexRequest) { + if (updateIndexRequest != null && updateIndexRequest.routing() != null) { + return updateIndexRequest.routing(); + } else if (getResult.getFields().containsKey(RoutingFieldMapper.NAME)) { + return getResult.field(RoutingFieldMapper.NAME).getValue().toString(); + } else { + return null; } + } - Tuple> sourceAndContent = XContentHelper.convertToMap(getResult.internalSourceRef(), true); - String operation = null; - final Map updatedSourceAsMap; - final XContentType updateSourceContentType = sourceAndContent.v1(); - String routing = getResult.getFields().containsKey(RoutingFieldMapper.NAME) ? getResult.field(RoutingFieldMapper.NAME).getValue().toString() : null; - String parent = getResult.getFields().containsKey(ParentFieldMapper.NAME) ? getResult.field(ParentFieldMapper.NAME).getValue().toString() : null; - - if (request.script() == null && request.doc() != null) { - IndexRequest indexRequest = request.doc(); - updatedSourceAsMap = sourceAndContent.v2(); - if (indexRequest.routing() != null) { - routing = indexRequest.routing(); - } - if (indexRequest.parent() != null) { - parent = indexRequest.parent(); - } - boolean noop = !XContentHelper.update(updatedSourceAsMap, indexRequest.sourceAsMap(), request.detectNoop()); - // noop could still be true even if detectNoop isn't because update detects empty maps as noops. BUT we can only - // actually turn the update into a noop if detectNoop is true to preserve backwards compatibility and to handle - // cases where users repopulating multi-fields or adding synonyms, etc. - if (request.detectNoop() && noop) { - operation = "none"; - } + /** + * Calculate a parent value to be used, either the included index request's parent, or retrieved document's parent when defined. + */ + @Nullable + static String calculateParent(GetResult getResult, @Nullable IndexRequest updateIndexRequest) { + if (updateIndexRequest != null && updateIndexRequest.parent() != null) { + return updateIndexRequest.parent(); + } else if (getResult.getFields().containsKey(ParentFieldMapper.NAME)) { + return getResult.field(ParentFieldMapper.NAME).getValue().toString(); } else { - Map ctx = new HashMap<>(16); - ctx.put("_index", getResult.getIndex()); - ctx.put("_type", getResult.getType()); - ctx.put("_id", getResult.getId()); - ctx.put("_version", getResult.getVersion()); - ctx.put("_routing", routing); - ctx.put("_parent", parent); - ctx.put("_source", sourceAndContent.v2()); - ctx.put("_now", nowInMillis.getAsLong()); - - ctx = executeScript(request.script, ctx); + return null; + } + } - operation = (String) ctx.get("op"); + /** + * Prepare the request for merging the existing document with a new one, can optionally detect a noop change. Returns a {@code Result} + * containing a new {@code IndexRequest} to be executed on the primary and replicas. + */ + Result prepareUpdateIndexRequest(ShardId shardId, UpdateRequest request, GetResult getResult, boolean detectNoop) { + final long updateVersion = calculateUpdateVersion(request, getResult); + final IndexRequest currentRequest = request.doc(); + final String routing = calculateRouting(getResult, currentRequest); + final String parent = calculateParent(getResult, currentRequest); + final Tuple> sourceAndContent = XContentHelper.convertToMap(getResult.internalSourceRef(), true); + final XContentType updateSourceContentType = sourceAndContent.v1(); + final Map updatedSourceAsMap = sourceAndContent.v2(); - updatedSourceAsMap = (Map) ctx.get("_source"); - } + final boolean noop = !XContentHelper.update(updatedSourceAsMap, currentRequest.sourceAsMap(), detectNoop); - if (operation == null || "index".equals(operation)) { - final IndexRequest indexRequest = Requests.indexRequest(request.index()).type(request.type()).id(request.id()).routing(routing).parent(parent) - .source(updatedSourceAsMap, updateSourceContentType) - .version(updateVersion).versionType(request.versionType()) - .waitForActiveShards(request.waitForActiveShards()) - .timeout(request.timeout()) - .setRefreshPolicy(request.getRefreshPolicy()); - return new Result(indexRequest, DocWriteResponse.Result.UPDATED, updatedSourceAsMap, updateSourceContentType); - } else if ("delete".equals(operation)) { - DeleteRequest deleteRequest = Requests.deleteRequest(request.index()).type(request.type()).id(request.id()).routing(routing).parent(parent) - .version(updateVersion).versionType(request.versionType()) - .waitForActiveShards(request.waitForActiveShards()) - .timeout(request.timeout()) - .setRefreshPolicy(request.getRefreshPolicy()); - return new Result(deleteRequest, DocWriteResponse.Result.DELETED, updatedSourceAsMap, updateSourceContentType); - } else if ("none".equals(operation)) { - UpdateResponse update = new UpdateResponse(shardId, getResult.getType(), getResult.getId(), getResult.getVersion(), DocWriteResponse.Result.NOOP); - update.setGetResult(extractGetResult(request, request.index(), getResult.getVersion(), updatedSourceAsMap, updateSourceContentType, getResult.internalSourceRef())); + // We can only actually turn the update into a noop if detectNoop is true to preserve backwards compatibility and to handle cases + // where users repopulating multi-fields or adding synonyms, etc. + if (detectNoop && noop) { + UpdateResponse update = new UpdateResponse(shardId, getResult.getType(), getResult.getId(), + getResult.getVersion(), DocWriteResponse.Result.NOOP); + update.setGetResult(extractGetResult(request, request.index(), getResult.getVersion(), updatedSourceAsMap, + updateSourceContentType, getResult.internalSourceRef())); return new Result(update, DocWriteResponse.Result.NOOP, updatedSourceAsMap, updateSourceContentType); } else { - logger.warn("Used update operation [{}] for script [{}], doing nothing...", operation, request.script.getIdOrCode()); - UpdateResponse update = new UpdateResponse(shardId, getResult.getType(), getResult.getId(), getResult.getVersion(), DocWriteResponse.Result.NOOP); - return new Result(update, DocWriteResponse.Result.NOOP, updatedSourceAsMap, updateSourceContentType); + final IndexRequest finalIndexRequest = Requests.indexRequest(request.index()) + .type(request.type()).id(request.id()).routing(routing).parent(parent) + .source(updatedSourceAsMap, updateSourceContentType).version(updateVersion).versionType(request.versionType()) + .waitForActiveShards(request.waitForActiveShards()).timeout(request.timeout()) + .setRefreshPolicy(request.getRefreshPolicy()); + return new Result(finalIndexRequest, DocWriteResponse.Result.UPDATED, updatedSourceAsMap, updateSourceContentType); + } + } + + /** + * Prepare the request for updating an existing document using a script. Executes the script and returns a {@code Result} containing + * either a new {@code IndexRequest} or {@code DeleteRequest} (depending on the script's returned "op" value) to be executed on the + * primary and replicas. + */ + Result prepareUpdateScriptRequest(ShardId shardId, UpdateRequest request, GetResult getResult, LongSupplier nowInMillis) { + final long updateVersion = calculateUpdateVersion(request, getResult); + final IndexRequest currentRequest = request.doc(); + final String routing = calculateRouting(getResult, currentRequest); + final String parent = calculateParent(getResult, currentRequest); + final Tuple> sourceAndContent = XContentHelper.convertToMap(getResult.internalSourceRef(), true); + final XContentType updateSourceContentType = sourceAndContent.v1(); + final Map sourceAsMap = sourceAndContent.v2(); + + Map ctx = new HashMap<>(16); + ctx.put(ContextFields.OP, UpdateOpType.INDEX.toString()); // The default operation is "index" + ctx.put(ContextFields.INDEX, getResult.getIndex()); + ctx.put(ContextFields.TYPE, getResult.getType()); + ctx.put(ContextFields.ID, getResult.getId()); + ctx.put(ContextFields.VERSION, getResult.getVersion()); + ctx.put(ContextFields.ROUTING, routing); + ctx.put(ContextFields.PARENT, parent); + ctx.put(ContextFields.SOURCE, sourceAsMap); + ctx.put(ContextFields.NOW, nowInMillis.getAsLong()); + + ctx = executeScript(request.script, ctx); + + UpdateOpType operation = UpdateOpType.lenientFromString((String) ctx.get(ContextFields.OP), logger, request.script.getIdOrCode()); + + final Map updatedSourceAsMap = (Map) ctx.get(ContextFields.SOURCE); + + switch (operation) { + case INDEX: + final IndexRequest indexRequest = Requests.indexRequest(request.index()) + .type(request.type()).id(request.id()).routing(routing).parent(parent) + .source(updatedSourceAsMap, updateSourceContentType).version(updateVersion).versionType(request.versionType()) + .waitForActiveShards(request.waitForActiveShards()).timeout(request.timeout()) + .setRefreshPolicy(request.getRefreshPolicy()); + return new Result(indexRequest, DocWriteResponse.Result.UPDATED, updatedSourceAsMap, updateSourceContentType); + case DELETE: + DeleteRequest deleteRequest = Requests.deleteRequest(request.index()) + .type(request.type()).id(request.id()).routing(routing).parent(parent) + .version(updateVersion).versionType(request.versionType()).waitForActiveShards(request.waitForActiveShards()) + .timeout(request.timeout()).setRefreshPolicy(request.getRefreshPolicy()); + return new Result(deleteRequest, DocWriteResponse.Result.DELETED, updatedSourceAsMap, updateSourceContentType); + default: + // If it was neither an INDEX or DELETE operation, treat it as a noop + UpdateResponse update = new UpdateResponse(shardId, getResult.getType(), getResult.getId(), + getResult.getVersion(), DocWriteResponse.Result.NOOP); + update.setGetResult(extractGetResult(request, request.index(), getResult.getVersion(), updatedSourceAsMap, + updateSourceContentType, getResult.internalSourceRef())); + return new Result(update, DocWriteResponse.Result.NOOP, updatedSourceAsMap, updateSourceContentType); + } } @@ -216,7 +303,7 @@ private Map executeScript(Script script, Map ctx if (scriptService != null) { CompiledScript compiledScript = scriptService.compile(script, ScriptContext.Standard.UPDATE); ExecutableScript executableScript = scriptService.executable(compiledScript, script.getParams()); - executableScript.setNextVar("ctx", ctx); + executableScript.setNextVar(ContextFields.CTX, ctx); executableScript.run(); } } catch (Exception e) { @@ -229,7 +316,8 @@ private Map executeScript(Script script, Map ctx * Applies {@link UpdateRequest#fetchSource()} to the _source of the updated document to be returned in a update response. * For BWC this function also extracts the {@link UpdateRequest#fields()} from the updated document to be returned in a update response */ - public GetResult extractGetResult(final UpdateRequest request, String concreteIndex, long version, final Map source, XContentType sourceContentType, @Nullable final BytesReference sourceAsBytes) { + public GetResult extractGetResult(final UpdateRequest request, String concreteIndex, long version, final Map source, + XContentType sourceContentType, @Nullable final BytesReference sourceAsBytes) { if ((request.fields() == null || request.fields().length == 0) && (request.fetchSource() == null || request.fetchSource().fetchSource() == false)) { return null; @@ -278,7 +366,8 @@ public GetResult extractGetResult(final UpdateRequest request, String concreteIn } // TODO when using delete/none, we can still return the source as bytes by generating it (using the sourceContentType) - return new GetResult(concreteIndex, request.type(), request.id(), version, true, sourceRequested ? sourceFilteredAsBytes : null, fields); + return new GetResult(concreteIndex, request.type(), request.id(), version, true, + sourceRequested ? sourceFilteredAsBytes : null, fields); } public static class Result { @@ -288,7 +377,8 @@ public static class Result { private final Map updatedSourceAsMap; private final XContentType updateSourceContentType; - public Result(Streamable action, DocWriteResponse.Result result, Map updatedSourceAsMap, XContentType updateSourceContentType) { + public Result(Streamable action, DocWriteResponse.Result result, Map updatedSourceAsMap, + XContentType updateSourceContentType) { this.action = action; this.result = result; this.updatedSourceAsMap = updatedSourceAsMap; @@ -313,4 +403,58 @@ public XContentType updateSourceContentType() { } } + /** + * After executing the script, this is the type of operation that will be used for subsequent actions. This corresponds to the "ctx.op" + * variable inside of scripts. + */ + enum UpdateOpType { + CREATE("create"), + INDEX("index"), + DELETE("delete"), + NONE("none"); + + private final String name; + + UpdateOpType(String name) { + this.name = name; + } + + public static UpdateOpType lenientFromString(String operation, Logger logger, String scriptId) { + switch (operation) { + case "create": + return UpdateOpType.CREATE; + case "index": + return UpdateOpType.INDEX; + case "delete": + return UpdateOpType.DELETE; + case "none": + return UpdateOpType.NONE; + default: + // TODO: can we remove this leniency yet?? + logger.warn("Used upsert operation [{}] for script [{}], doing nothing...", operation, scriptId); + return UpdateOpType.NONE; + } + } + + @Override + public String toString() { + return name; + } + } + + /** + * Field names used to populate the script context + */ + public static class ContextFields { + public static final String CTX = "ctx"; + public static final String OP = "op"; + public static final String SOURCE = "_source"; + public static final String NOW = "_now"; + public static final String INDEX = "_index"; + public static final String TYPE = "_type"; + public static final String ID = "_id"; + public static final String VERSION = "_version"; + public static final String ROUTING = "_routing"; + public static final String PARENT = "_parent"; + } } diff --git a/core/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java b/core/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java index 339976a70865d..6cdc5c6bdac00 100644 --- a/core/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java @@ -20,6 +20,8 @@ package org.elasticsearch.action.update; import org.elasticsearch.Version; +import org.elasticsearch.action.DocWriteResponse; +import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.support.replication.ReplicationRequest; import org.elasticsearch.common.bytes.BytesArray; @@ -34,6 +36,8 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.env.Environment; +import org.elasticsearch.index.VersionType; +import org.elasticsearch.index.get.GetField; import org.elasticsearch.index.get.GetResult; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.script.MockScriptEngine; @@ -50,6 +54,7 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @@ -92,6 +97,16 @@ public void setUp() throws Exception { source.put("update_timestamp", ctx.get("_now")); return null; }); + scripts.put( + "ctx._source.body = \"foo\"", + vars -> { + @SuppressWarnings("unchecked") + final Map ctx = (Map) vars.get("ctx"); + @SuppressWarnings("unchecked") + final Map source = (Map) ctx.get("_source"); + source.put("body", "foo"); + return null; + }); scripts.put( "ctx._timestamp = ctx._now", vars -> { @@ -108,6 +123,22 @@ public void setUp() throws Exception { ctx.put("op", "delete"); return null; }); + scripts.put( + "ctx.op = bad", + vars -> { + @SuppressWarnings("unchecked") + final Map ctx = (Map) vars.get("ctx"); + ctx.put("op", "bad"); + return null; + }); + scripts.put( + "ctx.op = none", + vars -> { + @SuppressWarnings("unchecked") + final Map ctx = (Map) vars.get("ctx"); + ctx.put("op", "none"); + return null; + }); scripts.put("return", vars -> null); final ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(emptyList()); final MockScriptEngine engine = new MockScriptEngine("mock", scripts); @@ -502,4 +533,131 @@ public void testToValidateUpsertRequestWithVersion() { updateRequest.upsert(new IndexRequest("index", "type", "1").version(1L)); assertThat(updateRequest.validate().validationErrors(), contains("can't provide version in upsert request")); } + + public void testParentAndRoutingExtraction() throws Exception { + GetResult getResult = new GetResult("test", "type", "1", 0, false, null, null); + IndexRequest indexRequest = new IndexRequest("test", "type", "1"); + + // There is no routing and parent because the document doesn't exist + assertNull(UpdateHelper.calculateRouting(getResult, null)); + assertNull(UpdateHelper.calculateParent(getResult, null)); + + // There is no routing and parent the indexing request + assertNull(UpdateHelper.calculateRouting(getResult, indexRequest)); + assertNull(UpdateHelper.calculateParent(getResult, indexRequest)); + + // Doc exists but has no source or fields + getResult = new GetResult("test", "type", "1", 0, true, null, null); + + // There is no routing and parent on either request + assertNull(UpdateHelper.calculateRouting(getResult, indexRequest)); + assertNull(UpdateHelper.calculateParent(getResult, indexRequest)); + + Map fields = new HashMap<>(); + fields.put("_parent", new GetField("_parent", Collections.singletonList("parent1"))); + fields.put("_routing", new GetField("_routing", Collections.singletonList("routing1"))); + + // Doc exists and has the parent and routing fields + getResult = new GetResult("test", "type", "1", 0, true, null, fields); + + // Use the get result parent and routing + assertThat(UpdateHelper.calculateRouting(getResult, indexRequest), equalTo("routing1")); + assertThat(UpdateHelper.calculateParent(getResult, indexRequest), equalTo("parent1")); + + // Index request has overriding parent and routing values + indexRequest = new IndexRequest("test", "type", "1").parent("parent2").routing("routing2"); + + // Use the request's parent and routing + assertThat(UpdateHelper.calculateRouting(getResult, indexRequest), equalTo("routing2")); + assertThat(UpdateHelper.calculateParent(getResult, indexRequest), equalTo("parent2")); + } + + @SuppressWarnings("deprecated") // VersionType.FORCE is deprecated + public void testCalculateUpdateVersion() throws Exception { + long randomVersion = randomIntBetween(0, 100); + GetResult getResult = new GetResult("test", "type", "1", randomVersion, true, new BytesArray("{}"), null); + + UpdateRequest request = new UpdateRequest("test", "type1", "1"); + long version = UpdateHelper.calculateUpdateVersion(request, getResult); + + // Use the get result's version + assertThat(version, equalTo(randomVersion)); + + request = new UpdateRequest("test", "type1", "1").versionType(VersionType.FORCE).version(1337); + version = UpdateHelper.calculateUpdateVersion(request, getResult); + + // Use the forced update request version + assertThat(version, equalTo(1337L)); + } + + public void testNoopDetection() throws Exception { + ShardId shardId = new ShardId("test", "", 0); + GetResult getResult = new GetResult("test", "type", "1", 0, true, + new BytesArray("{\"body\": \"foo\"}"), + null); + + UpdateRequest request = new UpdateRequest("test", "type1", "1").fromXContent( + createParser(JsonXContent.jsonXContent, new BytesArray("{\"doc\": {\"body\": \"foo\"}}"))); + + UpdateHelper.Result result = updateHelper.prepareUpdateIndexRequest(shardId, request, getResult, true); + + assertThat(result.action(), instanceOf(UpdateResponse.class)); + assertThat(result.getResponseResult(), equalTo(DocWriteResponse.Result.NOOP)); + + // Try again, with detectNoop turned off + result = updateHelper.prepareUpdateIndexRequest(shardId, request, getResult, false); + assertThat(result.action(), instanceOf(IndexRequest.class)); + assertThat(result.getResponseResult(), equalTo(DocWriteResponse.Result.UPDATED)); + assertThat(result.updatedSourceAsMap().get("body").toString(), equalTo("foo")); + + // Change the request to be a different doc + request = new UpdateRequest("test", "type1", "1").fromXContent( + createParser(JsonXContent.jsonXContent, new BytesArray("{\"doc\": {\"body\": \"bar\"}}"))); + result = updateHelper.prepareUpdateIndexRequest(shardId, request, getResult, true); + + assertThat(result.action(), instanceOf(IndexRequest.class)); + assertThat(result.getResponseResult(), equalTo(DocWriteResponse.Result.UPDATED)); + assertThat(result.updatedSourceAsMap().get("body").toString(), equalTo("bar")); + + } + + public void testUpdateScript() throws Exception { + ShardId shardId = new ShardId("test", "", 0); + GetResult getResult = new GetResult("test", "type", "1", 0, true, + new BytesArray("{\"body\": \"bar\"}"), + null); + + UpdateRequest request = new UpdateRequest("test", "type1", "1") + .script(mockInlineScript("ctx._source.body = \"foo\"")); + + UpdateHelper.Result result = updateHelper.prepareUpdateScriptRequest(shardId, request, getResult, + ESTestCase::randomNonNegativeLong); + + assertThat(result.action(), instanceOf(IndexRequest.class)); + assertThat(result.getResponseResult(), equalTo(DocWriteResponse.Result.UPDATED)); + assertThat(result.updatedSourceAsMap().get("body").toString(), equalTo("foo")); + + // Now where the script changes the op to "delete" + request = new UpdateRequest("test", "type1", "1").script(mockInlineScript("ctx.op = delete")); + + result = updateHelper.prepareUpdateScriptRequest(shardId, request, getResult, + ESTestCase::randomNonNegativeLong); + + assertThat(result.action(), instanceOf(DeleteRequest.class)); + assertThat(result.getResponseResult(), equalTo(DocWriteResponse.Result.DELETED)); + + // We treat everything else as a No-op + boolean goodNoop = randomBoolean(); + if (goodNoop) { + request = new UpdateRequest("test", "type1", "1").script(mockInlineScript("ctx.op = none")); + } else { + request = new UpdateRequest("test", "type1", "1").script(mockInlineScript("ctx.op = bad")); + } + + result = updateHelper.prepareUpdateScriptRequest(shardId, request, getResult, + ESTestCase::randomNonNegativeLong); + + assertThat(result.action(), instanceOf(UpdateResponse.class)); + assertThat(result.getResponseResult(), equalTo(DocWriteResponse.Result.NOOP)); + } } From bf32b0c59d817ddb7f83d6d34a470ffa72cada46 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 8 May 2017 12:32:57 -0400 Subject: [PATCH 238/619] Improve bootstrap checks error messages When multiple bootstrap checks fail, it's not clear where one error message begins and the next error message ends. This commit numbers the bootstrap check error messages so they are easier to read. Relates #24548 --- .../java/org/elasticsearch/bootstrap/BootstrapChecks.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java index 3d371f0f23fc5..54a1c5fa44483 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java @@ -149,8 +149,10 @@ static void check( if (!errors.isEmpty()) { final List messages = new ArrayList<>(1 + errors.size()); - messages.add("bootstrap checks failed"); - messages.addAll(errors); + messages.add("[" + errors.size() + "] bootstrap checks failed"); + for (int i = 0; i < errors.size(); i++) { + messages.add("[" + (i + 1) + "]: " + errors.get(i)); + } final NodeValidationException ne = new NodeValidationException(String.join("\n", messages)); errors.stream().map(IllegalStateException::new).forEach(ne::addSuppressed); throw ne; From 89b305c09e429228178fe8796dc6a5d39e69459c Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 8 May 2017 13:37:28 -0400 Subject: [PATCH 239/619] Add global checkpoint assertion in index shard We previously removed this assertion because it could be violated in races. This commit adds this assertion back with sampling done more carefully to avoid failures solely due to race conditions. --- .../java/org/elasticsearch/index/shard/IndexShard.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java index 146900542a07f..45e7bd3d1e9ff 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -1521,6 +1521,8 @@ public void updateGlobalCheckpointOnPrimary() { */ public void updateGlobalCheckpointOnReplica(final long globalCheckpoint) { verifyReplicationTarget(); + // we sample the recovery stage before sampling the local checkpoint or we are subject to a race condition in the below assertion + final RecoveryState.Stage stage = recoveryState().getStage(); final SequenceNumbersService seqNoService = getEngine().seqNoService(); final long localCheckpoint = seqNoService.getLocalCheckpoint(); if (globalCheckpoint > localCheckpoint) { @@ -1530,10 +1532,10 @@ public void updateGlobalCheckpointOnReplica(final long globalCheckpoint) { * case that the global checkpoint update from the primary is ahead of the local checkpoint on this shard. In this case, we * ignore the global checkpoint update. This can happen if we are in the translog stage of recovery. Prior to this, the engine * is not opened and this shard will not receive global checkpoint updates, and after this the shard will be contributing to - * calculations of the the global checkpoint. However, we can not assert that we are in the translog stage of recovery here as - * while the global checkpoint update may have emanated from the primary when we were in that state, we could subsequently move - * to recovery finalization, or even finished recovery before the update arrives here. + * calculations of the the global checkpoint. */ + assert stage == RecoveryState.Stage.TRANSLOG + : "expected recovery stage [" + RecoveryState.Stage.TRANSLOG + "] but was [" + stage + "]"; return; } seqNoService.updateGlobalCheckpointOnReplica(globalCheckpoint); From 1de5bb8cd5a2e989398345241a338ee218420300 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 8 May 2017 15:50:52 -0400 Subject: [PATCH 240/619] Ensure test replicas have valid recovery state When starting a new replication group in an index level replication test case, a started replica would not have a valid recovery state. This violates simple assumptions as replicas always have to have recovered before being started. This commit causes this to be the case that this assumption is not violated too. --- .../index/replication/ESIndexLevelReplicationTestCase.java | 4 ++++ .../index/replication/IndexLevelReplicationTests.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java b/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java index 68373ce529aff..0f61f4d02bfb7 100644 --- a/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java +++ b/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java @@ -209,6 +209,10 @@ public void startPrimary() throws IOException { primary.markAsRecovering("store", new RecoveryState(primary.routingEntry(), pNode, null)); primary.recoverFromStore(); primary.updateRoutingEntry(ShardRoutingHelper.moveToStarted(primary.routingEntry())); + for (final IndexShard replica : replicas) { + //replica.markAsRecovering("peer recovery", new RecoveryState(replica.routingEntry(), getDiscoveryNode(replica.routingEntry().currentNodeId()), pNode)); + recoverReplica(replica); + } updateAllocationIDsOnPrimary(); } diff --git a/core/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java b/core/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java index 6a347034a0c55..3e037bc9c7b37 100644 --- a/core/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java +++ b/core/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java @@ -77,7 +77,6 @@ public void testSimpleAppendOnlyReplication() throws Exception { public void testAppendWhileRecovering() throws Exception { try (ReplicationGroup shards = createGroup(0)) { shards.startAll(); - IndexShard replica = shards.addReplica(); CountDownLatch latch = new CountDownLatch(2); int numDocs = randomIntBetween(100, 200); shards.appendDocs(1);// just append one to the translog so we can assert below @@ -94,6 +93,7 @@ public void run() { } }; thread.start(); + IndexShard replica = shards.addReplica(); Future future = shards.asyncRecoverReplica(replica, (indexShard, node) -> new RecoveryTarget(indexShard, node, recoveryListener, version -> { }) { From 0944577ee8579cfb124d6ece01c153f0f64697bb Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 8 May 2017 16:20:58 -0400 Subject: [PATCH 241/619] Remove commented code from ESILRTC This commit removes some commented out code from ESIndexLevelReplicationTestCase.java that was inadvertently committed. --- .../index/replication/ESIndexLevelReplicationTestCase.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java b/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java index 0f61f4d02bfb7..57955adbf3b49 100644 --- a/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java +++ b/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java @@ -210,7 +210,6 @@ public void startPrimary() throws IOException { primary.recoverFromStore(); primary.updateRoutingEntry(ShardRoutingHelper.moveToStarted(primary.routingEntry())); for (final IndexShard replica : replicas) { - //replica.markAsRecovering("peer recovery", new RecoveryState(replica.routingEntry(), getDiscoveryNode(replica.routingEntry().currentNodeId()), pNode)); recoverReplica(replica); } updateAllocationIDsOnPrimary(); From bd3717a0f85226818f85cf7c157f0a2b5807a404 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 8 May 2017 14:06:45 -0700 Subject: [PATCH 242/619] Scripting: Deprecate file scripts (#24552) File scripts will be removed in 6.0. This commit adds a deprecation warning for 5.5 when the first file script is loaded. --- .../main/java/org/elasticsearch/script/ScriptService.java | 6 ++++++ .../test/java/org/elasticsearch/script/FileScriptTests.java | 2 ++ .../java/org/elasticsearch/script/ScriptServiceTests.java | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/core/src/main/java/org/elasticsearch/script/ScriptService.java b/core/src/main/java/org/elasticsearch/script/ScriptService.java index 7a8691656a4c5..3e612915a7b07 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptService.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptService.java @@ -542,6 +542,7 @@ public void onRemoval(RemovalNotification notification } private class ScriptChangesListener implements FileChangesListener { + private boolean deprecationEmitted = false; private Tuple getScriptNameExt(Path file) { Path scriptPath = scriptsDirectory.relativize(file); @@ -574,6 +575,11 @@ public void onFileInit(Path file) { if (engineService == null) { logger.warn("No script engine found for [{}]", scriptNameExt.v2()); } else { + if (deprecationEmitted == false) { + deprecationLogger.deprecated("File scripts are deprecated. Use stored or inline scripts instead."); + deprecationEmitted = true; + } + try { //we don't know yet what the script will be used for, but if all of the operations for this lang // with file scripts are disabled, it makes no sense to even compile it and cache it. diff --git a/core/src/test/java/org/elasticsearch/script/FileScriptTests.java b/core/src/test/java/org/elasticsearch/script/FileScriptTests.java index 92e659ac755d5..c502bd5f03020 100644 --- a/core/src/test/java/org/elasticsearch/script/FileScriptTests.java +++ b/core/src/test/java/org/elasticsearch/script/FileScriptTests.java @@ -59,6 +59,7 @@ public void testFileScriptFound() throws Exception { assertNotNull(compiledScript); MockCompiledScript executable = (MockCompiledScript) compiledScript.compiled(); assertEquals("script1.mockscript", executable.getName()); + assertWarnings("File scripts are deprecated. Use stored or inline scripts instead."); } public void testAllOpsDisabled() throws Exception { @@ -78,5 +79,6 @@ public void testAllOpsDisabled() throws Exception { assertTrue(e.getMessage(), e.getMessage().contains("scripts of type [file], operation [" + context.getKey() + "] and lang [" + MockScriptEngine.NAME + "] are disabled")); } } + assertWarnings("File scripts are deprecated. Use stored or inline scripts instead."); } } diff --git a/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java b/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java index 6c2bd6f1be2c0..026751f64436a 100644 --- a/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java +++ b/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java @@ -182,6 +182,7 @@ public void testScriptsWithoutExtensions() throws IOException { } catch (IllegalArgumentException ex) { assertThat(ex.getMessage(), containsString("unable to find file script [test_script] using lang [test]")); } + assertWarnings("File scripts are deprecated. Use stored or inline scripts instead."); } public void testScriptCompiledOnceHiddenFileDetected() throws IOException { @@ -201,6 +202,7 @@ public void testScriptCompiledOnceHiddenFileDetected() throws IOException { Files.delete(testHiddenFile); Files.delete(testFileScript); resourceWatcherService.notifyNow(); + assertWarnings("File scripts are deprecated. Use stored or inline scripts instead."); } public void testInlineScriptCompiledOnceCache() throws IOException { @@ -227,6 +229,7 @@ public void testDefaultBehaviourFineGrainedSettings() throws IOException { assertCompileRejected("dtest", "script", ScriptType.STORED, scriptContext); assertCompileAccepted("dtest", "file_script", ScriptType.FILE, scriptContext); } + assertWarnings("File scripts are deprecated. Use stored or inline scripts instead."); } public void testFineGrainedSettings() throws IOException { @@ -317,6 +320,7 @@ public void testFineGrainedSettings() throws IOException { } } } + assertWarnings("File scripts are deprecated. Use stored or inline scripts instead."); } public void testCompileNonRegisteredContext() throws IOException { @@ -384,6 +388,7 @@ public void testFileScriptCountedInCompilationStats() throws IOException { createFileScripts("test"); scriptService.compile(new Script(ScriptType.FILE, "test", "file_script", Collections.emptyMap()), randomFrom(scriptContexts)); assertEquals(1L, scriptService.stats().getCompilations()); + assertWarnings("File scripts are deprecated. Use stored or inline scripts instead."); } public void testIndexedScriptCountedInCompilationStats() throws IOException { From fec1802e2f622c1a126c9adda28fa53f1b55529a Mon Sep 17 00:00:00 2001 From: joachimdraeger Date: Mon, 8 May 2017 22:43:01 +0100 Subject: [PATCH 243/619] Fixes inefficient loading of snapshot repository data (#24510) This commit fixes inefficient (worst case exponential) loading of snapshot repository data when checking for incompatible snapshots, that was introduced in #22267. When getting snapshot information, getRepositoryData() was called on every snapshot, so if there are a large number of snapshots in the repository and _all snapshots were requested, the performance degraded exponentially. This commit fixes the issue by only calling getRepositoryData once and using the data from it in all subsequent calls to get snapshot information. Closes #24509 --- .../snapshots/get/TransportGetSnapshotsAction.java | 7 +++++-- .../blobstore/BlobStoreRepository.java | 9 --------- .../elasticsearch/snapshots/SnapshotsService.java | 14 ++++++++++++-- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java index ad8541ce9fd02..88b84f36ff6ca 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java @@ -30,6 +30,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.repositories.RepositoryData; import org.elasticsearch.snapshots.SnapshotId; import org.elasticsearch.snapshots.SnapshotInfo; import org.elasticsearch.snapshots.SnapshotMissingException; @@ -82,13 +83,14 @@ protected void masterOperation(final GetSnapshotsRequest request, ClusterState s List snapshotInfoBuilder = new ArrayList<>(); final Map allSnapshotIds = new HashMap<>(); final List currentSnapshotIds = new ArrayList<>(); + final RepositoryData repositoryData = snapshotsService.getRepositoryData(repository); for (SnapshotInfo snapshotInfo : snapshotsService.currentSnapshots(repository)) { SnapshotId snapshotId = snapshotInfo.snapshotId(); allSnapshotIds.put(snapshotId.getName(), snapshotId); currentSnapshotIds.add(snapshotId); } if (isCurrentSnapshotsOnly(request.snapshots()) == false) { - for (SnapshotId snapshotId : snapshotsService.getRepositoryData(repository).getAllSnapshotIds()) { + for (SnapshotId snapshotId : repositoryData.getAllSnapshotIds()) { allSnapshotIds.put(snapshotId.getName(), snapshotId); } } @@ -119,7 +121,8 @@ protected void masterOperation(final GetSnapshotsRequest request, ClusterState s } } - snapshotInfoBuilder.addAll(snapshotsService.snapshots(repository, new ArrayList<>(toResolve), request.ignoreUnavailable())); + snapshotInfoBuilder.addAll(snapshotsService.snapshots( + repository, new ArrayList<>(toResolve), repositoryData.getIncompatibleSnapshotIds(), request.ignoreUnavailable())); listener.onResponse(new GetSnapshotsResponse(snapshotInfoBuilder)); } catch (Exception e) { listener.onFailure(e); diff --git a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index 805f902100cf9..f9fcb3a470afb 100644 --- a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -490,15 +490,6 @@ public MetaData getSnapshotMetaData(SnapshotInfo snapshot, List indices @Override public SnapshotInfo getSnapshotInfo(final SnapshotId snapshotId) { - if (getRepositoryData().getIncompatibleSnapshotIds().contains(snapshotId)) { - // an incompatible snapshot - cannot read its snapshot metadata file, just return - // a SnapshotInfo indicating its incompatible - return SnapshotInfo.incompatible(snapshotId); - } - return getSnapshotInfoInternal(snapshotId); - } - - private SnapshotInfo getSnapshotInfoInternal(final SnapshotId snapshotId) { try { return snapshotFormat.read(snapshotsBlobContainer, snapshotId.getUUID()); } catch (NoSuchFileException ex) { diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java index 392127ac30b5a..a30324a13fc91 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java @@ -164,11 +164,15 @@ public SnapshotInfo snapshot(final String repositoryName, final SnapshotId snaps * * @param repositoryName repository name * @param snapshotIds snapshots for which to fetch snapshot information + * @param incompatibleSnapshotIds snapshots for which not to fetch snapshot information * @param ignoreUnavailable if true, snapshots that could not be read will only be logged with a warning, * if false, they will throw an error * @return list of snapshots */ - public List snapshots(final String repositoryName, List snapshotIds, final boolean ignoreUnavailable) { + public List snapshots(final String repositoryName, + final List snapshotIds, + final List incompatibleSnapshotIds, + final boolean ignoreUnavailable) { final Set snapshotSet = new HashSet<>(); final Set snapshotIdsToIterate = new HashSet<>(snapshotIds); // first, look at the snapshots in progress @@ -182,7 +186,13 @@ public List snapshots(final String repositoryName, List) () -> new ParameterizedMessage("failed to get snapshot [{}]", snapshotId), ex); From 423b0f5e3d30430c00f18f621c88fe2fcd44e1aa Mon Sep 17 00:00:00 2001 From: Tal Levy Date: Mon, 8 May 2017 15:06:39 -0700 Subject: [PATCH 244/619] add option for _ingest.timestamp to use new ZonedDateTime (#24030) Previously, Mustache would call `toString` on the `_ingest.timestamp` field and return a date format that did not match Elasticsearch's defaults for date-mapping parsing. The new ZonedDateTime class in Java 8 happens to do format itself in the same way ES is expecting. This commit adds support for a feature flag that enables the usage of this new date format that has more native behavior. Fixes #23168. This new fix can be found in the form of a cluster setting called `ingest.new_date_format`. By default, in 5.x, the existing behavior will remain the same. One will set this property to `true` in order to take advantage of this update for ingest-pipeline convenience. --- .../ingest/SimulatePipelineRequest.java | 8 +- .../common/settings/ClusterSettings.java | 4 +- .../elasticsearch/ingest/IngestDocument.java | 17 ++- .../elasticsearch/ingest/IngestService.java | 10 +- .../ingest/PipelineExecutionService.java | 3 +- .../elasticsearch/ingest/PipelineStore.java | 14 ++- .../java/org/elasticsearch/node/Node.java | 2 +- .../ingest/IngestDocumentTests.java | 33 +++++- .../ingest/IngestServiceTests.java | 7 +- .../ingest/PipelineStoreTests.java | 12 +- .../60_pipeline_timestamp_date_mapping.yaml | 107 ++++++++++++++++++ 11 files changed, 199 insertions(+), 18 deletions(-) create mode 100644 qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml diff --git a/core/src/main/java/org/elasticsearch/action/ingest/SimulatePipelineRequest.java b/core/src/main/java/org/elasticsearch/action/ingest/SimulatePipelineRequest.java index 200b83ff02b96..170af18968114 100644 --- a/core/src/main/java/org/elasticsearch/action/ingest/SimulatePipelineRequest.java +++ b/core/src/main/java/org/elasticsearch/action/ingest/SimulatePipelineRequest.java @@ -162,18 +162,18 @@ static Parsed parseWithPipelineId(String pipelineId, Map config, if (pipeline == null) { throw new IllegalArgumentException("pipeline [" + pipelineId + "] does not exist"); } - List ingestDocumentList = parseDocs(config); + List ingestDocumentList = parseDocs(config, pipelineStore.isNewIngestDateFormat()); return new Parsed(pipeline, ingestDocumentList, verbose); } static Parsed parse(Map config, boolean verbose, PipelineStore pipelineStore) throws Exception { Map pipelineConfig = ConfigurationUtils.readMap(null, null, config, Fields.PIPELINE); Pipeline pipeline = PIPELINE_FACTORY.create(SIMULATED_PIPELINE_ID, pipelineConfig, pipelineStore.getProcessorFactories()); - List ingestDocumentList = parseDocs(config); + List ingestDocumentList = parseDocs(config, pipelineStore.isNewIngestDateFormat()); return new Parsed(pipeline, ingestDocumentList, verbose); } - private static List parseDocs(Map config) { + private static List parseDocs(Map config, boolean newDateFormat) { List> docs = ConfigurationUtils.readList(null, null, config, Fields.DOCS); List ingestDocumentList = new ArrayList<>(); for (Map dataMap : docs) { @@ -183,7 +183,7 @@ private static List parseDocs(Map config) { ConfigurationUtils.readStringProperty(null, null, dataMap, MetaData.ID.getFieldName(), "_id"), ConfigurationUtils.readOptionalStringProperty(null, null, dataMap, MetaData.ROUTING.getFieldName()), ConfigurationUtils.readOptionalStringProperty(null, null, dataMap, MetaData.PARENT.getFieldName()), - document); + document, newDateFormat); ingestDocumentList.add(ingestDocument); } return ingestDocumentList; diff --git a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java index 778b02dd7915a..0b3f4d4cbc990 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java +++ b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java @@ -73,6 +73,7 @@ import org.elasticsearch.indices.fielddata.cache.IndicesFieldDataCache; import org.elasticsearch.indices.recovery.RecoverySettings; import org.elasticsearch.indices.store.IndicesStore; +import org.elasticsearch.ingest.IngestService; import org.elasticsearch.monitor.fs.FsService; import org.elasticsearch.monitor.jvm.JvmGcMonitorService; import org.elasticsearch.monitor.jvm.JvmService; @@ -404,6 +405,7 @@ public void apply(Settings value, Settings current, Settings previous) { SearchModule.INDICES_MAX_CLAUSE_COUNT_SETTING, ThreadPool.ESTIMATED_TIME_INTERVAL_SETTING, FastVectorHighlighter.SETTING_TV_HIGHLIGHT_MULTI_VALUE, - Node.BREAKER_TYPE_KEY + Node.BREAKER_TYPE_KEY, + IngestService.NEW_INGEST_DATE_FORMAT ))); } diff --git a/core/src/main/java/org/elasticsearch/ingest/IngestDocument.java b/core/src/main/java/org/elasticsearch/ingest/IngestDocument.java index fcf49ef6992ad..05b92b5772377 100644 --- a/core/src/main/java/org/elasticsearch/ingest/IngestDocument.java +++ b/core/src/main/java/org/elasticsearch/ingest/IngestDocument.java @@ -20,6 +20,7 @@ package org.elasticsearch.ingest; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.IndexFieldMapper; import org.elasticsearch.index.mapper.ParentFieldMapper; @@ -27,6 +28,8 @@ import org.elasticsearch.index.mapper.SourceFieldMapper; import org.elasticsearch.index.mapper.TypeFieldMapper; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -52,6 +55,11 @@ public final class IngestDocument { private final Map ingestMetadata; public IngestDocument(String index, String type, String id, String routing, String parent, Map source) { + this(index, type, id, routing, parent, source, false); + } + + public IngestDocument(String index, String type, String id, String routing, String parent, Map source, + boolean newDateFormat) { this.sourceAndMetadata = new HashMap<>(); this.sourceAndMetadata.putAll(source); this.sourceAndMetadata.put(MetaData.INDEX.getFieldName(), index); @@ -65,7 +73,11 @@ public IngestDocument(String index, String type, String id, String routing, Stri } this.ingestMetadata = new HashMap<>(); - this.ingestMetadata.put(TIMESTAMP, new Date()); + if (newDateFormat) { + this.ingestMetadata.put(TIMESTAMP, ZonedDateTime.now(ZoneOffset.UTC)); + } else { + this.ingestMetadata.put(TIMESTAMP, new Date()); + } } /** @@ -608,6 +620,9 @@ private static Object deepCopy(Object value) { return value; } else if (value instanceof Date) { return ((Date) value).clone(); + } else if (value instanceof ZonedDateTime) { + ZonedDateTime zonedDateTime = (ZonedDateTime) value; + return ZonedDateTime.of(zonedDateTime.toLocalDate(), zonedDateTime.toLocalTime(), zonedDateTime.getZone()); } else { throw new IllegalArgumentException("unexpected value type [" + value.getClass() + "]"); } diff --git a/core/src/main/java/org/elasticsearch/ingest/IngestService.java b/core/src/main/java/org/elasticsearch/ingest/IngestService.java index 5249ed7a7dc84..1455e37588a8c 100644 --- a/core/src/main/java/org/elasticsearch/ingest/IngestService.java +++ b/core/src/main/java/org/elasticsearch/ingest/IngestService.java @@ -25,6 +25,8 @@ import java.util.List; import java.util.Map; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.index.analysis.AnalysisRegistry; @@ -32,15 +34,19 @@ import org.elasticsearch.script.ScriptService; import org.elasticsearch.threadpool.ThreadPool; +import static org.elasticsearch.common.settings.Setting.Property; + /** * Holder class for several ingest related services. */ public class IngestService { + public static final Setting NEW_INGEST_DATE_FORMAT = + Setting.boolSetting("ingest.new_date_format", false, Property.NodeScope, Property.Dynamic, Property.Deprecated); private final PipelineStore pipelineStore; private final PipelineExecutionService pipelineExecutionService; - public IngestService(Settings settings, ThreadPool threadPool, + public IngestService(ClusterSettings clusterSettings, Settings settings, ThreadPool threadPool, Environment env, ScriptService scriptService, AnalysisRegistry analysisRegistry, List ingestPlugins) { @@ -56,7 +62,7 @@ public IngestService(Settings settings, ThreadPool threadPool, } } } - this.pipelineStore = new PipelineStore(settings, Collections.unmodifiableMap(processorFactories)); + this.pipelineStore = new PipelineStore(clusterSettings, settings, Collections.unmodifiableMap(processorFactories)); this.pipelineExecutionService = new PipelineExecutionService(pipelineStore, threadPool); } diff --git a/core/src/main/java/org/elasticsearch/ingest/PipelineExecutionService.java b/core/src/main/java/org/elasticsearch/ingest/PipelineExecutionService.java index c1b46e495678f..cd7787a691087 100644 --- a/core/src/main/java/org/elasticsearch/ingest/PipelineExecutionService.java +++ b/core/src/main/java/org/elasticsearch/ingest/PipelineExecutionService.java @@ -160,7 +160,8 @@ private void innerExecute(IndexRequest indexRequest, Pipeline pipeline) throws E String routing = indexRequest.routing(); String parent = indexRequest.parent(); Map sourceAsMap = indexRequest.sourceAsMap(); - IngestDocument ingestDocument = new IngestDocument(index, type, id, routing, parent, sourceAsMap); + IngestDocument ingestDocument = new IngestDocument(index, type, id, routing, parent, + sourceAsMap, store.isNewIngestDateFormat()); pipeline.execute(ingestDocument); Map metadataMap = ingestDocument.extractMetadata(); diff --git a/core/src/main/java/org/elasticsearch/ingest/PipelineStore.java b/core/src/main/java/org/elasticsearch/ingest/PipelineStore.java index 8c46c14fad656..327ab18907c56 100644 --- a/core/src/main/java/org/elasticsearch/ingest/PipelineStore.java +++ b/core/src/main/java/org/elasticsearch/ingest/PipelineStore.java @@ -35,6 +35,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.regex.Regex; +import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentHelper; @@ -51,6 +52,7 @@ public class PipelineStore extends AbstractComponent implements ClusterStateAppl private final Pipeline.Factory factory = new Pipeline.Factory(); private final Map processorFactories; + private volatile boolean newIngestDateFormat; // Ideally this should be in IngestMetadata class, but we don't have the processor factories around there. // We know of all the processor factories when a node with all its plugin have been initialized. Also some @@ -58,9 +60,15 @@ public class PipelineStore extends AbstractComponent implements ClusterStateAppl // are loaded, so in the cluster state we just save the pipeline config and here we keep the actual pipelines around. volatile Map pipelines = new HashMap<>(); - public PipelineStore(Settings settings, Map processorFactories) { + public PipelineStore(ClusterSettings clusterSettings, Settings settings, Map processorFactories) { super(settings); this.processorFactories = processorFactories; + this.newIngestDateFormat = IngestService.NEW_INGEST_DATE_FORMAT.get(settings); + clusterSettings.addSettingsUpdateConsumer(IngestService.NEW_INGEST_DATE_FORMAT, this::setNewIngestDateFormat); + } + + private void setNewIngestDateFormat(Boolean newIngestDateFormat) { + this.newIngestDateFormat = newIngestDateFormat; } @Override @@ -204,6 +212,10 @@ public Map getProcessorFactories() { return processorFactories; } + public boolean isNewIngestDateFormat() { + return newIngestDateFormat; + } + /** * @return pipeline configuration specified by id. If multiple ids or wildcards are specified multiple pipelines * may be returned diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index 28d2f42f592cd..a8f3921b6924a 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -339,7 +339,7 @@ protected Node(final Environment environment, Collection final ClusterService clusterService = new ClusterService(settings, settingsModule.getClusterSettings(), threadPool); clusterService.addListener(scriptModule.getScriptService()); resourcesToClose.add(clusterService); - final IngestService ingestService = new IngestService(settings, threadPool, this.environment, + final IngestService ingestService = new IngestService(clusterService.getClusterSettings(), settings, threadPool, this.environment, scriptModule.getScriptService(), analysisModule.getAnalysisRegistry(), pluginsService.filterPlugins(IngestPlugin.class)); final ClusterInfoService clusterInfoService = newClusterInfoService(settings, clusterService, threadPool, client); diff --git a/core/src/test/java/org/elasticsearch/ingest/IngestDocumentTests.java b/core/src/test/java/org/elasticsearch/ingest/IngestDocumentTests.java index e1ec12e29b692..1dc6a0d27ffbe 100644 --- a/core/src/test/java/org/elasticsearch/ingest/IngestDocumentTests.java +++ b/core/src/test/java/org/elasticsearch/ingest/IngestDocumentTests.java @@ -22,6 +22,8 @@ import org.elasticsearch.test.ESTestCase; import org.junit.Before; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -45,13 +47,18 @@ public class IngestDocumentTests extends ESTestCase { private static final Date BOGUS_TIMESTAMP = new Date(0L); + private static final ZonedDateTime BOGUS_TIMESTAMP_NEW_DATE_FORMAT = ZonedDateTime.of(2016, 10, 23, 0, 0, 0, 0, ZoneOffset.UTC); private IngestDocument ingestDocument; + private IngestDocument ingestDocumentWithNewDateFormat; - @Before - public void setIngestDocument() { + public IngestDocument getTestIngestDocument(boolean newDateFormat) { Map document = new HashMap<>(); Map ingestMap = new HashMap<>(); - ingestMap.put("timestamp", BOGUS_TIMESTAMP); + if (newDateFormat) { + ingestMap.put("timestamp", BOGUS_TIMESTAMP_NEW_DATE_FORMAT); + } else { + ingestMap.put("timestamp", BOGUS_TIMESTAMP); + } document.put("_ingest", ingestMap); document.put("foo", "bar"); document.put("int", 123); @@ -72,7 +79,18 @@ public void setIngestDocument() { list.add(null); document.put("list", list); - ingestDocument = new IngestDocument("index", "type", "id", null, null, document); + return new IngestDocument("index", "type", "id", null, null, document, newDateFormat); + } + + @Before + public void setIngestDocuments() { + ingestDocument = getTestIngestDocument(false); + ingestDocumentWithNewDateFormat = getTestIngestDocument(true); + } + + public void testDefaultConstructorUsesDateClass() { + IngestDocument ingestDocument = new IngestDocument("foo", "bar", "baz", "fuzz", "buzz", Collections.emptyMap()); + assertThat(ingestDocument.getFieldValue("_ingest.timestamp", Object.class).getClass(), equalTo(Date.class)); } public void testSimpleGetFieldValue() { @@ -88,6 +106,13 @@ public void testSimpleGetFieldValue() { assertThat(ingestDocument.getFieldValue("_source._ingest.timestamp", Date.class), equalTo(BOGUS_TIMESTAMP)); } + public void testNewDateFormat() { + assertThat(ingestDocumentWithNewDateFormat.getFieldValue("_ingest.timestamp", ZonedDateTime.class), + both(notNullValue()).and(not(equalTo(BOGUS_TIMESTAMP_NEW_DATE_FORMAT)))); + assertThat(ingestDocumentWithNewDateFormat.getFieldValue("_source._ingest.timestamp", ZonedDateTime.class), + equalTo(BOGUS_TIMESTAMP_NEW_DATE_FORMAT)); + } + public void testGetSourceObject() { try { ingestDocument.getFieldValue("_source", Object.class); diff --git a/core/src/test/java/org/elasticsearch/ingest/IngestServiceTests.java b/core/src/test/java/org/elasticsearch/ingest/IngestServiceTests.java index 3a842a4690afa..d36b3390e4164 100644 --- a/core/src/test/java/org/elasticsearch/ingest/IngestServiceTests.java +++ b/core/src/test/java/org/elasticsearch/ingest/IngestServiceTests.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.Map; +import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.IngestPlugin; import org.elasticsearch.test.ESTestCase; @@ -39,7 +40,8 @@ public Map getProcessors(Processor.Parameters paramet public void testIngestPlugin() { ThreadPool tp = Mockito.mock(ThreadPool.class); - IngestService ingestService = new IngestService(Settings.EMPTY, tp, null, null, null, Collections.singletonList(DUMMY_PLUGIN)); + IngestService ingestService = new IngestService(new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), + Settings.EMPTY, tp, null, null, null, Collections.singletonList(DUMMY_PLUGIN)); Map factories = ingestService.getPipelineStore().getProcessorFactories(); assertTrue(factories.containsKey("foo")); assertEquals(1, factories.size()); @@ -48,7 +50,8 @@ public void testIngestPlugin() { public void testIngestPluginDuplicate() { ThreadPool tp = Mockito.mock(ThreadPool.class); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> - new IngestService(Settings.EMPTY, tp, null, null, null, Arrays.asList(DUMMY_PLUGIN, DUMMY_PLUGIN)) + new IngestService(new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), + Settings.EMPTY, tp, null, null, null, Arrays.asList(DUMMY_PLUGIN, DUMMY_PLUGIN)) ); assertTrue(e.getMessage(), e.getMessage().contains("already registered")); } diff --git a/core/src/test/java/org/elasticsearch/ingest/PipelineStoreTests.java b/core/src/test/java/org/elasticsearch/ingest/PipelineStoreTests.java index 19a269c3f7181..a503ac9aaa820 100644 --- a/core/src/test/java/org/elasticsearch/ingest/PipelineStoreTests.java +++ b/core/src/test/java/org/elasticsearch/ingest/PipelineStoreTests.java @@ -29,6 +29,7 @@ import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.test.ESTestCase; @@ -49,6 +50,7 @@ public class PipelineStoreTests extends ESTestCase { + private ClusterSettings clusterSettings; private PipelineStore store; @Before @@ -93,7 +95,8 @@ public String getTag() { } }; }); - store = new PipelineStore(Settings.EMPTY, processorFactories); + clusterSettings = new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); + store = new PipelineStore(clusterSettings, Settings.EMPTY, processorFactories); } public void testUpdatePipelines() { @@ -369,4 +372,11 @@ public void testValidateNoIngestInfo() throws Exception { store.validatePipeline(Collections.singletonMap(discoveryNode, ingestInfo), putRequest); } + public void testUpdateIngestNewDateFormatSetting() throws Exception { + assertFalse(store.isNewIngestDateFormat()); + clusterSettings.applySettings(Settings.builder().put(IngestService.NEW_INGEST_DATE_FORMAT.getKey(), true).build()); + assertTrue(store.isNewIngestDateFormat()); + assertWarnings("[ingest.new_date_format] setting was deprecated in Elasticsearch and will be " + + "removed in a future release! See the breaking changes documentation for the next major version."); + } } diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml new file mode 100644 index 0000000000000..e172ad9f4ce64 --- /dev/null +++ b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml @@ -0,0 +1,107 @@ +--- +"Test timestamp templating does not match date-mapping defaults": + - do: + cluster.health: + wait_for_status: green + + - do: + indices.create: + index: timetest + body: + mappings: + test: { "properties": { "my_time": {"type": "date"}}} + + - do: + ingest.put_pipeline: + id: "my_timely_pipeline" + body: > + { + "description": "_description", + "processors": [ + { + "set" : { + "field": "my_time", + "value": "{{ _ingest.timestamp }}" + } + }, + { + "date" : { + "field" : "my_time", + "target_field": "my_time", + "formats": ["EEE MMM dd HH:mm:ss zzz yyyy"] + } + } + ] + } + - match: { acknowledged: true } + + - do: + index: + index: timetest + type: test + id: 1 + pipeline: "my_timely_pipeline" + body: {} + +--- +"Test timestamp templating matches date-mapping defaults with ingest.new_date_format": + - skip: + version: " - 5.3.99" + reason: deprecated in 5.4.0 + features: "warnings" + + - do: + cluster.health: + wait_for_status: green + + - do: + indices.create: + index: timetest_newdateformat + body: + mappings: + test: { "properties": { "my_time": {"type": "date"}}} + + - do: + cluster.put_settings: + body: + transient: + ingest.new_date_format: true + warnings: + - "[ingest.new_date_format] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version." + + - match: {transient: {ingest: {new_date_format: "true"}}} + + - do: + ingest.put_pipeline: + id: "my_timely_pipeline_with_new_date_format" + body: > + { + "description": "_description", + "processors": [ + { + "set" : { + "field": "my_time", + "value": "{{ _ingest.timestamp }}" + } + } + ] + } + - match: { acknowledged: true } + + - do: + index: + index: timetest + type: test + id: 1 + pipeline: "my_timely_pipeline_with_new_date_format" + body: {} + + - do: + cluster.put_settings: + body: + transient: + ingest.new_date_format: false + warnings: + - "[ingest.new_date_format] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version." + + - match: {transient: {ingest: {new_date_format: "false"}}} From 4df636b5efc06ee980cdc3befc5699f39f537805 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Tue, 9 May 2017 09:14:17 +0200 Subject: [PATCH 245/619] Fix single shard scroll within a cluster with nodes in version >= 5.3 and <= 5.3 (#24512) If a node in version >= 5.3 acts as a coordinating node during a scroll request that targets a single shard, the scroll may return the same documents over and over iff the targeted shard is hosted by a node with a version <= 5.3. The nodes in this version will advance the scroll only if the search_type has been set to `query_and_fetch` though this search type has been removed in 5.3. This change handles this situation by adding the removed search_type in the request that targets a node in version <= 5.3. --- .../action/search/SearchTransportService.java | 8 +++ .../internal/ShardSearchLocalRequest.java | 4 ++ .../internal/ShardSearchTransportRequest.java | 4 ++ .../rest-api-spec/test/scroll/10_basic.yaml | 72 +++++++++++++++++++ 4 files changed, 88 insertions(+) diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java index 221641097d362..3699d3d01392e 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java @@ -121,6 +121,14 @@ public void sendExecuteQuery(Transport.Connection connection, final ShardSearchT final boolean fetchDocuments = request.numberOfShards() == 1; Supplier supplier = fetchDocuments ? QueryFetchSearchResult::new : QuerySearchResult::new; if (connection.getVersion().onOrBefore(Version.V_5_3_0_UNRELEASED) && fetchDocuments) { + if (connection.getVersion().before(Version.V_5_3_0_UNRELEASED) && request.scroll() != null) { + /** + * This is needed for nodes pre 5.3 when the single shard optimization is used. + * These nodes will set the last emitted doc only if the removed `query_and_fetch` search type is set + * in the request. See {@link SearchType}. + */ + request.searchType(SearchType.fromId((byte) 3)); + } // TODO this BWC layer can be removed once this is back-ported to 5.3 transportService.sendChildRequest(connection, QUERY_FETCH_ACTION_NAME, request, task, new ActionListenerResponseHandler<>(listener, supplier)); diff --git a/core/src/main/java/org/elasticsearch/search/internal/ShardSearchLocalRequest.java b/core/src/main/java/org/elasticsearch/search/internal/ShardSearchLocalRequest.java index 1f3868c0dbac3..d21fc2faf5c20 100644 --- a/core/src/main/java/org/elasticsearch/search/internal/ShardSearchLocalRequest.java +++ b/core/src/main/java/org/elasticsearch/search/internal/ShardSearchLocalRequest.java @@ -169,6 +169,10 @@ public boolean isProfile() { return profile; } + void setSearchType(SearchType type) { + this.searchType = type; + } + protected void innerReadFrom(StreamInput in) throws IOException { shardId = ShardId.readShardId(in); searchType = SearchType.fromId(in.readByte()); diff --git a/core/src/main/java/org/elasticsearch/search/internal/ShardSearchTransportRequest.java b/core/src/main/java/org/elasticsearch/search/internal/ShardSearchTransportRequest.java index b9b78fca54aec..e841172448993 100644 --- a/core/src/main/java/org/elasticsearch/search/internal/ShardSearchTransportRequest.java +++ b/core/src/main/java/org/elasticsearch/search/internal/ShardSearchTransportRequest.java @@ -60,6 +60,10 @@ public ShardSearchTransportRequest(OriginalIndices originalIndices, SearchReques this.originalIndices = originalIndices; } + public void searchType(SearchType searchType) { + shardSearchLocalRequest.setSearchType(searchType); + } + @Override public String[] indices() { if (originalIndices == null) { diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/10_basic.yaml index 4013f315d4633..0f037b890fc4b 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/10_basic.yaml @@ -65,6 +65,78 @@ clear_scroll: scroll_id: $scroll_id +--- +"Basic scroll with 1 shard": + - do: + indices.create: + index: test_scroll + body: + settings: + index: + number_of_shards: 1 + + - do: + index: + index: test_scroll + type: test + id: 42 + body: { foo: 1 } + + - do: + index: + index: test_scroll + type: test + id: 43 + body: { foo: 2 } + + - do: + indices.refresh: {} + + - do: + search: + index: test_scroll + size: 1 + scroll: 1m + sort: foo + body: + query: + match_all: {} + + - set: {_scroll_id: scroll_id} + - match: {hits.total: 2 } + - length: {hits.hits: 1 } + - match: {hits.hits.0._id: "42" } + + - do: + index: + index: test_scroll + type: test + id: 44 + body: { foo: 3 } + + - do: + indices.refresh: {} + + - do: + scroll: + body: { "scroll_id": "$scroll_id", "scroll": "1m"} + + - match: {hits.total: 2 } + - length: {hits.hits: 1 } + - match: {hits.hits.0._id: "43" } + + - do: + scroll: + scroll_id: $scroll_id + scroll: 1m + + - match: {hits.total: 2 } + - length: {hits.hits: 0 } + + - do: + clear_scroll: + scroll_id: $scroll_id + --- "Body params override query string": - do: From d77757fc1b4dfe613747804ddb907fe146c4aecc Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Tue, 9 May 2017 13:51:54 +0200 Subject: [PATCH 246/619] Updated release notes for 6.0.0-alpha1 --- .../release-notes/6.0.0-alpha1-5x.asciidoc | 16 ++++++++++++++-- .../release-notes/6.0.0-alpha1.asciidoc | 12 +++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/docs/reference/release-notes/6.0.0-alpha1-5x.asciidoc b/docs/reference/release-notes/6.0.0-alpha1-5x.asciidoc index 699260fd0c8d2..c44fd17b69057 100644 --- a/docs/reference/release-notes/6.0.0-alpha1-5x.asciidoc +++ b/docs/reference/release-notes/6.0.0-alpha1-5x.asciidoc @@ -320,7 +320,9 @@ Ingest:: * introduce the JSON Processor {pull}20128[#20128] (issue: {issue}20052[#20052]) Internal:: +* Add cross cluster support to `_field_caps` {pull}24463[#24463] (issue: {issue}24334[#24334]) * Log JVM arguments on startup {pull}24451[#24451] +* Preserve cluster alias throughout search execution to lookup nodes by cluster and ID {pull}24438[#24438] * Move RemoteClusterService into TransportService {pull}24424[#24424] * Enum related performance additions. {pull}24274[#24274] (issue: {issue}24226[#24226]) * Add a dedicated TransportRemoteInfoAction for consistency {pull}24040[#24040] (issue: {issue}23969[#23969]) @@ -390,6 +392,7 @@ Logging:: * Warn on not enough masters during election {pull}20063[#20063] (issue: {issue}8362[#8362]) Mapping:: +* Do not index `_type` when there is at most one type. {pull}24363[#24363] * Only allow one type on 6.0 indices {pull}24317[#24317] (issue: {issue}15613[#15613]) * token_count type : add an option to count tokens (fix #23227) {pull}24175[#24175] (issue: {issue}23227[#23227]) * Atomic mapping updates across types {pull}22220[#22220] @@ -558,7 +561,6 @@ Search Templates:: Settings:: * Add secure file setting to keystore {pull}24001[#24001] -* Add a setting which specifies a list of setting {pull}23883[#23883] * Add a property to mark setting as final {pull}23872[#23872] * Remove obsolete index setting `index.version.minimum_compatible`. {pull}23593[#23593] * Provide a method to retrieve a closeable char[] from a SecureString {pull}23389[#23389] @@ -595,7 +597,6 @@ Suggesters:: * Allow different data types for category in Context suggester {pull}23491[#23491] (issue: {issue}22358[#22358]) Task Manager:: -* Allow task to be unregistered by ClusterStateApplier {pull}23931[#23931] * Limit IndexRequest toString() length {pull}22832[#22832] * Improve the error message if task and node isn't found {pull}22062[#22062] (issue: {issue}22027[#22027]) * Add descriptions to create snapshot and restore snapshot tasks. {pull}21901[#21901] (issue: {issue}21768[#21768]) @@ -605,6 +606,8 @@ Task Manager:: Tribe Node:: * Add support for merging custom meta data in tribe node {pull}21552[#21552] (issues: {issue}20544[#20544], {issue}20791[#20791], {issue}9372[#9372]) +Upgrade API:: +* Allow plugins to upgrade templates and index metadata on startup {pull}24379[#24379] [[bug-6.0.0-alpha1-5x]] @@ -634,6 +637,7 @@ Aggregations:: * The `top_hits` aggregation should compile scripts only once. {pull}20738[#20738] Allocation:: +* Discard stale node responses from async shard fetching {pull}24434[#24434] (issue: {issue}24007[#24007]) * Cannot force allocate primary to a node where the shard already exists {pull}22031[#22031] (issue: {issue}22021[#22021]) * Promote shadow replica to primary when initializing primary fails {pull}22021[#22021] * Trim in-sync allocations set only when it grows {pull}21976[#21976] (issue: {issue}21719[#21719]) @@ -746,6 +750,7 @@ Inner Hits:: * Skip adding a parent field to nested documents. {pull}21522[#21522] (issue: {issue}21503[#21503]) Internal:: +* Fix NPE if field caps request has a field that exists not in all indices {pull}24504[#24504] * Add infrastructure to mark contexts as system contexts {pull}23830[#23830] * Always restore the ThreadContext for operations delayed due to a block {pull}23349[#23349] * Index creation and setting update may not return deprecation logging {pull}22702[#22702] @@ -866,6 +871,9 @@ Plugin Repository Azure:: * Fixes default chunk size for Azure repositories {pull}22577[#22577] (issue: {issue}22513[#22513]) * readonly on azure repository must be taken into account {pull}22055[#22055] (issues: {issue}22007[#22007], {issue}22053[#22053]) +Plugin Repository HDFS:: +* Fixing permission errors for `KERBEROS` security mode for HDFS Repository {pull}23439[#23439] (issue: {issue}22156[#22156]) + Plugin Repository S3:: * Handle BlobPath's trailing separator case. Add test cases to BlobPathTests.java {pull}23091[#23091] * Fixes leading forward slash in S3 repository base_path {pull}20861[#20861] @@ -945,6 +953,7 @@ Scripting:: * Native scripts should be created once per index, not per segment. {pull}20609[#20609] Search:: +* Include all aliases including non-filtering in `_search_shards` response {pull}24489[#24489] * Cross Cluster Search: propagate original indices per cluster {pull}24328[#24328] * Query string default field {pull}24214[#24214] * Speed up parsing of large `terms` queries. {pull}24210[#24210] @@ -1042,6 +1051,9 @@ Highlighting:: Logging:: * Restores the original default format of search slow log {pull}21770[#21770] (issue: {issue}21711[#21711]) +Network:: +* You had one job Netty logging guard {pull}24469[#24469] (issues: {issue}5624[#5624], {issue}6568[#6568]) + Plugin Discovery EC2:: * Fix ec2 discovery when used with IAM profiles. {pull}21042[#21042] (issue: {issue}21039[#21039]) diff --git a/docs/reference/release-notes/6.0.0-alpha1.asciidoc b/docs/reference/release-notes/6.0.0-alpha1.asciidoc index 3af5dc49df823..a2001af7a2edf 100644 --- a/docs/reference/release-notes/6.0.0-alpha1.asciidoc +++ b/docs/reference/release-notes/6.0.0-alpha1.asciidoc @@ -27,6 +27,7 @@ Cluster:: * No longer allow cluster name in data path {pull}20433[#20433] (issue: {issue}20391[#20391]) Core:: +* Simplify file store {pull}24402[#24402] (issue: {issue}24390[#24390]) * Make boolean conversion strict {pull}22200[#22200] * Remove the `default` store type. {pull}21616[#21616] * Remove store throttling. {pull}21573[#21573] @@ -36,6 +37,7 @@ Geo:: * Reduce GeoDistance Insanity {pull}19846[#19846] Index APIs:: +* Open/Close index api to allow_no_indices by default {pull}24401[#24401] (issues: {issue}24031[#24031], {issue}24341[#24341]) * Remove support for controversial `ignore_unavailable` and `allow_no_indices` from indices exists api {pull}20712[#20712] Index Templates:: @@ -45,13 +47,11 @@ Java API:: * Enforce Content-Type requirement on the rest layer and remove deprecated methods {pull}23146[#23146] (issue: {issue}19388[#19388]) Mapping:: +* Enforce at most one type. {pull}24428[#24428] (issue: {issue}24317[#24317]) * Disallow `include_in_all` for 6.0+ indices {pull}22970[#22970] (issue: {issue}22923[#22923]) * Disable _all by default, disallow configuring _all on 6.0+ indices {pull}22144[#22144] (issues: {issue}19784[#19784], {issue}20925[#20925], {issue}21341[#21341]) * Throw an exception on unrecognized "match_mapping_type" {pull}22090[#22090] (issue: {issue}17285[#17285]) -NOT CLASSIFIED:: -* Add note to docs on duplicate keys in config {pull}24022[#24022] (issue: {issue}24006[#24006]) - Network:: * Remove blocking TCP clients and servers {pull}22639[#22639] * Remove `modules/transport_netty_3` in favor of `netty_4` {pull}21590[#21590] @@ -124,6 +124,9 @@ Shadow Replicas:: [float] === Breaking Java changes +Java API:: +* Java api: ActionRequestBuilder#execute to return a PlainActionFuture {pull}24415[#24415] (issues: {issue}24412[#24412], {issue}9201[#9201]) + Network:: * Simplify TransportAddress {pull}20798[#20798] @@ -147,7 +150,6 @@ Internal:: Core:: * Enable index-time sorting {pull}24055[#24055] (issue: {issue}6720[#6720]) -* Add new ip_range field type {pull}24433[#24433] @@ -200,7 +202,6 @@ Java High Level REST Client:: * Add Index API to High Level Rest Client {pull}23040[#23040] * Add get/exists method to RestHighLevelClient {pull}22706[#22706] * Add fromxcontent methods to delete response {pull}22680[#22680] (issue: {issue}22229[#22229]) -* Add parsing from xContent to SearchResponse {pull}22533[#22533] * Add REST high level client gradle submodule and first simple method {pull}22371[#22371] Java REST Client:: @@ -232,6 +233,7 @@ Search:: * Add parsing from xContent to ShardSearchFailure {pull}22699[#22699] Sequence IDs:: +* Block global checkpoint advances when recovering {pull}24404[#24404] (issue: {issue}10708[#10708]) * Add primary term to doc write response {pull}24171[#24171] (issue: {issue}10708[#10708]) * Preserve multiple translog generations {pull}24015[#24015] (issue: {issue}10708[#10708]) * Introduce translog generation rolling {pull}23606[#23606] (issue: {issue}10708[#10708]) From 41b10554fd7f15a48456cc737a75dd1557443e26 Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Tue, 9 May 2017 14:02:11 +0200 Subject: [PATCH 247/619] Mark 6.0.0-alpha1 as prerelease --- docs/Versions.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Versions.asciidoc b/docs/Versions.asciidoc index bbd5f3bce07ac..e8bba54d40fa0 100644 --- a/docs/Versions.asciidoc +++ b/docs/Versions.asciidoc @@ -9,7 +9,7 @@ release-state can be: released | prerelease | unreleased ////////// -:release-state: unreleased +:release-state: prerelease :ref: https://www.elastic.co/guide/en/elasticsearch/reference/{branch} :defguide: https://www.elastic.co/guide/en/elasticsearch/guide/master From f222748506dee3f50f99551b33b049e87c6cd425 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 9 May 2017 09:40:36 -0400 Subject: [PATCH 248/619] Increase compilation limit in ingest tests These tests are running into the script compilation limit. This commit increases the limit for these tests. --- qa/smoke-test-ingest-with-all-dependencies/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/qa/smoke-test-ingest-with-all-dependencies/build.gradle b/qa/smoke-test-ingest-with-all-dependencies/build.gradle index 1608ce9c37c25..73f80f29e2a5e 100644 --- a/qa/smoke-test-ingest-with-all-dependencies/build.gradle +++ b/qa/smoke-test-ingest-with-all-dependencies/build.gradle @@ -32,4 +32,5 @@ integTestCluster { plugin ':plugins:ingest-geoip' setting 'script.inline', 'true' setting 'path.scripts', "${project.buildDir}/resources/test/scripts" + setting 'script.max_compilations_per_minute', '1000' } From a72eaa8e0f5c0f305c507c5fcd918fed7c1bf6bf Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Tue, 9 May 2017 16:33:52 +0200 Subject: [PATCH 249/619] Identify documents by their `_id`. (#24460) Now that indices have a single type by default, we can move to the next step and identify documents using their `_id` rather than the `_uid`. One notable change in this commit is that I made deletions implicitly create types. This helps with the live version map in the case that documents are deleted before the first type is introduced. Otherwise there would be no way to differenciate `DELETE index/foo/1` followed by `PUT index/foo/1` from `DELETE index/bar/1` followed by `PUT index/foo/1`, even though those are different if versioning is involved. --- .../action/bulk/MappingUpdatePerformer.java | 7 +- .../action/bulk/TransportShardBulkAction.java | 60 ++++- .../explain/TransportExplainAction.java | 9 +- .../uid/PerThreadIDVersionAndSeqNoLookup.java | 13 +- .../lucene/uid/VersionsAndSeqNoResolver.java | 19 +- .../elasticsearch/index/IndexSettings.java | 13 +- .../elasticsearch/index/engine/Engine.java | 17 +- .../index/engine/InternalEngine.java | 15 +- .../index/fielddata/UidIndexFieldData.java | 186 ++++++++++++++ .../plain/AbstractIndexOrdinalsFieldData.java | 4 - .../index/fieldvisitor/FieldsVisitor.java | 28 +- .../fieldvisitor/JustUidFieldsVisitor.java | 39 --- .../fieldvisitor/SingleFieldsVisitor.java | 35 +-- .../index/get/ShardGetService.java | 34 ++- .../index/mapper/IdFieldMapper.java | 76 ++++-- .../index/mapper/MappedFieldType.java | 13 + .../index/mapper/MapperService.java | 14 +- .../index/mapper/ParsedDocument.java | 4 - .../index/mapper/TypeFieldMapper.java | 2 +- .../index/mapper/UidFieldMapper.java | 109 ++++++-- .../index/query/IdsQueryBuilder.java | 9 +- .../index/query/MoreLikeThisQueryBuilder.java | 12 +- .../RandomScoreFunctionBuilder.java | 11 +- .../elasticsearch/index/shard/IndexShard.java | 60 +++-- .../shard/TranslogRecoveryPerformer.java | 5 +- .../index/termvectors/TermVectorsService.java | 12 +- .../index/translog/Translog.java | 20 +- .../fetch/subphase/InnerHitsContext.java | 11 +- .../search/lookup/LeafFieldsLookup.java | 13 +- .../search/slice/SliceBuilder.java | 9 +- .../bulk/TransportShardBulkActionTests.java | 10 +- ...ulkByScrollParallelizationHelperTests.java | 4 +- .../elasticsearch/aliases/IndexAliasesIT.java | 6 +- .../common/lucene/uid/VersionLookupTests.java | 10 +- .../fieldstats/FieldStatsTests.java | 2 +- .../elasticsearch/index/IndexModuleTests.java | 5 +- .../index/engine/InternalEngineTests.java | 243 +++++++++--------- .../index/fielddata/UidFieldDataTests.java | 131 ++++++++++ .../index/mapper/DocumentParserTests.java | 44 +--- .../mapper/FieldNamesFieldMapperTests.java | 4 +- .../index/mapper/IdFieldMapperTests.java | 56 ++-- .../index/mapper/IdFieldTypeTests.java | 71 ++++- .../index/mapper/TextFieldMapperTests.java | 4 +- .../index/mapper/UidFieldMapperTests.java | 60 +++++ .../index/mapper/UidFieldTypeTests.java | 67 +++++ .../ESIndexLevelReplicationTestCase.java | 7 +- .../index/shard/IndexShardIT.java | 9 +- .../index/shard/IndexShardTests.java | 23 +- .../shard/IndexingOperationListenerTests.java | 7 +- .../index/shard/RefreshListenersTests.java | 17 +- .../index/translog/TranslogTests.java | 11 +- .../recovery/RecoverySourceHandlerTests.java | 2 +- .../search/SearchCancellationIT.java | 2 +- .../aggregations/AggregatorTestCase.java | 11 +- .../aggregations/metrics/TopHitsIT.java | 2 +- .../ScriptedMetricAggregatorTests.java | 8 +- .../search/fetch/subphase/InnerHitsIT.java | 17 +- .../search/profile/query/QueryProfilerIT.java | 4 +- .../search/query/MultiMatchQueryIT.java | 26 +- .../search/slice/SliceBuilderTests.java | 12 + .../search/sort/FieldSortIT.java | 4 +- .../mapping/fields/id-field.asciidoc | 10 +- .../mapping/fields/uid-field.asciidoc | 10 + .../migration/migrate_6_0/rest.asciidoc | 5 + .../migration/migrate_6_0/search.asciidoc | 8 + .../search/request/search-after.asciidoc | 8 +- .../test/search/90_search_after.yaml | 23 +- .../index/shard/IndexShardTestCase.java | 19 +- 68 files changed, 1288 insertions(+), 533 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/index/fielddata/UidIndexFieldData.java delete mode 100644 core/src/main/java/org/elasticsearch/index/fieldvisitor/JustUidFieldsVisitor.java create mode 100644 core/src/test/java/org/elasticsearch/index/fielddata/UidFieldDataTests.java create mode 100644 core/src/test/java/org/elasticsearch/index/mapper/UidFieldMapperTests.java diff --git a/core/src/main/java/org/elasticsearch/action/bulk/MappingUpdatePerformer.java b/core/src/main/java/org/elasticsearch/action/bulk/MappingUpdatePerformer.java index a5238273ccbb8..812653d58266b 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/MappingUpdatePerformer.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/MappingUpdatePerformer.java @@ -19,14 +19,9 @@ package org.elasticsearch.action.bulk; -import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.mapper.Mapping; -import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.ShardId; -import java.util.Objects; - public interface MappingUpdatePerformer { /** @@ -39,6 +34,6 @@ public interface MappingUpdatePerformer { * retried on the primary due to the mappings not being present yet, or a different exception if * updating the mappings on the master failed. */ - void verifyMappings(Engine.Index operation, ShardId shardId) throws Exception; + void verifyMappings(Mapping update, ShardId shardId) throws Exception; } diff --git a/core/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java b/core/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java index 21dd799122e39..4e1419099b0bf 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java @@ -56,6 +56,7 @@ import org.elasticsearch.index.engine.VersionConflictEngineException; import org.elasticsearch.index.get.GetResult; import org.elasticsearch.index.mapper.MapperParsingException; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.Mapping; import org.elasticsearch.index.mapper.SourceToParse; import org.elasticsearch.index.seqno.SequenceNumbersService; @@ -150,8 +151,9 @@ private static BulkItemResultHolder executeIndexRequest(final IndexRequest index private static BulkItemResultHolder executeDeleteRequest(final DeleteRequest deleteRequest, final BulkItemRequest bulkItemRequest, - final IndexShard primary) throws IOException { - Engine.DeleteResult deleteResult = executeDeleteRequestOnPrimary(deleteRequest, primary); + final IndexShard primary, + final MappingUpdatePerformer mappingUpdater) throws Exception { + Engine.DeleteResult deleteResult = executeDeleteRequestOnPrimary(deleteRequest, primary, mappingUpdater); if (deleteResult.hasFailure()) { return new BulkItemResultHolder(null, deleteResult, bulkItemRequest); } else { @@ -241,7 +243,7 @@ static Translog.Location executeBulkItemRequest(IndexMetaData metaData, IndexSha requestIndex, updateHelper, nowInMillisSupplier, mappingUpdater); break; case DELETE: - responseHolder = executeDeleteRequest((DeleteRequest) itemRequest, request.items()[requestIndex], primary); + responseHolder = executeDeleteRequest((DeleteRequest) itemRequest, request.items()[requestIndex], primary, mappingUpdater); break; default: throw new IllegalStateException("unexpected opType [" + itemRequest.opType() + "] found"); } @@ -303,7 +305,7 @@ private static BulkItemResultHolder executeUpdateRequest(UpdateRequest updateReq break; case DELETED: DeleteRequest deleteRequest = translate.action(); - result = executeDeleteRequestOnPrimary(deleteRequest, primary); + result = executeDeleteRequestOnPrimary(deleteRequest, primary, mappingUpdater); break; case NOOP: primary.noopUpdate(updateRequest.type()); @@ -609,7 +611,7 @@ static Engine.IndexResult executeIndexRequestOnPrimary(IndexRequest request, Ind if (mappingUpdateNeeded) { try { operation = prepareIndexOperationOnPrimary(request, primary); - mappingUpdater.verifyMappings(operation, primary.shardId()); + mappingUpdater.verifyMappings(operation.parsedDoc().dynamicMappingsUpdate(), primary.shardId()); } catch (MapperParsingException | IllegalStateException e) { // there was an error in parsing the document that was not because // of pending mapping updates, so return a failure for the result @@ -623,12 +625,52 @@ static Engine.IndexResult executeIndexRequestOnPrimary(IndexRequest request, Ind return primary.index(operation); } - private static Engine.DeleteResult executeDeleteRequestOnPrimary(DeleteRequest request, IndexShard primary) throws IOException { + private static Engine.DeleteResult executeDeleteRequestOnPrimary(DeleteRequest request, IndexShard primary, + final MappingUpdatePerformer mappingUpdater) throws Exception { + boolean mappingUpdateNeeded = false; + if (primary.indexSettings().isSingleType()) { + // When there is a single type, the unique identifier is only composed of the _id, + // so there is no way to differenciate foo#1 from bar#1. This is especially an issue + // if a user first deletes foo#1 and then indexes bar#1: since we do not encode the + // _type in the uid it might look like we are reindexing the same document, which + // would fail if bar#1 is indexed with a lower version than foo#1 was deleted with. + // In order to work around this issue, we make deletions create types. This way, we + // fail if index and delete operations do not use the same type. + try { + Mapping update = primary.mapperService().documentMapperWithAutoCreate(request.type()).getMapping(); + if (update != null) { + mappingUpdateNeeded = true; + mappingUpdater.updateMappings(update, primary.shardId(), request.type()); + } + } catch (MapperParsingException | IllegalArgumentException e) { + return new Engine.DeleteResult(e, request.version(), SequenceNumbersService.UNASSIGNED_SEQ_NO, false); + } + } + if (mappingUpdateNeeded) { + Mapping update = primary.mapperService().documentMapperWithAutoCreate(request.type()).getMapping(); + mappingUpdater.verifyMappings(update, primary.shardId()); + } final Engine.Delete delete = primary.prepareDeleteOnPrimary(request.type(), request.id(), request.version(), request.versionType()); return primary.delete(delete); } - private static Engine.DeleteResult executeDeleteRequestOnReplica(DocWriteResponse primaryResponse, DeleteRequest request, IndexShard replica) throws IOException { + private static Engine.DeleteResult executeDeleteRequestOnReplica(DocWriteResponse primaryResponse, DeleteRequest request, + IndexShard replica) throws Exception { + if (replica.indexSettings().isSingleType()) { + // We need to wait for the replica to have the mappings + Mapping update; + try { + update = replica.mapperService().documentMapperWithAutoCreate(request.type()).getMapping(); + } catch (MapperParsingException | IllegalArgumentException e) { + return new Engine.DeleteResult(e, request.version(), primaryResponse.getSeqNo(), false); + } + if (update != null) { + final ShardId shardId = replica.shardId(); + throw new RetryOnReplicaException(shardId, + "Mappings are not available on the replica yet, triggered update: " + update); + } + } + final VersionType versionType = request.versionType().versionTypeForReplicationAndRecovery(); final long version = primaryResponse.getVersion(); assert versionType.validateVersionForWrites(version); @@ -654,9 +696,9 @@ public void updateMappings(final Mapping update, final ShardId shardId, } } - public void verifyMappings(final Engine.Index operation, + public void verifyMappings(Mapping update, final ShardId shardId) throws Exception { - if (operation.parsedDoc().dynamicMappingsUpdate() != null) { + if (update != null) { throw new ReplicationOperation.RetryOnPrimaryException(shardId, "Dynamic mappings are not available on the node that holds the primary yet"); } diff --git a/core/src/main/java/org/elasticsearch/action/explain/TransportExplainAction.java b/core/src/main/java/org/elasticsearch/action/explain/TransportExplainAction.java index 65176c1df392c..72aaeb9eb371a 100644 --- a/core/src/main/java/org/elasticsearch/action/explain/TransportExplainAction.java +++ b/core/src/main/java/org/elasticsearch/action/explain/TransportExplainAction.java @@ -35,8 +35,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.get.GetResult; -import org.elasticsearch.index.mapper.Uid; -import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.SearchService; import org.elasticsearch.search.internal.AliasFilter; @@ -93,10 +91,13 @@ protected ExplainResponse shardOperation(ExplainRequest request, ShardId shardId ShardSearchLocalRequest shardSearchLocalRequest = new ShardSearchLocalRequest(shardId, new String[]{request.type()}, request.nowInMillis, request.filteringAlias()); SearchContext context = searchService.createSearchContext(shardSearchLocalRequest, SearchService.NO_TIMEOUT, null); - Term uidTerm = new Term(UidFieldMapper.NAME, Uid.createUidAsBytes(request.type(), request.id())); Engine.GetResult result = null; try { - result = context.indexShard().get(new Engine.Get(false, uidTerm)); + Term uidTerm = context.mapperService().createUidTerm(request.type(), request.id()); + if (uidTerm == null) { + return new ExplainResponse(shardId.getIndexName(), request.type(), request.id(), false); + } + result = context.indexShard().get(new Engine.Get(false, request.type(), request.id(), uidTerm)); if (!result.exists()) { return new ExplainResponse(shardId.getIndexName(), request.type(), request.id(), false); } diff --git a/core/src/main/java/org/elasticsearch/common/lucene/uid/PerThreadIDVersionAndSeqNoLookup.java b/core/src/main/java/org/elasticsearch/common/lucene/uid/PerThreadIDVersionAndSeqNoLookup.java index 96c9f30a9546f..e8b47783afb1d 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/uid/PerThreadIDVersionAndSeqNoLookup.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/uid/PerThreadIDVersionAndSeqNoLookup.java @@ -32,7 +32,6 @@ import org.elasticsearch.common.lucene.uid.VersionsAndSeqNoResolver.DocIdAndSeqNo; import org.elasticsearch.common.lucene.uid.VersionsAndSeqNoResolver.DocIdAndVersion; import org.elasticsearch.index.mapper.SeqNoFieldMapper; -import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.mapper.VersionFieldMapper; import org.elasticsearch.index.seqno.SequenceNumbersService; @@ -51,6 +50,7 @@ final class PerThreadIDVersionAndSeqNoLookup { // we keep it around for now, to reduce the amount of e.g. hash lookups by field and stuff /** terms enum for uid field */ + final String uidField; private final TermsEnum termsEnum; /** Reused for iteration (when the term exists) */ @@ -62,13 +62,14 @@ final class PerThreadIDVersionAndSeqNoLookup { /** * Initialize lookup for the provided segment */ - PerThreadIDVersionAndSeqNoLookup(LeafReader reader) throws IOException { + PerThreadIDVersionAndSeqNoLookup(LeafReader reader, String uidField) throws IOException { + this.uidField = uidField; Fields fields = reader.fields(); - Terms terms = fields.terms(UidFieldMapper.NAME); - termsEnum = terms.iterator(); - if (termsEnum == null) { - throw new IllegalArgumentException("reader misses the [" + UidFieldMapper.NAME + "] field"); + Terms terms = fields.terms(uidField); + if (terms == null) { + throw new IllegalArgumentException("reader misses the [" + uidField + "] field"); } + termsEnum = terms.iterator(); if (reader.getNumericDocValues(VersionFieldMapper.NAME) == null) { throw new IllegalArgumentException("reader misses the [" + VersionFieldMapper.NAME + "] field"); } diff --git a/core/src/main/java/org/elasticsearch/common/lucene/uid/VersionsAndSeqNoResolver.java b/core/src/main/java/org/elasticsearch/common/lucene/uid/VersionsAndSeqNoResolver.java index 409ce8dec29be..3cdbfa38b62e9 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/uid/VersionsAndSeqNoResolver.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/uid/VersionsAndSeqNoResolver.java @@ -25,10 +25,10 @@ import org.apache.lucene.index.Term; import org.apache.lucene.util.CloseableThreadLocal; import org.elasticsearch.common.util.concurrent.ConcurrentCollections; -import org.elasticsearch.index.mapper.UidFieldMapper; import java.io.IOException; import java.util.List; +import java.util.Objects; import java.util.concurrent.ConcurrentMap; import static org.elasticsearch.common.lucene.uid.Versions.NOT_FOUND; @@ -47,7 +47,7 @@ public final class VersionsAndSeqNoResolver { } }; - private static PerThreadIDVersionAndSeqNoLookup getLookupState(LeafReader reader) throws IOException { + private static PerThreadIDVersionAndSeqNoLookup getLookupState(LeafReader reader, String uidField) throws IOException { IndexReader.CacheHelper cacheHelper = reader.getCoreCacheHelper(); CloseableThreadLocal ctl = lookupStates.get(cacheHelper.getKey()); if (ctl == null) { @@ -65,8 +65,11 @@ private static PerThreadIDVersionAndSeqNoLookup getLookupState(LeafReader reader PerThreadIDVersionAndSeqNoLookup lookupState = ctl.get(); if (lookupState == null) { - lookupState = new PerThreadIDVersionAndSeqNoLookup(reader); + lookupState = new PerThreadIDVersionAndSeqNoLookup(reader, uidField); ctl.set(lookupState); + } else if (Objects.equals(lookupState.uidField, uidField) == false) { + throw new AssertionError("Index does not consistently use the same uid field: [" + + uidField + "] != [" + lookupState.uidField + "]"); } return lookupState; @@ -109,7 +112,6 @@ public static class DocIdAndSeqNo { * */ public static DocIdAndVersion loadDocIdAndVersion(IndexReader reader, Term term) throws IOException { - assert term.field().equals(UidFieldMapper.NAME) : "unexpected term field " + term.field(); List leaves = reader.leaves(); if (leaves.isEmpty()) { return null; @@ -119,7 +121,7 @@ public static DocIdAndVersion loadDocIdAndVersion(IndexReader reader, Term term) for (int i = leaves.size() - 1; i >= 0; i--) { LeafReaderContext context = leaves.get(i); LeafReader leaf = context.reader(); - PerThreadIDVersionAndSeqNoLookup lookup = getLookupState(leaf); + PerThreadIDVersionAndSeqNoLookup lookup = getLookupState(leaf, term.field()); DocIdAndVersion result = lookup.lookupVersion(term.bytes(), leaf.getLiveDocs(), context); if (result != null) { return result; @@ -135,7 +137,6 @@ public static DocIdAndVersion loadDocIdAndVersion(IndexReader reader, Term term) * */ public static DocIdAndSeqNo loadDocIdAndSeqNo(IndexReader reader, Term term) throws IOException { - assert term.field().equals(UidFieldMapper.NAME) : "unexpected term field " + term.field(); List leaves = reader.leaves(); if (leaves.isEmpty()) { return null; @@ -145,7 +146,7 @@ public static DocIdAndSeqNo loadDocIdAndSeqNo(IndexReader reader, Term term) thr for (int i = leaves.size() - 1; i >= 0; i--) { LeafReaderContext context = leaves.get(i); LeafReader leaf = context.reader(); - PerThreadIDVersionAndSeqNoLookup lookup = getLookupState(leaf); + PerThreadIDVersionAndSeqNoLookup lookup = getLookupState(leaf, term.field()); DocIdAndSeqNo result = lookup.lookupSeqNo(term.bytes(), leaf.getLiveDocs(), context); if (result != null) { return result; @@ -157,9 +158,9 @@ public static DocIdAndSeqNo loadDocIdAndSeqNo(IndexReader reader, Term term) thr /** * Load the primaryTerm associated with the given {@link DocIdAndSeqNo} */ - public static long loadPrimaryTerm(DocIdAndSeqNo docIdAndSeqNo) throws IOException { + public static long loadPrimaryTerm(DocIdAndSeqNo docIdAndSeqNo, String uidField) throws IOException { LeafReader leaf = docIdAndSeqNo.context.reader(); - PerThreadIDVersionAndSeqNoLookup lookup = getLookupState(leaf); + PerThreadIDVersionAndSeqNoLookup lookup = getLookupState(leaf, uidField); long result = lookup.lookUpPrimaryTerm(docIdAndSeqNo.docId, leaf); assert result > 0 : "should always resolve a primary term for a resolved sequence number. primary_term [" + result + "]" + " docId [" + docIdAndSeqNo.docId + "] seqNo [" + docIdAndSeqNo.seqNo + "]"; diff --git a/core/src/main/java/org/elasticsearch/index/IndexSettings.java b/core/src/main/java/org/elasticsearch/index/IndexSettings.java index 8acdf7d1360cb..05b8f509eae25 100644 --- a/core/src/main/java/org/elasticsearch/index/IndexSettings.java +++ b/core/src/main/java/org/elasticsearch/index/IndexSettings.java @@ -31,12 +31,12 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.index.mapper.AllFieldMapper; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.translog.Translog; import org.elasticsearch.node.Node; import java.util.Locale; import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; import java.util.function.Function; /** @@ -192,7 +192,10 @@ public final class IndexSettings { * The maximum number of slices allowed in a scroll request. */ private volatile int maxSlicesPerScroll; - + /** + * Whether the index is required to have at most one type. + */ + private final boolean singleType; /** * Returns the default search field for this index. @@ -280,6 +283,7 @@ public IndexSettings(final IndexMetaData indexMetaData, final Settings nodeSetti maxSlicesPerScroll = scopedSettings.get(MAX_SLICES_PER_SCROLL); this.mergePolicyConfig = new MergePolicyConfig(logger, this); this.indexSortConfig = new IndexSortConfig(this); + singleType = scopedSettings.get(MapperService.INDEX_MAPPING_SINGLE_TYPE_SETTING); scopedSettings.addSettingsUpdateConsumer(MergePolicyConfig.INDEX_COMPOUND_FORMAT_SETTING, mergePolicyConfig::setNoCFSRatio); scopedSettings.addSettingsUpdateConsumer(MergePolicyConfig.INDEX_MERGE_POLICY_EXPUNGE_DELETES_ALLOWED_SETTING, mergePolicyConfig::setExpungeDeletesAllowed); @@ -391,6 +395,11 @@ public IndexMetaData getIndexMetaData() { */ public int getNumberOfReplicas() { return settings.getAsInt(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, null); } + /** + * Returns whether the index enforces at most one type. + */ + public boolean isSingleType() { return singleType; } + /** * Returns the node settings. The settings returned from {@link #getSettings()} are a merged version of the * index settings and the node settings where node settings are overwritten by index settings. diff --git a/core/src/main/java/org/elasticsearch/index/engine/Engine.java b/core/src/main/java/org/elasticsearch/index/engine/Engine.java index 31e0d1e422acf..295f559da1698 100644 --- a/core/src/main/java/org/elasticsearch/index/engine/Engine.java +++ b/core/src/main/java/org/elasticsearch/index/engine/Engine.java @@ -473,8 +473,7 @@ protected final GetResult getFromSearcher(Get get, Function se if (docIdAndVersion != null) { if (get.versionType().isVersionConflictForReads(docIdAndVersion.version, get.version())) { Releasables.close(searcher); - Uid uid = Uid.createUid(get.uid().text()); - throw new VersionConflictEngineException(shardId, uid.type(), uid.id(), + throw new VersionConflictEngineException(shardId, get.type(), get.id(), get.versionType().explainConflictForReads(docIdAndVersion.version, get.version())); } } @@ -1028,7 +1027,6 @@ public static class Index extends Operation { public Index(Term uid, ParsedDocument doc, long seqNo, long primaryTerm, long version, VersionType versionType, Origin origin, long startTime, long autoGeneratedIdTimestamp, boolean isRetry) { super(uid, seqNo, primaryTerm, version, versionType, origin, startTime); - assert uid.bytes().equals(doc.uid()) : "term uid " + uid + " doesn't match doc uid " + doc.uid(); this.doc = doc; this.isRetry = isRetry; this.autoGeneratedIdTimestamp = autoGeneratedIdTimestamp; @@ -1199,11 +1197,14 @@ public int estimatedSizeInBytes() { public static class Get { private final boolean realtime; private final Term uid; + private final String type, id; private long version = Versions.MATCH_ANY; private VersionType versionType = VersionType.INTERNAL; - public Get(boolean realtime, Term uid) { + public Get(boolean realtime, String type, String id, Term uid) { this.realtime = realtime; + this.type = type; + this.id = id; this.uid = uid; } @@ -1211,6 +1212,14 @@ public boolean realtime() { return this.realtime; } + public String type() { + return type; + } + + public String id() { + return id; + } + public Term uid() { return uid; } diff --git a/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java b/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java index 9bfbb1467a20f..03007d96b9f23 100644 --- a/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java +++ b/core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java @@ -60,8 +60,9 @@ import org.elasticsearch.common.util.concurrent.ReleasableLock; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.VersionType; +import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.ParseContext; -import org.elasticsearch.index.mapper.Uid; +import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.merge.MergeStats; import org.elasticsearch.index.merge.OnGoingMerge; import org.elasticsearch.index.seqno.SeqNoStats; @@ -80,6 +81,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -122,6 +124,8 @@ public class InternalEngine extends Engine { private final SequenceNumbersService seqNoService; + private final String uidField; + // How many callers are currently requesting index throttling. Currently there are only two situations where we do this: when merges // are falling behind and when writing indexing buffer to disk is too slow. When this is 0, there is no throttling, else we throttling // incoming indexing ops to a single thread: @@ -139,6 +143,7 @@ public InternalEngine(EngineConfig engineConfig) throws EngineException { if (engineConfig.isAutoGeneratedIDsOptimizationEnabled() == false) { maxUnsafeAutoIdTimestamp.set(Long.MAX_VALUE); } + this.uidField = engineConfig.getIndexSettings().isSingleType() ? IdFieldMapper.NAME : UidFieldMapper.NAME; this.versionMap = new LiveVersionMap(); store.incRef(); IndexWriter writer = null; @@ -404,6 +409,7 @@ private SearcherManager createSearcherManager() throws EngineException { @Override public GetResult get(Get get, Function searcherFactory) throws EngineException { + assert Objects.equals(get.uid().field(), uidField) : get.uid().field(); try (ReleasableLock ignored = readLock.acquire()) { ensureOpen(); if (get.realtime()) { @@ -413,8 +419,7 @@ public GetResult get(Get get, Function searcherFactory) throws return GetResult.NOT_EXISTS; } if (get.versionType().isVersionConflictForReads(versionValue.version, get.version())) { - Uid uid = Uid.createUid(get.uid().text()); - throw new VersionConflictEngineException(shardId, uid.type(), uid.id(), + throw new VersionConflictEngineException(shardId, get.type(), get.id(), get.versionType().explainConflictForReads(versionValue.version, get.version())); } refresh("realtime_get"); @@ -462,7 +467,7 @@ private OpVsLuceneDocStatus compareOpToLuceneDocBasedOnSeqNo(final Operation op) status = OpVsLuceneDocStatus.OP_NEWER; } else if (op.seqNo() == docAndSeqNo.seqNo) { // load term to tie break - final long existingTerm = VersionsAndSeqNoResolver.loadPrimaryTerm(docAndSeqNo); + final long existingTerm = VersionsAndSeqNoResolver.loadPrimaryTerm(docAndSeqNo, op.uid().field()); if (op.primaryTerm() > existingTerm) { status = OpVsLuceneDocStatus.OP_NEWER; } else { @@ -571,6 +576,7 @@ private boolean assertSequenceNumberBeforeIndexing(final Engine.Operation.Origin @Override public IndexResult index(Index index) throws IOException { + assert Objects.equals(index.uid().field(), uidField) : index.uid().field(); final boolean doThrottle = index.origin().isRecovery() == false; try (ReleasableLock releasableLock = readLock.acquire()) { ensureOpen(); @@ -891,6 +897,7 @@ private static void update(final Term uid, final List doc @Override public DeleteResult delete(Delete delete) throws IOException { + assert Objects.equals(delete.uid().field(), uidField) : delete.uid().field(); assert assertVersionType(delete); assert assertIncomingSequenceNumber(delete.origin(), delete.seqNo()); final DeleteResult deleteResult; diff --git a/core/src/main/java/org/elasticsearch/index/fielddata/UidIndexFieldData.java b/core/src/main/java/org/elasticsearch/index/fielddata/UidIndexFieldData.java new file mode 100644 index 0000000000000..e8dea836e322c --- /dev/null +++ b/core/src/main/java/org/elasticsearch/index/fielddata/UidIndexFieldData.java @@ -0,0 +1,186 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.index.fielddata; + +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedSetDocValues; +import org.apache.lucene.search.SortField; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.BytesRefBuilder; +import org.elasticsearch.index.Index; +import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource.Nested; +import org.elasticsearch.index.fielddata.fieldcomparator.BytesRefFieldComparatorSource; +import org.elasticsearch.index.fielddata.plain.AbstractAtomicOrdinalsFieldData; +import org.elasticsearch.index.mapper.UidFieldMapper; +import org.elasticsearch.search.MultiValueMode; + +import java.io.IOException; + +/** Fielddata view of the _uid field on indices that do not index _uid but _id. + * It gets fielddata on the {@code _id field}, which is in-memory since the _id + * field does not have doc values, and prepends {@code ${type}#} to all values. + * Note that it does not add memory compared to what fielddata on the _id is + * already using: this is just a view. + * TODO: Remove fielddata access on _uid and _id, or add doc values to _id. + */ +public final class UidIndexFieldData implements IndexOrdinalsFieldData { + + private final Index index; + private final String type; + private final BytesRef prefix; + private final IndexOrdinalsFieldData idFieldData; + + public UidIndexFieldData(Index index, String type, IndexOrdinalsFieldData idFieldData) { + this.index = index; + this.type = type; + BytesRefBuilder prefix = new BytesRefBuilder(); + prefix.append(new BytesRef(type)); + prefix.append((byte) '#'); + this.prefix = prefix.toBytesRef(); + this.idFieldData = idFieldData; + } + + @Override + public Index index() { + return index; + } + + @Override + public String getFieldName() { + return UidFieldMapper.NAME; + } + + @Override + public SortField sortField(Object missingValue, MultiValueMode sortMode, Nested nested, boolean reverse) { + XFieldComparatorSource source = new BytesRefFieldComparatorSource(this, missingValue, sortMode, nested); + return new SortField(getFieldName(), source, reverse); + } + + @Override + public AtomicOrdinalsFieldData load(LeafReaderContext context) { + return new UidAtomicFieldData(prefix, idFieldData.load(context)); + } + + @Override + public AtomicOrdinalsFieldData loadDirect(LeafReaderContext context) throws Exception { + return new UidAtomicFieldData(prefix, idFieldData.loadDirect(context)); + } + + @Override + public void clear() { + idFieldData.clear(); + } + + @Override + public IndexOrdinalsFieldData loadGlobal(DirectoryReader indexReader) { + return new UidIndexFieldData(index, type, idFieldData.loadGlobal(indexReader)); + } + + @Override + public IndexOrdinalsFieldData localGlobalDirect(DirectoryReader indexReader) throws Exception { + return new UidIndexFieldData(index, type, idFieldData.localGlobalDirect(indexReader)); + } + + static final class UidAtomicFieldData implements AtomicOrdinalsFieldData { + + private final BytesRef prefix; + private final AtomicOrdinalsFieldData idFieldData; + + UidAtomicFieldData(BytesRef prefix, AtomicOrdinalsFieldData idFieldData) { + this.prefix = prefix; + this.idFieldData = idFieldData; + } + + @Override + public ScriptDocValues getScriptValues() { + return AbstractAtomicOrdinalsFieldData.DEFAULT_SCRIPT_FUNCTION.apply(getOrdinalsValues()); + } + + @Override + public SortedBinaryDocValues getBytesValues() { + return FieldData.toString(getOrdinalsValues()); + } + + @Override + public long ramBytesUsed() { + return 0; // simple wrapper + } + + @Override + public void close() { + idFieldData.close(); + } + + @Override + public SortedSetDocValues getOrdinalsValues() { + SortedSetDocValues idValues = idFieldData.getOrdinalsValues(); + return new SortedSetDocValues() { + + private final BytesRefBuilder scratch = new BytesRefBuilder(); + + @Override + public int nextDoc() throws IOException { + return idValues.nextDoc(); + } + + @Override + public int docID() { + return idValues.docID(); + } + + @Override + public long cost() { + return idValues.cost(); + } + + @Override + public int advance(int target) throws IOException { + return idValues.advance(target); + } + + @Override + public boolean advanceExact(int target) throws IOException { + return idValues.advanceExact(target); + } + + @Override + public long nextOrd() throws IOException { + return idValues.nextOrd(); + } + + @Override + public BytesRef lookupOrd(long ord) throws IOException { + scratch.setLength(0); + scratch.append(prefix); + scratch.append(idValues.lookupOrd(ord)); + return scratch.get(); + } + + @Override + public long getValueCount() { + return idValues.getValueCount(); + } + }; + } + + } + +} diff --git a/core/src/main/java/org/elasticsearch/index/fielddata/plain/AbstractIndexOrdinalsFieldData.java b/core/src/main/java/org/elasticsearch/index/fielddata/plain/AbstractIndexOrdinalsFieldData.java index fef4b3b0a7d70..ce05e226b7e13 100644 --- a/core/src/main/java/org/elasticsearch/index/fielddata/plain/AbstractIndexOrdinalsFieldData.java +++ b/core/src/main/java/org/elasticsearch/index/fielddata/plain/AbstractIndexOrdinalsFieldData.java @@ -26,16 +26,12 @@ import org.apache.lucene.index.TermsEnum; import org.apache.lucene.util.BytesRef; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.common.Nullable; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.fielddata.AtomicOrdinalsFieldData; -import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource.Nested; import org.elasticsearch.index.fielddata.IndexFieldDataCache; import org.elasticsearch.index.fielddata.IndexOrdinalsFieldData; -import org.elasticsearch.index.fielddata.fieldcomparator.BytesRefFieldComparatorSource; import org.elasticsearch.index.fielddata.ordinals.GlobalOrdinalsBuilder; import org.elasticsearch.indices.breaker.CircuitBreakerService; -import org.elasticsearch.search.MultiValueMode; import java.io.IOException; diff --git a/core/src/main/java/org/elasticsearch/index/fieldvisitor/FieldsVisitor.java b/core/src/main/java/org/elasticsearch/index/fieldvisitor/FieldsVisitor.java index 372e7caf921d5..8c49185197f24 100644 --- a/core/src/main/java/org/elasticsearch/index/fieldvisitor/FieldsVisitor.java +++ b/core/src/main/java/org/elasticsearch/index/fieldvisitor/FieldsVisitor.java @@ -23,6 +23,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.ParentFieldMapper; @@ -34,6 +35,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -50,13 +52,14 @@ public class FieldsVisitor extends StoredFieldVisitor { private static final Set BASE_REQUIRED_FIELDS = unmodifiableSet(newHashSet( UidFieldMapper.NAME, + IdFieldMapper.NAME, RoutingFieldMapper.NAME, ParentFieldMapper.NAME)); private final boolean loadSource; private final Set requiredFields; protected BytesReference source; - protected Uid uid; + protected String type, id; protected Map> fieldsValues; public FieldsVisitor(boolean loadSource) { @@ -78,6 +81,13 @@ public Status needsField(FieldInfo fieldInfo) throws IOException { } public void postProcess(MapperService mapperService) { + if (mapperService.getIndexSettings().isSingleType()) { + final Collection types = mapperService.types(); + assert types.size() <= 1 : types; + if (types.isEmpty() == false) { + type = types.iterator().next(); + } + } for (Map.Entry> entry : fields().entrySet()) { MappedFieldType fieldType = mapperService.fullName(entry.getKey()); if (fieldType == null) { @@ -104,7 +114,11 @@ public void binaryField(FieldInfo fieldInfo, byte[] value) throws IOException { public void stringField(FieldInfo fieldInfo, byte[] bytes) throws IOException { final String value = new String(bytes, StandardCharsets.UTF_8); if (UidFieldMapper.NAME.equals(fieldInfo.name)) { - uid = Uid.createUid(value); + Uid uid = Uid.createUid(value); + type = uid.type(); + id = uid.id(); + } else if (IdFieldMapper.NAME.equals(fieldInfo.name)) { + id = value; } else { addValue(fieldInfo.name, value); } @@ -135,7 +149,12 @@ public BytesReference source() { } public Uid uid() { - return uid; + if (id == null) { + return null; + } else if (type == null) { + throw new IllegalStateException("Call postProcess before getting the uid"); + } + return new Uid(type, id); } public String routing() { @@ -157,7 +176,8 @@ public Map> fields() { public void reset() { if (fieldsValues != null) fieldsValues.clear(); source = null; - uid = null; + type = null; + id = null; requiredFields.addAll(BASE_REQUIRED_FIELDS); if (loadSource) { diff --git a/core/src/main/java/org/elasticsearch/index/fieldvisitor/JustUidFieldsVisitor.java b/core/src/main/java/org/elasticsearch/index/fieldvisitor/JustUidFieldsVisitor.java deleted file mode 100644 index 661d729b55f79..0000000000000 --- a/core/src/main/java/org/elasticsearch/index/fieldvisitor/JustUidFieldsVisitor.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -package org.elasticsearch.index.fieldvisitor; - -import org.apache.lucene.index.FieldInfo; -import org.elasticsearch.index.mapper.UidFieldMapper; - -import java.io.IOException; - -public class JustUidFieldsVisitor extends FieldsVisitor { - - public JustUidFieldsVisitor() { - super(false); - } - - @Override - public Status needsField(FieldInfo fieldInfo) throws IOException { - if (UidFieldMapper.NAME.equals(fieldInfo.name)) { - return Status.YES; - } - return uid != null ? Status.STOP : Status.NO; - } -} diff --git a/core/src/main/java/org/elasticsearch/index/fieldvisitor/SingleFieldsVisitor.java b/core/src/main/java/org/elasticsearch/index/fieldvisitor/SingleFieldsVisitor.java index 556f43d06a1c6..5d5040c637f04 100644 --- a/core/src/main/java/org/elasticsearch/index/fieldvisitor/SingleFieldsVisitor.java +++ b/core/src/main/java/org/elasticsearch/index/fieldvisitor/SingleFieldsVisitor.java @@ -20,12 +20,12 @@ import org.apache.lucene.index.FieldInfo; import org.elasticsearch.index.mapper.IdFieldMapper; -import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.TypeFieldMapper; +import org.elasticsearch.index.mapper.Uid; import org.elasticsearch.index.mapper.UidFieldMapper; import java.io.IOException; -import java.util.List; public class SingleFieldsVisitor extends FieldsVisitor { @@ -54,30 +54,17 @@ public void reset(String field) { super.reset(); } - public void postProcess(MappedFieldType fieldType) { - if (uid != null) { - switch (field) { - case UidFieldMapper.NAME: - addValue(field, uid.toString()); - break; - case IdFieldMapper.NAME: - addValue(field, uid.id()); - break; - case TypeFieldMapper.NAME: - addValue(field, uid.type()); - break; - } - } - - if (fieldsValues == null) { - return; + @Override + public void postProcess(MapperService mapperService) { + super.postProcess(mapperService); + if (id != null) { + addValue(IdFieldMapper.NAME, id); } - List fieldValues = fieldsValues.get(fieldType.name()); - if (fieldValues == null) { - return; + if (type != null) { + addValue(TypeFieldMapper.NAME, type); } - for (int i = 0; i < fieldValues.size(); i++) { - fieldValues.set(i, fieldType.valueForDisplay(fieldValues.get(i))); + if (type != null && id != null) { + addValue(UidFieldMapper.NAME, Uid.createUid(type, id)); } } } diff --git a/core/src/main/java/org/elasticsearch/index/get/ShardGetService.java b/core/src/main/java/org/elasticsearch/index/get/ShardGetService.java index 6d3e1e3ab6a56..15e6e23428465 100644 --- a/core/src/main/java/org/elasticsearch/index/get/ShardGetService.java +++ b/core/src/main/java/org/elasticsearch/index/get/ShardGetService.java @@ -42,14 +42,13 @@ import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.ParentFieldMapper; import org.elasticsearch.index.mapper.SourceFieldMapper; -import org.elasticsearch.index.mapper.Uid; -import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.shard.AbstractIndexShardComponent; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.search.fetch.subphase.FetchSourceContext; import org.elasticsearch.search.fetch.subphase.ParentFieldSubFetchPhase; import java.io.IOException; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -139,11 +138,18 @@ private FetchSourceContext normalizeFetchSourceContent(@Nullable FetchSourceCont private GetResult innerGet(String type, String id, String[] gFields, boolean realtime, long version, VersionType versionType, FetchSourceContext fetchSourceContext) { fetchSourceContext = normalizeFetchSourceContent(fetchSourceContext, gFields); + final Collection types; + if (type == null || type.equals("_all")) { + types = mapperService.types(); + } else { + types = Collections.singleton(type); + } Engine.GetResult get = null; - if (type == null || type.equals("_all")) { - for (String typeX : mapperService.types()) { - get = indexShard.get(new Engine.Get(realtime, new Term(UidFieldMapper.NAME, Uid.createUidAsBytes(typeX, id))) + for (String typeX : types) { + Term uidTerm = mapperService.createUidTerm(typeX, id); + if (uidTerm != null) { + get = indexShard.get(new Engine.Get(realtime, typeX, id, uidTerm) .version(version).versionType(versionType)); if (get.exists()) { type = typeX; @@ -152,20 +158,10 @@ private GetResult innerGet(String type, String id, String[] gFields, boolean rea get.release(); } } - if (get == null) { - return new GetResult(shardId.getIndexName(), type, id, -1, false, null, null); - } - if (!get.exists()) { - // no need to release here as well..., we release in the for loop for non exists - return new GetResult(shardId.getIndexName(), type, id, -1, false, null, null); - } - } else { - get = indexShard.get(new Engine.Get(realtime, new Term(UidFieldMapper.NAME, Uid.createUidAsBytes(type, id))) - .version(version).versionType(versionType)); - if (!get.exists()) { - get.release(); - return new GetResult(shardId.getIndexName(), type, id, -1, false, null, null); - } + } + + if (get == null || get.exists() == false) { + return new GetResult(shardId.getIndexName(), type, id, -1, false, null, null); } try { diff --git a/core/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java b/core/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java index 8e18c820b7965..a9a765f1c3a0e 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java @@ -22,26 +22,18 @@ import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexableField; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.TermInSetQuery; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.MultiTermQuery; -import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; -import org.apache.lucene.search.RegexpQuery; -import org.apache.lucene.util.BytesRef; +import org.apache.lucene.search.TermInSetQuery; import org.elasticsearch.common.Nullable; -import org.elasticsearch.common.lucene.BytesRefs; import org.elasticsearch.common.lucene.Lucene; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.util.iterable.Iterables; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.index.mapper.Mapper.TypeParser.ParserContext; +import org.elasticsearch.index.IndexSettings; +import org.elasticsearch.index.fielddata.IndexFieldData; +import org.elasticsearch.index.fielddata.plain.PagedBytesIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; import java.io.IOException; -import java.util.Collection; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -63,8 +55,8 @@ public static class Defaults { static { FIELD_TYPE.setTokenized(false); - FIELD_TYPE.setIndexOptions(IndexOptions.NONE); - FIELD_TYPE.setStored(false); + FIELD_TYPE.setIndexOptions(IndexOptions.DOCS); + FIELD_TYPE.setStored(true); FIELD_TYPE.setOmitNorms(true); FIELD_TYPE.setIndexAnalyzer(Lucene.KEYWORD_ANALYZER); FIELD_TYPE.setSearchAnalyzer(Lucene.KEYWORD_ANALYZER); @@ -81,7 +73,7 @@ public MetadataFieldMapper.Builder parse(String name, Map node, @Override public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - final Settings indexSettings = context.mapperService().getIndexSettings().getSettings(); + final IndexSettings indexSettings = context.mapperService().getIndexSettings(); return new IdFieldMapper(indexSettings, fieldType); } } @@ -113,32 +105,66 @@ public boolean isSearchable() { @Override public Query termQuery(Object value, @Nullable QueryShardContext context) { - final BytesRef[] uids = Uid.createUidsForTypesAndId(context.queryTypes(), value); - return new TermInSetQuery(UidFieldMapper.NAME, uids); + return termsQuery(Arrays.asList(value), context); } @Override - public Query termsQuery(List values, @Nullable QueryShardContext context) { + public Query termsQuery(List values, @Nullable QueryShardContext context) { + if (indexOptions() != IndexOptions.NONE) { + // 6.x index, _id is indexed + return super.termsQuery(values, context); + } + // 5.x index, _uid is indexed return new TermInSetQuery(UidFieldMapper.NAME, Uid.createUidsForTypesAndIds(context.queryTypes(), values)); } + + @Override + public IndexFieldData.Builder fielddataBuilder() { + if (indexOptions() == IndexOptions.NONE) { + throw new IllegalArgumentException("Fielddata access on the _uid field is disallowed"); + } + return new PagedBytesIndexFieldData.Builder( + TextFieldMapper.Defaults.FIELDDATA_MIN_FREQUENCY, + TextFieldMapper.Defaults.FIELDDATA_MAX_FREQUENCY, + TextFieldMapper.Defaults.FIELDDATA_MIN_SEGMENT_SIZE); + } } - private IdFieldMapper(Settings indexSettings, MappedFieldType existing) { - this(existing != null ? existing : Defaults.FIELD_TYPE, indexSettings); + static MappedFieldType defaultFieldType(IndexSettings indexSettings) { + MappedFieldType defaultFieldType = Defaults.FIELD_TYPE.clone(); + if (indexSettings.isSingleType()) { + defaultFieldType.setIndexOptions(IndexOptions.DOCS); + defaultFieldType.setStored(true); + } else { + defaultFieldType.setIndexOptions(IndexOptions.NONE); + defaultFieldType.setStored(false); + } + return defaultFieldType; } - private IdFieldMapper(MappedFieldType fieldType, Settings indexSettings) { - super(NAME, fieldType, Defaults.FIELD_TYPE, indexSettings); + private IdFieldMapper(IndexSettings indexSettings, MappedFieldType existing) { + this(existing == null ? defaultFieldType(indexSettings) : existing, indexSettings); + } + + private IdFieldMapper(MappedFieldType fieldType, IndexSettings indexSettings) { + super(NAME, fieldType, defaultFieldType(indexSettings), indexSettings.getSettings()); } @Override - public void preParse(ParseContext context) throws IOException {} + public void preParse(ParseContext context) throws IOException { + super.parse(context); + } @Override public void postParse(ParseContext context) throws IOException {} @Override - protected void parseCreateField(ParseContext context, List fields) throws IOException {} + protected void parseCreateField(ParseContext context, List fields) throws IOException { + if (fieldType.indexOptions() != IndexOptions.NONE || fieldType.stored()) { + Field id = new Field(NAME, context.sourceToParse().id(), fieldType); + fields.add(id); + } + } @Override protected String contentType() { diff --git a/core/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java b/core/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java index 55c2e4cb3c698..9e8b2d9018f25 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java @@ -24,15 +24,19 @@ import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.MultiFields; +import org.apache.lucene.index.PrefixCodedTerms; import org.apache.lucene.index.Term; import org.apache.lucene.index.Terms; +import org.apache.lucene.index.PrefixCodedTerms.TermIterator; import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermInSetQuery; import org.apache.lucene.search.TermQuery; +import org.apache.lucene.util.BytesRef; import org.elasticsearch.action.fieldstats.FieldStats; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.joda.DateMathParser; @@ -473,6 +477,15 @@ public static Term extractTerm(Query termQuery) { assert ((TypeFieldMapper.TypesQuery) termQuery).getTerms().length == 1; return new Term(TypeFieldMapper.NAME, ((TypeFieldMapper.TypesQuery) termQuery).getTerms()[0]); } + if (termQuery instanceof TermInSetQuery) { + TermInSetQuery tisQuery = (TermInSetQuery) termQuery; + PrefixCodedTerms terms = tisQuery.getTermData(); + if (terms.size() == 1) { + TermIterator it = terms.iterator(); + BytesRef term = it.next(); + return new Term(it.field(), term); + } + } if (termQuery instanceof TermQuery == false) { throw new IllegalArgumentException("Cannot extract a term from a query of type " + termQuery.getClass() + ": " + termQuery); diff --git a/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java b/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java index bd65d123f81d6..4d68deef3b7c0 100755 --- a/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java @@ -24,6 +24,7 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.DelegatingAnalyzerWrapper; +import org.apache.lucene.index.Term; import org.elasticsearch.ElasticsearchGenerationException; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetaData; @@ -470,7 +471,7 @@ private synchronized Map internalMerge(@Nullable Documen } } - if (indexSettings.getValue(INDEX_MAPPING_SINGLE_TYPE_SETTING)) { + if (indexSettings.isSingleType()) { Set actualTypes = new HashSet<>(mappers.keySet()); actualTypes.remove(DEFAULT_MAPPING); if (actualTypes.size() > 1) { @@ -803,4 +804,15 @@ protected Analyzer getWrappedAnalyzer(String fieldName) { } } + /** Return a term that uniquely identifies the document, or {@code null} if the type is not allowed. */ + public Term createUidTerm(String type, String id) { + if (hasMapping(type) == false) { + return null; + } + if (indexSettings.isSingleType()) { + return new Term(IdFieldMapper.NAME, id); + } else { + return new Term(UidFieldMapper.NAME, Uid.createUidAsBytes(type, id)); + } + } } diff --git a/core/src/main/java/org/elasticsearch/index/mapper/ParsedDocument.java b/core/src/main/java/org/elasticsearch/index/mapper/ParsedDocument.java index 91cf2aa4fa4a8..14b3291f441a9 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/ParsedDocument.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/ParsedDocument.java @@ -70,10 +70,6 @@ public ParsedDocument(Field version, this.xContentType = xContentType; } - public BytesRef uid() { - return uid; - } - public String id() { return this.id; } diff --git a/core/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java b/core/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java index 9d4a4a6987ba3..2092e2521d38e 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java @@ -149,7 +149,7 @@ public Query termQuery(Object value, QueryShardContext context) { @Override public Query termsQuery(List values, QueryShardContext context) { - if (context.getIndexSettings().getValue(MapperService.INDEX_MAPPING_SINGLE_TYPE_SETTING)) { + if (context.getIndexSettings().isSingleType()) { Collection indexTypes = context.getMapperService().types(); if (indexTypes.isEmpty()) { return new MatchNoDocsQuery("No types"); diff --git a/core/src/main/java/org/elasticsearch/index/mapper/UidFieldMapper.java b/core/src/main/java/org/elasticsearch/index/mapper/UidFieldMapper.java index a2d76dcf2c091..ba06361502353 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/UidFieldMapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/UidFieldMapper.java @@ -19,18 +19,33 @@ package org.elasticsearch.index.mapper; -import org.apache.lucene.document.BinaryDocValuesField; import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexableField; +import org.apache.lucene.search.MatchNoDocsQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermInSetQuery; import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.BytesRefBuilder; +import org.apache.lucene.util.StringHelper; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.logging.DeprecationLogger; +import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.lucene.Lucene; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.fielddata.IndexFieldData; +import org.elasticsearch.index.fielddata.IndexFieldDataCache; +import org.elasticsearch.index.fielddata.IndexOrdinalsFieldData; +import org.elasticsearch.index.fielddata.UidIndexFieldData; import org.elasticsearch.index.fielddata.plain.PagedBytesIndexFieldData; +import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.indices.breaker.CircuitBreakerService; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -62,6 +77,8 @@ public static class Defaults { } } + private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(Loggers.getLogger(UidFieldMapper.class)); + public static class TypeParser implements MetadataFieldMapper.TypeParser { @Override public MetadataFieldMapper.Builder parse(String name, Map node, ParserContext parserContext) throws MapperParsingException { @@ -70,7 +87,7 @@ public static class TypeParser implements MetadataFieldMapper.TypeParser { @Override public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - final Settings indexSettings = context.mapperService().getIndexSettings().getSettings(); + final IndexSettings indexSettings = context.mapperService().getIndexSettings(); return new UidFieldMapper(indexSettings, fieldType); } } @@ -96,20 +113,81 @@ public String typeName() { @Override public IndexFieldData.Builder fielddataBuilder() { - // TODO: add doc values support? - return new PagedBytesIndexFieldData.Builder( - TextFieldMapper.Defaults.FIELDDATA_MIN_FREQUENCY, - TextFieldMapper.Defaults.FIELDDATA_MAX_FREQUENCY, - TextFieldMapper.Defaults.FIELDDATA_MIN_SEGMENT_SIZE); + if (indexOptions() == IndexOptions.NONE) { + DEPRECATION_LOGGER.deprecated("Fielddata access on the _uid field is deprecated, use _id instead"); + return new IndexFieldData.Builder() { + @Override + public IndexFieldData build(IndexSettings indexSettings, MappedFieldType fieldType, IndexFieldDataCache cache, + CircuitBreakerService breakerService, MapperService mapperService) { + MappedFieldType idFieldType = mapperService.fullName(IdFieldMapper.NAME); + IndexOrdinalsFieldData idFieldData = (IndexOrdinalsFieldData) idFieldType.fielddataBuilder() + .build(indexSettings, idFieldType, cache, breakerService, mapperService); + final String type = mapperService.types().iterator().next(); + return new UidIndexFieldData(indexSettings.getIndex(), type, idFieldData); + } + }; + } else { + // Old index, _uid was indexed + return new PagedBytesIndexFieldData.Builder( + TextFieldMapper.Defaults.FIELDDATA_MIN_FREQUENCY, + TextFieldMapper.Defaults.FIELDDATA_MAX_FREQUENCY, + TextFieldMapper.Defaults.FIELDDATA_MIN_SEGMENT_SIZE); + } + } + + @Override + public Query termQuery(Object value, @Nullable QueryShardContext context) { + return termsQuery(Arrays.asList(value), context); + } + + @Override + public Query termsQuery(List values, @Nullable QueryShardContext context) { + if (indexOptions() != IndexOptions.NONE) { + return super.termsQuery(values, context); + } + Collection indexTypes = context.getMapperService().types(); + if (indexTypes.isEmpty()) { + return new MatchNoDocsQuery("No types"); + } + assert indexTypes.size() == 1; + BytesRef indexType = indexedValueForSearch(indexTypes.iterator().next()); + BytesRefBuilder prefixBuilder = new BytesRefBuilder(); + prefixBuilder.append(indexType); + prefixBuilder.append((byte) '#'); + BytesRef expectedPrefix = prefixBuilder.get(); + List ids = new ArrayList<>(); + for (Object uid : values) { + BytesRef uidBytes = indexedValueForSearch(uid); + if (StringHelper.startsWith(uidBytes, expectedPrefix)) { + BytesRef id = new BytesRef(); + id.bytes = uidBytes.bytes; + id.offset = uidBytes.offset + expectedPrefix.length; + id.length = uidBytes.length - expectedPrefix.length; + ids.add(id); + } + } + return new TermInSetQuery(IdFieldMapper.NAME, ids); + } + } + + static MappedFieldType defaultFieldType(IndexSettings indexSettings) { + MappedFieldType defaultFieldType = Defaults.FIELD_TYPE.clone(); + if (indexSettings.isSingleType()) { + defaultFieldType.setIndexOptions(IndexOptions.NONE); + defaultFieldType.setStored(false); + } else { + defaultFieldType.setIndexOptions(IndexOptions.DOCS); + defaultFieldType.setStored(true); } + return defaultFieldType; } - private UidFieldMapper(Settings indexSettings, MappedFieldType existing) { - this(existing == null ? Defaults.FIELD_TYPE.clone() : existing, Defaults.FIELD_TYPE, indexSettings); + private UidFieldMapper(IndexSettings indexSettings, MappedFieldType existing) { + this(existing == null ? defaultFieldType(indexSettings) : existing, indexSettings); } - private UidFieldMapper(MappedFieldType fieldType, MappedFieldType defaultFieldType, Settings indexSettings) { - super(NAME, fieldType, defaultFieldType, indexSettings); + private UidFieldMapper(MappedFieldType fieldType, IndexSettings indexSettings) { + super(NAME, fieldType, defaultFieldType(indexSettings), indexSettings.getSettings()); } @Override @@ -128,10 +206,9 @@ public Mapper parse(ParseContext context) throws IOException { @Override protected void parseCreateField(ParseContext context, List fields) throws IOException { - Field uid = new Field(NAME, Uid.createUid(context.sourceToParse().type(), context.sourceToParse().id()), Defaults.FIELD_TYPE); - fields.add(uid); - if (fieldType().hasDocValues()) { - fields.add(new BinaryDocValuesField(NAME, new BytesRef(uid.stringValue()))); + if (fieldType.indexOptions() != IndexOptions.NONE || fieldType.stored()) { + Field uid = new Field(NAME, Uid.createUid(context.sourceToParse().type(), context.sourceToParse().id()), fieldType); + fields.add(uid); } } diff --git a/core/src/main/java/org/elasticsearch/index/query/IdsQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/IdsQueryBuilder.java index 5857ef9abf373..1b756062dc9bd 100644 --- a/core/src/main/java/org/elasticsearch/index/query/IdsQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/IdsQueryBuilder.java @@ -19,8 +19,8 @@ package org.elasticsearch.index.query; +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermInSetQuery; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; @@ -30,6 +30,7 @@ import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.Uid; import org.elasticsearch.index.mapper.UidFieldMapper; @@ -162,6 +163,10 @@ public String getWriteableName() { @Override protected Query doToQuery(QueryShardContext context) throws IOException { Query query; + MappedFieldType uidField = context.fieldMapper(UidFieldMapper.NAME); + if (uidField == null) { + return new MatchNoDocsQuery("No mappings"); + } if (this.ids.isEmpty()) { query = Queries.newMatchNoDocsQuery("Missing ids in \"" + this.getName() + "\" query."); } else { @@ -175,7 +180,7 @@ protected Query doToQuery(QueryShardContext context) throws IOException { Collections.addAll(typesForQuery, types); } - query = new TermInSetQuery(UidFieldMapper.NAME, Uid.createUidsForTypesAndIds(typesForQuery, ids)); + query = uidField.termsQuery(Arrays.asList(Uid.createUidsForTypesAndIds(typesForQuery, ids)), context); } return query; } diff --git a/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java index 5df7ace69bb39..fd83465b7e2f7 100644 --- a/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java @@ -24,7 +24,6 @@ import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermInSetQuery; import org.apache.lucene.util.BytesRef; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ExceptionsHelper; @@ -1102,7 +1101,7 @@ private Query handleItems(QueryShardContext context, MoreLikeThisQuery mltQuery, // exclude the items from the search if (!include) { - handleExclude(boolQuery, likeItems); + handleExclude(boolQuery, likeItems, context); } return boolQuery.build(); } @@ -1155,7 +1154,12 @@ private static Fields[] getFieldsFor(MultiTermVectorsResponse responses) throws return likeFields.toArray(Fields.EMPTY_ARRAY); } - private static void handleExclude(BooleanQuery.Builder boolQuery, Item[] likeItems) { + private static void handleExclude(BooleanQuery.Builder boolQuery, Item[] likeItems, QueryShardContext context) { + MappedFieldType uidField = context.fieldMapper(UidFieldMapper.NAME); + if (uidField == null) { + // no mappings, nothing to exclude + return; + } // artificial docs get assigned a random id and should be disregarded List uids = new ArrayList<>(); for (Item item : likeItems) { @@ -1165,7 +1169,7 @@ private static void handleExclude(BooleanQuery.Builder boolQuery, Item[] likeIte uids.add(createUidAsBytes(item.type(), item.id())); } if (!uids.isEmpty()) { - TermInSetQuery query = new TermInSetQuery(UidFieldMapper.NAME, uids.toArray(new BytesRef[uids.size()])); + Query query = uidField.termsQuery(uids, context); boolQuery.add(query, BooleanClause.Occur.MUST_NOT); } } diff --git a/core/src/main/java/org/elasticsearch/index/query/functionscore/RandomScoreFunctionBuilder.java b/core/src/main/java/org/elasticsearch/index/query/functionscore/RandomScoreFunctionBuilder.java index ddf796503b705..f2c92cc7d1ded 100644 --- a/core/src/main/java/org/elasticsearch/index/query/functionscore/RandomScoreFunctionBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/functionscore/RandomScoreFunctionBuilder.java @@ -26,10 +26,12 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.fielddata.IndexFieldData; +import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryShardContext; -import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; import java.util.Objects; @@ -126,7 +128,12 @@ protected int doHashCode() { @Override protected ScoreFunction doToFunction(QueryShardContext context) { - final MappedFieldType fieldType = context.getMapperService().fullName("_uid"); + final MappedFieldType fieldType; + if (context.getIndexSettings().isSingleType()) { + fieldType = context.getMapperService().fullName(IdFieldMapper.NAME); + } else { + fieldType = context.getMapperService().fullName(UidFieldMapper.NAME); + } if (fieldType == null) { // mapper could be null if we are on a shard with no docs yet, so this won't actually be used return new RandomScoreFunction(); diff --git a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java index 45e7bd3d1e9ff..e08011d8f296f 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -21,16 +21,13 @@ import org.apache.logging.log4j.Logger; import org.apache.lucene.index.CheckIndex; -import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.IndexCommit; -import org.apache.lucene.index.IndexFormatTooNewException; -import org.apache.lucene.index.IndexFormatTooOldException; +import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.KeepOnlyLastCommitDeletionPolicy; import org.apache.lucene.index.SegmentInfos; import org.apache.lucene.index.SnapshotDeletionPolicy; import org.apache.lucene.index.Term; -import org.apache.lucene.search.Query; import org.apache.lucene.search.QueryCachingPolicy; import org.apache.lucene.search.Sort; import org.apache.lucene.search.UsageTrackingQueryCachingPolicy; @@ -91,19 +88,18 @@ import org.elasticsearch.index.flush.FlushStats; import org.elasticsearch.index.get.GetStats; import org.elasticsearch.index.get.ShardGetService; -import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.DocumentMapperForType; -import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.SourceToParse; import org.elasticsearch.index.mapper.Uid; +import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.merge.MergeStats; import org.elasticsearch.index.recovery.RecoveryStats; import org.elasticsearch.index.refresh.RefreshStats; import org.elasticsearch.index.search.stats.SearchStats; import org.elasticsearch.index.search.stats.ShardSearchStats; -import org.elasticsearch.index.seqno.GlobalCheckpointTracker; import org.elasticsearch.index.seqno.SeqNoStats; import org.elasticsearch.index.seqno.SequenceNumbersService; import org.elasticsearch.index.similarity.SimilarityService; @@ -128,12 +124,10 @@ import org.elasticsearch.search.suggest.completion.CompletionStats; import org.elasticsearch.threadpool.ThreadPool; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; import java.nio.channels.ClosedByInterruptException; import java.nio.charset.StandardCharsets; -import java.nio.file.NoSuchFileException; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; @@ -550,9 +544,12 @@ static Engine.Index prepareIndex(DocumentMapperForType docMapper, SourceToParse if (docMapper.getMapping() != null) { doc.addDynamicMappingsUpdate(docMapper.getMapping()); } - MappedFieldType uidFieldType = docMapper.getDocumentMapper().uidMapper().fieldType(); - Query uidQuery = uidFieldType.termQuery(doc.uid(), null); - Term uid = MappedFieldType.extractTerm(uidQuery); + Term uid; + if (docMapper.getDocumentMapper().idFieldMapper().fieldType().indexOptions() != IndexOptions.NONE) { + uid = new Term(IdFieldMapper.NAME, doc.id()); + } else { + uid = new Term(UidFieldMapper.NAME, Uid.createUidAsBytes(doc.type(), doc.id())); + } return new Engine.Index(uid, doc, seqNo, primaryTerm, version, versionType, origin, startTime, autoGeneratedIdTimestamp, isRetry); } @@ -594,7 +591,7 @@ public Engine.NoOpResult markSeqNoAsNoOp(Engine.NoOp noOp) throws IOException { public Engine.Delete prepareDeleteOnPrimary(String type, String id, long version, VersionType versionType) { verifyPrimary(); - final Term uid = extractUid(type, id); + final Term uid = extractUidForDelete(type, id); return prepareDelete(type, id, uid, SequenceNumbersService.UNASSIGNED_SEQ_NO, primaryTerm, version, versionType, Engine.Operation.Origin.PRIMARY); } @@ -602,7 +599,7 @@ public Engine.Delete prepareDeleteOnPrimary(String type, String id, long version public Engine.Delete prepareDeleteOnReplica(String type, String id, long seqNo, long primaryTerm, long version, VersionType versionType) { verifyReplicationTarget(); - final Term uid = extractUid(type, id); + final Term uid = extractUidForDelete(type, id); return prepareDelete(type, id, uid, seqNo, primaryTerm, version, versionType, Engine.Operation.Origin.REPLICA); } @@ -618,11 +615,14 @@ public Engine.DeleteResult delete(Engine.Delete delete) throws IOException { return delete(engine, delete); } - private Term extractUid(String type, String id) { - final DocumentMapper documentMapper = docMapper(type).getDocumentMapper(); - final MappedFieldType uidFieldType = documentMapper.uidMapper().fieldType(); - final Query uidQuery = uidFieldType.termQuery(Uid.createUid(type, id), null); - return MappedFieldType.extractTerm(uidQuery); + private Term extractUidForDelete(String type, String id) { + if (indexSettings.isSingleType()) { + // This is only correct because we create types dynamically on delete operations + // otherwise this could match the same _id from a different type + return new Term(IdFieldMapper.NAME, id); + } else { + return new Term(UidFieldMapper.NAME, Uid.createUidAsBytes(type, id)); + } } private Engine.DeleteResult delete(Engine engine, Engine.Delete delete) throws IOException { @@ -907,12 +907,13 @@ public void releaseIndexCommit(IndexCommit snapshot) throws IOException { * without having to worry about the current state of the engine and concurrent flushes. * * @throws org.apache.lucene.index.IndexNotFoundException if no index is found in the current directory - * @throws CorruptIndexException if the lucene index is corrupted. This can be caused by a checksum mismatch or an - * unexpected exception when opening the index reading the segments file. - * @throws IndexFormatTooOldException if the lucene index is too old to be opened. - * @throws IndexFormatTooNewException if the lucene index is too new to be opened. - * @throws FileNotFoundException if one or more files referenced by a commit are not present. - * @throws NoSuchFileException if one or more files referenced by a commit are not present. + * @throws org.apache.lucene.index.CorruptIndexException if the lucene index is corrupted. This can be caused by a checksum + * mismatch or an unexpected exception when opening the index reading the + * segments file. + * @throws org.apache.lucene.index.IndexFormatTooOldException if the lucene index is too old to be opened. + * @throws org.apache.lucene.index.IndexFormatTooNewException if the lucene index is too new to be opened. + * @throws java.io.FileNotFoundException if one or more files referenced by a commit are not present. + * @throws java.nio.file.NoSuchFileException if one or more files referenced by a commit are not present. */ public Store.MetadataSnapshot snapshotStoreMetadata() throws IOException { IndexCommit indexCommit = null; @@ -1453,7 +1454,8 @@ public void writeIndexingBuffer() { /** * Notifies the service to update the local checkpoint for the shard with the provided allocation ID. See - * {@link GlobalCheckpointTracker#updateLocalCheckpoint(String, long)} for details. + * {@link org.elasticsearch.index.seqno.GlobalCheckpointTracker#updateLocalCheckpoint(String, long)} for + * details. * * @param allocationId the allocation ID of the shard to update the local checkpoint for * @param checkpoint the local checkpoint for the shard @@ -1475,7 +1477,8 @@ public void waitForOpsToComplete(final long seqNo) throws InterruptedException { /** * Marks the shard with the provided allocation ID as in-sync with the primary shard. See - * {@link GlobalCheckpointTracker#markAllocationIdAsInSync(String, long)} for additional details. + * {@link org.elasticsearch.index.seqno.GlobalCheckpointTracker#markAllocationIdAsInSync(String, long)} + * for additional details. * * @param allocationId the allocation ID of the shard to mark as in-sync * @param localCheckpoint the current local checkpoint on the shard @@ -1543,7 +1546,8 @@ public void updateGlobalCheckpointOnReplica(final long globalCheckpoint) { /** * Notifies the service of the current allocation IDs in the cluster state. See - * {@link GlobalCheckpointTracker#updateAllocationIdsFromMaster(Set, Set)} for details. + * {@link org.elasticsearch.index.seqno.GlobalCheckpointTracker#updateAllocationIdsFromMaster(Set, Set)} + * for details. * * @param activeAllocationIds the allocation IDs of the currently active shard copies * @param initializingAllocationIds the allocation IDs of the currently initializing shard copies diff --git a/core/src/main/java/org/elasticsearch/index/shard/TranslogRecoveryPerformer.java b/core/src/main/java/org/elasticsearch/index/shard/TranslogRecoveryPerformer.java index 8842cbf3c0bd4..f4f611b733b5a 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/TranslogRecoveryPerformer.java +++ b/core/src/main/java/org/elasticsearch/index/shard/TranslogRecoveryPerformer.java @@ -166,9 +166,8 @@ private void performRecoveryOperation(Engine engine, Translog.Operation operatio break; case DELETE: Translog.Delete delete = (Translog.Delete) operation; - Uid uid = Uid.createUid(delete.uid().text()); - logger.trace("[translog] recover [delete] op [({}, {})] of [{}][{}]", delete.seqNo(), delete.primaryTerm(), uid.type(), uid.id()); - final Engine.Delete engineDelete = new Engine.Delete(uid.type(), uid.id(), delete.uid(), delete.seqNo(), + logger.trace("[translog] recover [delete] op [({}, {})] of [{}][{}]", delete.seqNo(), delete.primaryTerm(), delete.type(), delete.id()); + final Engine.Delete engineDelete = new Engine.Delete(delete.type(), delete.id(), delete.uid(), delete.seqNo(), delete.primaryTerm(), delete.version(), delete.versionType().versionTypeForReplicationAndRecovery(), origin, System.nanoTime()); delete(engine, engineDelete); diff --git a/core/src/main/java/org/elasticsearch/index/termvectors/TermVectorsService.java b/core/src/main/java/org/elasticsearch/index/termvectors/TermVectorsService.java index 6351282a38a18..8291e76c2acac 100644 --- a/core/src/main/java/org/elasticsearch/index/termvectors/TermVectorsService.java +++ b/core/src/main/java/org/elasticsearch/index/termvectors/TermVectorsService.java @@ -49,8 +49,6 @@ import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.SourceFieldMapper; import org.elasticsearch.index.mapper.TextFieldMapper; -import org.elasticsearch.index.mapper.Uid; -import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.search.dfs.AggregatedDfs; @@ -82,9 +80,13 @@ public static TermVectorsResponse getTermVectors(IndexShard indexShard, TermVect static TermVectorsResponse getTermVectors(IndexShard indexShard, TermVectorsRequest request, LongSupplier nanoTimeSupplier) { final long startTime = nanoTimeSupplier.getAsLong(); final TermVectorsResponse termVectorsResponse = new TermVectorsResponse(indexShard.shardId().getIndex().getName(), request.type(), request.id()); - final Term uidTerm = new Term(UidFieldMapper.NAME, Uid.createUidAsBytes(request.type(), request.id())); - - Engine.GetResult get = indexShard.get(new Engine.Get(request.realtime(), uidTerm).version(request.version()).versionType(request.versionType())); + final Term uidTerm = indexShard.mapperService().createUidTerm(request.type(), request.id()); + if (uidTerm == null) { + termVectorsResponse.setExists(false); + return termVectorsResponse; + } + Engine.GetResult get = indexShard.get(new Engine.Get(request.realtime(), request.type(), request.id(), uidTerm) + .version(request.version()).versionType(request.versionType())); Fields termVectorsByField = null; AggregatedDfs dfs = null; diff --git a/core/src/main/java/org/elasticsearch/index/translog/Translog.java b/core/src/main/java/org/elasticsearch/index/translog/Translog.java index a5e6255ecb6dd..d6457e1d32af7 100644 --- a/core/src/main/java/org/elasticsearch/index/translog/Translog.java +++ b/core/src/main/java/org/elasticsearch/index/translog/Translog.java @@ -22,7 +22,6 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; import org.apache.lucene.index.Term; -import org.apache.lucene.index.TwoPhaseCommit; import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.util.IOUtils; import org.elasticsearch.action.index.IndexRequest; @@ -1052,6 +1051,7 @@ public static class Delete implements Operation { private static final int FORMAT_SEQ_NO = FORMAT_5_X + 1; public static final int SERIALIZATION_FORMAT = FORMAT_SEQ_NO; + private String type, id; private Term uid; private long seqNo = SequenceNumbersService.UNASSIGNED_SEQ_NO; private long primaryTerm = 0; @@ -1072,15 +1072,17 @@ public Delete(StreamInput in) throws IOException { } public Delete(Engine.Delete delete, Engine.DeleteResult deleteResult) { - this(delete.uid(), deleteResult.getSeqNo(), delete.primaryTerm(), deleteResult.getVersion(), delete.versionType()); + this(delete.type(), delete.id(), delete.uid(), deleteResult.getSeqNo(), delete.primaryTerm(), deleteResult.getVersion(), delete.versionType()); } /** utility for testing */ - public Delete(Term uid) { - this(uid, 0, 0, Versions.MATCH_ANY, VersionType.INTERNAL); + public Delete(String type, String id, Term uid) { + this(type, id, uid, 0, 0, Versions.MATCH_ANY, VersionType.INTERNAL); } - public Delete(Term uid, long seqNo, long primaryTerm, long version, VersionType versionType) { + public Delete(String type, String id, Term uid, long seqNo, long primaryTerm, long version, VersionType versionType) { + this.type = type; + this.id = id; this.uid = uid; this.seqNo = seqNo; this.primaryTerm = primaryTerm; @@ -1098,6 +1100,14 @@ public long estimateSize() { return ((uid.field().length() + uid.text().length()) * 2) + 20; } + public String type() { + return type; + } + + public String id() { + return id; + } + public Term uid() { return this.uid; } diff --git a/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsContext.java b/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsContext.java index e84fcc1f51dc8..c1155bdfbd6bf 100644 --- a/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsContext.java +++ b/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsContext.java @@ -23,6 +23,7 @@ import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.DocValuesTermsQuery; +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TopDocs; @@ -31,15 +32,12 @@ import org.apache.lucene.search.TopScoreDocCollector; import org.apache.lucene.search.join.BitSetProducer; import org.apache.lucene.search.join.ParentChildrenBlockJoinQuery; -import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.ObjectMapper; import org.elasticsearch.index.mapper.ParentFieldMapper; -import org.elasticsearch.index.mapper.Uid; -import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHitField; import org.elasticsearch.search.fetch.FetchSubPhase; @@ -170,7 +168,12 @@ public TopDocs topDocs(SearchContext context, FetchSubPhase.HitContext hitContex if (parentField == null) { throw new IllegalStateException("All children must have a _parent"); } - hitQuery = new TermQuery(new Term(UidFieldMapper.NAME, Uid.createUid(parentType, parentField.getValue()))); + Term uidTerm = context.mapperService().createUidTerm(parentType, parentField.getValue()); + if (uidTerm == null) { + hitQuery = new MatchNoDocsQuery("Missing type: " + parentType); + } else { + hitQuery = new TermQuery(uidTerm); + } } else { return Lucene.EMPTY_TOP_DOCS; } diff --git a/core/src/main/java/org/elasticsearch/search/lookup/LeafFieldsLookup.java b/core/src/main/java/org/elasticsearch/search/lookup/LeafFieldsLookup.java index 2d1c855b7c328..4a104797d7c1b 100644 --- a/core/src/main/java/org/elasticsearch/search/lookup/LeafFieldsLookup.java +++ b/core/src/main/java/org/elasticsearch/search/lookup/LeafFieldsLookup.java @@ -22,13 +22,16 @@ import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.Nullable; import org.elasticsearch.index.fieldvisitor.SingleFieldsVisitor; +import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.mapper.UidFieldMapper; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -37,6 +40,7 @@ public class LeafFieldsLookup implements Map { private final MapperService mapperService; + private final boolean singleType; @Nullable private final String[] types; @@ -51,6 +55,7 @@ public class LeafFieldsLookup implements Map { LeafFieldsLookup(MapperService mapperService, @Nullable String[] types, LeafReader reader) { this.mapperService = mapperService; + this.singleType = mapperService.getIndexSettings().isSingleType(); this.types = types; this.reader = reader; this.fieldVisitor = new SingleFieldsVisitor(null); @@ -142,11 +147,15 @@ private FieldLookup loadFieldData(String name) { } if (data.fields() == null) { String fieldName = data.fieldType().name(); + if (singleType && UidFieldMapper.NAME.equals(fieldName)) { + fieldName = IdFieldMapper.NAME; + } fieldVisitor.reset(fieldName); try { reader.document(docId, fieldVisitor); - fieldVisitor.postProcess(data.fieldType()); - data.fields(singletonMap(name, fieldVisitor.fields().get(data.fieldType().name()))); + fieldVisitor.postProcess(mapperService); + List storedFields = fieldVisitor.fields().get(data.fieldType().name()); + data.fields(singletonMap(name, storedFields)); } catch (IOException e) { throw new ElasticsearchParseException("failed to load field [{}]", e, name); } diff --git a/core/src/main/java/org/elasticsearch/search/slice/SliceBuilder.java b/core/src/main/java/org/elasticsearch/search/slice/SliceBuilder.java index 4fcf3a94f98d2..522ecb44d09bd 100644 --- a/core/src/main/java/org/elasticsearch/search/slice/SliceBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/slice/SliceBuilder.java @@ -32,7 +32,9 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData; +import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryShardContext; @@ -194,9 +196,14 @@ public Query toFilter(QueryShardContext context, int shardId, int numShards) { throw new IllegalArgumentException("field " + field + " not found"); } + String field = this.field; boolean useTermQuery = false; if (UidFieldMapper.NAME.equals(field)) { - useTermQuery = true; + if (context.getIndexSettings().isSingleType()) { + // on new indices, the _id acts as a _uid + field = IdFieldMapper.NAME; + } + useTermQuery = true; } else if (type.hasDocValues() == false) { throw new IllegalArgumentException("cannot load numeric doc values on " + field); } else { diff --git a/core/src/test/java/org/elasticsearch/action/bulk/TransportShardBulkActionTests.java b/core/src/test/java/org/elasticsearch/action/bulk/TransportShardBulkActionTests.java index 941cdbf995752..20406acc9b6e7 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/TransportShardBulkActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/TransportShardBulkActionTests.java @@ -571,7 +571,7 @@ public void updateMappings(Mapping update, ShardId shardId, } @Override - public void verifyMappings(Engine.Index operation, + public void verifyMappings(Mapping update, ShardId shardId) throws Exception { // No-op, will be called logger.info("--> verifying mappings noop"); @@ -600,7 +600,7 @@ public void updateMappings(Mapping update, ShardId shardId, } @Override - public void verifyMappings(Engine.Index operation, + public void verifyMappings(Mapping update, ShardId shardId) throws Exception { fail("should not have had to update the mappings"); } @@ -668,7 +668,7 @@ public static class NoopMappingUpdatePerformer implements MappingUpdatePerformer public void updateMappings(Mapping update, ShardId shardId, String type) throws Exception { } - public void verifyMappings(Engine.Index operation, ShardId shardId) throws Exception { + public void verifyMappings(Mapping update, ShardId shardId) throws Exception { } } @@ -683,7 +683,7 @@ public void updateMappings(Mapping update, ShardId shardId, String type) throws throw e; } - public void verifyMappings(Engine.Index operation, ShardId shardId) throws Exception { + public void verifyMappings(Mapping update, ShardId shardId) throws Exception { fail("should not have gotten to this point"); } } @@ -698,7 +698,7 @@ private class ThrowingVerifyingMappingUpdatePerformer implements MappingUpdatePe public void updateMappings(Mapping update, ShardId shardId, String type) throws Exception { } - public void verifyMappings(Engine.Index operation, ShardId shardId) throws Exception { + public void verifyMappings(Mapping update, ShardId shardId) throws Exception { throw e; } } diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelperTests.java b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelperTests.java index 5a12d54de04ca..498c6bf5286f9 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelperTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelperTests.java @@ -20,7 +20,7 @@ package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.index.mapper.UidFieldMapper; +import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.test.ESTestCase; @@ -44,7 +44,7 @@ public void testSliceIntoSubRequests() throws IOException { searchRequest.source().slice(null); } int times = between(2, 100); - String field = randomBoolean() ? UidFieldMapper.NAME : randomAlphaOfLength(5); + String field = randomBoolean() ? IdFieldMapper.NAME : randomAlphaOfLength(5); int currentSliceId = 0; for (SearchRequest slice : sliceIntoSubRequests(searchRequest, field, times)) { assertEquals(field, slice.source().slice().getField()); diff --git a/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java b/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java index fcec3d6b59445..711804153cf1e 100644 --- a/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java +++ b/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java @@ -188,7 +188,7 @@ public void testSearchingFilteringAliasesSingleIndex() throws Exception { assertHits(searchResponse.getHits(), "1", "2", "3"); logger.info("--> checking single filtering alias search with sort"); - searchResponse = client().prepareSearch("tests").setQuery(QueryBuilders.matchAllQuery()).addSort("_uid", SortOrder.ASC).get(); + searchResponse = client().prepareSearch("tests").setQuery(QueryBuilders.matchAllQuery()).addSort("_index", SortOrder.ASC).get(); assertHits(searchResponse.getHits(), "1", "2", "3"); logger.info("--> checking single filtering alias search with global facets"); @@ -203,7 +203,7 @@ public void testSearchingFilteringAliasesSingleIndex() throws Exception { logger.info("--> checking single filtering alias search with global facets and sort"); searchResponse = client().prepareSearch("tests").setQuery(QueryBuilders.matchQuery("name", "bar")) .addAggregation(AggregationBuilders.global("global").subAggregation(AggregationBuilders.terms("test").field("name"))) - .addSort("_uid", SortOrder.ASC).get(); + .addSort("_index", SortOrder.ASC).get(); assertSearchResponse(searchResponse); global = searchResponse.getAggregations().get("global"); terms = global.getAggregations().get("test"); @@ -212,7 +212,7 @@ public void testSearchingFilteringAliasesSingleIndex() throws Exception { logger.info("--> checking single filtering alias search with non-global facets"); searchResponse = client().prepareSearch("tests").setQuery(QueryBuilders.matchQuery("name", "bar")) .addAggregation(AggregationBuilders.terms("test").field("name")) - .addSort("_uid", SortOrder.ASC).get(); + .addSort("_index", SortOrder.ASC).get(); assertSearchResponse(searchResponse); terms = searchResponse.getAggregations().get("test"); assertThat(terms.getBuckets().size(), equalTo(2)); diff --git a/core/src/test/java/org/elasticsearch/common/lucene/uid/VersionLookupTests.java b/core/src/test/java/org/elasticsearch/common/lucene/uid/VersionLookupTests.java index 8b68e76957058..e8b5220396daa 100644 --- a/core/src/test/java/org/elasticsearch/common/lucene/uid/VersionLookupTests.java +++ b/core/src/test/java/org/elasticsearch/common/lucene/uid/VersionLookupTests.java @@ -32,7 +32,7 @@ import org.apache.lucene.util.FixedBitSet; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.lucene.uid.VersionsAndSeqNoResolver.DocIdAndVersion; -import org.elasticsearch.index.mapper.UidFieldMapper; +import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.VersionFieldMapper; import org.elasticsearch.test.ESTestCase; @@ -48,12 +48,12 @@ public void testSimple() throws Exception { Directory dir = newDirectory(); IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig(Lucene.STANDARD_ANALYZER)); Document doc = new Document(); - doc.add(new Field(UidFieldMapper.NAME, "6", UidFieldMapper.Defaults.FIELD_TYPE)); + doc.add(new Field(IdFieldMapper.NAME, "6", IdFieldMapper.Defaults.FIELD_TYPE)); doc.add(new NumericDocValuesField(VersionFieldMapper.NAME, 87)); writer.addDocument(doc); DirectoryReader reader = DirectoryReader.open(writer); LeafReaderContext segment = reader.leaves().get(0); - PerThreadIDVersionAndSeqNoLookup lookup = new PerThreadIDVersionAndSeqNoLookup(segment.reader()); + PerThreadIDVersionAndSeqNoLookup lookup = new PerThreadIDVersionAndSeqNoLookup(segment.reader(), IdFieldMapper.NAME); // found doc DocIdAndVersion result = lookup.lookupVersion(new BytesRef("6"), null, segment); assertNotNull(result); @@ -75,13 +75,13 @@ public void testTwoDocuments() throws Exception { Directory dir = newDirectory(); IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig(Lucene.STANDARD_ANALYZER)); Document doc = new Document(); - doc.add(new Field(UidFieldMapper.NAME, "6", UidFieldMapper.Defaults.FIELD_TYPE)); + doc.add(new Field(IdFieldMapper.NAME, "6", IdFieldMapper.Defaults.FIELD_TYPE)); doc.add(new NumericDocValuesField(VersionFieldMapper.NAME, 87)); writer.addDocument(doc); writer.addDocument(doc); DirectoryReader reader = DirectoryReader.open(writer); LeafReaderContext segment = reader.leaves().get(0); - PerThreadIDVersionAndSeqNoLookup lookup = new PerThreadIDVersionAndSeqNoLookup(segment.reader()); + PerThreadIDVersionAndSeqNoLookup lookup = new PerThreadIDVersionAndSeqNoLookup(segment.reader(), IdFieldMapper.NAME); // return the last doc when there are duplicates DocIdAndVersion result = lookup.lookupVersion(new BytesRef("6"), null, segment); assertNotNull(result); diff --git a/core/src/test/java/org/elasticsearch/fieldstats/FieldStatsTests.java b/core/src/test/java/org/elasticsearch/fieldstats/FieldStatsTests.java index 379806a7166fd..ee1f654642ce7 100644 --- a/core/src/test/java/org/elasticsearch/fieldstats/FieldStatsTests.java +++ b/core/src/test/java/org/elasticsearch/fieldstats/FieldStatsTests.java @@ -596,7 +596,7 @@ public void testMetaFieldsNotIndexed() { client().admin().indices().prepareRefresh().get(); FieldStatsResponse response = client().prepareFieldStats() - .setFields("_id", "_type") + .setFields("_uid", "_type") .get(); assertEquals(response.getAllFieldStats().size(), 1); assertEquals(response.getAllFieldStats().get("_type").isSearchable(), true); diff --git a/core/src/test/java/org/elasticsearch/index/IndexModuleTests.java b/core/src/test/java/org/elasticsearch/index/IndexModuleTests.java index 6f80da2d639c7..1ae125cecd19e 100644 --- a/core/src/test/java/org/elasticsearch/index/IndexModuleTests.java +++ b/core/src/test/java/org/elasticsearch/index/IndexModuleTests.java @@ -51,6 +51,7 @@ import org.elasticsearch.index.engine.InternalEngineTests; import org.elasticsearch.index.fielddata.IndexFieldDataCache; import org.elasticsearch.index.mapper.ParsedDocument; +import org.elasticsearch.index.mapper.Uid; import org.elasticsearch.index.shard.IndexEventListener; import org.elasticsearch.index.shard.IndexSearcherWrapper; import org.elasticsearch.index.shard.IndexingOperationListener; @@ -249,8 +250,8 @@ public Engine.Index preIndex(ShardId shardId, Engine.Index operation) { assertEquals(IndexingSlowLog.class, indexService.getIndexOperationListeners().get(0).getClass()); assertSame(listener, indexService.getIndexOperationListeners().get(1)); - ParsedDocument doc = InternalEngineTests.createParsedDoc("1", "test", null); - Engine.Index index = new Engine.Index(new Term("_uid", doc.uid()), doc); + ParsedDocument doc = InternalEngineTests.createParsedDoc("1", null); + Engine.Index index = new Engine.Index(new Term("_uid", Uid.createUidAsBytes(doc.type(), doc.id())), doc); ShardId shardId = new ShardId(new Index("foo", "bar"), 0); for (IndexingOperationListener l : indexService.getIndexOperationListeners()) { l.preIndex(shardId, index); diff --git a/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java b/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java index 2a611adb7635f..1b40316fca1f3 100644 --- a/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java +++ b/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java @@ -105,6 +105,7 @@ import org.elasticsearch.index.mapper.ContentPath; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.DocumentMapperForType; +import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.Mapper.BuilderContext; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.Mapping; @@ -291,12 +292,12 @@ private static Document testDocument() { return new Document(); } - public static ParsedDocument createParsedDoc(String id, String type, String routing) { - return testParsedDocument(id, type, routing, testDocumentWithTextField(), new BytesArray("{ \"value\" : \"test\" }"), null); + public static ParsedDocument createParsedDoc(String id, String routing) { + return testParsedDocument(id, routing, testDocumentWithTextField(), new BytesArray("{ \"value\" : \"test\" }"), null); } - private static ParsedDocument testParsedDocument(String id, String type, String routing, Document document, BytesReference source, Mapping mappingUpdate) { - Field uidField = new Field("_uid", Uid.createUid(type, id), UidFieldMapper.Defaults.FIELD_TYPE); + private static ParsedDocument testParsedDocument(String id, String routing, Document document, BytesReference source, Mapping mappingUpdate) { + Field uidField = new Field("_id", id, IdFieldMapper.Defaults.FIELD_TYPE); Field versionField = new NumericDocValuesField("_version", 0); SeqNoFieldMapper.SequenceIDFields seqID = SeqNoFieldMapper.SequenceIDFields.emptySeqID(); document.add(uidField); @@ -306,7 +307,7 @@ private static ParsedDocument testParsedDocument(String id, String type, String document.add(seqID.primaryTerm); BytesRef ref = source.toBytesRef(); document.add(new StoredField(SourceFieldMapper.NAME, ref.bytes, ref.offset, ref.length)); - return new ParsedDocument(versionField, seqID, id, type, routing, Arrays.asList(document), source, XContentType.JSON, + return new ParsedDocument(versionField, seqID, id, "test", routing, Arrays.asList(document), source, XContentType.JSON, mappingUpdate); } @@ -441,8 +442,9 @@ public void onFailedEngine(String reason, @Nullable Exception e) { }; EngineConfig config = new EngineConfig(openMode, shardId, threadPool, indexSettings, null, store, deletionPolicy, mergePolicy, iwc.getAnalyzer(), iwc.getSimilarity(), new CodecService(null, logger), listener, - new TranslogHandler(xContentRegistry(), shardId.getIndexName(), logger), IndexSearcher.getDefaultQueryCache(), - IndexSearcher.getDefaultQueryCachingPolicy(), translogConfig, TimeValue.timeValueMinutes(5), refreshListener, indexSort); + new TranslogHandler(xContentRegistry(), shardId.getIndexName(), indexSettings.getSettings(), logger), + IndexSearcher.getDefaultQueryCache(), IndexSearcher.getDefaultQueryCachingPolicy(), translogConfig, + TimeValue.timeValueMinutes(5), refreshListener, indexSort); return config; } @@ -465,10 +467,10 @@ public void testSegments() throws Exception { assertThat(engine.segmentsStats(false).getMemoryInBytes(), equalTo(0L)); // create two docs and refresh - ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), B_1, null); Engine.Index first = indexForDoc(doc); Engine.IndexResult firstResult = engine.index(first); - ParsedDocument doc2 = testParsedDocument("2", "test", null, testDocumentWithTextField(), B_2, null); + ParsedDocument doc2 = testParsedDocument("2", null, testDocumentWithTextField(), B_2, null); Engine.Index second = indexForDoc(doc2); Engine.IndexResult secondResult = engine.index(second); assertThat(secondResult.getTranslogLocation(), greaterThan(firstResult.getTranslogLocation())); @@ -501,7 +503,7 @@ public void testSegments() throws Exception { assertThat(segments.get(0).getDeletedDocs(), equalTo(0)); assertThat(segments.get(0).isCompound(), equalTo(true)); - ParsedDocument doc3 = testParsedDocument("3", "test", null, testDocumentWithTextField(), B_3, null); + ParsedDocument doc3 = testParsedDocument("3", null, testDocumentWithTextField(), B_3, null); engine.index(indexForDoc(doc3)); engine.refresh("test"); @@ -548,7 +550,7 @@ public void testSegments() throws Exception { assertThat(segments.get(1).isCompound(), equalTo(true)); engine.onSettingsChanged(); - ParsedDocument doc4 = testParsedDocument("4", "test", null, testDocumentWithTextField(), B_3, null); + ParsedDocument doc4 = testParsedDocument("4", null, testDocumentWithTextField(), B_3, null); engine.index(indexForDoc(doc4)); engine.refresh("test"); @@ -582,7 +584,7 @@ public void testVerboseSegments() throws Exception { List segments = engine.segments(true); assertThat(segments.isEmpty(), equalTo(true)); - ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), B_1, null); engine.index(indexForDoc(doc)); engine.refresh("test"); @@ -590,10 +592,10 @@ public void testVerboseSegments() throws Exception { assertThat(segments.size(), equalTo(1)); assertThat(segments.get(0).ramTree, notNullValue()); - ParsedDocument doc2 = testParsedDocument("2", "test", null, testDocumentWithTextField(), B_2, null); + ParsedDocument doc2 = testParsedDocument("2", null, testDocumentWithTextField(), B_2, null); engine.index(indexForDoc(doc2)); engine.refresh("test"); - ParsedDocument doc3 = testParsedDocument("3", "test", null, testDocumentWithTextField(), B_3, null); + ParsedDocument doc3 = testParsedDocument("3", null, testDocumentWithTextField(), B_3, null); engine.index(indexForDoc(doc3)); engine.refresh("test"); @@ -608,12 +610,12 @@ public void testVerboseSegments() throws Exception { public void testSegmentsWithMergeFlag() throws Exception { try (Store store = createStore(); Engine engine = createEngine(defaultSettings, store, createTempDir(), new TieredMergePolicy())) { - ParsedDocument doc = testParsedDocument("1", "test", null, testDocument(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocument(), B_1, null); Engine.Index index = indexForDoc(doc); engine.index(index); engine.flush(); assertThat(engine.segments(false).size(), equalTo(1)); - index = indexForDoc(testParsedDocument("2", "test", null, testDocument(), B_1, null)); + index = indexForDoc(testParsedDocument("2", null, testDocument(), B_1, null)); engine.index(index); engine.flush(); List segments = engine.segments(false); @@ -621,7 +623,7 @@ public void testSegmentsWithMergeFlag() throws Exception { for (Segment segment : segments) { assertThat(segment.getMergeId(), nullValue()); } - index = indexForDoc(testParsedDocument("3", "test", null, testDocument(), B_1, null)); + index = indexForDoc(testParsedDocument("3", null, testDocument(), B_1, null)); engine.index(index); engine.flush(); segments = engine.segments(false); @@ -665,7 +667,7 @@ public void testSegmentsWithIndexSort() throws Exception { List segments = engine.segments(true); assertThat(segments.isEmpty(), equalTo(true)); - ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), B_1, null); engine.index(indexForDoc(doc)); engine.refresh("test"); @@ -673,10 +675,10 @@ public void testSegmentsWithIndexSort() throws Exception { assertThat(segments.size(), equalTo(1)); assertThat(segments.get(0).getSegmentSort(), equalTo(indexSort)); - ParsedDocument doc2 = testParsedDocument("2", "test", null, testDocumentWithTextField(), B_2, null); + ParsedDocument doc2 = testParsedDocument("2", null, testDocumentWithTextField(), B_2, null); engine.index(indexForDoc(doc2)); engine.refresh("test"); - ParsedDocument doc3 = testParsedDocument("3", "test", null, testDocumentWithTextField(), B_3, null); + ParsedDocument doc3 = testParsedDocument("3", null, testDocumentWithTextField(), B_3, null); engine.index(indexForDoc(doc3)); engine.refresh("test"); @@ -693,7 +695,7 @@ public void testSegmentsStatsIncludingFileSizes() throws Exception { Engine engine = createEngine(defaultSettings, store, createTempDir(), NoMergePolicy.INSTANCE)) { assertThat(engine.segmentsStats(true).getFileSizes().size(), equalTo(0)); - ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), B_1, null); engine.index(indexForDoc(doc)); engine.refresh("test"); @@ -703,7 +705,7 @@ public void testSegmentsStatsIncludingFileSizes() throws Exception { ObjectObjectCursor firstEntry = stats.getFileSizes().iterator().next(); - ParsedDocument doc2 = testParsedDocument("2", "test", null, testDocumentWithTextField(), B_2, null); + ParsedDocument doc2 = testParsedDocument("2", null, testDocumentWithTextField(), B_2, null); engine.index(indexForDoc(doc2)); engine.refresh("test"); @@ -804,7 +806,7 @@ public IndexSearcher wrap(IndexSearcher searcher) throws EngineException { public void testFlushIsDisabledDuringTranslogRecovery() throws IOException { assertFalse(engine.isRecovering()); - ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), SOURCE, null); + ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), SOURCE, null); engine.index(indexForDoc(doc)); engine.close(); @@ -813,7 +815,7 @@ public void testFlushIsDisabledDuringTranslogRecovery() throws IOException { assertTrue(engine.isRecovering()); engine.recoverFromTranslog(); assertFalse(engine.isRecovering()); - doc = testParsedDocument("2", "test", null, testDocumentWithTextField(), SOURCE, null); + doc = testParsedDocument("2", null, testDocumentWithTextField(), SOURCE, null); engine.index(indexForDoc(doc)); engine.flush(); } @@ -825,7 +827,7 @@ public void testTranslogMultipleOperationsSameDocument() throws IOException { try { initialEngine = engine; for (int i = 0; i < ops; i++) { - final ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), SOURCE, null); + final ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), SOURCE, null); if (randomBoolean()) { final Engine.Index operation = new Engine.Index(newUid(doc), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, i, VersionType.EXTERNAL, Engine.Operation.Origin.PRIMARY, System.nanoTime(), -1, false); operations.add(operation); @@ -861,7 +863,7 @@ public void testTranslogRecoveryDoesNotReplayIntoTranslog() throws IOException { initialEngine = engine; for (int i = 0; i < docs; i++) { final String id = Integer.toString(i); - final ParsedDocument doc = testParsedDocument(id, "test", null, testDocumentWithTextField(), SOURCE, null); + final ParsedDocument doc = testParsedDocument(id, null, testDocumentWithTextField(), SOURCE, null); initialEngine.index(indexForDoc(doc)); } } finally { @@ -915,7 +917,7 @@ public long generateSeqNo() { }; for (int i = 0; i < docs; i++) { final String id = Integer.toString(i); - final ParsedDocument doc = testParsedDocument(id, "test", null, testDocumentWithTextField(), SOURCE, null); + final ParsedDocument doc = testParsedDocument(id, null, testDocumentWithTextField(), SOURCE, null); initialEngine.index(indexForDoc(doc)); if (rarely()) { initialEngine.getTranslog().rollGeneration(); @@ -942,11 +944,11 @@ public long generateSeqNo() { } public void testConcurrentGetAndFlush() throws Exception { - ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), B_1, null); engine.index(indexForDoc(doc)); final AtomicReference latestGetResult = new AtomicReference<>(); - latestGetResult.set(engine.get(new Engine.Get(true, newUid(doc)))); + latestGetResult.set(engine.get(newGet(true, doc))); final AtomicBoolean flushFinished = new AtomicBoolean(false); final CyclicBarrier barrier = new CyclicBarrier(2); Thread getThread = new Thread(() -> { @@ -960,7 +962,7 @@ public void testConcurrentGetAndFlush() throws Exception { if (previousGetResult != null) { previousGetResult.release(); } - latestGetResult.set(engine.get(new Engine.Get(true, newUid(doc)))); + latestGetResult.set(engine.get(newGet(true, doc))); if (latestGetResult.get().exists() == false) { break; } @@ -983,7 +985,7 @@ public void testSimpleOperations() throws Exception { // create a document Document document = testDocumentWithTextField(); document.add(new Field(SourceFieldMapper.NAME, BytesReference.toBytes(B_1), SourceFieldMapper.Defaults.FIELD_TYPE)); - ParsedDocument doc = testParsedDocument("1", "test", null, document, B_1, null); + ParsedDocument doc = testParsedDocument("1", null, document, B_1, null); engine.index(indexForDoc(doc)); // its not there... @@ -993,12 +995,12 @@ public void testSimpleOperations() throws Exception { searchResult.close(); // but, not there non realtime - Engine.GetResult getResult = engine.get(new Engine.Get(false, newUid(doc))); + Engine.GetResult getResult = engine.get(newGet(false, doc)); assertThat(getResult.exists(), equalTo(false)); getResult.release(); // but, we can still get it (in realtime) - getResult = engine.get(new Engine.Get(true, newUid(doc))); + getResult = engine.get(newGet(true, doc)); assertThat(getResult.exists(), equalTo(true)); assertThat(getResult.docIdAndVersion(), notNullValue()); getResult.release(); @@ -1013,7 +1015,7 @@ public void testSimpleOperations() throws Exception { searchResult.close(); // also in non realtime - getResult = engine.get(new Engine.Get(false, newUid(doc))); + getResult = engine.get(newGet(false, doc)); assertThat(getResult.exists(), equalTo(true)); assertThat(getResult.docIdAndVersion(), notNullValue()); getResult.release(); @@ -1022,7 +1024,7 @@ public void testSimpleOperations() throws Exception { document = testDocument(); document.add(new TextField("value", "test1", Field.Store.YES)); document.add(new Field(SourceFieldMapper.NAME, BytesReference.toBytes(B_2), SourceFieldMapper.Defaults.FIELD_TYPE)); - doc = testParsedDocument("1", "test", null, document, B_2, null); + doc = testParsedDocument("1", null, document, B_2, null); engine.index(indexForDoc(doc)); // its not updated yet... @@ -1033,7 +1035,7 @@ public void testSimpleOperations() throws Exception { searchResult.close(); // but, we can still get it (in realtime) - getResult = engine.get(new Engine.Get(true, newUid(doc))); + getResult = engine.get(newGet(true, doc)); assertThat(getResult.exists(), equalTo(true)); assertThat(getResult.docIdAndVersion(), notNullValue()); getResult.release(); @@ -1058,7 +1060,7 @@ public void testSimpleOperations() throws Exception { searchResult.close(); // but, get should not see it (in realtime) - getResult = engine.get(new Engine.Get(true, newUid(doc))); + getResult = engine.get(newGet(true, doc)); assertThat(getResult.exists(), equalTo(false)); getResult.release(); @@ -1074,7 +1076,7 @@ public void testSimpleOperations() throws Exception { // add it back document = testDocumentWithTextField(); document.add(new Field(SourceFieldMapper.NAME, BytesReference.toBytes(B_1), SourceFieldMapper.Defaults.FIELD_TYPE)); - doc = testParsedDocument("1", "test", null, document, B_1, null); + doc = testParsedDocument("1", null, document, B_1, null); engine.index(new Engine.Index(newUid(doc), doc, Versions.MATCH_DELETED)); // its not there... @@ -1098,7 +1100,7 @@ public void testSimpleOperations() throws Exception { engine.flush(); // and, verify get (in real time) - getResult = engine.get(new Engine.Get(true, newUid(doc))); + getResult = engine.get(newGet(true, doc)); assertThat(getResult.exists(), equalTo(true)); assertThat(getResult.docIdAndVersion(), notNullValue()); getResult.release(); @@ -1107,7 +1109,7 @@ public void testSimpleOperations() throws Exception { // now do an update document = testDocument(); document.add(new TextField("value", "test1", Field.Store.YES)); - doc = testParsedDocument("1", "test", null, document, B_1, null); + doc = testParsedDocument("1", null, document, B_1, null); engine.index(indexForDoc(doc)); // its not updated yet... @@ -1133,7 +1135,7 @@ public void testSearchResultRelease() throws Exception { searchResult.close(); // create a document - ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), B_1, null); engine.index(indexForDoc(doc)); // its not there... @@ -1168,7 +1170,7 @@ public void testSyncedFlush() throws IOException { try (Store store = createStore(); Engine engine = new InternalEngine(config(defaultSettings, store, createTempDir(), new LogByteSizeMergePolicy(), null))) { final String syncId = randomUnicodeOfCodepointLengthBetween(10, 20); - ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), B_1, null); engine.index(indexForDoc(doc)); Engine.CommitId commitID = engine.flush(); assertThat(commitID, equalTo(new Engine.CommitId(store.readLastCommittedSegmentsInfo().getId()))); @@ -1195,16 +1197,16 @@ public void testRenewSyncFlush() throws Exception { InternalEngine engine = new InternalEngine(config(defaultSettings, store, createTempDir(), new LogDocMergePolicy(), null))) { final String syncId = randomUnicodeOfCodepointLengthBetween(10, 20); - Engine.Index doc1 = indexForDoc(testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null)); + Engine.Index doc1 = indexForDoc(testParsedDocument("1", null, testDocumentWithTextField(), B_1, null)); engine.index(doc1); assertEquals(engine.getLastWriteNanos(), doc1.startTime()); engine.flush(); - Engine.Index doc2 = indexForDoc(testParsedDocument("2", "test", null, testDocumentWithTextField(), B_1, null)); + Engine.Index doc2 = indexForDoc(testParsedDocument("2", null, testDocumentWithTextField(), B_1, null)); engine.index(doc2); assertEquals(engine.getLastWriteNanos(), doc2.startTime()); engine.flush(); final boolean forceMergeFlushes = randomBoolean(); - final ParsedDocument parsedDoc3 = testParsedDocument("3", "test", null, testDocumentWithTextField(), B_1, null); + final ParsedDocument parsedDoc3 = testParsedDocument("3", null, testDocumentWithTextField(), B_1, null); if (forceMergeFlushes) { engine.index(new Engine.Index(newUid(parsedDoc3), parsedDoc3, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, Versions.MATCH_ANY, VersionType.INTERNAL, Engine.Operation.Origin.PRIMARY, System.nanoTime() - engine.engineConfig.getFlushMergesAfter().nanos(), -1, false)); } else { @@ -1230,7 +1232,7 @@ public void testRenewSyncFlush() throws Exception { assertEquals(engine.getLastCommittedSegmentInfos().getUserData().get(Engine.SYNC_COMMIT_ID), syncId); if (randomBoolean()) { - Engine.Index doc4 = indexForDoc(testParsedDocument("4", "test", null, testDocumentWithTextField(), B_1, null)); + Engine.Index doc4 = indexForDoc(testParsedDocument("4", null, testDocumentWithTextField(), B_1, null)); engine.index(doc4); assertEquals(engine.getLastWriteNanos(), doc4.startTime()); } else { @@ -1248,7 +1250,7 @@ public void testRenewSyncFlush() throws Exception { public void testSyncedFlushSurvivesEngineRestart() throws IOException { final String syncId = randomUnicodeOfCodepointLengthBetween(10, 20); - ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), B_1, null); engine.index(indexForDoc(doc)); final Engine.CommitId commitID = engine.flush(); assertEquals("should succeed to flush commit with right id and no pending doc", engine.syncFlush(syncId, commitID), @@ -1271,14 +1273,14 @@ public void testSyncedFlushSurvivesEngineRestart() throws IOException { public void testSyncedFlushVanishesOnReplay() throws IOException { final String syncId = randomUnicodeOfCodepointLengthBetween(10, 20); - ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), B_1, null); engine.index(indexForDoc(doc)); final Engine.CommitId commitID = engine.flush(); assertEquals("should succeed to flush commit with right id and no pending doc", engine.syncFlush(syncId, commitID), Engine.SyncedFlushResult.SUCCESS); assertEquals(store.readLastCommittedSegmentsInfo().getUserData().get(Engine.SYNC_COMMIT_ID), syncId); assertEquals(engine.getLastCommittedSegmentInfos().getUserData().get(Engine.SYNC_COMMIT_ID), syncId); - doc = testParsedDocument("2", "test", null, testDocumentWithTextField(), new BytesArray("{}"), null); + doc = testParsedDocument("2", null, testDocumentWithTextField(), new BytesArray("{}"), null); engine.index(indexForDoc(doc)); EngineConfig config = engine.config(); engine.close(); @@ -1288,7 +1290,7 @@ public void testSyncedFlushVanishesOnReplay() throws IOException { } public void testVersioningNewCreate() throws IOException { - ParsedDocument doc = testParsedDocument("1", "test", null, testDocument(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocument(), B_1, null); Engine.Index create = new Engine.Index(newUid(doc), doc, Versions.MATCH_DELETED); Engine.IndexResult indexResult = engine.index(create); assertThat(indexResult.getVersion(), equalTo(1L)); @@ -1299,7 +1301,7 @@ public void testVersioningNewCreate() throws IOException { } public void testVersioningNewIndex() throws IOException { - ParsedDocument doc = testParsedDocument("1", "test", null, testDocument(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocument(), B_1, null); Engine.Index index = indexForDoc(doc); Engine.IndexResult indexResult = engine.index(index); assertThat(indexResult.getVersion(), equalTo(1L)); @@ -1315,7 +1317,7 @@ public void testForceMerge() throws IOException { new LogByteSizeMergePolicy(), null))) { // use log MP here we test some behavior in ESMP int numDocs = randomIntBetween(10, 100); for (int i = 0; i < numDocs; i++) { - ParsedDocument doc = testParsedDocument(Integer.toString(i), "test", null, testDocument(), B_1, null); + ParsedDocument doc = testParsedDocument(Integer.toString(i), null, testDocument(), B_1, null); Engine.Index index = indexForDoc(doc); engine.index(index); engine.refresh("test"); @@ -1326,7 +1328,7 @@ public void testForceMerge() throws IOException { engine.forceMerge(true, 1, false, false, false); assertEquals(engine.segments(true).size(), 1); - ParsedDocument doc = testParsedDocument(Integer.toString(0), "test", null, testDocument(), B_1, null); + ParsedDocument doc = testParsedDocument(Integer.toString(0), null, testDocument(), B_1, null); Engine.Index index = indexForDoc(doc); engine.delete(new Engine.Delete(index.type(), index.id(), index.uid())); engine.forceMerge(true, 10, true, false, false); //expunge deletes @@ -1337,7 +1339,7 @@ public void testForceMerge() throws IOException { assertEquals(engine.config().getMergePolicy().toString(), numDocs - 1, test.reader().maxDoc()); } - doc = testParsedDocument(Integer.toString(1), "test", null, testDocument(), B_1, null); + doc = testParsedDocument(Integer.toString(1), null, testDocument(), B_1, null); index = indexForDoc(doc); engine.delete(new Engine.Delete(index.type(), index.id(), index.uid())); engine.forceMerge(true, 10, false, false, false); //expunge deletes @@ -1372,7 +1374,7 @@ public void run() { int numDocs = randomIntBetween(1, 20); for (int j = 0; j < numDocs; j++) { i++; - ParsedDocument doc = testParsedDocument(Integer.toString(i), "test", null, testDocument(), B_1, null); + ParsedDocument doc = testParsedDocument(Integer.toString(i), null, testDocument(), B_1, null); Engine.Index index = indexForDoc(doc); engine.index(index); } @@ -1407,7 +1409,7 @@ public void run() { } public void testVersioningCreateExistsException() throws IOException { - ParsedDocument doc = testParsedDocument("1", "test", null, testDocument(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocument(), B_1, null); Engine.Index create = new Engine.Index(newUid(doc), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, Versions.MATCH_DELETED, VersionType.INTERNAL, PRIMARY, 0, -1, false); Engine.IndexResult indexResult = engine.index(create); assertThat(indexResult.getVersion(), equalTo(1L)); @@ -1423,7 +1425,7 @@ protected List generateSingleDocHistory(boolean forReplica, Ve int minOpCount, int maxOpCount) { final int numOfOps = randomIntBetween(minOpCount, maxOpCount); final List ops = new ArrayList<>(); - final Term id = newUid(Uid.createUid("test", "1")); + final Term id = newUid("1"); final int startWithSeqNo; if (partialOldPrimary) { startWithSeqNo = randomBoolean() ? numOfOps - 1 : randomIntBetween(0, numOfOps - 1); @@ -1452,7 +1454,7 @@ protected List generateSingleDocHistory(boolean forReplica, Ve throw new UnsupportedOperationException("unknown version type: " + versionType); } if (randomBoolean()) { - op = new Engine.Index(id, testParsedDocument("1", "test", null, testDocumentWithTextField(valuePrefix + i), B_1, null), + op = new Engine.Index(id, testParsedDocument("1", null, testDocumentWithTextField(valuePrefix + i), B_1, null), forReplica && i >= startWithSeqNo ? i * 2 : SequenceNumbersService.UNASSIGNED_SEQ_NO, forReplica && i >= startWithSeqNo && incrementTermWhenIntroducingSeqNo ? primaryTerm + 1 : primaryTerm, version, @@ -1485,6 +1487,7 @@ public void testOutOfOrderDocsOnReplicaOldPrimary() throws IOException { .put(IndexSettings.INDEX_GC_DELETES_SETTING.getKey(), "1h") // make sure this doesn't kick in on us .put(EngineConfig.INDEX_CODEC_SETTING.getKey(), codecName) .put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_5_4_0_UNRELEASED) + .put(MapperService.INDEX_MAPPING_SINGLE_TYPE_SETTING.getKey(), true) .put(IndexSettings.MAX_REFRESH_LISTENERS_PER_SHARD.getKey(), between(10, 10 * IndexSettings.MAX_REFRESH_LISTENERS_PER_SHARD.get(Settings.EMPTY))) .build()); @@ -1813,7 +1816,8 @@ public void testVersioningPromotedReplica() throws IOException { final long finalReplicaSeqNo = lastReplicaOp.seqNo(); assertOpsOnReplica(replicaOps, replicaEngine, true); final int opsOnPrimary = assertOpsOnPrimary(primaryOps, finalReplicaVersion, deletedOnReplica, replicaEngine); - final long currentSeqNo = getSequenceID(replicaEngine, new Engine.Get(false, lastReplicaOp.uid())).v1(); + final long currentSeqNo = getSequenceID(replicaEngine, + new Engine.Get(false, "type", lastReplicaOp.uid().text(), lastReplicaOp.uid())).v1(); try (Searcher searcher = engine.acquireSearcher("test")) { final TotalHitCountCollector collector = new TotalHitCountCollector(); searcher.searcher().search(new MatchAllDocsQuery(), collector); @@ -1865,7 +1869,7 @@ class OpAndVersion { } final AtomicInteger idGenerator = new AtomicInteger(); final Queue history = ConcurrentCollections.newQueue(); - ParsedDocument doc = testParsedDocument("1", "test", null, testDocument(), bytesArray(""), null); + ParsedDocument doc = testParsedDocument("1", null, testDocument(), bytesArray(""), null); final Term uidTerm = newUid(doc); engine.index(indexForDoc(doc)); for (int i = 0; i < thread.length; i++) { @@ -1877,7 +1881,7 @@ class OpAndVersion { throw new AssertionError(e); } for (int op = 0; op < opsPerThread; op++) { - try (Engine.GetResult get = engine.get(new Engine.Get(true, uidTerm))) { + try (Engine.GetResult get = engine.get(new Engine.Get(true, doc.type(), doc.id(), uidTerm))) { FieldsVisitor visitor = new FieldsVisitor(true); get.docIdAndVersion().context.reader().document(get.docIdAndVersion().docId, visitor); List values = new ArrayList<>(Strings.commaDelimitedListToSet(visitor.source().utf8ToString())); @@ -1885,7 +1889,7 @@ class OpAndVersion { String added = "v_" + idGenerator.incrementAndGet(); values.add(added); Engine.Index index = new Engine.Index(uidTerm, - testParsedDocument("1", "test", null, testDocument(), + testParsedDocument("1", null, testDocument(), bytesArray(Strings.collectionToCommaDelimitedString(values)), null), SequenceNumbersService.UNASSIGNED_SEQ_NO, 2, get.version(), VersionType.INTERNAL, @@ -1919,7 +1923,7 @@ class OpAndVersion { assertTrue(op.added + " should not exist", exists); } - try (Engine.GetResult get = engine.get(new Engine.Get(true, uidTerm))) { + try (Engine.GetResult get = engine.get(new Engine.Get(true, doc.type(), doc.id(), uidTerm))) { FieldsVisitor visitor = new FieldsVisitor(true); get.docIdAndVersion().context.reader().document(get.docIdAndVersion().docId, visitor); List values = Arrays.asList(Strings.commaDelimitedListToStringArray(visitor.source().utf8ToString())); @@ -1928,7 +1932,7 @@ class OpAndVersion { } public void testBasicCreatedFlag() throws IOException { - ParsedDocument doc = testParsedDocument("1", "test", null, testDocument(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocument(), B_1, null); Engine.Index index = indexForDoc(doc); Engine.IndexResult indexResult = engine.index(index); assertTrue(indexResult.isCreated()); @@ -1984,7 +1988,7 @@ public void testIndexWriterInfoStream() throws IllegalAccessException, IOExcepti try { // First, with DEBUG, which should NOT log IndexWriter output: - ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), B_1, null); engine.index(indexForDoc(doc)); engine.flush(); assertFalse(mockAppender.sawIndexWriterMessage); @@ -2025,7 +2029,7 @@ public void testSeqNoAndCheckpoints() throws IOException { // we have some docs indexed, so delete one of them id = randomFrom(indexedIds); final Engine.Delete delete = new Engine.Delete( - "test", id, newUid("test#" + id), SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, + "test", id, newUid(id), SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, rarely() ? 100 : Versions.MATCH_ANY, VersionType.INTERNAL, PRIMARY, 0); final Engine.DeleteResult result = initialEngine.delete(delete); if (!result.hasFailure()) { @@ -2040,7 +2044,7 @@ public void testSeqNoAndCheckpoints() throws IOException { } else { // index a document id = randomFrom(ids); - ParsedDocument doc = testParsedDocument(id, "test", null, testDocumentWithTextField(), SOURCE, null); + ParsedDocument doc = testParsedDocument(id, null, testDocumentWithTextField(), SOURCE, null); final Engine.Index index = new Engine.Index(newUid(doc), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, rarely() ? 100 : Versions.MATCH_ANY, VersionType.INTERNAL, @@ -2142,7 +2146,7 @@ public void testConcurrentWritesAndCommits() throws Exception { // index random number of docs for (int i = 0; i < numDocsPerThread; i++) { final String id = "thread" + threadIdx + "#" + i; - ParsedDocument doc = testParsedDocument(id, "test", null, testDocument(), B_1, null); + ParsedDocument doc = testParsedDocument(id, null, testDocument(), B_1, null); engine.index(indexForDoc(doc)); } } catch (Exception e) { @@ -2246,7 +2250,7 @@ public void testIndexWriterIFDInfoStream() throws IllegalAccessException, IOExce try { // First, with DEBUG, which should NOT log IndexWriter output: - ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null); + ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), B_1, null); engine.index(indexForDoc(doc)); engine.flush(); assertFalse(mockAppender.sawIndexWriterMessage); @@ -2275,14 +2279,14 @@ public void testEnableGcDeletes() throws Exception { Document document = testDocument(); document.add(new TextField("value", "test1", Field.Store.YES)); - ParsedDocument doc = testParsedDocument("1", "test", null, document, B_2, null); + ParsedDocument doc = testParsedDocument("1", null, document, B_2, null); engine.index(new Engine.Index(newUid(doc), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, 1, VersionType.EXTERNAL, Engine.Operation.Origin.PRIMARY, System.nanoTime(), -1, false)); // Delete document we just added: engine.delete(new Engine.Delete("test", "1", newUid(doc), SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, 10, VersionType.EXTERNAL, Engine.Operation.Origin.PRIMARY, System.nanoTime())); // Get should not find the document - Engine.GetResult getResult = engine.get(new Engine.Get(true, newUid(doc))); + Engine.GetResult getResult = engine.get(newGet(true, doc)); assertThat(getResult.exists(), equalTo(false)); // Give the gc pruning logic a chance to kick in @@ -2296,7 +2300,7 @@ public void testEnableGcDeletes() throws Exception { engine.delete(new Engine.Delete("test", "2", newUid("2"), SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, 10, VersionType.EXTERNAL, Engine.Operation.Origin.PRIMARY, System.nanoTime())); // Get should not find the document (we never indexed uid=2): - getResult = engine.get(new Engine.Get(true, newUid("2"))); + getResult = engine.get(new Engine.Get(true, "type", "2", newUid("2"))); assertThat(getResult.exists(), equalTo(false)); // Try to index uid=1 with a too-old version, should fail: @@ -2306,7 +2310,7 @@ public void testEnableGcDeletes() throws Exception { assertThat(indexResult.getFailure(), instanceOf(VersionConflictEngineException.class)); // Get should still not find the document - getResult = engine.get(new Engine.Get(true, newUid(doc))); + getResult = engine.get(newGet(true, doc)); assertThat(getResult.exists(), equalTo(false)); // Try to index uid=2 with a too-old version, should fail: @@ -2316,17 +2320,21 @@ public void testEnableGcDeletes() throws Exception { assertThat(indexResult.getFailure(), instanceOf(VersionConflictEngineException.class)); // Get should not find the document - getResult = engine.get(new Engine.Get(true, newUid(doc))); + getResult = engine.get(newGet(true, doc)); assertThat(getResult.exists(), equalTo(false)); } } protected Term newUid(String id) { - return new Term("_uid", id); + return new Term("_id", id); } protected Term newUid(ParsedDocument doc) { - return new Term("_uid", doc.uid()); + return new Term("_id", doc.id()); + } + + protected Engine.Get newGet(boolean realtime, ParsedDocument doc) { + return new Engine.Get(realtime, doc.type(), doc.id(), newUid(doc)); } private Engine.Index indexForDoc(ParsedDocument doc) { @@ -2423,7 +2431,7 @@ public void testMissingTranslog() throws IOException { public void testTranslogReplayWithFailure() throws IOException { final int numDocs = randomIntBetween(1, 10); for (int i = 0; i < numDocs; i++) { - ParsedDocument doc = testParsedDocument(Integer.toString(i), "test", null, testDocument(), new BytesArray("{}"), null); + ParsedDocument doc = testParsedDocument(Integer.toString(i), null, testDocument(), new BytesArray("{}"), null); Engine.Index firstIndexRequest = new Engine.Index(newUid(doc), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, Versions.MATCH_DELETED, VersionType.INTERNAL, PRIMARY, System.nanoTime(), -1, false); Engine.IndexResult indexResult = engine.index(firstIndexRequest); assertThat(indexResult.getVersion(), equalTo(1L)); @@ -2481,7 +2489,7 @@ private static void assertVisibleCount(InternalEngine engine, int numDocs, boole public void testSkipTranslogReplay() throws IOException { final int numDocs = randomIntBetween(1, 10); for (int i = 0; i < numDocs; i++) { - ParsedDocument doc = testParsedDocument(Integer.toString(i), "test", null, testDocument(), new BytesArray("{}"), null); + ParsedDocument doc = testParsedDocument(Integer.toString(i), null, testDocument(), new BytesArray("{}"), null); Engine.Index firstIndexRequest = new Engine.Index(newUid(doc), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, Versions.MATCH_DELETED, VersionType.INTERNAL, PRIMARY, System.nanoTime(), -1, false); Engine.IndexResult indexResult = engine.index(firstIndexRequest); assertThat(indexResult.getVersion(), equalTo(1L)); @@ -2570,8 +2578,14 @@ public void testUpgradeOldIndex() throws IOException { } final int numExtraDocs = randomIntBetween(1, 10); for (int i = 0; i < numExtraDocs; i++) { - ParsedDocument doc = testParsedDocument("extra" + Integer.toString(i), "test", null, testDocument(), new BytesArray("{}"), null); - Engine.Index firstIndexRequest = new Engine.Index(newUid(doc), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, Versions.MATCH_DELETED, VersionType.INTERNAL, PRIMARY, System.nanoTime(), -1, false); + ParsedDocument doc = testParsedDocument("extra" + Integer.toString(i), null, testDocument(), new BytesArray("{}"), null); + Term uid; + if (indexMetaData.getCreationVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { + uid = new Term(IdFieldMapper.NAME, doc.id()); + } else { + uid = new Term(UidFieldMapper.NAME, Uid.createUid(doc.type(), doc.id())); + } + Engine.Index firstIndexRequest = new Engine.Index(uid, doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, Versions.MATCH_DELETED, VersionType.INTERNAL, PRIMARY, System.nanoTime(), -1, false); Engine.IndexResult indexResult = engine.index(firstIndexRequest); assertThat(indexResult.getVersion(), equalTo(1L)); } @@ -2599,7 +2613,7 @@ private Path[] filterExtraFSFiles(Path[] files) { public void testTranslogReplay() throws IOException { final int numDocs = randomIntBetween(1, 10); for (int i = 0; i < numDocs; i++) { - ParsedDocument doc = testParsedDocument(Integer.toString(i), "test", null, testDocument(), new BytesArray("{}"), null); + ParsedDocument doc = testParsedDocument(Integer.toString(i), null, testDocument(), new BytesArray("{}"), null); Engine.Index firstIndexRequest = new Engine.Index(newUid(doc), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, Versions.MATCH_DELETED, VersionType.INTERNAL, PRIMARY, System.nanoTime(), -1, false); Engine.IndexResult indexResult = engine.index(firstIndexRequest); assertThat(indexResult.getVersion(), equalTo(1L)); @@ -2631,7 +2645,7 @@ public void testTranslogReplay() throws IOException { final boolean flush = randomBoolean(); int randomId = randomIntBetween(numDocs + 1, numDocs + 10); - ParsedDocument doc = testParsedDocument(Integer.toString(randomId), "test", null, testDocument(), new BytesArray("{}"), null); + ParsedDocument doc = testParsedDocument(Integer.toString(randomId), null, testDocument(), new BytesArray("{}"), null); Engine.Index firstIndexRequest = new Engine.Index(newUid(doc), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, 1, VersionType.EXTERNAL, PRIMARY, System.nanoTime(), -1, false); Engine.IndexResult indexResult = engine.index(firstIndexRequest); assertThat(indexResult.getVersion(), equalTo(1L)); @@ -2639,7 +2653,7 @@ public void testTranslogReplay() throws IOException { engine.flush(); } - doc = testParsedDocument(Integer.toString(randomId), "test", null, testDocument(), new BytesArray("{}"), null); + doc = testParsedDocument(Integer.toString(randomId), null, testDocument(), new BytesArray("{}"), null); Engine.Index idxRequest = new Engine.Index(newUid(doc), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, 2, VersionType.EXTERNAL, PRIMARY, System.nanoTime(), -1, false); Engine.IndexResult result = engine.index(idxRequest); engine.refresh("test"); @@ -2677,9 +2691,8 @@ public static class TranslogHandler extends TranslogRecoveryPerformer { public final AtomicInteger recoveredOps = new AtomicInteger(0); - public TranslogHandler(NamedXContentRegistry xContentRegistry, String indexName, Logger logger) { + public TranslogHandler(NamedXContentRegistry xContentRegistry, String indexName, Settings settings, Logger logger) { super(new ShardId("test", "_na_", 0), null, logger); - Settings settings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build(); Index index = new Index(indexName, "_na_"); IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(index, settings); NamedAnalyzer defaultAnalyzer = new NamedAnalyzer("default", AnalyzerScope.INDEX, new StandardAnalyzer()); @@ -2706,7 +2719,7 @@ protected void operationProcessed() { public void testRecoverFromForeignTranslog() throws IOException { final int numDocs = randomIntBetween(1, 10); for (int i = 0; i < numDocs; i++) { - ParsedDocument doc = testParsedDocument(Integer.toString(i), "test", null, testDocument(), new BytesArray("{}"), null); + ParsedDocument doc = testParsedDocument(Integer.toString(i), null, testDocument(), new BytesArray("{}"), null); Engine.Index firstIndexRequest = new Engine.Index(newUid(doc), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, Versions.MATCH_DELETED, VersionType.INTERNAL, PRIMARY, System.nanoTime(), -1, false); Engine.IndexResult index = engine.index(firstIndexRequest); assertThat(index.getVersion(), equalTo(1L)); @@ -2787,7 +2800,7 @@ public void testCurrentTranslogIDisCommitted() throws IOException { // create { - ParsedDocument doc = testParsedDocument(Integer.toString(0), "test", null, testDocument(), new BytesArray("{}"), null); + ParsedDocument doc = testParsedDocument(Integer.toString(0), null, testDocument(), new BytesArray("{}"), null); Engine.Index firstIndexRequest = new Engine.Index(newUid(doc), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, Versions.MATCH_DELETED, VersionType.INTERNAL, PRIMARY, System.nanoTime(), -1, false); try (InternalEngine engine = new InternalEngine(copy(config, EngineConfig.OpenMode.CREATE_INDEX_AND_TRANSLOG))){ @@ -2889,9 +2902,9 @@ public void clearFailure() { public void testHandleDocumentFailure() throws Exception { try (Store store = createStore()) { - final ParsedDocument doc1 = testParsedDocument("1", "test", null, testDocumentWithTextField(), B_1, null); - final ParsedDocument doc2 = testParsedDocument("2", "test", null, testDocumentWithTextField(), B_1, null); - final ParsedDocument doc3 = testParsedDocument("3", "test", null, testDocumentWithTextField(), B_1, null); + final ParsedDocument doc1 = testParsedDocument("1", null, testDocumentWithTextField(), B_1, null); + final ParsedDocument doc2 = testParsedDocument("2", null, testDocumentWithTextField(), B_1, null); + final ParsedDocument doc3 = testParsedDocument("3", null, testDocumentWithTextField(), B_1, null); AtomicReference throwingIndexWriter = new AtomicReference<>(); try (Engine engine = createEngine(defaultSettings, store, createTempDir(), NoMergePolicy.INSTANCE, @@ -2974,7 +2987,7 @@ public BytesRef binaryValue() { } public void testDoubleDeliveryPrimary() throws IOException { - final ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); + final ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); Engine.Index operation = appendOnlyPrimary(doc, false, 1); Engine.Index retry = appendOnlyPrimary(doc, true, 1); if (randomBoolean()) { @@ -3028,7 +3041,7 @@ public void testDoubleDeliveryPrimary() throws IOException { } public void testDoubleDeliveryReplicaAppendingOnly() throws IOException { - final ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), + final ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); Engine.Index operation = appendOnlyReplica(doc, false, 1, randomIntBetween(0, 20)); Engine.Index retry = appendOnlyReplica(doc, true, 1, operation.seqNo()); @@ -3083,7 +3096,7 @@ public void testDoubleDeliveryReplicaAppendingOnly() throws IOException { } public void testDoubleDeliveryReplica() throws IOException { - final ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), + final ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); Engine.Index operation = replicaIndexForDoc(doc, 1, 20, false); Engine.Index duplicate = replicaIndexForDoc(doc, 1, 20, true); @@ -3129,7 +3142,7 @@ public void testDoubleDeliveryReplica() throws IOException { public void testRetryWithAutogeneratedIdWorksAndNoDuplicateDocs() throws IOException { - final ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); + final ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); boolean isRetry = false; long autoGeneratedIdTimestamp = 0; @@ -3163,7 +3176,7 @@ public void testRetryWithAutogeneratedIdWorksAndNoDuplicateDocs() throws IOExcep public void testRetryWithAutogeneratedIdsAndWrongOrderWorksAndNoDuplicateDocs() throws IOException { - final ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); + final ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); boolean isRetry = true; long autoGeneratedIdTimestamp = 0; @@ -3218,7 +3231,7 @@ public void testRetryConcurrently() throws InterruptedException, IOException { List docs = new ArrayList<>(); final boolean primary = randomBoolean(); for (int i = 0; i < numDocs; i++) { - final ParsedDocument doc = testParsedDocument(Integer.toString(i), "test", null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); + final ParsedDocument doc = testParsedDocument(Integer.toString(i), null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); final Engine.Index originalIndex; final Engine.Index retryIndex; if (primary) { @@ -3288,7 +3301,7 @@ public void testEngineMaxTimestampIsInitialized() throws IOException { try (Store store = createStore(newFSDirectory(storeDir)); Engine engine = new InternalEngine(config(defaultSettings, store, translogDir, NoMergePolicy.INSTANCE, null))) { assertEquals(IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); - final ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), + final ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); engine.index(appendOnlyPrimary(doc, true, timestamp1)); assertEquals(timestamp1, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); @@ -3298,7 +3311,7 @@ public void testEngineMaxTimestampIsInitialized() throws IOException { assertEquals(IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); engine.recoverFromTranslog(); assertEquals(timestamp1, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); - final ParsedDocument doc = testParsedDocument("1", "test", null, testDocumentWithTextField(), + final ParsedDocument doc = testParsedDocument("1", null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); engine.index(appendOnlyPrimary(doc, true, timestamp2)); assertEquals(maxTimestamp12, engine.segmentsStats(false).getMaxUnsafeAutoIdTimestamp()); @@ -3319,7 +3332,7 @@ public void testAppendConcurrently() throws InterruptedException, IOException { assertEquals(0, engine.getNumIndexVersionsLookups()); List docs = new ArrayList<>(); for (int i = 0; i < numDocs; i++) { - final ParsedDocument doc = testParsedDocument(Integer.toString(i), "test", null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); + final ParsedDocument doc = testParsedDocument(Integer.toString(i), null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); Engine.Index index = randomAppendOnly(doc, false, i); docs.add(index); } @@ -3400,7 +3413,7 @@ public void afterRefresh(boolean didRefresh) throws IOException { }); InternalEngine internalEngine = new InternalEngine(config); int docId = 0; - final ParsedDocument doc = testParsedDocument(Integer.toString(docId), "test", null, + final ParsedDocument doc = testParsedDocument(Integer.toString(docId), null, testDocumentWithTextField(), new BytesArray("{}".getBytes(Charset.defaultCharset())), null); Engine.Index index = randomBoolean() ? indexForDoc(doc) : randomAppendOnly(doc, false, docId); @@ -3437,7 +3450,7 @@ public void afterRefresh(boolean didRefresh) throws IOException { } public void testSequenceIDs() throws Exception { - Tuple seqID = getSequenceID(engine, new Engine.Get(false, newUid("1"))); + Tuple seqID = getSequenceID(engine, new Engine.Get(false, "type", "2", newUid("1"))); // Non-existent doc returns no seqnum and no primary term assertThat(seqID.v1(), equalTo(SequenceNumbersService.UNASSIGNED_SEQ_NO)); assertThat(seqID.v2(), equalTo(0L)); @@ -3445,11 +3458,11 @@ public void testSequenceIDs() throws Exception { // create a document Document document = testDocumentWithTextField(); document.add(new Field(SourceFieldMapper.NAME, BytesReference.toBytes(B_1), SourceFieldMapper.Defaults.FIELD_TYPE)); - ParsedDocument doc = testParsedDocument("1", "test", null, document, B_1, null); + ParsedDocument doc = testParsedDocument("1", null, document, B_1, null); engine.index(indexForDoc(doc)); engine.refresh("test"); - seqID = getSequenceID(engine, new Engine.Get(false, newUid(doc))); + seqID = getSequenceID(engine, newGet(false, doc)); logger.info("--> got seqID: {}", seqID); assertThat(seqID.v1(), equalTo(0L)); assertThat(seqID.v2(), equalTo(2L)); @@ -3457,11 +3470,11 @@ public void testSequenceIDs() throws Exception { // Index the same document again document = testDocumentWithTextField(); document.add(new Field(SourceFieldMapper.NAME, BytesReference.toBytes(B_1), SourceFieldMapper.Defaults.FIELD_TYPE)); - doc = testParsedDocument("1", "test", null, document, B_1, null); + doc = testParsedDocument("1", null, document, B_1, null); engine.index(indexForDoc(doc)); engine.refresh("test"); - seqID = getSequenceID(engine, new Engine.Get(false, newUid(doc))); + seqID = getSequenceID(engine, newGet(false, doc)); logger.info("--> got seqID: {}", seqID); assertThat(seqID.v1(), equalTo(1L)); assertThat(seqID.v2(), equalTo(2L)); @@ -3469,13 +3482,13 @@ public void testSequenceIDs() throws Exception { // Index the same document for the third time, this time changing the primary term document = testDocumentWithTextField(); document.add(new Field(SourceFieldMapper.NAME, BytesReference.toBytes(B_1), SourceFieldMapper.Defaults.FIELD_TYPE)); - doc = testParsedDocument("1", "test", null, document, B_1, null); + doc = testParsedDocument("1", null, document, B_1, null); engine.index(new Engine.Index(newUid(doc), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 3, Versions.MATCH_ANY, VersionType.INTERNAL, Engine.Operation.Origin.PRIMARY, System.nanoTime(), -1, false)); engine.refresh("test"); - seqID = getSequenceID(engine, new Engine.Get(false, newUid(doc))); + seqID = getSequenceID(engine, newGet(false, doc)); logger.info("--> got seqID: {}", seqID); assertThat(seqID.v1(), equalTo(2L)); assertThat(seqID.v2(), equalTo(3L)); @@ -3546,7 +3559,7 @@ public void testSequenceNumberAdvancesToMaxSeqOnEngineOpenOnPrimary() throws Bro final InternalEngine finalInitialEngine = initialEngine; for (int i = 0; i < docs; i++) { final String id = Integer.toString(i); - final ParsedDocument doc = testParsedDocument(id, "test", null, testDocumentWithTextField(), SOURCE, null); + final ParsedDocument doc = testParsedDocument(id, null, testDocumentWithTextField(), SOURCE, null); stall.set(randomBoolean()); final Thread thread = new Thread(() -> { @@ -3595,7 +3608,7 @@ public void testSequenceNumberAdvancesToMaxSeqNoOnEngineOpenOnReplica() throws I initialEngine = engine; for (int i = 0; i < docs; i++) { final String id = Integer.toString(i); - final ParsedDocument doc = testParsedDocument(id, "test", null, testDocumentWithTextField(), SOURCE, null); + final ParsedDocument doc = testParsedDocument(id, null, testDocumentWithTextField(), SOURCE, null); final Term uid = newUid(doc); // create a gap at sequence number 3 * i + 1 initialEngine.index(new Engine.Index(uid, doc, 3 * i, 1, v, t, REPLICA, System.nanoTime(), ts, false)); @@ -3609,7 +3622,7 @@ public void testSequenceNumberAdvancesToMaxSeqNoOnEngineOpenOnReplica() throws I for (int i = 0; i < docs; i++) { final String id = Integer.toString(i); - final ParsedDocument doc = testParsedDocument(id, "test", null, testDocumentWithTextField(), SOURCE, null); + final ParsedDocument doc = testParsedDocument(id, null, testDocumentWithTextField(), SOURCE, null); final Term uid = newUid(doc); initialEngine.index(new Engine.Index(uid, doc, 3 * i + 1, 1, v, t, REPLICA, System.nanoTime(), ts, false)); } @@ -3636,7 +3649,7 @@ public void testOutOfOrderSequenceNumbersWithVersionConflict() throws IOExceptio final LongSupplier sequenceNumberSupplier = origin == PRIMARY ? () -> SequenceNumbersService.UNASSIGNED_SEQ_NO : sequenceNumber::getAndIncrement; document.add(new Field(SourceFieldMapper.NAME, BytesReference.toBytes(B_1), SourceFieldMapper.Defaults.FIELD_TYPE)); - final ParsedDocument doc = testParsedDocument("1", "test", null, document, B_1, null); + final ParsedDocument doc = testParsedDocument("1", null, document, B_1, null); final Term uid = newUid(doc); for (int i = 0; i < numberOfOperations; i++) { if (randomBoolean()) { @@ -3699,7 +3712,7 @@ public void testOutOfOrderSequenceNumbersWithVersionConflict() throws IOExceptio } assertThat(engine.seqNoService().getLocalCheckpoint(), equalTo(expectedLocalCheckpoint)); - try (Engine.GetResult result = engine.get(new Engine.Get(true, uid))) { + try (Engine.GetResult result = engine.get(new Engine.Get(true, "type", "2", uid))) { assertThat(result.exists(), equalTo(exists)); } } @@ -3827,7 +3840,7 @@ public void testMinGenerationForSeqNo() throws IOException, BrokenBarrierExcepti private void index(final InternalEngine engine, final int id) throws IOException { final String docId = Integer.toString(id); final ParsedDocument doc = - testParsedDocument(docId, "test", null, testDocumentWithTextField(), SOURCE, null); + testParsedDocument(docId, null, testDocumentWithTextField(), SOURCE, null); engine.index(indexForDoc(doc)); } @@ -3846,7 +3859,7 @@ private Tuple getSequenceID(Engine engine, Engine.Get get) throws En seqNo = SequenceNumbersService.UNASSIGNED_SEQ_NO; } else { seqNo = docIdAndSeqNo.seqNo; - primaryTerm = VersionsAndSeqNoResolver.loadPrimaryTerm(docIdAndSeqNo); + primaryTerm = VersionsAndSeqNoResolver.loadPrimaryTerm(docIdAndSeqNo, get.uid().field()); } return new Tuple<>(seqNo, primaryTerm); } catch (Exception e) { @@ -3863,7 +3876,7 @@ public void testFillUpSequenceIdGapsOnRecovery() throws IOException { for (int i = 0; i < docs; i++) { final String docId = Integer.toString(i); final ParsedDocument doc = - testParsedDocument(docId, "test", null, testDocumentWithTextField(), SOURCE, null); + testParsedDocument(docId, null, testDocumentWithTextField(), SOURCE, null); Engine.Index primaryResponse = indexForDoc(doc); Engine.IndexResult indexResult = engine.index(primaryResponse); if (randomBoolean()) { diff --git a/core/src/test/java/org/elasticsearch/index/fielddata/UidFieldDataTests.java b/core/src/test/java/org/elasticsearch/index/fielddata/UidFieldDataTests.java new file mode 100644 index 0000000000000..6fa5fa8775e51 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/index/fielddata/UidFieldDataTests.java @@ -0,0 +1,131 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.index.fielddata; + +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.index.SortedSetDocValues; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.Collections; +import java.util.Locale; + +public class UidFieldDataTests extends ESTestCase { + + private static class DummySortedDocValues extends SortedDocValues { + + private int doc = -1; + + @Override + public int ordValue() throws IOException { + return doc; + } + + @Override + public BytesRef lookupOrd(int ord) throws IOException { + return new BytesRef(String.format(Locale.ENGLISH, "%03d", ord)); + } + + @Override + public int getValueCount() { + return 100; + } + + @Override + public boolean advanceExact(int target) throws IOException { + doc = target; + return true; + } + + @Override + public int docID() { + return doc; + } + + @Override + public int nextDoc() throws IOException { + return advance(doc + 1); + } + + @Override + public int advance(int target) throws IOException { + if (target >= getValueCount()) { + return doc = NO_MORE_DOCS; + } else { + return doc = target; + } + } + + @Override + public long cost() { + return getValueCount(); + } + + } + + private static class DummyAtomicOrdinalsFieldData implements AtomicOrdinalsFieldData { + + @Override + public ScriptDocValues getScriptValues() { + throw new UnsupportedOperationException(); + } + + @Override + public SortedBinaryDocValues getBytesValues() { + return FieldData.toString(getOrdinalsValues()); + } + + @Override + public long ramBytesUsed() { + return 0; + } + + @Override + public void close() { + } + + @Override + public SortedSetDocValues getOrdinalsValues() { + return DocValues.singleton(new DummySortedDocValues()); + } + + } + + public void testSortedSetValues() throws Exception { + AtomicOrdinalsFieldData fd = new UidIndexFieldData.UidAtomicFieldData(new BytesRef("type#"), new DummyAtomicOrdinalsFieldData()); + SortedSetDocValues dv = fd.getOrdinalsValues(); + assertTrue(dv.advanceExact(30)); + assertEquals(30, dv.nextOrd()); + assertEquals(SortedSetDocValues.NO_MORE_ORDS, dv.nextOrd()); + assertEquals(new BytesRef("type#030"), dv.lookupOrd(30)); + assertEquals(30, dv.lookupTerm(new BytesRef("type#030"))); + assertEquals(-1 - 31, dv.lookupTerm(new BytesRef("type#0305"))); + } + + public void testScriptValues() throws IOException { + AtomicOrdinalsFieldData fd = new UidIndexFieldData.UidAtomicFieldData(new BytesRef("type#"), new DummyAtomicOrdinalsFieldData()); + ScriptDocValues values = fd.getScriptValues(); + values.setNextDocId(30); + assertEquals(Collections.singletonList("type#030"), values); + } + +} diff --git a/core/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java b/core/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java index cd4d1d21c6cd0..49864768edf8b 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java @@ -19,23 +19,11 @@ package org.elasticsearch.index.mapper; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.apache.lucene.index.IndexableField; -import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.compress.CompressedXContent; -import org.elasticsearch.common.lucene.all.AllField; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; @@ -45,6 +33,13 @@ import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.test.InternalSettingsPlugin; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.test.StreamsUtils.copyToBytesFromClasspath; import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; @@ -61,21 +56,6 @@ protected Collection> getPlugins() { return pluginList(InternalSettingsPlugin.class); } - public void testTypeDisabled() throws Exception { - DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser(); - String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") - .field("enabled", false).endObject().endObject().string(); - DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping)); - - BytesReference bytes = XContentFactory.jsonBuilder() - .startObject().startObject("foo") - .field("field", "1234") - .endObject().endObject().bytes(); - ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", bytes, XContentType.JSON)); - assertNull(doc.rootDoc().getField("field")); - assertNotNull(doc.rootDoc().getField(UidFieldMapper.NAME)); - } - public void testFieldDisabled() throws Exception { DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser(); String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties") @@ -92,7 +72,7 @@ public void testFieldDisabled() throws Exception { ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", bytes, XContentType.JSON)); assertNull(doc.rootDoc().getField("foo")); assertNotNull(doc.rootDoc().getField("bar")); - assertNotNull(doc.rootDoc().getField(UidFieldMapper.NAME)); + assertNotNull(doc.rootDoc().getField(IdFieldMapper.NAME)); } public void testDotsWithExistingMapper() throws Exception { @@ -973,7 +953,7 @@ public void testParseToJsonAndParse() throws Exception { DocumentMapper builtDocMapper = parser.parse("person", new CompressedXContent(builtMapping)); BytesReference json = new BytesArray(copyToBytesFromClasspath("/org/elasticsearch/index/mapper/simple/test1.json")); Document doc = builtDocMapper.parse(SourceToParse.source("test", "person", "1", json, XContentType.JSON)).rootDoc(); - assertThat(doc.get(docMapper.uidMapper().fieldType().name()), equalTo(Uid.createUid("person", "1"))); + assertThat(doc.get(docMapper.idFieldMapper().fieldType().name()), equalTo("1")); assertThat(doc.get(docMapper.mappers().getMapper("name.first").fieldType().name()), equalTo("shay")); } @@ -985,7 +965,7 @@ public void testSimpleParser() throws Exception { BytesReference json = new BytesArray(copyToBytesFromClasspath("/org/elasticsearch/index/mapper/simple/test1.json")); Document doc = docMapper.parse(SourceToParse.source("test", "person", "1", json, XContentType.JSON)).rootDoc(); - assertThat(doc.get(docMapper.uidMapper().fieldType().name()), equalTo(Uid.createUid("person", "1"))); + assertThat(doc.get(docMapper.idFieldMapper().fieldType().name()), equalTo("1")); assertThat(doc.get(docMapper.mappers().getMapper("name.first").fieldType().name()), equalTo("shay")); } @@ -994,7 +974,7 @@ public void testSimpleParserNoTypeNoId() throws Exception { DocumentMapper docMapper = createIndex("test").mapperService().documentMapperParser().parse("person", new CompressedXContent(mapping)); BytesReference json = new BytesArray(copyToBytesFromClasspath("/org/elasticsearch/index/mapper/simple/test1-notype-noid.json")); Document doc = docMapper.parse(SourceToParse.source("test", "person", "1", json, XContentType.JSON)).rootDoc(); - assertThat(doc.get(docMapper.uidMapper().fieldType().name()), equalTo(Uid.createUid("person", "1"))); + assertThat(doc.get(docMapper.idFieldMapper().fieldType().name()), equalTo("1")); assertThat(doc.get(docMapper.mappers().getMapper("name.first").fieldType().name()), equalTo("shay")); } diff --git a/core/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java index aedd332471ae6..af5460241278a 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java @@ -99,7 +99,7 @@ public void testInjectIntoDocDuringParsing() throws Exception { .bytes(), XContentType.JSON)); - assertFieldNames(set("a", "a.keyword", "b", "b.c", "_uid", "_version", "_seq_no", "_primary_term", "_source"), doc); + assertFieldNames(set("a", "a.keyword", "b", "b.c", "_id", "_version", "_seq_no", "_primary_term", "_source"), doc); } public void testExplicitEnabled() throws Exception { @@ -117,7 +117,7 @@ public void testExplicitEnabled() throws Exception { .bytes(), XContentType.JSON)); - assertFieldNames(set("field", "field.keyword", "_uid", "_version", "_seq_no", "_primary_term", "_source"), doc); + assertFieldNames(set("field", "field.keyword", "_id", "_version", "_seq_no", "_primary_term", "_source"), doc); } public void testDisabled() throws Exception { diff --git a/core/src/test/java/org/elasticsearch/index/mapper/IdFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/IdFieldMapperTests.java index 3c12d18b12861..185f1c51d2e91 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/IdFieldMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/IdFieldMapperTests.java @@ -19,37 +19,23 @@ package org.elasticsearch.index.mapper; +import org.apache.lucene.index.DocValuesType; +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.IndexableField; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.compress.CompressedXContent; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; -import org.elasticsearch.index.mapper.DocumentMapper; -import org.elasticsearch.index.mapper.IdFieldMapper; -import org.elasticsearch.index.mapper.MapperParsingException; -import org.elasticsearch.index.mapper.ParsedDocument; -import org.elasticsearch.index.mapper.SourceToParse; -import org.elasticsearch.index.mapper.UidFieldMapper; +import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.test.ESSingleNodeTestCase; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; public class IdFieldMapperTests extends ESSingleNodeTestCase { - public void testId() throws Exception { - String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") - .endObject().endObject().string(); - DocumentMapper docMapper = createIndex("test").mapperService().documentMapperParser().parse("type", new CompressedXContent(mapping)); - - ParsedDocument doc = docMapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder() - .startObject() - .endObject() - .bytes(), - XContentType.JSON)); - - assertThat(doc.rootDoc().get(UidFieldMapper.NAME), notNullValue()); - assertThat(doc.rootDoc().get(IdFieldMapper.NAME), nullValue()); - } - public void testIncludeInObjectNotAllowed() throws Exception { String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").endObject().endObject().string(); DocumentMapper docMapper = createIndex("test").mapperService().documentMapperParser().parse("type", new CompressedXContent(mapping)); @@ -62,4 +48,28 @@ public void testIncludeInObjectNotAllowed() throws Exception { assertTrue(e.getMessage(), e.getMessage().contains("Field [_id] is a metadata field and cannot be added inside a document")); } } + + public void testDefaultsMultipleTypes() throws IOException { + Settings indexSettings = Settings.builder() + .put("index.mapping.single_type", false) + .build(); + MapperService mapperService = createIndex("test", indexSettings).mapperService(); + DocumentMapper mapper = mapperService.merge("type", new CompressedXContent("{\"type\":{}}"), MergeReason.MAPPING_UPDATE, false); + ParsedDocument document = mapper.parse(SourceToParse.source("index", "type", "id", new BytesArray("{}"), XContentType.JSON)); + assertEquals(Collections.emptyList(), Arrays.asList(document.rootDoc().getFields(IdFieldMapper.NAME))); + } + + public void testDefaultsSingleType() throws IOException { + Settings indexSettings = Settings.builder() + .put("index.mapping.single_type", true) + .build(); + MapperService mapperService = createIndex("test", indexSettings).mapperService(); + DocumentMapper mapper = mapperService.merge("type", new CompressedXContent("{\"type\":{}}"), MergeReason.MAPPING_UPDATE, false); + ParsedDocument document = mapper.parse(SourceToParse.source("index", "type", "id", new BytesArray("{}"), XContentType.JSON)); + IndexableField[] fields = document.rootDoc().getFields(IdFieldMapper.NAME); + assertEquals(1, fields.length); + assertEquals(IndexOptions.DOCS, fields[0].fieldType().indexOptions()); + assertTrue(fields[0].fieldType().stored()); + assertEquals("id", fields[0].stringValue()); + } } diff --git a/core/src/test/java/org/elasticsearch/index/mapper/IdFieldTypeTests.java b/core/src/test/java/org/elasticsearch/index/mapper/IdFieldTypeTests.java index b17722eaa768d..2209027c12f37 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/IdFieldTypeTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/IdFieldTypeTests.java @@ -18,8 +18,19 @@ */ package org.elasticsearch.index.mapper; -import org.elasticsearch.index.mapper.IdFieldMapper; -import org.elasticsearch.index.mapper.MappedFieldType; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermInSetQuery; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.Version; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.UUIDs; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.IndexSettings; +import org.elasticsearch.index.query.QueryShardContext; +import org.mockito.Mockito; + +import java.util.Collection; +import java.util.Collections; public class IdFieldTypeTests extends FieldTypeTestCase { @Override @@ -34,4 +45,60 @@ public void testRangeQuery() { () -> ft.rangeQuery(null, null, randomBoolean(), randomBoolean(), null)); assertEquals("Field [_id] of type [_id] does not support range queries", e.getMessage()); } + + public void testTermsQueryWhenTypesAreEnabled() throws Exception { + QueryShardContext context = Mockito.mock(QueryShardContext.class); + Settings indexSettings = Settings.builder() + .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0) + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()) + .put("index.mapping.single_type", false).build(); + IndexMetaData indexMetaData = IndexMetaData.builder(IndexMetaData.INDEX_UUID_NA_VALUE).settings(indexSettings).build(); + IndexSettings mockSettings = new IndexSettings(indexMetaData, Settings.EMPTY); + Mockito.when(context.getIndexSettings()).thenReturn(mockSettings); + + MapperService mapperService = Mockito.mock(MapperService.class); + Collection types = Collections.emptySet(); + Mockito.when(context.queryTypes()).thenReturn(types); + Mockito.when(context.getMapperService()).thenReturn(mapperService); + + MappedFieldType ft = IdFieldMapper.defaultFieldType(mockSettings); + ft.setName(IdFieldMapper.NAME); + Query query = ft.termQuery("id", context); + assertEquals(new TermInSetQuery("_uid"), query); + + types = Collections.singleton("type"); + Mockito.when(context.queryTypes()).thenReturn(types); + query = ft.termQuery("id", context); + assertEquals(new TermInSetQuery("_uid", new BytesRef("type#id")), query); + } + + public void testTermsQueryWhenTypesAreDisabled() throws Exception { + QueryShardContext context = Mockito.mock(QueryShardContext.class); + Settings indexSettings = Settings.builder() + .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0) + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()) + .put("index.mapping.single_type", true).build(); + IndexMetaData indexMetaData = IndexMetaData.builder(IndexMetaData.INDEX_UUID_NA_VALUE).settings(indexSettings).build(); + IndexSettings mockSettings = new IndexSettings(indexMetaData, Settings.EMPTY); + Mockito.when(context.getIndexSettings()).thenReturn(mockSettings); + + MapperService mapperService = Mockito.mock(MapperService.class); + Collection types = Collections.emptySet(); + Mockito.when(context.queryTypes()).thenReturn(types); + Mockito.when(context.getMapperService()).thenReturn(mapperService); + + MappedFieldType ft = IdFieldMapper.defaultFieldType(mockSettings); + ft.setName(IdFieldMapper.NAME); + Query query = ft.termQuery("id", context); + assertEquals(new TermInSetQuery("_id", new BytesRef("id")), query); + + types = Collections.singleton("type"); + Mockito.when(context.queryTypes()).thenReturn(types); + query = ft.termQuery("id", context); + assertEquals(new TermInSetQuery("_id", new BytesRef("id")), query); + } } diff --git a/core/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java index e2dc7bc7a094c..367f79e5980b7 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java @@ -221,7 +221,7 @@ public void testDefaultPositionIncrementGap() throws IOException { assertEquals("b", fields[1].stringValue()); IndexShard shard = indexService.getShard(0); - shard.index(new Engine.Index(new Term("_uid", doc.uid() ), doc)); + shard.index(new Engine.Index(new Term("_id", doc.id()), doc)); shard.refresh("test"); try (Engine.Searcher searcher = shard.acquireSearcher("test")) { LeafReader leaf = searcher.getDirectoryReader().leaves().get(0).reader(); @@ -261,7 +261,7 @@ public void testPositionIncrementGap() throws IOException { assertEquals("b", fields[1].stringValue()); IndexShard shard = indexService.getShard(0); - shard.index(new Engine.Index(new Term("_uid", doc.uid()), doc)); + shard.index(new Engine.Index(new Term("_id", doc.id()), doc)); shard.refresh("test"); try (Engine.Searcher searcher = shard.acquireSearcher("test")) { LeafReader leaf = searcher.getDirectoryReader().leaves().get(0).reader(); diff --git a/core/src/test/java/org/elasticsearch/index/mapper/UidFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/UidFieldMapperTests.java new file mode 100644 index 0000000000000..e5503738f0686 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/index/mapper/UidFieldMapperTests.java @@ -0,0 +1,60 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.index.mapper; + +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.IndexableField; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.compress.CompressedXContent; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.mapper.MapperService.MergeReason; +import org.elasticsearch.test.ESSingleNodeTestCase; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; + +public class UidFieldMapperTests extends ESSingleNodeTestCase { + + public void testDefaultsMultipleTypes() throws IOException { + Settings indexSettings = Settings.builder() + .put("index.mapping.single_type", false) + .build(); + MapperService mapperService = createIndex("test", indexSettings).mapperService(); + DocumentMapper mapper = mapperService.merge("type", new CompressedXContent("{\"type\":{}}"), MergeReason.MAPPING_UPDATE, false); + ParsedDocument document = mapper.parse(SourceToParse.source("index", "type", "id", new BytesArray("{}"), XContentType.JSON)); + IndexableField[] fields = document.rootDoc().getFields(UidFieldMapper.NAME); + assertEquals(1, fields.length); + assertEquals(IndexOptions.DOCS, fields[0].fieldType().indexOptions()); + assertTrue(fields[0].fieldType().stored()); + assertEquals("type#id", fields[0].stringValue()); + } + + public void testDefaultsSingleType() throws IOException { + Settings indexSettings = Settings.builder() + .put("index.mapping.single_type", true) + .build(); + MapperService mapperService = createIndex("test", indexSettings).mapperService(); + DocumentMapper mapper = mapperService.merge("type", new CompressedXContent("{\"type\":{}}"), MergeReason.MAPPING_UPDATE, false); + ParsedDocument document = mapper.parse(SourceToParse.source("index", "type", "id", new BytesArray("{}"), XContentType.JSON)); + assertEquals(Collections.emptyList(), Arrays.asList(document.rootDoc().getFields(UidFieldMapper.NAME))); + } +} diff --git a/core/src/test/java/org/elasticsearch/index/mapper/UidFieldTypeTests.java b/core/src/test/java/org/elasticsearch/index/mapper/UidFieldTypeTests.java index 803c7013d457a..1a9a78f51cf2e 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/UidFieldTypeTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/UidFieldTypeTests.java @@ -18,8 +18,22 @@ */ package org.elasticsearch.index.mapper; +import org.apache.lucene.search.MatchNoDocsQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermInSetQuery; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.Version; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.UUIDs; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.UidFieldMapper; +import org.elasticsearch.index.query.QueryShardContext; +import org.mockito.Mockito; + +import java.util.Collection; +import java.util.Collections; public class UidFieldTypeTests extends FieldTypeTestCase { @Override @@ -34,4 +48,57 @@ public void testRangeQuery() { () -> ft.rangeQuery(null, null, randomBoolean(), randomBoolean(), null)); assertEquals("Field [_uid] of type [_uid] does not support range queries", e.getMessage()); } + + public void testTermsQueryWhenTypesAreEnabled() throws Exception { + QueryShardContext context = Mockito.mock(QueryShardContext.class); + Settings indexSettings = Settings.builder() + .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0) + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()) + .put("index.mapping.single_type", false).build(); + IndexMetaData indexMetaData = IndexMetaData.builder(IndexMetaData.INDEX_UUID_NA_VALUE).settings(indexSettings).build(); + IndexSettings mockSettings = new IndexSettings(indexMetaData, Settings.EMPTY); + Mockito.when(context.getIndexSettings()).thenReturn(mockSettings); + + MapperService mapperService = Mockito.mock(MapperService.class); + Collection types = Collections.emptySet(); + Mockito.when(context.queryTypes()).thenReturn(types); + Mockito.when(context.getMapperService()).thenReturn(mapperService); + + MappedFieldType ft = UidFieldMapper.defaultFieldType(mockSettings); + ft.setName(UidFieldMapper.NAME); + Query query = ft.termQuery("type#id", context); + assertEquals(new TermInSetQuery("_uid", new BytesRef("type#id")), query); + } + + public void testTermsQueryWhenTypesAreDisabled() throws Exception { + QueryShardContext context = Mockito.mock(QueryShardContext.class); + Settings indexSettings = Settings.builder() + .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0) + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()) + .put("index.mapping.single_type", true).build(); + IndexMetaData indexMetaData = IndexMetaData.builder(IndexMetaData.INDEX_UUID_NA_VALUE).settings(indexSettings).build(); + IndexSettings mockSettings = new IndexSettings(indexMetaData, Settings.EMPTY); + Mockito.when(context.getIndexSettings()).thenReturn(mockSettings); + + MapperService mapperService = Mockito.mock(MapperService.class); + Collection types = Collections.emptySet(); + Mockito.when(mapperService.types()).thenReturn(types); + Mockito.when(context.getMapperService()).thenReturn(mapperService); + + MappedFieldType ft = UidFieldMapper.defaultFieldType(mockSettings); + ft.setName(UidFieldMapper.NAME); + Query query = ft.termQuery("type#id", context); + assertEquals(new MatchNoDocsQuery(), query); + + types = Collections.singleton("type"); + Mockito.when(mapperService.types()).thenReturn(types); + query = ft.termQuery("type#id", context); + assertEquals(new TermInSetQuery("_id", new BytesRef("id")), query); + query = ft.termQuery("type2#id", context); + assertEquals(new TermInSetQuery("_id"), query); + } } diff --git a/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java b/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java index 57955adbf3b49..dc518dba45238 100644 --- a/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java +++ b/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java @@ -51,7 +51,6 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.Index; import org.elasticsearch.index.engine.EngineFactory; -import org.elasticsearch.index.mapper.Uid; import org.elasticsearch.index.seqno.GlobalCheckpointSyncAction; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.IndexShardTestCase; @@ -310,11 +309,11 @@ public Future asyncRecoverReplica( } public synchronized void assertAllEqual(int expectedCount) throws IOException { - Set primaryIds = getShardDocUIDs(primary); + Set primaryIds = getShardDocUIDs(primary); assertThat(primaryIds.size(), equalTo(expectedCount)); for (IndexShard replica : replicas) { - Set replicaIds = getShardDocUIDs(replica); - Set temp = new HashSet<>(primaryIds); + Set replicaIds = getShardDocUIDs(replica); + Set temp = new HashSet<>(primaryIds); temp.removeAll(replicaIds); assertThat(replica.routingEntry() + " is missing docs", temp, empty()); temp = new HashSet<>(replicaIds); diff --git a/core/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java b/core/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java index e68ee0758fc24..bcaccabd37869 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java +++ b/core/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java @@ -53,12 +53,11 @@ import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.flush.FlushStats; +import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.Mapping; import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.SeqNoFieldMapper; -import org.elasticsearch.index.mapper.Uid; -import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.seqno.SequenceNumbersService; import org.elasticsearch.index.translog.Translog; import org.elasticsearch.indices.IndicesService; @@ -105,7 +104,7 @@ protected Collection> getPlugins() { private ParsedDocument testParsedDocument(String id, String type, String routing, long seqNo, ParseContext.Document document, BytesReference source, XContentType xContentType, Mapping mappingUpdate) { - Field uidField = new Field("_uid", Uid.createUid(type, id), UidFieldMapper.Defaults.FIELD_TYPE); + Field uidField = new Field("_id", id, IdFieldMapper.Defaults.FIELD_TYPE); Field versionField = new NumericDocValuesField("_version", 0); SeqNoFieldMapper.SequenceIDFields seqID = SeqNoFieldMapper.SequenceIDFields.emptySeqID(); document.add(uidField); @@ -335,7 +334,7 @@ public void testMaybeFlush() throws Exception { SequenceNumbersService.UNASSIGNED_SEQ_NO, new ParseContext.Document(), new BytesArray(new byte[]{1}), XContentType.JSON, null); - Engine.Index index = new Engine.Index(new Term("_uid", doc.uid()), doc); + Engine.Index index = new Engine.Index(new Term("_id", doc.id()), doc); shard.index(index); assertTrue(shard.shouldFlush()); assertEquals(2, shard.getEngine().getTranslog().totalOperations()); @@ -390,7 +389,7 @@ public void testMaybeRollTranslogGeneration() throws Exception { SequenceNumbersService.UNASSIGNED_SEQ_NO, new ParseContext.Document(), new BytesArray(new byte[]{1}), XContentType.JSON, null); - final Engine.Index index = new Engine.Index(new Term("_uid", doc.uid()), doc); + final Engine.Index index = new Engine.Index(new Term("_id", doc.id()), doc); final Engine.IndexResult result = shard.index(index); final Translog.Location location = result.getTranslogLocation(); shard.afterWriteOperation(); diff --git a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java index a0d356c03e79c..b8e621df8872d 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java +++ b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java @@ -73,14 +73,13 @@ import org.elasticsearch.index.engine.EngineException; import org.elasticsearch.index.fielddata.FieldDataStats; import org.elasticsearch.index.fielddata.IndexFieldData; +import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.Mapping; import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.SeqNoFieldMapper; import org.elasticsearch.index.mapper.SourceToParse; -import org.elasticsearch.index.mapper.Uid; -import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.seqno.SequenceNumbersService; import org.elasticsearch.index.snapshots.IndexShardSnapshotStatus; import org.elasticsearch.index.store.Store; @@ -551,10 +550,10 @@ public void testShardStats() throws IOException { private ParsedDocument testParsedDocument(String id, String type, String routing, ParseContext.Document document, BytesReference source, Mapping mappingUpdate) { - Field uidField = new Field("_uid", Uid.createUid(type, id), UidFieldMapper.Defaults.FIELD_TYPE); + Field idField = new Field("_id", id, IdFieldMapper.Defaults.FIELD_TYPE); Field versionField = new NumericDocValuesField("_version", 0); SeqNoFieldMapper.SequenceIDFields seqID = SeqNoFieldMapper.SequenceIDFields.emptySeqID(); - document.add(uidField); + document.add(idField); document.add(versionField); document.add(seqID.seqNo); document.add(seqID.seqNoDocValue); @@ -624,7 +623,7 @@ public void postDelete(ShardId shardId, Engine.Delete delete, Exception ex) { ParsedDocument doc = testParsedDocument("1", "test", null, new ParseContext.Document(), new BytesArray(new byte[]{1}), null); - Engine.Index index = new Engine.Index(new Term("_uid", doc.uid()), doc); + Engine.Index index = new Engine.Index(new Term("_id", doc.id()), doc); shard.index(index); assertEquals(1, preIndex.get()); assertEquals(1, postIndexCreate.get()); @@ -643,7 +642,7 @@ public void postDelete(ShardId shardId, Engine.Delete delete, Exception ex) { assertEquals(0, postDelete.get()); assertEquals(0, postDeleteException.get()); - Engine.Delete delete = new Engine.Delete("test", "1", new Term("_uid", doc.uid())); + Engine.Delete delete = new Engine.Delete("test", "1", new Term("_id", doc.id())); shard.delete(delete); assertEquals(2, preIndex.get()); @@ -1037,7 +1036,7 @@ public void testRestoreShard() throws IOException { } indexDoc(target, "test", "1"); target.refresh("test"); - assertDocs(target, new Uid("test", "1")); + assertDocs(target, "1"); flushShard(source); // only flush source final ShardRouting origRouting = target.routingEntry(); ShardRouting routing = ShardRoutingHelper.reinitPrimary(origRouting); @@ -1069,7 +1068,7 @@ public void restoreShard(IndexShard shard, SnapshotId snapshotId, Version versio })); target.updateRoutingEntry(routing.moveToStarted()); - assertDocs(target, new Uid("test", "0")); + assertDocs(target, "0"); closeShards(source, target); } @@ -1080,7 +1079,7 @@ public void testSearcherWrapperIsUsed() throws IOException { indexDoc(shard, "test", "1", "{\"foobar\" : \"bar\"}"); shard.refresh("test"); - Engine.GetResult getResult = shard.get(new Engine.Get(false, new Term(UidFieldMapper.NAME, Uid.createUid("test", "1")))); + Engine.GetResult getResult = shard.get(new Engine.Get(false, "test", "1", new Term(IdFieldMapper.NAME, "1"))); assertTrue(getResult.exists()); assertNotNull(getResult.searcher()); getResult.release(); @@ -1113,7 +1112,7 @@ public IndexSearcher wrap(IndexSearcher searcher) throws EngineException { search = searcher.searcher().search(new TermQuery(new Term("foobar", "bar")), 10); assertEquals(search.totalHits, 1); } - getResult = newShard.get(new Engine.Get(false, new Term(UidFieldMapper.NAME, Uid.createUid("test", "1")))); + getResult = newShard.get(new Engine.Get(false, "test", "1", new Term(IdFieldMapper.NAME, "1"))); assertTrue(getResult.exists()); assertNotNull(getResult.searcher()); // make sure get uses the wrapped reader assertTrue(getResult.searcher().reader() instanceof FieldMaskingReader); @@ -1424,7 +1423,7 @@ public void testDocStats() throws IOException { testParsedDocument(id, "test", null, new ParseContext.Document(), new BytesArray("{}"), null); final Engine.Index index = new Engine.Index( - new Term("_uid", doc.uid()), + new Term("_id", doc.id()), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, @@ -1454,7 +1453,7 @@ public void testDocStats() throws IOException { testParsedDocument(id, "test", null, new ParseContext.Document(), new BytesArray("{}"), null); final Engine.Index index = new Engine.Index( - new Term("_uid", doc.uid()), + new Term("_id", doc.id()), doc, SequenceNumbersService.UNASSIGNED_SEQ_NO, 0, diff --git a/core/src/test/java/org/elasticsearch/index/shard/IndexingOperationListenerTests.java b/core/src/test/java/org/elasticsearch/index/shard/IndexingOperationListenerTests.java index 88d8a075e1b3e..91ea9c6073a4e 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/IndexingOperationListenerTests.java +++ b/core/src/test/java/org/elasticsearch/index/shard/IndexingOperationListenerTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.engine.InternalEngineTests; import org.elasticsearch.index.mapper.ParsedDocument; +import org.elasticsearch.index.mapper.Uid; import org.elasticsearch.index.seqno.SequenceNumbersService; import org.elasticsearch.test.ESTestCase; @@ -134,9 +135,9 @@ public void postDelete(ShardId shardId, Engine.Delete delete, Exception ex) { Collections.shuffle(indexingOperationListeners, random()); IndexingOperationListener.CompositeListener compositeListener = new IndexingOperationListener.CompositeListener(indexingOperationListeners, logger); - ParsedDocument doc = InternalEngineTests.createParsedDoc("1", "test", null); - Engine.Delete delete = new Engine.Delete("test", "1", new Term("_uid", doc.uid())); - Engine.Index index = new Engine.Index(new Term("_uid", doc.uid()), doc); + ParsedDocument doc = InternalEngineTests.createParsedDoc("1", null); + Engine.Delete delete = new Engine.Delete("test", "1", new Term("_uid", Uid.createUidAsBytes(doc.type(), doc.id()))); + Engine.Index index = new Engine.Index(new Term("_uid", Uid.createUidAsBytes(doc.type(), doc.id())), doc); compositeListener.postDelete(randomShardId, delete, new Engine.DeleteResult(1, SequenceNumbersService.UNASSIGNED_SEQ_NO, true)); assertEquals(0, preIndex.get()); assertEquals(0, postIndex.get()); diff --git a/core/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java b/core/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java index 5ad430ca59e24..7ddd229a1172d 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java +++ b/core/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java @@ -45,11 +45,10 @@ import org.elasticsearch.index.engine.InternalEngine; import org.elasticsearch.index.engine.InternalEngineTests.TranslogHandler; import org.elasticsearch.index.fieldvisitor.SingleFieldsVisitor; +import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.ParseContext.Document; import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.SeqNoFieldMapper; -import org.elasticsearch.index.mapper.Uid; -import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.store.DirectoryService; import org.elasticsearch.index.store.Store; import org.elasticsearch.index.translog.TranslogConfig; @@ -118,7 +117,7 @@ public void onFailedEngine(String reason, @Nullable Exception e) { // we don't need to notify anybody in this test } }; - TranslogHandler translogHandler = new TranslogHandler(xContentRegistry(), shardId.getIndexName(), logger); + TranslogHandler translogHandler = new TranslogHandler(xContentRegistry(), shardId.getIndexName(), Settings.EMPTY, logger); EngineConfig config = new EngineConfig(EngineConfig.OpenMode.CREATE_INDEX_AND_TRANSLOG, shardId, threadPool, indexSettings, null, store, new SnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy()), newMergePolicy(), iwc.getAnalyzer(), iwc.getSimilarity(), new CodecService(null, logger), eventListener, translogHandler, @@ -300,7 +299,7 @@ public void testLotsOfThreads() throws Exception { } listener.assertNoError(); - Engine.Get get = new Engine.Get(false, new Term("_uid", Uid.createUid("test", threadId))); + Engine.Get get = new Engine.Get(false, "test", threadId, new Term(IdFieldMapper.NAME, threadId)); try (Engine.GetResult getResult = engine.get(get)) { assertTrue("document not found", getResult.exists()); assertEquals(iteration, getResult.version()); @@ -327,22 +326,20 @@ private Engine.IndexResult index(String id) throws IOException { } private Engine.IndexResult index(String id, String testFieldValue) throws IOException { - String type = "test"; - String uid = type + ":" + id; Document document = new Document(); document.add(new TextField("test", testFieldValue, Field.Store.YES)); - Field uidField = new Field("_uid", Uid.createUid(type, id), UidFieldMapper.Defaults.FIELD_TYPE); + Field idField = new Field("_id", id, IdFieldMapper.Defaults.FIELD_TYPE); Field versionField = new NumericDocValuesField("_version", Versions.MATCH_ANY); SeqNoFieldMapper.SequenceIDFields seqID = SeqNoFieldMapper.SequenceIDFields.emptySeqID(); - document.add(uidField); + document.add(idField); document.add(versionField); document.add(seqID.seqNo); document.add(seqID.seqNoDocValue); document.add(seqID.primaryTerm); BytesReference source = new BytesArray(new byte[] { 1 }); - ParsedDocument doc = new ParsedDocument(versionField, seqID, id, type, null, Arrays.asList(document), source, XContentType.JSON, + ParsedDocument doc = new ParsedDocument(versionField, seqID, id, "test", null, Arrays.asList(document), source, XContentType.JSON, null); - Engine.Index index = new Engine.Index(new Term("_uid", doc.uid()), doc); + Engine.Index index = new Engine.Index(new Term("_id", doc.id()), doc); return engine.index(index); } diff --git a/core/src/test/java/org/elasticsearch/index/translog/TranslogTests.java b/core/src/test/java/org/elasticsearch/index/translog/TranslogTests.java index dfb8efb9ab943..8a684e2f79cde 100644 --- a/core/src/test/java/org/elasticsearch/index/translog/TranslogTests.java +++ b/core/src/test/java/org/elasticsearch/index/translog/TranslogTests.java @@ -255,7 +255,7 @@ public void testSimpleOperations() throws IOException { assertThat(snapshot, SnapshotMatchers.equalsTo(ops)); assertThat(snapshot.totalOperations(), equalTo(ops.size())); - addToTranslogAndList(translog, ops, new Translog.Delete(newUid("2"))); + addToTranslogAndList(translog, ops, new Translog.Delete("test", "2", newUid("2"))); snapshot = translog.newSnapshot(); assertThat(snapshot, SnapshotMatchers.equalsTo(ops)); assertThat(snapshot.totalOperations(), equalTo(ops.size())); @@ -326,14 +326,14 @@ public void testStats() throws IOException { assertThat(stats.getTranslogSizeInBytes(), equalTo(97L)); } - translog.add(new Translog.Delete(newUid("2"))); + translog.add(new Translog.Delete("test", "2", newUid("2"))); { final TranslogStats stats = stats(); assertThat(stats.estimatedNumberOfOperations(), equalTo(2L)); assertThat(stats.getTranslogSizeInBytes(), equalTo(139L)); } - translog.add(new Translog.Delete(newUid("3"))); + translog.add(new Translog.Delete("test", "3", newUid("3"))); { final TranslogStats stats = stats(); assertThat(stats.estimatedNumberOfOperations(), equalTo(3L)); @@ -648,7 +648,7 @@ private void corruptTranslogs(Path directory) throws Exception { } private Term newUid(ParsedDocument doc) { - return new Term("_uid", doc.uid()); + return new Term("_uid", Uid.createUidAsBytes(doc.type(), doc.id())); } private Term newUid(String uid) { @@ -707,7 +707,7 @@ public void doRun() throws BrokenBarrierException, InterruptedException, IOExcep op = new Translog.Index("type", "" + id, new byte[]{(byte) id}); break; case DELETE: - op = new Translog.Delete(newUid("" + id)); + op = new Translog.Delete("test", Long.toString(id), newUid(Long.toString(id))); break; case NO_OP: op = new Translog.NoOp(id, id, Long.toString(id)); @@ -1403,6 +1403,7 @@ public void run() { break; case DELETE: op = new Translog.Delete( + "test", threadId + "_" + opCount, new Term("_uid", threadId + "_" + opCount), opCount, 0, diff --git a/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java b/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java index 17497af8838bc..6a059b7484a04 100644 --- a/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java +++ b/core/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java @@ -215,7 +215,7 @@ private Engine.Index getIndex(final String id) { final BytesReference source = new BytesArray(new byte[] { 1 }); final ParsedDocument doc = new ParsedDocument(versionField, seqID, id, type, null, Arrays.asList(document), source, XContentType.JSON, null); - return new Engine.Index(new Term("_uid", doc.uid()), doc); + return new Engine.Index(new Term("_uid", Uid.createUidAsBytes(doc.type(), doc.id())), doc); } public void testHandleCorruptedIndexOnSendSendFiles() throws Throwable { diff --git a/core/src/test/java/org/elasticsearch/search/SearchCancellationIT.java b/core/src/test/java/org/elasticsearch/search/SearchCancellationIT.java index 02607f0c1fd06..1a55b38f390f6 100644 --- a/core/src/test/java/org/elasticsearch/search/SearchCancellationIT.java +++ b/core/src/test/java/org/elasticsearch/search/SearchCancellationIT.java @@ -301,7 +301,7 @@ public String getName() { public class NativeTestScriptedBlock extends AbstractSearchScript { @Override public Object run() { - Loggers.getLogger(SearchCancellationIT.class).info("Blocking on the document {}", doc().get("_uid")); + Loggers.getLogger(SearchCancellationIT.class).info("Blocking on the document {}", fields().get("_uid")); hits.incrementAndGet(); try { awaitBusy(() -> shouldBlock.get() == false); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java index fd36a03ddf6ce..99cd626a7d7e1 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java @@ -96,6 +96,7 @@ protected AggregatorFactory createAggregatorFactory(AggregationBuilder aggreg when(searchContext.bigArrays()).thenReturn(new MockBigArrays(Settings.EMPTY, circuitBreakerService)); // TODO: now just needed for top_hits, this will need to be revised for other agg unit tests: MapperService mapperService = mapperServiceMock(); + when(mapperService.getIndexSettings()).thenReturn(indexSettings); when(mapperService.hasNested()).thenReturn(false); when(searchContext.mapperService()).thenReturn(mapperService); IndexFieldDataService ifds = new IndexFieldDataService(indexSettings, @@ -106,7 +107,7 @@ protected AggregatorFactory createAggregatorFactory(AggregationBuilder aggreg SearchLookup searchLookup = new SearchLookup(mapperService, ifds, new String[]{"type"}); when(searchContext.lookup()).thenReturn(searchLookup); - QueryShardContext queryShardContext = queryShardContextMock(fieldTypes, indexSettings, circuitBreakerService); + QueryShardContext queryShardContext = queryShardContextMock(mapperService, fieldTypes, circuitBreakerService); when(searchContext.getQueryShardContext()).thenReturn(queryShardContext); return aggregationBuilder.build(searchContext, null); @@ -180,13 +181,15 @@ protected MapperService mapperServiceMock() { /** * sub-tests that need a more complex mock can overwrite this */ - protected QueryShardContext queryShardContextMock(MappedFieldType[] fieldTypes, IndexSettings indexSettings, + protected QueryShardContext queryShardContextMock(MapperService mapperService, MappedFieldType[] fieldTypes, CircuitBreakerService circuitBreakerService) { QueryShardContext queryShardContext = mock(QueryShardContext.class); + when(queryShardContext.getMapperService()).thenReturn(mapperService); for (MappedFieldType fieldType : fieldTypes) { when(queryShardContext.fieldMapper(fieldType.name())).thenReturn(fieldType); - when(queryShardContext.getForField(fieldType)).then(invocation -> fieldType.fielddataBuilder().build(indexSettings, fieldType, - new IndexFieldDataCache.None(), circuitBreakerService, mock(MapperService.class))); + when(queryShardContext.getForField(fieldType)).then(invocation -> fieldType.fielddataBuilder().build( + mapperService.getIndexSettings(), fieldType, new IndexFieldDataCache.None(), circuitBreakerService, + mapperService)); } NestedScope nestedScope = new NestedScope(); when(queryShardContext.isFilter()).thenCallRealMethod(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java index e9b9ae20407da..6819fddf3e3e5 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java @@ -667,7 +667,7 @@ public void testTrackScores() throws Exception { topHits("hits") .trackScores(trackScore) .size(1) - .sort("_uid", SortOrder.DESC) + .sort("_index", SortOrder.DESC) ) ) .get(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregatorTests.java index aa48c64c3d788..7c34ef0549694 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregatorTests.java @@ -28,8 +28,8 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; -import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.script.MockScriptEngine; @@ -196,7 +196,7 @@ public void testScriptedMetricWithCombineAccessesScores() throws IOException { * is final and cannot be mocked */ @Override - protected QueryShardContext queryShardContextMock(final MappedFieldType[] fieldTypes, IndexSettings idxSettings, + protected QueryShardContext queryShardContextMock(MapperService mapperService, final MappedFieldType[] fieldTypes, CircuitBreakerService circuitBreakerService) { Settings settings = Settings.builder().put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) // no file watching, so we don't need a ResourceWatcherService @@ -212,7 +212,7 @@ protected QueryShardContext queryShardContextMock(final MappedFieldType[] fieldT } catch (IOException e) { throw new ElasticsearchException(e); } - return new QueryShardContext(0, idxSettings, null, null, null, null, scriptService, xContentRegistry(), - null, null, System::currentTimeMillis); + return new QueryShardContext(0, mapperService.getIndexSettings(), null, null, mapperService, null, scriptService, + xContentRegistry(), null, null, System::currentTimeMillis); } } diff --git a/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java b/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java index f93fbabdf2674..6fbda92ba2699 100644 --- a/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java +++ b/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java @@ -64,6 +64,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAllSuccessful; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; @@ -191,7 +192,9 @@ public void testRandomNested() throws Exception { int[] field2InnerObjects = new int[numDocs]; for (int i = 0; i < numDocs; i++) { int numInnerObjects = field1InnerObjects[i] = scaledRandomIntBetween(1, numDocs); - XContentBuilder source = jsonBuilder().startObject().startArray("field1"); + XContentBuilder source = jsonBuilder().startObject() + .field("foo", i) + .startArray("field1"); for (int j = 0; j < numInnerObjects; j++) { source.startObject().field("x", "y").endObject(); } @@ -201,7 +204,7 @@ public void testRandomNested() throws Exception { source.startObject().field("x", "y").endObject(); } source.endArray().endObject(); - requestBuilders.add(client().prepareIndex("idx", "type", String.format(Locale.ENGLISH, "%03d", i)).setSource(source)); + requestBuilders.add(client().prepareIndex("idx", "type", Integer.toString(i)).setSource(source)); } indexRandom(true, requestBuilders); @@ -214,7 +217,7 @@ public void testRandomNested() throws Exception { SearchResponse searchResponse = client().prepareSearch("idx") .setQuery(boolQuery) .setSize(numDocs) - .addSort("_uid", SortOrder.ASC) + .addSort("foo", SortOrder.ASC) .get(); assertNoFailures(searchResponse); @@ -965,7 +968,6 @@ public void testDontExplode() throws Exception { .innerHit(new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1), false); SearchResponse response = client().prepareSearch("index1") .setQuery(query) - .addSort("_uid", SortOrder.ASC) .get(); assertNoFailures(response); assertHitCount(response, 1); @@ -985,7 +987,6 @@ public void testDontExplode() throws Exception { .innerHit(new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1), false); response = client().prepareSearch("index2") .setQuery(query) - .addSort("_uid", SortOrder.ASC) .get(); assertNoFailures(response); assertHitCount(response, 1); @@ -1057,11 +1058,10 @@ public void testInnerHitsWithIgnoreUnmapped() throws Exception { .innerHit(new InnerHitBuilder(), true)) .should(termQuery("key", "value")) ) - .addSort("_uid", SortOrder.ASC) .get(); assertNoFailures(response); assertHitCount(response, 2); - assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + assertSearchHits(response, "1", "3"); response = client().prepareSearch("index1", "index2") .setQuery(boolQuery() @@ -1069,11 +1069,10 @@ public void testInnerHitsWithIgnoreUnmapped() throws Exception { .innerHit(new InnerHitBuilder(), true)) .should(termQuery("key", "value")) ) - .addSort("_uid", SortOrder.ASC) .get(); assertNoFailures(response); assertHitCount(response, 2); - assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + assertSearchHits(response, "1", "3"); } } diff --git a/core/src/test/java/org/elasticsearch/search/profile/query/QueryProfilerIT.java b/core/src/test/java/org/elasticsearch/search/profile/query/QueryProfilerIT.java index f20c575f0be8e..d5198485351b1 100644 --- a/core/src/test/java/org/elasticsearch/search/profile/query/QueryProfilerIT.java +++ b/core/src/test/java/org/elasticsearch/search/profile/query/QueryProfilerIT.java @@ -133,14 +133,14 @@ public void testProfileMatchesRegular() throws Exception { SearchRequestBuilder vanilla = client().prepareSearch("test") .setQuery(q) .setProfile(false) - .addSort("_uid", SortOrder.ASC) + .addSort("_id", SortOrder.ASC) .setPreference("_primary") .setSearchType(SearchType.QUERY_THEN_FETCH); SearchRequestBuilder profile = client().prepareSearch("test") .setQuery(q) .setProfile(true) - .addSort("_uid", SortOrder.ASC) + .addSort("_id", SortOrder.ASC) .setPreference("_primary") .setSearchType(SearchType.QUERY_THEN_FETCH); diff --git a/core/src/test/java/org/elasticsearch/search/query/MultiMatchQueryIT.java b/core/src/test/java/org/elasticsearch/search/query/MultiMatchQueryIT.java index 7ae22485df3d4..ba7a13cf0102e 100644 --- a/core/src/test/java/org/elasticsearch/search/query/MultiMatchQueryIT.java +++ b/core/src/test/java/org/elasticsearch/search/query/MultiMatchQueryIT.java @@ -264,19 +264,19 @@ public void testSingleField() throws NoSuchFieldException, IllegalAccessExceptio } MultiMatchQueryBuilder multiMatchQueryBuilder = randomizeType(multiMatchQuery(builder.toString(), field)); SearchResponse multiMatchResp = client().prepareSearch("test") - // _uid sort field is a tie, in case hits have the same score, + // _id sort field is a tie, in case hits have the same score, // the hits will be sorted the same consistently .addSort("_score", SortOrder.DESC) - .addSort("_uid", SortOrder.ASC) + .addSort("_id", SortOrder.ASC) .setQuery(multiMatchQueryBuilder).get(); MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(field, builder.toString()); if (multiMatchQueryBuilder.getType() != null) { matchQueryBuilder.type(MatchQuery.Type.valueOf(multiMatchQueryBuilder.getType().matchQueryType().toString())); } SearchResponse matchResp = client().prepareSearch("test") - // _uid tie sort + // _id tie sort .addSort("_score", SortOrder.DESC) - .addSort("_uid", SortOrder.ASC) + .addSort("_id", SortOrder.ASC) .setQuery(matchQueryBuilder).get(); assertThat("field: " + field + " query: " + builder.toString(), multiMatchResp.getHits().getTotalHits(), equalTo(matchResp.getHits().getTotalHits())); SearchHits hits = multiMatchResp.getHits(); @@ -361,12 +361,12 @@ public void testEquivalence() { MultiMatchQueryBuilder multiMatchQueryBuilder = randomBoolean() ? multiMatchQuery("marvel hero captain america", "full_name", "first_name", "last_name", "category") : multiMatchQuery("marvel hero captain america", "*_name", randomBoolean() ? "category" : "categ*"); SearchResponse left = client().prepareSearch("test").setSize(numDocs) - .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_uid")) + .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) .setQuery(randomizeType(multiMatchQueryBuilder .operator(Operator.OR).type(type))).get(); SearchResponse right = client().prepareSearch("test").setSize(numDocs) - .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_uid")) + .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) .setQuery(disMaxQuery(). add(matchQuery("full_name", "marvel hero captain america")) .add(matchQuery("first_name", "marvel hero captain america")) @@ -383,12 +383,12 @@ public void testEquivalence() { MultiMatchQueryBuilder multiMatchQueryBuilder = randomBoolean() ? multiMatchQuery("captain america", "full_name", "first_name", "last_name", "category") : multiMatchQuery("captain america", "*_name", randomBoolean() ? "category" : "categ*"); SearchResponse left = client().prepareSearch("test").setSize(numDocs) - .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_uid")) + .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) .setQuery(randomizeType(multiMatchQueryBuilder .operator(op).useDisMax(false).minimumShouldMatch(minShouldMatch).type(type))).get(); SearchResponse right = client().prepareSearch("test").setSize(numDocs) - .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_uid")) + .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) .setQuery(boolQuery().minimumShouldMatch(minShouldMatch) .should(randomBoolean() ? termQuery("full_name", "captain america") : matchQuery("full_name", "captain america").operator(op)) .should(matchQuery("first_name", "captain america").operator(op)) @@ -401,12 +401,12 @@ public void testEquivalence() { { String minShouldMatch = randomBoolean() ? null : "" + between(0, 1); SearchResponse left = client().prepareSearch("test").setSize(numDocs) - .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_uid")) + .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) .setQuery(randomizeType(multiMatchQuery("capta", "full_name", "first_name", "last_name", "category") .type(MatchQuery.Type.PHRASE_PREFIX).useDisMax(false).minimumShouldMatch(minShouldMatch))).get(); SearchResponse right = client().prepareSearch("test").setSize(numDocs) - .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_uid")) + .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) .setQuery(boolQuery().minimumShouldMatch(minShouldMatch) .should(matchPhrasePrefixQuery("full_name", "capta")) .should(matchPhrasePrefixQuery("first_name", "capta")) @@ -420,17 +420,17 @@ public void testEquivalence() { SearchResponse left; if (randomBoolean()) { left = client().prepareSearch("test").setSize(numDocs) - .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_uid")) + .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) .setQuery(randomizeType(multiMatchQuery("captain america", "full_name", "first_name", "last_name", "category") .type(MatchQuery.Type.PHRASE).useDisMax(false).minimumShouldMatch(minShouldMatch))).get(); } else { left = client().prepareSearch("test").setSize(numDocs) - .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_uid")) + .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) .setQuery(randomizeType(multiMatchQuery("captain america", "full_name", "first_name", "last_name", "category") .type(MatchQuery.Type.PHRASE).tieBreaker(1.0f).minimumShouldMatch(minShouldMatch))).get(); } SearchResponse right = client().prepareSearch("test").setSize(numDocs) - .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_uid")) + .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) .setQuery(boolQuery().minimumShouldMatch(minShouldMatch) .should(matchPhraseQuery("full_name", "captain america")) .should(matchPhraseQuery("first_name", "captain america")) diff --git a/core/src/test/java/org/elasticsearch/search/slice/SliceBuilderTests.java b/core/src/test/java/org/elasticsearch/search/slice/SliceBuilderTests.java index f7ba4286c2b3a..33c4f502b7033 100644 --- a/core/src/test/java/org/elasticsearch/search/slice/SliceBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/search/slice/SliceBuilderTests.java @@ -29,12 +29,16 @@ import org.apache.lucene.search.Query; import org.apache.lucene.store.Directory; import org.apache.lucene.store.RAMDirectory; +import org.elasticsearch.Version; +import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.UidFieldMapper; @@ -156,6 +160,14 @@ public Query termQuery(Object value, @Nullable QueryShardContext context) { fieldType.setHasDocValues(false); when(context.fieldMapper(UidFieldMapper.NAME)).thenReturn(fieldType); when(context.getIndexReader()).thenReturn(reader); + Settings settings = Settings.builder() + .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 2) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0) + .build(); + IndexMetaData indexState = IndexMetaData.builder("index").settings(settings).build(); + IndexSettings indexSettings = new IndexSettings(indexState, Settings.EMPTY); + when(context.getIndexSettings()).thenReturn(indexSettings); SliceBuilder builder = new SliceBuilder(5, 10); Query query = builder.toFilter(context, 0, 1); assertThat(query, instanceOf(TermsSliceQuery.class)); diff --git a/core/src/test/java/org/elasticsearch/search/sort/FieldSortIT.java b/core/src/test/java/org/elasticsearch/search/sort/FieldSortIT.java index 4bddc1c556adc..91d6686714fb6 100644 --- a/core/src/test/java/org/elasticsearch/search/sort/FieldSortIT.java +++ b/core/src/test/java/org/elasticsearch/search/sort/FieldSortIT.java @@ -1356,7 +1356,9 @@ public void testSortMetaField() throws Exception { SearchHit[] hits = searchResponse.getHits().getHits(); BytesRef previous = order == SortOrder.ASC ? new BytesRef() : UnicodeUtil.BIG_TERM; for (int i = 0; i < hits.length; ++i) { - final BytesRef uid = new BytesRef(Uid.createUid(hits[i].getType(), hits[i].getId())); + String uidString = Uid.createUid(hits[i].getType(), hits[i].getId()); + final BytesRef uid = new BytesRef(uidString); + assertEquals(uidString, hits[i].getSortValues()[0]); assertThat(previous, order == SortOrder.ASC ? lessThan(uid) : greaterThan(uid)); previous = uid; } diff --git a/docs/reference/mapping/fields/id-field.asciidoc b/docs/reference/mapping/fields/id-field.asciidoc index 94c95a8bbeba5..82447fc44297d 100644 --- a/docs/reference/mapping/fields/id-field.asciidoc +++ b/docs/reference/mapping/fields/id-field.asciidoc @@ -7,9 +7,7 @@ indexed as its value can be derived automatically from the <> field. The value of the `_id` field is accessible in certain queries (`term`, -`terms`, `match`, `query_string`, `simple_query_string`), but -_not_ in aggregations, scripts or when sorting, where the <> -field should be used instead: +`terms`, `match`, `query_string`, `simple_query_string`). [source,js] -------------------------- @@ -36,3 +34,9 @@ GET my_index/_search // CONSOLE <1> Querying on the `_id` field (also see the <>) + +The value of the `_id` field is also accessible in aggregations or for sorting, +but doing so is discouraged as it requires to load a lot of data in memory. In +case sorting or aggregating on the `_id` field is required, it is advised to +duplicate the content of the `_id` field in another field that has `doc_values` +enabled. diff --git a/docs/reference/mapping/fields/uid-field.asciidoc b/docs/reference/mapping/fields/uid-field.asciidoc index bad9eb4768b47..a1a3e8d14bf2a 100644 --- a/docs/reference/mapping/fields/uid-field.asciidoc +++ b/docs/reference/mapping/fields/uid-field.asciidoc @@ -1,6 +1,10 @@ [[mapping-uid-field]] === `_uid` field +deprecated[6.0.0, Now that types have been removed, documents are uniquely +identified by their `_id` and the `_uid` field has only been kept as a view +over the `_id` field for backward compatibility.] + Each document indexed is associated with a <> (see <>) and an <>. These values are combined as `{type}#{id}` and indexed as the `_uid` field. @@ -20,7 +24,11 @@ PUT my_index/my_type/2?refresh=true { "text": "Document with ID 2" } +-------------------------- +// CONSOLE +[source,js] +-------------------------- GET my_index/_search { "query": { @@ -54,6 +62,8 @@ GET my_index/_search } -------------------------- // CONSOLE +// TEST[continued] +// TEST[warning:Fielddata access on the _uid field is deprecated, use _id instead] <1> Querying on the `_uid` field (also see the <>) <2> Aggregating on the `_uid` field diff --git a/docs/reference/migration/migrate_6_0/rest.asciidoc b/docs/reference/migration/migrate_6_0/rest.asciidoc index cfd8a9511a2ee..d7e55f0942fe9 100644 --- a/docs/reference/migration/migrate_6_0/rest.asciidoc +++ b/docs/reference/migration/migrate_6_0/rest.asciidoc @@ -53,3 +53,8 @@ response in the event there is at least one failure. In previous versions of Elasticsearch, delete by query requests without an explicit query were accepted, match_all was used as the default query and all documents were deleted as a result. From version 6.0.0, delete by query requests require an explicit query. + +=== DELETE document calls now implicitly create the type + +Running `DELETE index/type/id` now implicitly creates `type` with a default +mapping if it did not exist yet. diff --git a/docs/reference/migration/migrate_6_0/search.asciidoc b/docs/reference/migration/migrate_6_0/search.asciidoc index 7b080eeb3be83..80d67eae72d47 100644 --- a/docs/reference/migration/migrate_6_0/search.asciidoc +++ b/docs/reference/migration/migrate_6_0/search.asciidoc @@ -88,3 +88,11 @@ produces. BM25 is recommended instead. See https://issues.apache.org/jira/browse/LUCENE-7347[`LUCENE-7347`] for more information. + +==== Fielddata on _uid + +Fielddata on `_uid` is deprecated. It is possible to switch to `_id` instead +but the only reason why it has not been deprecated too is because it is used +for the `random_score` function. If you really need access to the id of +documents for sorting, aggregations or search scripts, the recommandation is +to duplicate the id as a field in the document. diff --git a/docs/reference/search/request/search-after.asciidoc b/docs/reference/search/request/search-after.asciidoc index bc94af935c2e3..87f5abba8b2e3 100644 --- a/docs/reference/search/request/search-after.asciidoc +++ b/docs/reference/search/request/search-after.asciidoc @@ -21,7 +21,7 @@ GET twitter/tweet/_search }, "sort": [ {"date": "asc"}, - {"_uid": "desc"} + {"_id": "desc"} ] } -------------------------------------------------- @@ -30,7 +30,7 @@ GET twitter/tweet/_search NOTE: A field with one unique value per document should be used as the tiebreaker of the sort specification. Otherwise the sort order for documents that have the same sort values would be undefined. The recommended way is to use -the field `_uid` which is certain to contain one unique value for each document. +the field `_id` which is certain to contain one unique value for each document. The result from the above request includes an array of `sort values` for each document. These `sort values` can be used in conjunction with the `search_after` parameter to start returning results "after" any @@ -47,10 +47,10 @@ GET twitter/tweet/_search "title" : "elasticsearch" } }, - "search_after": [1463538857, "tweet#654323"], + "search_after": [1463538857, "654323"], "sort": [ {"date": "asc"}, - {"_uid": "desc"} + {"_id": "desc"} ] } -------------------------------------------------- diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/90_search_after.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/90_search_after.yaml index 8135d25399c1b..920f55ae1be5d 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search/90_search_after.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/90_search_after.yaml @@ -30,6 +30,9 @@ setup: --- "search with search_after parameter": + - skip: + version: " - 5.99.99" + reason: fielddata on _id is only available as of 6.0.0 - do: search: index: test @@ -39,14 +42,14 @@ setup: query: match: foo: bar - sort: [{ age: desc }, { _uid: desc }] + sort: [{ age: desc }, { _id: desc }] - match: {hits.total: 3 } - length: {hits.hits: 1 } - match: {hits.hits.0._index: test } - match: {hits.hits.0._type: test } - match: {hits.hits.0._id: "172" } - - match: {hits.hits.0.sort: [24, "test#172"] } + - match: {hits.hits.0.sort: [24, "172"] } - do: search: @@ -57,15 +60,15 @@ setup: query: match: foo: bar - sort: [{ age: desc }, { _uid: desc }] - search_after: [24, "test#172"] + sort: [{ age: desc }, { _id: desc }] + search_after: [24, "172"] - match: {hits.total: 3 } - length: {hits.hits: 1 } - match: {hits.hits.0._index: test } - match: {hits.hits.0._type: test } - match: {hits.hits.0._id: "42" } - - match: {hits.hits.0.sort: [18, "test#42"] } + - match: {hits.hits.0.sort: [18, "42"] } - do: search: @@ -76,15 +79,15 @@ setup: query: match: foo: bar - sort: [ { age: desc }, { _uid: desc } ] - search_after: [18, "test#42"] + sort: [ { age: desc }, { _id: desc } ] + search_after: [18, "42"] - match: {hits.total: 3} - length: {hits.hits: 1 } - match: {hits.hits.0._index: test } - match: {hits.hits.0._type: test } - match: {hits.hits.0._id: "1" } - - match: {hits.hits.0.sort: [18, "test#1"] } + - match: {hits.hits.0.sort: [18, "1"] } - do: search: @@ -95,8 +98,8 @@ setup: query: match: foo: bar - sort: [{ age: desc }, { _uid: desc } ] - search_after: [18, "test#1"] + sort: [{ age: desc }, { _id: desc } ] + search_after: [18, "1"] - match: {hits.total: 3} - length: {hits.hits: 0 } diff --git a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java index 80a0e9486f1ae..f0c4a9c44d0a5 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java @@ -52,10 +52,9 @@ import org.elasticsearch.index.engine.EngineFactory; import org.elasticsearch.index.fielddata.IndexFieldDataCache; import org.elasticsearch.index.fielddata.IndexFieldDataService; +import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.SourceToParse; -import org.elasticsearch.index.mapper.Uid; -import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.seqno.SequenceNumbersService; import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.index.store.DirectoryService; @@ -444,17 +443,17 @@ private Store.MetadataSnapshot getMetadataSnapshotOrEmpty(IndexShard replica) th return result; } - protected Set getShardDocUIDs(final IndexShard shard) throws IOException { + protected Set getShardDocUIDs(final IndexShard shard) throws IOException { shard.refresh("get_uids"); try (Engine.Searcher searcher = shard.acquireSearcher("test")) { - Set ids = new HashSet<>(); + Set ids = new HashSet<>(); for (LeafReaderContext leafContext : searcher.reader().leaves()) { LeafReader reader = leafContext.reader(); Bits liveDocs = reader.getLiveDocs(); for (int i = 0; i < reader.maxDoc(); i++) { if (liveDocs == null || liveDocs.get(i)) { - Document uuid = reader.document(i, Collections.singleton(UidFieldMapper.NAME)); - ids.add(Uid.createUid(uuid.get(UidFieldMapper.NAME))); + Document uuid = reader.document(i, Collections.singleton(IdFieldMapper.NAME)); + ids.add(uuid.get(IdFieldMapper.NAME)); } } } @@ -466,10 +465,10 @@ protected void assertDocCount(IndexShard shard, int docDount) throws IOException assertThat(getShardDocUIDs(shard), hasSize(docDount)); } - protected void assertDocs(IndexShard shard, Uid... uids) throws IOException { - final Set shardDocUIDs = getShardDocUIDs(shard); - assertThat(shardDocUIDs, contains(uids)); - assertThat(shardDocUIDs, hasSize(uids.length)); + protected void assertDocs(IndexShard shard, String... ids) throws IOException { + final Set shardDocUIDs = getShardDocUIDs(shard); + assertThat(shardDocUIDs, contains(ids)); + assertThat(shardDocUIDs, hasSize(ids.length)); } From a404f0aca75cb1be3ca161636a8fa787a001281f Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Tue, 9 May 2017 16:54:44 +0200 Subject: [PATCH 250/619] Cleanup BWC for single shard optimization The BWC layer is needed only for pre 5.3 indices. --- .../action/search/SearchTransportService.java | 7 ++++--- .../java/org/elasticsearch/action/search/SearchType.java | 9 +++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java index 3699d3d01392e..2ecfc213f9761 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java @@ -120,14 +120,15 @@ public void sendExecuteQuery(Transport.Connection connection, final ShardSearchT // this used to be the QUERY_AND_FETCH which doesn't exists anymore. final boolean fetchDocuments = request.numberOfShards() == 1; Supplier supplier = fetchDocuments ? QueryFetchSearchResult::new : QuerySearchResult::new; - if (connection.getVersion().onOrBefore(Version.V_5_3_0_UNRELEASED) && fetchDocuments) { - if (connection.getVersion().before(Version.V_5_3_0_UNRELEASED) && request.scroll() != null) { + if (connection.getVersion().before(Version.V_5_3_0_UNRELEASED) && fetchDocuments) { + // this is a BWC layer for pre 5.3 indices + if (request.scroll() != null) { /** * This is needed for nodes pre 5.3 when the single shard optimization is used. * These nodes will set the last emitted doc only if the removed `query_and_fetch` search type is set * in the request. See {@link SearchType}. */ - request.searchType(SearchType.fromId((byte) 3)); + request.searchType(SearchType.QUERY_AND_FETCH); } // TODO this BWC layer can be removed once this is back-ported to 5.3 transportService.sendChildRequest(connection, QUERY_FETCH_ACTION_NAME, request, task, diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchType.java b/core/src/main/java/org/elasticsearch/action/search/SearchType.java index 7dd4aebc5e01e..b800120408739 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchType.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchType.java @@ -36,9 +36,14 @@ public enum SearchType { * document content. The return number of hits is exactly as specified in size, since they are the only ones that * are fetched. This is very handy when the index has a lot of shards (not replicas, shard id groups). */ - QUERY_THEN_FETCH((byte) 1); + QUERY_THEN_FETCH((byte) 1), // 2 used to be DFS_QUERY_AND_FETCH - // 3 used to be QUERY_AND_FETCH + + /** + * Only used for pre 5.3 request where this type is still needed + */ + @Deprecated + QUERY_AND_FETCH((byte) 3); /** * The default search type ({@link #QUERY_THEN_FETCH}. From b8dede06b5fd88e1e9b3ccb2a5addbf586a1f185 Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Tue, 9 May 2017 18:33:13 +0200 Subject: [PATCH 251/619] Fixed prerelease URLs for RPM and Deb repos in docs --- docs/reference/setup/install/deb.asciidoc | 4 ++-- docs/reference/setup/install/rpm.asciidoc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/reference/setup/install/deb.asciidoc b/docs/reference/setup/install/deb.asciidoc index c73ed327e908c..179aa244072b2 100644 --- a/docs/reference/setup/install/deb.asciidoc +++ b/docs/reference/setup/install/deb.asciidoc @@ -47,7 +47,7 @@ ifeval::["{release-state}"=="released"] ["source","sh",subs="attributes,callouts"] -------------------------------------------------- -echo "deb https://artifacts.elastic.co/packages/{major-version}-prerelease/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-{major-version}.list +echo "deb https://artifacts.elastic.co/packages/{major-version}/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-{major-version}.list -------------------------------------------------- endif::[] @@ -56,7 +56,7 @@ ifeval::["{release-state}"=="prerelease"] ["source","sh",subs="attributes,callouts"] -------------------------------------------------- -echo "deb https://artifacts.elastic.co/packages/{major-version}/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-{major-version}.list +echo "deb https://artifacts.elastic.co/packages/{major-version}-prerelease/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-{major-version}.list -------------------------------------------------- endif::[] diff --git a/docs/reference/setup/install/rpm.asciidoc b/docs/reference/setup/install/rpm.asciidoc index 269eb6720e60e..27806c2f98692 100644 --- a/docs/reference/setup/install/rpm.asciidoc +++ b/docs/reference/setup/install/rpm.asciidoc @@ -48,7 +48,7 @@ ifeval::["{release-state}"=="released"] -------------------------------------------------- [elasticsearch-{major-version}] name=Elasticsearch repository for {major-version} packages -baseurl=https://artifacts.elastic.co/packages/{major-version}-prerelease/yum +baseurl=https://artifacts.elastic.co/packages/{major-version}/yum gpgcheck=1 gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch enabled=1 @@ -64,7 +64,7 @@ ifeval::["{release-state}"=="prerelease"] -------------------------------------------------- [elasticsearch-{major-version}] name=Elasticsearch repository for {major-version} packages -baseurl=https://artifacts.elastic.co/packages/{major-version}/yum +baseurl=https://artifacts.elastic.co/packages/{major-version}-prerelease/yum gpgcheck=1 gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch enabled=1 From b6c714ccc8a2f670de2c0d7cd872335a4c5410c0 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Tue, 9 May 2017 18:51:49 +0200 Subject: [PATCH 252/619] Fix BWC for query_and_fetch --- .../org/elasticsearch/action/search/TransportSearchAction.java | 1 + .../test/java/org/elasticsearch/search/SearchRequestTests.java | 3 ++- .../org/elasticsearch/search/RandomSearchRequestGenerator.java | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index f65597a966bc2..720fb17ae948b 100644 --- a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -342,6 +342,7 @@ private AbstractSearchAsyncAction searchAsyncAction(SearchTask task, SearchReque aliasFilter, concreteIndexBoosts, searchPhaseController, executor, searchRequest, listener, shardIterators, timeProvider, clusterStateVersion, task); break; + case QUERY_AND_FETCH: case QUERY_THEN_FETCH: searchAsyncAction = new SearchQueryThenFetchAsyncAction(logger, searchTransportService, connectionLookup, aliasFilter, concreteIndexBoosts, searchPhaseController, executor, searchRequest, listener, shardIterators, diff --git a/core/src/test/java/org/elasticsearch/search/SearchRequestTests.java b/core/src/test/java/org/elasticsearch/search/SearchRequestTests.java index 47fc61fb6a697..fb83bcfaa4543 100644 --- a/core/src/test/java/org/elasticsearch/search/SearchRequestTests.java +++ b/core/src/test/java/org/elasticsearch/search/SearchRequestTests.java @@ -96,7 +96,8 @@ private SearchRequest mutate(SearchRequest searchRequest) throws IOException { mutators.add(() -> mutation.requestCache((randomValueOtherThan(searchRequest.requestCache(), () -> randomBoolean())))); mutators.add(() -> mutation .scroll(randomValueOtherThan(searchRequest.scroll(), () -> new Scroll(new TimeValue(randomNonNegativeLong() % 100000))))); - mutators.add(() -> mutation.searchType(randomValueOtherThan(searchRequest.searchType(), () -> randomFrom(SearchType.values())))); + mutators.add(() -> mutation.searchType(randomValueOtherThan(searchRequest.searchType(), + () -> randomFrom(SearchType.DFS_QUERY_THEN_FETCH, SearchType.QUERY_THEN_FETCH)))); mutators.add(() -> mutation.source(randomValueOtherThan(searchRequest.source(), this::createSearchSourceBuilder))); randomFrom(mutators).run(); return mutation; diff --git a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java index 18448b5829c13..4cd46fe5076d3 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java +++ b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java @@ -104,7 +104,7 @@ public static SearchRequest randomSearchRequest(Supplier ra searchRequest.scroll(randomPositiveTimeValue()); } if (randomBoolean()) { - searchRequest.searchType(randomFrom(SearchType.values())); + searchRequest.searchType(randomFrom(SearchType.DFS_QUERY_THEN_FETCH, SearchType.QUERY_THEN_FETCH)); } if (randomBoolean()) { searchRequest.source(randomSearchSourceBuilder.get()); From 428390865c9c6de7497170236a80f0a62a73b924 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Tue, 9 May 2017 12:27:53 -0400 Subject: [PATCH 253/619] Tests: Give stats tests long to wait for listener This test waited 10 seconds for a refresh listener to appear in the stats. It turns out that in our NFS testing infrastructure this can take a lot longer than 10 seconds. The error reported here: https://elasticsearch-ci.elastic.co/job/elastic+elasticsearch+master+nfs/257/consoleFull has it taking something like 15 seconds. This bumps the timeout to a solid minute. Closes #24417 --- .../action/admin/indices/stats/IndicesStatsTests.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java b/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java index 6c1f44ea69a14..be84a8880641f 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java @@ -118,7 +118,6 @@ public void testCommitStats() throws Exception { } } - @TestLogging("_root:debug") public void testRefreshListeners() throws Exception { // Create an index without automatic refreshes createIndex("test", Settings.builder().put("refresh_interval", -1).build()); @@ -127,8 +126,9 @@ public void testRefreshListeners() throws Exception { ActionFuture index = client().prepareIndex("test", "test", "test").setSource("test", "test") .setRefreshPolicy(RefreshPolicy.WAIT_UNTIL).execute(); - // Wait for the refresh listener to appear in the stats - long end = System.nanoTime() + TimeUnit.SECONDS.toNanos(10); + // Wait for the refresh listener to appear in the stats. Wait a long time because NFS tests can be quite slow! + logger.info("starting to wait"); + long end = System.nanoTime() + TimeUnit.MINUTES.toNanos(1); while (true) { IndicesStatsResponse stats = client().admin().indices().prepareStats("test").clear().setRefresh(true).setDocs(true).get(); CommonStats common = stats.getIndices().get("test").getTotal(); @@ -138,6 +138,7 @@ public void testRefreshListeners() throws Exception { break; } if (end - System.nanoTime() < 0) { + logger.info("timed out"); fail("didn't get a refresh listener in time: " + Strings.toString(common)); } } From bb06d8ec4f3290c05ca50e19649e5ebd1f8840fa Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Tue, 9 May 2017 14:50:49 -0400 Subject: [PATCH 254/619] Allow plugins to build pre-configured token filters (#24223) This changes the way we register pre-configured token filters so that plugins can declare them and starts to move all of the pre-configured token filters out of core. It doesn't finish the job because doing so would make the change unreviewably large. So this PR includes a shim that keeps the "old" way of registering pre-configured token filters around. The Lowercase token filter is special because there is a "special" interaction between it and the lowercase tokenizer. I'm not sure exactly what to do about it so for now I'm leaving it alone with the intent of figuring out what to do with it in a followup. This also renames these pre-configured token filters from "pre-built" to "pre-configured" because that seemed like a more descriptive name. This is a part of #23658 --- .../index/analysis/AnalysisRegistry.java | 37 ++-- .../PreBuiltTokenFilterFactoryFactory.java | 50 ----- .../analysis/PreConfiguredTokenFilter.java | 123 ++++++++++++ .../indices/analysis/AnalysisModule.java | 46 ++++- .../analysis/PreBuiltCacheFactory.java | 2 +- .../analysis/PreBuiltTokenFilters.java | 176 +----------------- .../elasticsearch/plugins/AnalysisPlugin.java | 14 ++ .../indices/TransportAnalyzeActionTests.java | 35 ++-- .../elasticsearch/index/IndexModuleTests.java | 41 ++-- .../index/analysis/AnalysisFactoryTests.java | 26 --- .../index/analysis/AnalysisRegistryTests.java | 71 ++++--- .../analysis/CoreAnalysisFactoryTests.java | 37 ++++ .../index/analysis/CustomNormalizerTests.java | 58 +++--- .../index/mapper/KeywordFieldMapperTests.java | 23 ++- .../analysis/common/CommonAnalysisPlugin.java | 72 ++++++- .../common/CommonAnalysisFactoryTests.java | 36 +++- .../analysis-common/40_token_filters.yaml | 12 ++ .../analysis/AnalysisICUFactoryTests.java | 6 +- .../AnalysisKuromojiFactoryTests.java | 6 +- .../AnalysisPhoneticFactoryTests.java | 6 +- .../AnalysisSmartChineseFactoryTests.java | 7 +- .../analysis/AnalysisPolishFactoryTests.java | 6 +- .../analysis}/AnalysisFactoryTestCase.java | 116 +++++++----- 23 files changed, 579 insertions(+), 427 deletions(-) delete mode 100644 core/src/main/java/org/elasticsearch/index/analysis/PreBuiltTokenFilterFactoryFactory.java create mode 100644 core/src/main/java/org/elasticsearch/index/analysis/PreConfiguredTokenFilter.java delete mode 100644 core/src/test/java/org/elasticsearch/index/analysis/AnalysisFactoryTests.java create mode 100644 core/src/test/java/org/elasticsearch/index/analysis/CoreAnalysisFactoryTests.java rename test/framework/src/main/java/org/elasticsearch/{ => indices/analysis}/AnalysisFactoryTestCase.java (89%) diff --git a/core/src/main/java/org/elasticsearch/index/analysis/AnalysisRegistry.java b/core/src/main/java/org/elasticsearch/index/analysis/AnalysisRegistry.java index 36357afe678b5..b438cd5af4155 100644 --- a/core/src/main/java/org/elasticsearch/index/analysis/AnalysisRegistry.java +++ b/core/src/main/java/org/elasticsearch/index/analysis/AnalysisRegistry.java @@ -36,7 +36,6 @@ import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; import org.elasticsearch.indices.analysis.PreBuiltAnalyzers; import org.elasticsearch.indices.analysis.PreBuiltCharFilters; -import org.elasticsearch.indices.analysis.PreBuiltTokenFilters; import org.elasticsearch.indices.analysis.PreBuiltTokenizers; import java.io.Closeable; @@ -59,7 +58,7 @@ public final class AnalysisRegistry implements Closeable { public static final String INDEX_ANALYSIS_CHAR_FILTER = "index.analysis.char_filter"; public static final String INDEX_ANALYSIS_FILTER = "index.analysis.filter"; public static final String INDEX_ANALYSIS_TOKENIZER = "index.analysis.tokenizer"; - private final PrebuiltAnalysis prebuiltAnalysis = new PrebuiltAnalysis(); + private final PrebuiltAnalysis prebuiltAnalysis; private final Map cachedAnalyzer = new ConcurrentHashMap<>(); private final Environment environment; @@ -74,13 +73,15 @@ public AnalysisRegistry(Environment environment, Map> tokenFilters, Map> tokenizers, Map>> analyzers, - Map>> normalizers) { + Map>> normalizers, + Map preConfiguredTokenFilters) { this.environment = environment; this.charFilters = unmodifiableMap(charFilters); this.tokenFilters = unmodifiableMap(tokenFilters); this.tokenizers = unmodifiableMap(tokenizers); this.analyzers = unmodifiableMap(analyzers); this.normalizers = unmodifiableMap(normalizers); + prebuiltAnalysis = new PrebuiltAnalysis(preConfiguredTokenFilters); } /** @@ -305,8 +306,8 @@ public String toString() { } private Map buildMapping(Component component, IndexSettings settings, Map settingsMap, - Map> providerMap, Map> defaultInstance) - throws IOException { + Map> providerMap, + Map> defaultInstance) throws IOException { Settings defaultSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, settings.getIndexVersionCreated()).build(); Map factories = new HashMap<>(); for (Map.Entry entry : settingsMap.entrySet()) { @@ -344,7 +345,7 @@ private Map buildMapping(Component component, IndexSettings setti } // go over the char filters in the bindings and register the ones that are not configured - for (Map.Entry> entry : providerMap.entrySet()) { + for (Map.Entry> entry : providerMap.entrySet()) { String name = entry.getKey(); AnalysisModule.AnalysisProvider provider = entry.getValue(); // we don't want to re-register one that already exists @@ -365,7 +366,7 @@ private Map buildMapping(Component component, IndexSettings setti factories.put(name, instance); } - for (Map.Entry> entry : defaultInstance.entrySet()) { + for (Map.Entry> entry : defaultInstance.entrySet()) { final String name = entry.getKey(); final AnalysisModule.AnalysisProvider provider = entry.getValue(); if (factories.containsKey(name) == false) { @@ -378,7 +379,8 @@ private Map buildMapping(Component component, IndexSettings setti return factories; } - private AnalysisProvider getAnalysisProvider(Component component, Map> providerMap, String name, String typeName) { + private AnalysisProvider getAnalysisProvider(Component component, Map> providerMap, + String name, String typeName) { if (typeName == null) { throw new IllegalArgumentException(component + " [" + name + "] must specify either an analyzer type, or a tokenizer"); } @@ -393,13 +395,12 @@ private static class PrebuiltAnalysis implements Closeable { final Map>> analyzerProviderFactories; final Map> tokenizerFactories; - final Map> tokenFilterFactories; + final Map> tokenFilterFactories; final Map> charFilterFactories; - private PrebuiltAnalysis() { + private PrebuiltAnalysis(Map preConfiguredTokenFilters) { Map analyzerProviderFactories = new HashMap<>(); Map tokenizerFactories = new HashMap<>(); - Map tokenFilterFactories = new HashMap<>(); Map charFilterFactories = new HashMap<>(); // Analyzers for (PreBuiltAnalyzers preBuiltAnalyzerEnum : PreBuiltAnalyzers.values()) { @@ -418,17 +419,6 @@ private PrebuiltAnalysis() { tokenizerFactories.put("edgeNGram", new PreBuiltTokenizerFactoryFactory(PreBuiltTokenizers.EDGE_NGRAM.getTokenizerFactory(Version.CURRENT))); tokenizerFactories.put("PathHierarchy", new PreBuiltTokenizerFactoryFactory(PreBuiltTokenizers.PATH_HIERARCHY.getTokenizerFactory(Version.CURRENT))); - - // Token filters - for (PreBuiltTokenFilters preBuiltTokenFilter : PreBuiltTokenFilters.values()) { - String name = preBuiltTokenFilter.name().toLowerCase(Locale.ROOT); - tokenFilterFactories.put(name, new PreBuiltTokenFilterFactoryFactory(preBuiltTokenFilter.getTokenFilterFactory(Version.CURRENT))); - } - // Token filter aliases - tokenFilterFactories.put("nGram", new PreBuiltTokenFilterFactoryFactory(PreBuiltTokenFilters.NGRAM.getTokenFilterFactory(Version.CURRENT))); - tokenFilterFactories.put("edgeNGram", new PreBuiltTokenFilterFactoryFactory(PreBuiltTokenFilters.EDGE_NGRAM.getTokenFilterFactory(Version.CURRENT))); - - // Char Filters for (PreBuiltCharFilters preBuiltCharFilter : PreBuiltCharFilters.values()) { String name = preBuiltCharFilter.name().toLowerCase(Locale.ROOT); @@ -436,10 +426,11 @@ private PrebuiltAnalysis() { } // Char filter aliases charFilterFactories.put("htmlStrip", new PreBuiltCharFilterFactoryFactory(PreBuiltCharFilters.HTML_STRIP.getCharFilterFactory(Version.CURRENT))); + this.analyzerProviderFactories = Collections.unmodifiableMap(analyzerProviderFactories); this.charFilterFactories = Collections.unmodifiableMap(charFilterFactories); - this.tokenFilterFactories = Collections.unmodifiableMap(tokenFilterFactories); this.tokenizerFactories = Collections.unmodifiableMap(tokenizerFactories); + tokenFilterFactories = preConfiguredTokenFilters; } public AnalysisModule.AnalysisProvider getCharFilterFactory(String name) { diff --git a/core/src/main/java/org/elasticsearch/index/analysis/PreBuiltTokenFilterFactoryFactory.java b/core/src/main/java/org/elasticsearch/index/analysis/PreBuiltTokenFilterFactoryFactory.java deleted file mode 100644 index 52c9f2851a29f..0000000000000 --- a/core/src/main/java/org/elasticsearch/index/analysis/PreBuiltTokenFilterFactoryFactory.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.index.analysis; - -import org.elasticsearch.Version; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.index.IndexSettings; -import org.elasticsearch.indices.analysis.AnalysisModule; -import org.elasticsearch.indices.analysis.PreBuiltTokenFilters; - -import java.io.IOException; - -public class PreBuiltTokenFilterFactoryFactory implements AnalysisModule.AnalysisProvider { - - private final TokenFilterFactory tokenFilterFactory; - - public PreBuiltTokenFilterFactoryFactory(TokenFilterFactory tokenFilterFactory) { - this.tokenFilterFactory = tokenFilterFactory; - } - - @Override - public TokenFilterFactory get(IndexSettings indexSettings, Environment environment, String name, Settings settings) throws IOException { - Version indexVersion = Version.indexCreated(settings); - if (!Version.CURRENT.equals(indexVersion)) { - PreBuiltTokenFilters preBuiltTokenFilters = PreBuiltTokenFilters.getOrDefault(name, null); - if (preBuiltTokenFilters != null) { - return preBuiltTokenFilters.getTokenFilterFactory(indexVersion); - } - } - return tokenFilterFactory; - } -} diff --git a/core/src/main/java/org/elasticsearch/index/analysis/PreConfiguredTokenFilter.java b/core/src/main/java/org/elasticsearch/index/analysis/PreConfiguredTokenFilter.java new file mode 100644 index 0000000000000..b410e8fb70e85 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/index/analysis/PreConfiguredTokenFilter.java @@ -0,0 +1,123 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.index.analysis; + +import org.apache.lucene.analysis.TokenFilter; +import org.apache.lucene.analysis.TokenStream; +import org.elasticsearch.Version; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.index.IndexSettings; +import org.elasticsearch.indices.analysis.AnalysisModule; +import org.elasticsearch.indices.analysis.PreBuiltCacheFactory; + +import java.io.IOException; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * Provides pre-configured, shared {@link TokenFilter}s. + */ +public final class PreConfiguredTokenFilter implements AnalysisModule.AnalysisProvider { + private final String name; + private final boolean useFilterForMultitermQueries; + private final PreBuiltCacheFactory.PreBuiltCache cache; + private final BiFunction create; + + /** + * Standard ctor with all the power. + */ + public PreConfiguredTokenFilter(String name, boolean useFilterForMultitermQueries, + PreBuiltCacheFactory.CachingStrategy cachingStrategy, BiFunction create) { + this.name = name; + this.useFilterForMultitermQueries = useFilterForMultitermQueries; + cache = PreBuiltCacheFactory.getCache(cachingStrategy); + this.create = create; + } + + /** + * Convenience ctor for token streams that don't vary based on version. + */ + public PreConfiguredTokenFilter(String name, boolean useFilterForMultitermQueries, + PreBuiltCacheFactory.CachingStrategy cachingStrategy, Function create) { + this(name, useFilterForMultitermQueries, cachingStrategy, (input, version) -> create.apply(input)); + // TODO why oh why aren't these all CachingStrategy.ONE? They *can't* vary based on version because they don't get it, right?! + } + + @Override + public TokenFilterFactory get(IndexSettings indexSettings, Environment environment, String name, Settings settings) throws IOException { + return getTokenFilterFactory(Version.indexCreated(settings)); + } + + /** + * The name of the {@link TokenFilter} in the API. + */ + public String getName() { + return name; + } + + /** + * Can this {@link TokenFilter} be used in multi-term queries? + */ + public boolean shouldUseFilterForMultitermQueries() { + return useFilterForMultitermQueries; + } + + private interface MultiTermAwareTokenFilterFactory extends TokenFilterFactory, MultiTermAwareComponent {} + + private synchronized TokenFilterFactory getTokenFilterFactory(final Version version) { + TokenFilterFactory factory = cache.get(version); + if (factory == null) { + if (useFilterForMultitermQueries) { + factory = new MultiTermAwareTokenFilterFactory() { + @Override + public String name() { + return name; + } + + @Override + public TokenStream create(TokenStream tokenStream) { + return create.apply(tokenStream, version); + } + + @Override + public Object getMultiTermComponent() { + return this; + } + }; + } else { + factory = new TokenFilterFactory() { + @Override + public String name() { + return name; + } + + @Override + public TokenStream create(TokenStream tokenStream) { + return create.apply(tokenStream, version); + } + }; + } + cache.put(version, factory); + } + + return factory; + } +} diff --git a/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java b/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java index 26a4e4c1c5c3a..06ef3e315c6ab 100644 --- a/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java +++ b/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java @@ -19,6 +19,8 @@ package org.elasticsearch.indices.analysis; +import org.apache.lucene.analysis.LowerCaseFilter; +import org.apache.lucene.analysis.standard.StandardFilter; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.NamedRegistry; @@ -101,6 +103,7 @@ import org.elasticsearch.index.analysis.PersianNormalizationFilterFactory; import org.elasticsearch.index.analysis.PorterStemTokenFilterFactory; import org.elasticsearch.index.analysis.PortugueseAnalyzerProvider; +import org.elasticsearch.index.analysis.PreConfiguredTokenFilter; import org.elasticsearch.index.analysis.ReverseTokenFilterFactory; import org.elasticsearch.index.analysis.RomanianAnalyzerProvider; import org.elasticsearch.index.analysis.RussianAnalyzerProvider; @@ -138,11 +141,15 @@ import org.elasticsearch.index.analysis.WhitespaceTokenizerFactory; import org.elasticsearch.index.analysis.compound.DictionaryCompoundWordTokenFilterFactory; import org.elasticsearch.index.analysis.compound.HyphenationCompoundWordTokenFilterFactory; +import org.elasticsearch.indices.analysis.PreBuiltCacheFactory.CachingStrategy; import org.elasticsearch.plugins.AnalysisPlugin; import java.io.IOException; import java.util.List; +import java.util.Locale; +import java.util.Map; +import static java.util.Collections.unmodifiableMap; import static org.elasticsearch.plugins.AnalysisPlugin.requriesAnalysisSettings; /** @@ -169,8 +176,11 @@ public AnalysisModule(Environment environment, List plugins) thr NamedRegistry> tokenizers = setupTokenizers(plugins); NamedRegistry>> analyzers = setupAnalyzers(plugins); NamedRegistry>> normalizers = setupNormalizers(plugins); + + Map preConfiguredTokenFilters = setupPreConfiguredTokenFilters(plugins); + analysisRegistry = new AnalysisRegistry(environment, charFilters.getRegistry(), tokenFilters.getRegistry(), tokenizers - .getRegistry(), analyzers.getRegistry(), normalizers.getRegistry()); + .getRegistry(), analyzers.getRegistry(), normalizers.getRegistry(), preConfiguredTokenFilters); } HunspellService getHunspellService() { @@ -258,6 +268,40 @@ private NamedRegistry> setupTokenFilters(Li return tokenFilters; } + static Map setupPreConfiguredTokenFilters(List plugins) { + NamedRegistry preConfiguredTokenFilters = new NamedRegistry<>("pre-configured token_filter"); + + // Add filters available in lucene-core + preConfiguredTokenFilters.register("lowercase", + new PreConfiguredTokenFilter("lowercase", true, CachingStrategy.LUCENE, LowerCaseFilter::new)); + preConfiguredTokenFilters.register("standard", + new PreConfiguredTokenFilter("standard", false, CachingStrategy.LUCENE, StandardFilter::new)); + /* Note that "stop" is available in lucene-core but it's pre-built + * version uses a set of English stop words that are in + * lucene-analyzers-common so "stop" is defined in the analysis-common + * module. */ + + // Add token filters declared in PreBuiltTokenFilters until they have all been migrated + for (PreBuiltTokenFilters preBuilt : PreBuiltTokenFilters.values()) { + switch (preBuilt) { + case LOWERCASE: + // This has been migrated but has to stick around until PreBuiltTokenizers is removed. + continue; + default: + String name = preBuilt.name().toLowerCase(Locale.ROOT); + preConfiguredTokenFilters.register(name, + new PreConfiguredTokenFilter(name, preBuilt.isMultiTermAware(), preBuilt.getCachingStrategy(), preBuilt::create)); + } + } + + for (AnalysisPlugin plugin: plugins) { + for (PreConfiguredTokenFilter filter : plugin.getPreConfiguredTokenFilters()) { + preConfiguredTokenFilters.register(filter.getName(), filter); + } + } + return unmodifiableMap(preConfiguredTokenFilters.getRegistry()); + } + private NamedRegistry> setupTokenizers(List plugins) { NamedRegistry> tokenizers = new NamedRegistry<>("tokenizer"); tokenizers.register("standard", StandardTokenizerFactory::new); diff --git a/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltCacheFactory.java b/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltCacheFactory.java index 823152e6d9e9e..8636e04f20f10 100644 --- a/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltCacheFactory.java +++ b/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltCacheFactory.java @@ -42,7 +42,7 @@ public interface PreBuiltCache { private PreBuiltCacheFactory() {} - static PreBuiltCache getCache(CachingStrategy cachingStrategy) { + public static PreBuiltCache getCache(CachingStrategy cachingStrategy) { switch (cachingStrategy) { case ONE: return new PreBuiltCacheStrategyOne<>(); diff --git a/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltTokenFilters.java b/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltTokenFilters.java index 6c58ab884db27..02f6d8aadc5e0 100644 --- a/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltTokenFilters.java +++ b/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltTokenFilters.java @@ -18,9 +18,7 @@ */ package org.elasticsearch.indices.analysis; -import org.apache.lucene.analysis.CharArraySet; import org.apache.lucene.analysis.LowerCaseFilter; -import org.apache.lucene.analysis.StopFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.ar.ArabicNormalizationFilter; import org.apache.lucene.analysis.ar.ArabicStemFilter; @@ -28,39 +26,23 @@ import org.apache.lucene.analysis.cjk.CJKBigramFilter; import org.apache.lucene.analysis.cjk.CJKWidthFilter; import org.apache.lucene.analysis.ckb.SoraniNormalizationFilter; -import org.apache.lucene.analysis.commongrams.CommonGramsFilter; import org.apache.lucene.analysis.core.DecimalDigitFilter; -import org.apache.lucene.analysis.core.StopAnalyzer; -import org.apache.lucene.analysis.core.UpperCaseFilter; import org.apache.lucene.analysis.cz.CzechStemFilter; import org.apache.lucene.analysis.de.GermanNormalizationFilter; import org.apache.lucene.analysis.de.GermanStemFilter; -import org.apache.lucene.analysis.en.KStemFilter; import org.apache.lucene.analysis.en.PorterStemFilter; import org.apache.lucene.analysis.fa.PersianNormalizationFilter; import org.apache.lucene.analysis.fr.FrenchAnalyzer; import org.apache.lucene.analysis.hi.HindiNormalizationFilter; import org.apache.lucene.analysis.in.IndicNormalizationFilter; -import org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter; import org.apache.lucene.analysis.miscellaneous.KeywordRepeatFilter; -import org.apache.lucene.analysis.miscellaneous.LengthFilter; import org.apache.lucene.analysis.miscellaneous.LimitTokenCountFilter; import org.apache.lucene.analysis.miscellaneous.ScandinavianFoldingFilter; import org.apache.lucene.analysis.miscellaneous.ScandinavianNormalizationFilter; -import org.apache.lucene.analysis.miscellaneous.TrimFilter; -import org.apache.lucene.analysis.miscellaneous.TruncateTokenFilter; -import org.apache.lucene.analysis.miscellaneous.UniqueTokenFilter; -import org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter; -import org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter; -import org.apache.lucene.analysis.ngram.EdgeNGramTokenFilter; -import org.apache.lucene.analysis.ngram.NGramTokenFilter; import org.apache.lucene.analysis.payloads.DelimitedPayloadTokenFilter; import org.apache.lucene.analysis.payloads.TypeAsPayloadTokenFilter; -import org.apache.lucene.analysis.reverse.ReverseStringFilter; import org.apache.lucene.analysis.shingle.ShingleFilter; import org.apache.lucene.analysis.snowball.SnowballFilter; -import org.apache.lucene.analysis.standard.ClassicFilter; -import org.apache.lucene.analysis.standard.StandardFilter; import org.apache.lucene.analysis.tr.ApostropheFilter; import org.apache.lucene.analysis.util.ElisionFilter; import org.elasticsearch.Version; @@ -75,77 +57,7 @@ import java.util.Locale; public enum PreBuiltTokenFilters { - - WORD_DELIMITER(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new WordDelimiterFilter(tokenStream, - WordDelimiterFilter.GENERATE_WORD_PARTS | - WordDelimiterFilter.GENERATE_NUMBER_PARTS | - WordDelimiterFilter.SPLIT_ON_CASE_CHANGE | - WordDelimiterFilter.SPLIT_ON_NUMERICS | - WordDelimiterFilter.STEM_ENGLISH_POSSESSIVE, null); - } - }, - - WORD_DELIMITER_GRAPH(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new WordDelimiterGraphFilter(tokenStream, - WordDelimiterGraphFilter.GENERATE_WORD_PARTS | - WordDelimiterGraphFilter.GENERATE_NUMBER_PARTS | - WordDelimiterGraphFilter.SPLIT_ON_CASE_CHANGE | - WordDelimiterGraphFilter.SPLIT_ON_NUMERICS | - WordDelimiterGraphFilter.STEM_ENGLISH_POSSESSIVE, null); - } - }, - - STOP(CachingStrategy.LUCENE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new StopFilter(tokenStream, StopAnalyzer.ENGLISH_STOP_WORDS_SET); - } - }, - - TRIM(CachingStrategy.LUCENE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new TrimFilter(tokenStream); - } - }, - - REVERSE(CachingStrategy.LUCENE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new ReverseStringFilter(tokenStream); - } - }, - - ASCIIFOLDING(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new ASCIIFoldingFilter(tokenStream); - } - @Override - protected boolean isMultiTermAware() { - return true; - } - }, - - LENGTH(CachingStrategy.LUCENE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new LengthFilter(tokenStream, 0, Integer.MAX_VALUE); - } - }, - - COMMON_GRAMS(CachingStrategy.LUCENE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new CommonGramsFilter(tokenStream, CharArraySet.EMPTY_SET); - } - }, - + // TODO remove this entire class when PreBuiltTokenizers no longer needs it..... LOWERCASE(CachingStrategy.LUCENE) { @Override public TokenStream create(TokenStream tokenStream, Version version) { @@ -157,73 +69,6 @@ protected boolean isMultiTermAware() { } }, - UPPERCASE(CachingStrategy.LUCENE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new UpperCaseFilter(tokenStream); - } - @Override - protected boolean isMultiTermAware() { - return true; - } - }, - - KSTEM(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new KStemFilter(tokenStream); - } - }, - - PORTER_STEM(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new PorterStemFilter(tokenStream); - } - }, - - STANDARD(CachingStrategy.LUCENE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new StandardFilter(tokenStream); - } - }, - - CLASSIC(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new ClassicFilter(tokenStream); - } - }, - - NGRAM(CachingStrategy.LUCENE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new NGramTokenFilter(tokenStream); - } - }, - - EDGE_NGRAM(CachingStrategy.LUCENE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new EdgeNGramTokenFilter(tokenStream, EdgeNGramTokenFilter.DEFAULT_MIN_GRAM_SIZE, EdgeNGramTokenFilter.DEFAULT_MAX_GRAM_SIZE); - } - }, - - UNIQUE(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new UniqueTokenFilter(tokenStream); - } - }, - - TRUNCATE(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new TruncateTokenFilter(tokenStream, 10); - } - }, - // Extended Token Filters SNOWBALL(CachingStrategy.ONE) { @Override @@ -469,10 +314,16 @@ protected boolean isMultiTermAware() { protected final PreBuiltCacheFactory.PreBuiltCache cache; + private final CachingStrategy cachingStrategy; PreBuiltTokenFilters(CachingStrategy cachingStrategy) { + this.cachingStrategy = cachingStrategy; cache = PreBuiltCacheFactory.getCache(cachingStrategy); } + public CachingStrategy getCachingStrategy() { + return cachingStrategy; + } + private interface MultiTermAwareTokenFilterFactory extends TokenFilterFactory, MultiTermAwareComponent {} public synchronized TokenFilterFactory getTokenFilterFactory(final Version version) { @@ -514,17 +365,4 @@ public TokenStream create(TokenStream tokenStream) { return factory; } - - /** - * Get a pre built TokenFilter by its name or fallback to the default one - * @param name TokenFilter name - * @param defaultTokenFilter default TokenFilter if name not found - */ - public static PreBuiltTokenFilters getOrDefault(String name, PreBuiltTokenFilters defaultTokenFilter) { - try { - return valueOf(name.toUpperCase(Locale.ROOT)); - } catch (IllegalArgumentException e) { - return defaultTokenFilter; - } - } } diff --git a/core/src/main/java/org/elasticsearch/plugins/AnalysisPlugin.java b/core/src/main/java/org/elasticsearch/plugins/AnalysisPlugin.java index 5e7e1053add58..c248c706f2321 100644 --- a/core/src/main/java/org/elasticsearch/plugins/AnalysisPlugin.java +++ b/core/src/main/java/org/elasticsearch/plugins/AnalysisPlugin.java @@ -22,19 +22,26 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.CharFilter; import org.apache.lucene.analysis.TokenFilter; +import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.Tokenizer; +import org.elasticsearch.Version; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.analysis.AnalyzerProvider; import org.elasticsearch.index.analysis.CharFilterFactory; +import org.elasticsearch.index.analysis.PreConfiguredTokenFilter; import org.elasticsearch.index.analysis.TokenFilterFactory; import org.elasticsearch.index.analysis.TokenizerFactory; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; +import org.elasticsearch.indices.analysis.PreBuiltCacheFactory; import java.io.IOException; +import java.util.List; import java.util.Map; +import java.util.function.BiFunction; +import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; /** @@ -87,6 +94,13 @@ default Map>> getA return emptyMap(); } + /** + * Override to add additional pre-configured token filters. + */ + default List getPreConfiguredTokenFilters() { + return emptyList(); + } + /** * Override to add additional hunspell {@link org.apache.lucene.analysis.hunspell.Dictionary}s. */ diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/TransportAnalyzeActionTests.java b/core/src/test/java/org/elasticsearch/action/admin/indices/TransportAnalyzeActionTests.java index 57a83b2c68081..0e1414bdbefda 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/TransportAnalyzeActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/TransportAnalyzeActionTests.java @@ -48,8 +48,8 @@ import static java.util.Collections.singletonMap; /** - * Tests for {@link TransportAnalyzeAction}. See the more "intense" version of this test in the - * {@code common-analysis} module. + * Tests for {@link TransportAnalyzeAction}. See the rest tests in the {@code analysis-common} module for places where this code gets a ton + * more exercise. */ public class TransportAnalyzeActionTests extends ESTestCase { @@ -90,7 +90,11 @@ public Map> getTokenFilters() { indexAnalyzers = registry.build(idxSettings); } + /** + * Test behavior when the named analysis component isn't defined on the index. In that case we should build with defaults. + */ public void testNoIndexAnalyzers() throws IOException { + // Refer to an analyzer by its type so we get its default configuration AnalyzeRequest request = new AnalyzeRequest(); request.analyzer("standard"); request.text("the quick brown fox"); @@ -98,33 +102,30 @@ public void testNoIndexAnalyzers() throws IOException { List tokens = analyze.getTokens(); assertEquals(4, tokens.size()); + // Refer to a token filter by its type so we get its default configuration request.analyzer(null); request.tokenizer("whitespace"); - request.addTokenFilter("lowercase"); - request.addTokenFilter("word_delimiter"); + request.addTokenFilter("mock"); request.text("the qu1ck brown fox"); analyze = TransportAnalyzeAction.analyze(request, AllFieldMapper.NAME, null, randomBoolean() ? indexAnalyzers : null, registry, environment); tokens = analyze.getTokens(); - assertEquals(6, tokens.size()); - assertEquals("qu", tokens.get(1).getTerm()); - assertEquals("1", tokens.get(2).getTerm()); - assertEquals("ck", tokens.get(3).getTerm()); + assertEquals(3, tokens.size()); + assertEquals("qu1ck", tokens.get(0).getTerm()); + assertEquals("brown", tokens.get(1).getTerm()); + assertEquals("fox", tokens.get(2).getTerm()); + // Refer to a char filter by its type so we get its default configuration request.analyzer(null); request.tokenizer("whitespace"); request.addCharFilter("html_strip"); - request.addTokenFilter("lowercase"); - request.addTokenFilter("word_delimiter"); + request.addTokenFilter("mock"); request.text("

the qu1ck brown fox

"); analyze = TransportAnalyzeAction.analyze(request, AllFieldMapper.NAME, null, randomBoolean() ? indexAnalyzers : null, registry, environment); tokens = analyze.getTokens(); - assertEquals(6, tokens.size()); - assertEquals("the", tokens.get(0).getTerm()); - assertEquals("qu", tokens.get(1).getTerm()); - assertEquals("1", tokens.get(2).getTerm()); - assertEquals("ck", tokens.get(3).getTerm()); - assertEquals("brown", tokens.get(4).getTerm()); - assertEquals("fox", tokens.get(5).getTerm()); + assertEquals(3, tokens.size()); + assertEquals("qu1ck", tokens.get(0).getTerm()); + assertEquals("brown", tokens.get(1).getTerm()); + assertEquals("fox", tokens.get(2).getTerm()); } public void testFillsAttributes() throws IOException { diff --git a/core/src/test/java/org/elasticsearch/index/IndexModuleTests.java b/core/src/test/java/org/elasticsearch/index/IndexModuleTests.java index 1ae125cecd19e..209bd1648b66e 100644 --- a/core/src/test/java/org/elasticsearch/index/IndexModuleTests.java +++ b/core/src/test/java/org/elasticsearch/index/IndexModuleTests.java @@ -94,6 +94,7 @@ public class IndexModuleTests extends ESTestCase { private Settings settings; private IndexSettings indexSettings; private Environment environment; + private AnalysisRegistry emptyAnalysisRegistry; private NodeEnvironment nodeEnvironment; private IndicesQueryCache indicesQueryCache; @@ -123,6 +124,7 @@ public void setUp() throws Exception { indexSettings = IndexSettingsModule.newIndexSettings("foo", settings); index = indexSettings.getIndex(); environment = new Environment(settings); + emptyAnalysisRegistry = new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap()); threadPool = new TestThreadPool("test"); circuitBreakerService = new NoneCircuitBreakerService(); bigArrays = new BigArrays(settings, circuitBreakerService); @@ -150,8 +152,7 @@ private IndexService newIndexService(IndexModule module) throws IOException { } public void testWrapperIsBound() throws IOException { - IndexModule module = new IndexModule(indexSettings, - new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())); + IndexModule module = new IndexModule(indexSettings, emptyAnalysisRegistry); module.setSearcherWrapper((s) -> new Wrapper()); module.engineFactory.set(new MockEngineFactory(AssertingDirectoryReader.class)); @@ -170,8 +171,7 @@ public void testRegisterIndexStore() throws IOException { .put(IndexModule.INDEX_STORE_TYPE_SETTING.getKey(), "foo_store") .build(); IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(index, settings); - IndexModule module = new IndexModule(indexSettings, - new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())); + IndexModule module = new IndexModule(indexSettings, emptyAnalysisRegistry); module.addIndexStore("foo_store", FooStore::new); try { module.addIndexStore("foo_store", FooStore::new); @@ -195,8 +195,7 @@ public void beforeIndexRemoved(IndexService indexService, IndexRemovalReason rea } }; IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(index, settings); - IndexModule module = new IndexModule(indexSettings, - new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())); + IndexModule module = new IndexModule(indexSettings, emptyAnalysisRegistry); module.addIndexEventListener(eventListener); IndexService indexService = newIndexService(module); IndexSettings x = indexService.getIndexSettings(); @@ -210,8 +209,7 @@ public void beforeIndexRemoved(IndexService indexService, IndexRemovalReason rea public void testListener() throws IOException { Setting booleanSetting = Setting.boolSetting("index.foo.bar", false, Property.Dynamic, Property.IndexScope); - IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings(index, settings, booleanSetting), - new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())); + IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings(index, settings, booleanSetting), emptyAnalysisRegistry); Setting booleanSetting2 = Setting.boolSetting("index.foo.bar.baz", false, Property.Dynamic, Property.IndexScope); AtomicBoolean atomicBoolean = new AtomicBoolean(false); module.addSettingsUpdateConsumer(booleanSetting, atomicBoolean::set); @@ -230,8 +228,7 @@ public void testListener() throws IOException { } public void testAddIndexOperationListener() throws IOException { - IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings(index, settings), - new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())); + IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings(index, settings), emptyAnalysisRegistry); AtomicBoolean executed = new AtomicBoolean(false); IndexingOperationListener listener = new IndexingOperationListener() { @Override @@ -261,8 +258,7 @@ public Engine.Index preIndex(ShardId shardId, Engine.Index operation) { } public void testAddSearchOperationListener() throws IOException { - IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings(index, settings), - new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())); + IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings(index, settings), emptyAnalysisRegistry); AtomicBoolean executed = new AtomicBoolean(false); SearchOperationListener listener = new SearchOperationListener() { @@ -295,8 +291,7 @@ public void testAddSimilarity() throws IOException { .put("index.similarity.my_similarity.key", "there is a key") .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) .build(); - IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings("foo", indexSettings), - new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())); + IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings("foo", indexSettings), emptyAnalysisRegistry); module.addSimilarity("test_similarity", (string, providerSettings, indexLevelSettings) -> new SimilarityProvider() { @Override public String name() { @@ -319,8 +314,7 @@ public Similarity get() { } public void testFrozen() { - IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings(index, settings), - new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())); + IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings(index, settings), emptyAnalysisRegistry); module.freeze(); String msg = "Can't modify IndexModule once the index service has been created"; assertEquals(msg, expectThrows(IllegalStateException.class, () -> module.addSearchOperationListener(null)).getMessage()); @@ -338,8 +332,7 @@ public void testSetupUnknownSimilarity() throws IOException { .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) .build(); - IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings("foo", indexSettings), - new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())); + IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings("foo", indexSettings), emptyAnalysisRegistry); Exception ex = expectThrows(IllegalArgumentException.class, () -> newIndexService(module)); assertEquals("Unknown Similarity type [test_similarity] for [my_similarity]", ex.getMessage()); } @@ -350,8 +343,7 @@ public void testSetupWithoutType() throws IOException { .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) .build(); - IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings("foo", indexSettings), - new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())); + IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings("foo", indexSettings), emptyAnalysisRegistry); Exception ex = expectThrows(IllegalArgumentException.class, () -> newIndexService(module)); assertEquals("Similarity [my_similarity] must have an associated type", ex.getMessage()); } @@ -360,8 +352,7 @@ public void testForceCustomQueryCache() throws IOException { Settings indexSettings = Settings.builder() .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build(); - IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings("foo", indexSettings), - new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())); + IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings("foo", indexSettings), emptyAnalysisRegistry); module.forceQueryCacheProvider((a, b) -> new CustomQueryCache()); expectThrows(AlreadySetException.class, () -> module.forceQueryCacheProvider((a, b) -> new CustomQueryCache())); IndexService indexService = newIndexService(module); @@ -373,8 +364,7 @@ public void testDefaultQueryCacheImplIsSelected() throws IOException { Settings indexSettings = Settings.builder() .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build(); - IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings("foo", indexSettings), - new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())); + IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings("foo", indexSettings), emptyAnalysisRegistry); IndexService indexService = newIndexService(module); assertTrue(indexService.cache().query() instanceof IndexQueryCache); indexService.close("simon says", false); @@ -385,8 +375,7 @@ public void testDisableQueryCacheHasPrecedenceOverForceQueryCache() throws IOExc .put(IndexModule.INDEX_QUERY_CACHE_ENABLED_SETTING.getKey(), false) .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build(); - IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings("foo", indexSettings), - new AnalysisRegistry(environment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())); + IndexModule module = new IndexModule(IndexSettingsModule.newIndexSettings("foo", indexSettings), emptyAnalysisRegistry); module.forceQueryCacheProvider((a, b) -> new CustomQueryCache()); IndexService indexService = newIndexService(module); assertTrue(indexService.cache().query() instanceof DisabledQueryCache); diff --git a/core/src/test/java/org/elasticsearch/index/analysis/AnalysisFactoryTests.java b/core/src/test/java/org/elasticsearch/index/analysis/AnalysisFactoryTests.java deleted file mode 100644 index 0a62e8c491588..0000000000000 --- a/core/src/test/java/org/elasticsearch/index/analysis/AnalysisFactoryTests.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.index.analysis; - -import org.elasticsearch.AnalysisFactoryTestCase; - -public class AnalysisFactoryTests extends AnalysisFactoryTestCase { - // tests are inherited and nothing needs to be defined here -} diff --git a/core/src/test/java/org/elasticsearch/index/analysis/AnalysisRegistryTests.java b/core/src/test/java/org/elasticsearch/index/analysis/AnalysisRegistryTests.java index 6033186c81289..471d6f9cccc29 100644 --- a/core/src/test/java/org/elasticsearch/index/analysis/AnalysisRegistryTests.java +++ b/core/src/test/java/org/elasticsearch/index/analysis/AnalysisRegistryTests.java @@ -34,6 +34,7 @@ import org.elasticsearch.indices.analysis.AnalysisModule; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; import org.elasticsearch.indices.analysis.PreBuiltAnalyzers; +import org.elasticsearch.indices.analysis.PreBuiltCacheFactory; import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.IndexSettingsModule; @@ -41,6 +42,7 @@ import java.io.IOException; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; @@ -50,7 +52,9 @@ public class AnalysisRegistryTests extends ESTestCase { - private AnalysisRegistry registry; + private Environment emptyEnvironment; + private AnalysisRegistry emptyRegistry; + private IndexSettings emptyIndexSettingsOfCurrentVersion; private static AnalyzerProvider analyzerProvider(final String name) { return new PreBuiltAnalyzerProvider(name, AnalyzerScope.INDEX, new EnglishAnalyzer()); @@ -59,12 +63,13 @@ private static AnalyzerProvider analyzerProvider(final String name) { @Override public void setUp() throws Exception { super.setUp(); - Settings settings = Settings - .builder() - .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) - .build(); - registry = new AnalysisRegistry(new Environment(settings), - emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap()); + emptyEnvironment = new Environment(Settings.builder() + .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) + .build()); + emptyRegistry = new AnalysisRegistry(emptyEnvironment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap()); + emptyIndexSettingsOfCurrentVersion = IndexSettingsModule.newIndexSettings("index", Settings.builder() + .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) + .build()); } public void testDefaultAnalyzers() throws IOException { @@ -75,9 +80,7 @@ public void testDefaultAnalyzers() throws IOException { .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) .build(); IndexSettings idxSettings = IndexSettingsModule.newIndexSettings("index", settings); - IndexAnalyzers indexAnalyzers = new AnalysisRegistry(new Environment(settings), - emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap()) - .build(idxSettings); + IndexAnalyzers indexAnalyzers = emptyRegistry.build(idxSettings); assertThat(indexAnalyzers.getDefaultIndexAnalyzer().analyzer(), instanceOf(StandardAnalyzer.class)); assertThat(indexAnalyzers.getDefaultSearchAnalyzer().analyzer(), instanceOf(StandardAnalyzer.class)); assertThat(indexAnalyzers.getDefaultSearchQuoteAnalyzer().analyzer(), instanceOf(StandardAnalyzer.class)); @@ -86,7 +89,7 @@ public void testDefaultAnalyzers() throws IOException { public void testOverrideDefaultAnalyzer() throws IOException { Version version = VersionUtils.randomVersion(random()); Settings settings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, version).build(); - IndexAnalyzers indexAnalyzers = registry.build(IndexSettingsModule.newIndexSettings("index", settings), + IndexAnalyzers indexAnalyzers = emptyRegistry.build(IndexSettingsModule.newIndexSettings("index", settings), singletonMap("default", analyzerProvider("default")) , emptyMap(), emptyMap(), emptyMap(), emptyMap()); assertThat(indexAnalyzers.getDefaultIndexAnalyzer().analyzer(), instanceOf(EnglishAnalyzer.class)); @@ -99,7 +102,7 @@ public void testOverrideDefaultIndexAnalyzerIsUnsupported() { Settings settings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, version).build(); AnalyzerProvider defaultIndex = new PreBuiltAnalyzerProvider("default_index", AnalyzerScope.INDEX, new EnglishAnalyzer()); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> registry.build(IndexSettingsModule.newIndexSettings("index", settings), + () -> emptyRegistry.build(IndexSettingsModule.newIndexSettings("index", settings), singletonMap("default_index", defaultIndex), emptyMap(), emptyMap(), emptyMap(), emptyMap())); assertTrue(e.getMessage().contains("[index.analysis.analyzer.default_index] is not supported")); } @@ -107,7 +110,7 @@ public void testOverrideDefaultIndexAnalyzerIsUnsupported() { public void testOverrideDefaultSearchAnalyzer() { Version version = VersionUtils.randomVersion(random()); Settings settings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, version).build(); - IndexAnalyzers indexAnalyzers = registry.build(IndexSettingsModule.newIndexSettings("index", settings), + IndexAnalyzers indexAnalyzers = emptyRegistry.build(IndexSettingsModule.newIndexSettings("index", settings), singletonMap("default_search", analyzerProvider("default_search")), emptyMap(), emptyMap(), emptyMap(), emptyMap()); assertThat(indexAnalyzers.getDefaultIndexAnalyzer().analyzer(), instanceOf(StandardAnalyzer.class)); assertThat(indexAnalyzers.getDefaultSearchAnalyzer().analyzer(), instanceOf(EnglishAnalyzer.class)); @@ -189,11 +192,12 @@ public void testBuiltInAnalyzersAreCached() throws IOException { Settings indexSettings = Settings.builder() .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build(); IndexSettings idxSettings = IndexSettingsModule.newIndexSettings("index", indexSettings); - IndexAnalyzers indexAnalyzers = new AnalysisRegistry(new Environment(settings), - emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap()) - .build(idxSettings); - IndexAnalyzers otherIndexAnalyzers = new AnalysisRegistry(new Environment(settings), emptyMap(), emptyMap(), emptyMap(), - emptyMap(), emptyMap()).build(idxSettings); + IndexAnalyzers indexAnalyzers = + new AnalysisRegistry(new Environment(settings), emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap()) + .build(idxSettings); + IndexAnalyzers otherIndexAnalyzers = + new AnalysisRegistry(new Environment(settings), emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap()) + .build(idxSettings); final int numIters = randomIntBetween(5, 20); for (int i = 0; i < numIters; i++) { PreBuiltAnalyzers preBuiltAnalyzers = RandomPicks.randomFrom(random(), PreBuiltAnalyzers.values()); @@ -201,6 +205,23 @@ public void testBuiltInAnalyzersAreCached() throws IOException { } } + public void testPreConfiguredTokenFiltersAreCached() throws IOException { + AtomicBoolean built = new AtomicBoolean(false); + PreConfiguredTokenFilter assertsBuiltOnce = new PreConfiguredTokenFilter("asserts_built_once", false, + PreBuiltCacheFactory.CachingStrategy.ONE, (tokens, version) -> { + if (false == built.compareAndSet(false, true)) { + fail("Attempted to build the token filter twice when it should have been cached"); + } + return new MockTokenFilter(tokens, MockTokenFilter.EMPTY_STOPSET); + }); + try (AnalysisRegistry registryWithPreBuiltTokenFilter = new AnalysisRegistry(emptyEnvironment, emptyMap(), emptyMap(), emptyMap(), + emptyMap(), emptyMap(), singletonMap("asserts_built_once", assertsBuiltOnce))) { + IndexAnalyzers indexAnalyzers = registryWithPreBuiltTokenFilter.build(emptyIndexSettingsOfCurrentVersion); + IndexAnalyzers otherIndexAnalyzers = registryWithPreBuiltTokenFilter.build(emptyIndexSettingsOfCurrentVersion); + assertSame(indexAnalyzers.get("asserts_built_once"), otherIndexAnalyzers.get("asserts_built_once")); + } + } + public void testNoTypeOrTokenizerErrorMessage() throws IOException { Version version = VersionUtils.randomVersion(random()); Settings settings = Settings @@ -212,20 +233,14 @@ public void testNoTypeOrTokenizerErrorMessage() throws IOException { .build(); IndexSettings idxSettings = IndexSettingsModule.newIndexSettings("index", settings); - IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> new AnalysisRegistry(new Environment(settings), - emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap()).build(idxSettings)); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> + new AnalysisRegistry(new Environment(settings), emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap()) + .build(idxSettings)); assertThat(e.getMessage(), equalTo("analyzer [test_analyzer] must specify either an analyzer type, or a tokenizer")); } public void testCloseIndexAnalyzersMultipleTimes() throws IOException { - Settings settings = Settings.builder().put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()).build(); - Settings indexSettings = Settings.builder() - .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build(); - IndexSettings idxSettings = IndexSettingsModule.newIndexSettings("index", indexSettings); - IndexAnalyzers indexAnalyzers = new AnalysisRegistry(new Environment(settings), - emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap()) - .build(idxSettings); + IndexAnalyzers indexAnalyzers = emptyRegistry.build(emptyIndexSettingsOfCurrentVersion); indexAnalyzers.close(); indexAnalyzers.close(); } diff --git a/core/src/test/java/org/elasticsearch/index/analysis/CoreAnalysisFactoryTests.java b/core/src/test/java/org/elasticsearch/index/analysis/CoreAnalysisFactoryTests.java new file mode 100644 index 0000000000000..3b4897b588988 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/index/analysis/CoreAnalysisFactoryTests.java @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.index.analysis; + +import org.elasticsearch.indices.analysis.AnalysisFactoryTestCase; +import org.elasticsearch.plugins.AnalysisPlugin; + +/** + * Checks on the analysis components that are part of core to make sure that any that are added + * to lucene are either enabled or explicitly not enabled. During the migration of analysis + * components to the {@code analysis-common} module this test ignores many components that are + * available to es-core but mapping in {@code analysis-common}. When the migration is complete + * no such ignoring will be needed because the analysis components won't be available to core. + */ +public class CoreAnalysisFactoryTests extends AnalysisFactoryTestCase { + public CoreAnalysisFactoryTests() { + // Use an empty plugin that doesn't define anything so the test doesn't need a ton of null checks. + super(new AnalysisPlugin() {}); + } +} diff --git a/core/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java b/core/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java index c6b5806099699..5cdc589405714 100644 --- a/core/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java +++ b/core/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java @@ -19,33 +19,39 @@ package org.elasticsearch.index.analysis; +import org.apache.lucene.analysis.MockLowerCaseFilter; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; +import org.elasticsearch.indices.analysis.PreBuiltCacheFactory.CachingStrategy; import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTokenStreamTestCase; import java.io.IOException; import java.io.Reader; +import java.util.List; import java.util.Map; +import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; public class CustomNormalizerTests extends ESTokenStreamTestCase { + private static final AnalysisPlugin MOCK_ANALYSIS_PLUGIN = new MockAnalysisPlugin(); + public void testBasics() throws IOException { Settings settings = Settings.builder() - .putArray("index.analysis.normalizer.my_normalizer.filter", "lowercase", "asciifolding") + .putArray("index.analysis.normalizer.my_normalizer.filter", "lowercase") .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) .build(); - ESTestCase.TestAnalysis analysis = AnalysisTestsHelper.createTestAnalysisFromSettings(settings); + ESTestCase.TestAnalysis analysis = AnalysisTestsHelper.createTestAnalysisFromSettings(settings, MOCK_ANALYSIS_PLUGIN); assertNull(analysis.indexAnalyzers.get("my_normalizer")); NamedAnalyzer normalizer = analysis.indexAnalyzers.getNormalizer("my_normalizer"); assertNotNull(normalizer); assertEquals("my_normalizer", normalizer.name()); - assertTokenStreamContents(normalizer.tokenStream("foo", "Cet été-là"), new String[] {"cet ete-la"}); - assertEquals(new BytesRef("cet ete-la"), normalizer.normalize("foo", "Cet été-là")); + assertTokenStreamContents(normalizer.tokenStream("foo", "Cet été-là"), new String[] {"cet été-là"}); + assertEquals(new BytesRef("cet été-là"), normalizer.normalize("foo", "Cet été-là")); } public void testUnknownType() { @@ -75,7 +81,7 @@ public void testCharFilters() throws IOException { .putArray("index.analysis.normalizer.my_normalizer.char_filter", "my_mapping") .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) .build(); - ESTestCase.TestAnalysis analysis = AnalysisTestsHelper.createTestAnalysisFromSettings(settings, new MockCharFilterPlugin()); + ESTestCase.TestAnalysis analysis = AnalysisTestsHelper.createTestAnalysisFromSettings(settings, MOCK_ANALYSIS_PLUGIN); assertNull(analysis.indexAnalyzers.get("my_normalizer")); NamedAnalyzer normalizer = analysis.indexAnalyzers.getNormalizer("my_normalizer"); assertNotNull(normalizer); @@ -86,12 +92,12 @@ public void testCharFilters() throws IOException { public void testIllegalFilters() throws IOException { Settings settings = Settings.builder() - .putArray("index.analysis.normalizer.my_normalizer.filter", "porter_stem") + .putArray("index.analysis.normalizer.my_normalizer.filter", "mock_forbidden") .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) .build(); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> AnalysisTestsHelper.createTestAnalysisFromSettings(settings)); - assertEquals("Custom normalizer [my_normalizer] may not use filter [porter_stem]", e.getMessage()); + () -> AnalysisTestsHelper.createTestAnalysisFromSettings(settings, MOCK_ANALYSIS_PLUGIN)); + assertEquals("Custom normalizer [my_normalizer] may not use filter [mock_forbidden]", e.getMessage()); } public void testIllegalCharFilters() throws IOException { @@ -104,7 +110,12 @@ public void testIllegalCharFilters() throws IOException { assertEquals("Custom normalizer [my_normalizer] may not use char filter [html_strip]", e.getMessage()); } - private class MockCharFilterPlugin implements AnalysisPlugin { + private static class MockAnalysisPlugin implements AnalysisPlugin { + @Override + public List getPreConfiguredTokenFilters() { + return singletonList(new PreConfiguredTokenFilter("mock_forbidden", false, CachingStrategy.ONE, MockLowerCaseFilter::new)); + } + @Override public Map> getCharFilters() { return singletonMap("mock_char_filter", (indexSettings, env, name, settings) -> { @@ -116,22 +127,21 @@ public String name() { @Override public Reader create(Reader reader) { return new Reader() { + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + int result = reader.read(cbuf, off, len); + for (int i = off; i < result; i++) { + if (cbuf[i] == 'a') { + cbuf[i] = 'z'; + } + } + return result; + } - @Override - public int read(char[] cbuf, int off, int len) throws IOException { - int result = reader.read(cbuf, off, len); - for (int i = off; i < result; i++) { - if (cbuf[i] == 'a') { - cbuf[i] = 'z'; - } - } - return result; - } - - @Override - public void close() throws IOException { - reader.close(); - } + @Override + public void close() throws IOException { + reader.close(); + } }; } @Override diff --git a/core/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java index 2da44d57f00aa..518f669f81f3f 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.mapper; +import org.apache.lucene.analysis.MockLowerCaseFilter; import org.apache.lucene.index.DocValuesType; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexableField; @@ -29,7 +30,10 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.IndexService; +import org.elasticsearch.index.analysis.PreConfiguredTokenFilter; import org.elasticsearch.index.mapper.MapperService.MergeReason; +import org.elasticsearch.indices.analysis.PreBuiltCacheFactory.CachingStrategy; +import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.test.InternalSettingsPlugin; @@ -38,15 +42,26 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collection; +import java.util.List; +import static java.util.Collections.singletonList; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; public class KeywordFieldMapperTests extends ESSingleNodeTestCase { + /** + * Creates a copy of the lowercase token filter which we use for testing merge errors. + */ + public static class MockAnalysisPlugin extends Plugin implements AnalysisPlugin { + @Override + public List getPreConfiguredTokenFilters() { + return singletonList(new PreConfiguredTokenFilter("mock_other_lowercase", true, CachingStrategy.ONE, MockLowerCaseFilter::new)); + } + }; @Override protected Collection> getPlugins() { - return pluginList(InternalSettingsPlugin.class); + return pluginList(InternalSettingsPlugin.class, MockAnalysisPlugin.class); } IndexService indexService; @@ -57,8 +72,8 @@ public void setup() { indexService = createIndex("test", Settings.builder() .put("index.analysis.normalizer.my_lowercase.type", "custom") .putArray("index.analysis.normalizer.my_lowercase.filter", "lowercase") - .put("index.analysis.normalizer.my_asciifolding.type", "custom") - .putArray("index.analysis.normalizer.my_asciifolding.filter", "asciifolding").build()); + .put("index.analysis.normalizer.my_other_lowercase.type", "custom") + .putArray("index.analysis.normalizer.my_other_lowercase.filter", "mock_other_lowercase").build()); parser = indexService.mapperService().documentMapperParser(); } @@ -348,7 +363,7 @@ public void testUpdateNormalizer() throws IOException { String mapping2 = XContentFactory.jsonBuilder().startObject().startObject("type") .startObject("properties").startObject("field") - .field("type", "keyword").field("normalizer", "my_asciifolding").endObject().endObject() + .field("type", "keyword").field("normalizer", "my_other_lowercase").endObject().endObject() .endObject().endObject().string(); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> indexService.mapperService().merge("type", diff --git a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java index e17df4b44631e..afe235ac8a5f9 100644 --- a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java +++ b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java @@ -19,14 +19,35 @@ package org.elasticsearch.analysis.common; +import org.apache.lucene.analysis.CharArraySet; +import org.apache.lucene.analysis.StopFilter; +import org.apache.lucene.analysis.commongrams.CommonGramsFilter; +import org.apache.lucene.analysis.core.StopAnalyzer; +import org.apache.lucene.analysis.core.UpperCaseFilter; +import org.apache.lucene.analysis.en.KStemFilter; +import org.apache.lucene.analysis.en.PorterStemFilter; +import org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter; +import org.apache.lucene.analysis.miscellaneous.LengthFilter; +import org.apache.lucene.analysis.miscellaneous.TrimFilter; +import org.apache.lucene.analysis.miscellaneous.TruncateTokenFilter; +import org.apache.lucene.analysis.miscellaneous.UniqueTokenFilter; +import org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter; +import org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter; +import org.apache.lucene.analysis.ngram.EdgeNGramTokenFilter; +import org.apache.lucene.analysis.ngram.NGramTokenFilter; +import org.apache.lucene.analysis.reverse.ReverseStringFilter; +import org.apache.lucene.analysis.standard.ClassicFilter; import org.elasticsearch.index.analysis.CharFilterFactory; import org.elasticsearch.index.analysis.HtmlStripCharFilterFactory; +import org.elasticsearch.index.analysis.PreConfiguredTokenFilter; import org.elasticsearch.index.analysis.TokenFilterFactory; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; +import org.elasticsearch.indices.analysis.PreBuiltCacheFactory.CachingStrategy; import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.plugins.Plugin; -import java.util.HashMap; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -35,14 +56,13 @@ public class CommonAnalysisPlugin extends Plugin implements AnalysisPlugin { @Override public Map> getTokenFilters() { - Map> filters = new HashMap<>(); + Map> filters = new TreeMap<>(); filters.put("asciifolding", ASCIIFoldingTokenFilterFactory::new); filters.put("word_delimiter", WordDelimiterTokenFilterFactory::new); filters.put("word_delimiter_graph", WordDelimiterGraphTokenFilterFactory::new); return filters; } - @Override public Map> getCharFilters() { Map> filters = new TreeMap<>(); filters.put("html_strip", HtmlStripCharFilterFactory::new); @@ -50,4 +70,50 @@ public Map> getCharFilters() { filters.put("mapping", requriesAnalysisSettings(MappingCharFilterFactory::new)); return filters; } + + @Override + public List getPreConfiguredTokenFilters() { + // TODO we should revisit the caching strategies. + List filters = new ArrayList<>(); + filters.add(new PreConfiguredTokenFilter("asciifolding", true, CachingStrategy.ONE, input -> new ASCIIFoldingFilter(input))); + filters.add(new PreConfiguredTokenFilter("classic", false, CachingStrategy.ONE, ClassicFilter::new)); + filters.add(new PreConfiguredTokenFilter("common_grams", false, CachingStrategy.LUCENE, input -> + new CommonGramsFilter(input, CharArraySet.EMPTY_SET))); + filters.add(new PreConfiguredTokenFilter("edge_ngram", false, CachingStrategy.LUCENE, input -> + new EdgeNGramTokenFilter(input, EdgeNGramTokenFilter.DEFAULT_MIN_GRAM_SIZE, EdgeNGramTokenFilter.DEFAULT_MAX_GRAM_SIZE))); + // TODO deprecate edgeNGram + filters.add(new PreConfiguredTokenFilter("edgeNGram", false, CachingStrategy.LUCENE, input -> + new EdgeNGramTokenFilter(input, EdgeNGramTokenFilter.DEFAULT_MIN_GRAM_SIZE, EdgeNGramTokenFilter.DEFAULT_MAX_GRAM_SIZE))); + filters.add(new PreConfiguredTokenFilter("kstem", false, CachingStrategy.ONE, KStemFilter::new)); + filters.add(new PreConfiguredTokenFilter("length", false, CachingStrategy.LUCENE, input -> + new LengthFilter(input, 0, Integer.MAX_VALUE))); // TODO this one seems useless + filters.add(new PreConfiguredTokenFilter("ngram", false, CachingStrategy.LUCENE, NGramTokenFilter::new)); + // TODO deprecate nGram + filters.add(new PreConfiguredTokenFilter("nGram", false, CachingStrategy.LUCENE, NGramTokenFilter::new)); + filters.add(new PreConfiguredTokenFilter("porter_stem", false, CachingStrategy.ONE, PorterStemFilter::new)); + filters.add(new PreConfiguredTokenFilter("reverse", false, CachingStrategy.LUCENE, input -> new ReverseStringFilter(input))); + // The stop filter is in lucene-core but the English stop words set is in lucene-analyzers-common + filters.add(new PreConfiguredTokenFilter("stop", false, CachingStrategy.LUCENE, input -> + new StopFilter(input, StopAnalyzer.ENGLISH_STOP_WORDS_SET))); + filters.add(new PreConfiguredTokenFilter("trim", false, CachingStrategy.LUCENE, TrimFilter::new)); + filters.add(new PreConfiguredTokenFilter("truncate", false, CachingStrategy.ONE, input -> + new TruncateTokenFilter(input, 10))); + filters.add(new PreConfiguredTokenFilter("unique", false, CachingStrategy.ONE, input -> new UniqueTokenFilter(input))); + filters.add(new PreConfiguredTokenFilter("uppercase", true, CachingStrategy.LUCENE, UpperCaseFilter::new)); + filters.add(new PreConfiguredTokenFilter("word_delimiter", false, CachingStrategy.ONE, input -> + new WordDelimiterFilter(input, + WordDelimiterFilter.GENERATE_WORD_PARTS + | WordDelimiterFilter.GENERATE_NUMBER_PARTS + | WordDelimiterFilter.SPLIT_ON_CASE_CHANGE + | WordDelimiterFilter.SPLIT_ON_NUMERICS + | WordDelimiterFilter.STEM_ENGLISH_POSSESSIVE, null))); + filters.add(new PreConfiguredTokenFilter("word_delimiter_graph", false, CachingStrategy.ONE, input -> + new WordDelimiterGraphFilter(input, + WordDelimiterGraphFilter.GENERATE_WORD_PARTS + | WordDelimiterGraphFilter.GENERATE_NUMBER_PARTS + | WordDelimiterGraphFilter.SPLIT_ON_CASE_CHANGE + | WordDelimiterGraphFilter.SPLIT_ON_NUMERICS + | WordDelimiterGraphFilter.STEM_ENGLISH_POSSESSIVE, null))); + return filters; + } } diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java index 78522f3b6f3fd..73a6c3d273291 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java @@ -19,10 +19,10 @@ package org.elasticsearch.analysis.common; -import org.elasticsearch.AnalysisFactoryTestCase; +import org.apache.lucene.analysis.reverse.ReverseStringFilterFactory; import org.elasticsearch.index.analysis.HtmlStripCharFilterFactory; +import org.elasticsearch.indices.analysis.AnalysisFactoryTestCase; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -31,15 +31,19 @@ import static java.util.stream.Collectors.toList; public class CommonAnalysisFactoryTests extends AnalysisFactoryTestCase { + public CommonAnalysisFactoryTests() { + super(new CommonAnalysisPlugin()); + } + @Override protected Map> getTokenizers() { - Map> tokenizers = new HashMap<>(super.getTokenizers()); + Map> tokenizers = new TreeMap<>(super.getTokenizers()); return tokenizers; } @Override protected Map> getTokenFilters() { - Map> filters = new HashMap<>(super.getTokenFilters()); + Map> filters = new TreeMap<>(super.getTokenFilters()); filters.put("asciifolding", ASCIIFoldingTokenFilterFactory.class); filters.put("worddelimiter", WordDelimiterTokenFilterFactory.class); filters.put("worddelimitergraph", WordDelimiterGraphTokenFilterFactory.class); @@ -59,6 +63,30 @@ protected Map> getCharFilters() { return filters; } + @Override + protected Map> getPreConfiguredTokenFilters() { + Map> filters = new TreeMap<>(super.getPreConfiguredTokenFilters()); + filters.put("asciifolding", null); + filters.put("classic", null); + filters.put("common_grams", null); + filters.put("edge_ngram", null); + filters.put("edgeNGram", null); + filters.put("kstem", null); + filters.put("length", null); + filters.put("ngram", null); + filters.put("nGram", null); + filters.put("porter_stem", null); + filters.put("reverse", ReverseStringFilterFactory.class); + filters.put("stop", null); + filters.put("trim", null); + filters.put("truncate", null); + filters.put("unique", Void.class); + filters.put("uppercase", null); + filters.put("word_delimiter", null); + filters.put("word_delimiter_graph", null); + return filters; + } + /** * Fails if a tokenizer is marked in the superclass with {@link MovedToAnalysisCommon} but * hasn't been marked in this class with its proper factory. diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/40_token_filters.yaml b/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/40_token_filters.yaml index 0666a31623b10..39d55c15acec0 100644 --- a/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/40_token_filters.yaml +++ b/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/40_token_filters.yaml @@ -10,6 +10,18 @@ - length: { tokens: 1 } - match: { tokens.0.token: Musee d'Orsay } + - do: + indices.analyze: + body: + text: Musée d'Orsay + tokenizer: keyword + filter: + - type: asciifolding + preserve_original: true + - length: { tokens: 2 } + - match: { tokens.0.token: Musee d'Orsay } + - match: { tokens.1.token: Musée d'Orsay } + --- "lowercase": - do: diff --git a/plugins/analysis-icu/src/test/java/org/elasticsearch/index/analysis/AnalysisICUFactoryTests.java b/plugins/analysis-icu/src/test/java/org/elasticsearch/index/analysis/AnalysisICUFactoryTests.java index 704ca61985aa6..d222189651e1c 100644 --- a/plugins/analysis-icu/src/test/java/org/elasticsearch/index/analysis/AnalysisICUFactoryTests.java +++ b/plugins/analysis-icu/src/test/java/org/elasticsearch/index/analysis/AnalysisICUFactoryTests.java @@ -19,12 +19,16 @@ package org.elasticsearch.index.analysis; -import org.elasticsearch.AnalysisFactoryTestCase; +import org.elasticsearch.indices.analysis.AnalysisFactoryTestCase; +import org.elasticsearch.plugin.analysis.icu.AnalysisICUPlugin; import java.util.HashMap; import java.util.Map; public class AnalysisICUFactoryTests extends AnalysisFactoryTestCase { + public AnalysisICUFactoryTests() { + super(new AnalysisICUPlugin()); + } @Override protected Map> getTokenizers() { diff --git a/plugins/analysis-kuromoji/src/test/java/org/elasticsearch/index/analysis/AnalysisKuromojiFactoryTests.java b/plugins/analysis-kuromoji/src/test/java/org/elasticsearch/index/analysis/AnalysisKuromojiFactoryTests.java index 9db7def101ef8..dbdc5795b38f8 100644 --- a/plugins/analysis-kuromoji/src/test/java/org/elasticsearch/index/analysis/AnalysisKuromojiFactoryTests.java +++ b/plugins/analysis-kuromoji/src/test/java/org/elasticsearch/index/analysis/AnalysisKuromojiFactoryTests.java @@ -20,12 +20,16 @@ package org.elasticsearch.index.analysis; import org.apache.lucene.analysis.ja.JapaneseTokenizerFactory; -import org.elasticsearch.AnalysisFactoryTestCase; +import org.elasticsearch.indices.analysis.AnalysisFactoryTestCase; +import org.elasticsearch.plugin.analysis.kuromoji.AnalysisKuromojiPlugin; import java.util.HashMap; import java.util.Map; public class AnalysisKuromojiFactoryTests extends AnalysisFactoryTestCase { + public AnalysisKuromojiFactoryTests() { + super(new AnalysisKuromojiPlugin()); + } @Override protected Map> getTokenizers() { diff --git a/plugins/analysis-phonetic/src/test/java/org/elasticsearch/index/analysis/AnalysisPhoneticFactoryTests.java b/plugins/analysis-phonetic/src/test/java/org/elasticsearch/index/analysis/AnalysisPhoneticFactoryTests.java index 0546fb468c924..8c551aee9190e 100644 --- a/plugins/analysis-phonetic/src/test/java/org/elasticsearch/index/analysis/AnalysisPhoneticFactoryTests.java +++ b/plugins/analysis-phonetic/src/test/java/org/elasticsearch/index/analysis/AnalysisPhoneticFactoryTests.java @@ -19,12 +19,16 @@ package org.elasticsearch.index.analysis; -import org.elasticsearch.AnalysisFactoryTestCase; +import org.elasticsearch.indices.analysis.AnalysisFactoryTestCase; +import org.elasticsearch.plugin.analysis.AnalysisPhoneticPlugin; import java.util.HashMap; import java.util.Map; public class AnalysisPhoneticFactoryTests extends AnalysisFactoryTestCase { + public AnalysisPhoneticFactoryTests() { + super(new AnalysisPhoneticPlugin()); + } @Override protected Map> getTokenFilters() { diff --git a/plugins/analysis-smartcn/src/test/java/org/elasticsearch/index/analysis/AnalysisSmartChineseFactoryTests.java b/plugins/analysis-smartcn/src/test/java/org/elasticsearch/index/analysis/AnalysisSmartChineseFactoryTests.java index d8aad322dcb93..53652c55f018a 100644 --- a/plugins/analysis-smartcn/src/test/java/org/elasticsearch/index/analysis/AnalysisSmartChineseFactoryTests.java +++ b/plugins/analysis-smartcn/src/test/java/org/elasticsearch/index/analysis/AnalysisSmartChineseFactoryTests.java @@ -19,13 +19,16 @@ package org.elasticsearch.index.analysis; -import org.elasticsearch.AnalysisFactoryTestCase; +import org.elasticsearch.indices.analysis.AnalysisFactoryTestCase; +import org.elasticsearch.plugin.analysis.smartcn.AnalysisSmartChinesePlugin; import java.util.HashMap; import java.util.Map; public class AnalysisSmartChineseFactoryTests extends AnalysisFactoryTestCase { - + public AnalysisSmartChineseFactoryTests() { + super(new AnalysisSmartChinesePlugin()); + } @Override protected Map> getTokenizers() { Map> tokenizers = new HashMap<>(super.getTokenizers()); diff --git a/plugins/analysis-stempel/src/test/java/org/elasticsearch/index/analysis/AnalysisPolishFactoryTests.java b/plugins/analysis-stempel/src/test/java/org/elasticsearch/index/analysis/AnalysisPolishFactoryTests.java index 8301529627670..ae78b9c01b3f8 100644 --- a/plugins/analysis-stempel/src/test/java/org/elasticsearch/index/analysis/AnalysisPolishFactoryTests.java +++ b/plugins/analysis-stempel/src/test/java/org/elasticsearch/index/analysis/AnalysisPolishFactoryTests.java @@ -23,7 +23,6 @@ import org.apache.lucene.analysis.BaseTokenStreamTestCase; import org.apache.lucene.analysis.MockTokenizer; import org.apache.lucene.analysis.Tokenizer; -import org.elasticsearch.AnalysisFactoryTestCase; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.UUIDs; @@ -31,12 +30,17 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.analysis.pl.PolishStemTokenFilterFactory; +import org.elasticsearch.indices.analysis.AnalysisFactoryTestCase; +import org.elasticsearch.plugin.analysis.stempel.AnalysisStempelPlugin; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class AnalysisPolishFactoryTests extends AnalysisFactoryTestCase { + public AnalysisPolishFactoryTests() { + super(new AnalysisStempelPlugin()); + } @Override protected Map> getTokenFilters() { diff --git a/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java b/test/framework/src/main/java/org/elasticsearch/indices/analysis/AnalysisFactoryTestCase.java similarity index 89% rename from test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java rename to test/framework/src/main/java/org/elasticsearch/indices/analysis/AnalysisFactoryTestCase.java index d14f81c61dfeb..534db0be39fb7 100644 --- a/test/framework/src/main/java/org/elasticsearch/AnalysisFactoryTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/indices/analysis/AnalysisFactoryTestCase.java @@ -17,14 +17,14 @@ * under the License. */ -package org.elasticsearch; +package org.elasticsearch.indices.analysis; import org.apache.lucene.analysis.en.PorterStemFilterFactory; -import org.apache.lucene.analysis.reverse.ReverseStringFilterFactory; import org.apache.lucene.analysis.snowball.SnowballPorterFilterFactory; import org.apache.lucene.analysis.util.CharFilterFactory; import org.apache.lucene.analysis.util.TokenFilterFactory; import org.apache.lucene.analysis.util.TokenizerFactory; +import org.elasticsearch.Version; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.index.analysis.ApostropheFilterFactory; import org.elasticsearch.index.analysis.ArabicNormalizationFilterFactory; @@ -67,6 +67,7 @@ import org.elasticsearch.index.analysis.PatternTokenizerFactory; import org.elasticsearch.index.analysis.PersianNormalizationFilterFactory; import org.elasticsearch.index.analysis.PorterStemTokenFilterFactory; +import org.elasticsearch.index.analysis.PreConfiguredTokenFilter; import org.elasticsearch.index.analysis.ReverseTokenFilterFactory; import org.elasticsearch.index.analysis.ScandinavianFoldingFilterFactory; import org.elasticsearch.index.analysis.ScandinavianNormalizationFilterFactory; @@ -89,21 +90,23 @@ import org.elasticsearch.index.analysis.WhitespaceTokenizerFactory; import org.elasticsearch.index.analysis.compound.DictionaryCompoundWordTokenFilterFactory; import org.elasticsearch.index.analysis.compound.HyphenationCompoundWordTokenFilterFactory; -import org.elasticsearch.indices.analysis.PreBuiltCharFilters; -import org.elasticsearch.indices.analysis.PreBuiltTokenFilters; -import org.elasticsearch.indices.analysis.PreBuiltTokenizers; +import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.test.ESTestCase; import java.util.Collection; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; +import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static java.util.Collections.singletonList; + /** * Alerts us if new analysis components are added to Lucene, so we don't miss them. *

@@ -285,41 +288,6 @@ private static String toCamelCase(String s) { .immutableMap(); - static final Map> PREBUILT_TOKENFILTERS; - static { - PREBUILT_TOKENFILTERS = new EnumMap<>(PreBuiltTokenFilters.class); - for (PreBuiltTokenFilters tokenizer : PreBuiltTokenFilters.values()) { - Class luceneFactoryClazz; - switch (tokenizer) { - case REVERSE: - luceneFactoryClazz = ReverseStringFilterFactory.class; - break; - case UNIQUE: - luceneFactoryClazz = Void.class; - break; - case SNOWBALL: - case DUTCH_STEM: - case FRENCH_STEM: - case RUSSIAN_STEM: - luceneFactoryClazz = SnowballPorterFilterFactory.class; - break; - case STEMMER: - luceneFactoryClazz = PorterStemFilterFactory.class; - break; - case DELIMITED_PAYLOAD_FILTER: - luceneFactoryClazz = org.apache.lucene.analysis.payloads.DelimitedPayloadTokenFilterFactory.class; - break; - case LIMIT: - luceneFactoryClazz = org.apache.lucene.analysis.miscellaneous.LimitTokenCountFilterFactory.class; - break; - default: - luceneFactoryClazz = org.apache.lucene.analysis.util.TokenFilterFactory.lookupClass( - toCamelCase(tokenizer.getTokenFilterFactory(Version.CURRENT).name())); - } - PREBUILT_TOKENFILTERS.put(tokenizer, luceneFactoryClazz); - } - } - static final Map> KNOWN_CHARFILTERS = new MapBuilder>() // exposed in ES .put("htmlstrip", MovedToAnalysisCommon.class) @@ -345,6 +313,15 @@ private static String toCamelCase(String s) { } } + /** + * The plugin being tested. Core uses an "empty" plugin so we don't have to throw null checks all over the place. + */ + private final AnalysisPlugin plugin; + + public AnalysisFactoryTestCase(AnalysisPlugin plugin) { + this.plugin = Objects.requireNonNull(plugin, "plugin is required. use an empty plugin for core"); + } + protected Map> getTokenizers() { return KNOWN_TOKENIZERS; } @@ -353,6 +330,49 @@ protected Map> getTokenFilters() { return KNOWN_TOKENFILTERS; } + /** + * Map containing pre-configured token filters that should be available + * after installing this plugin. The map is from the name of the token + * filter to the class of the Lucene {@link TokenFilterFactory} that it + * is emulating. If the Lucene filter factory is {@code null} then the + * test will look it up for you from the name. If there is no Lucene + * {@linkplain TokenFilterFactory} then the right hand side should + * be {@link Void}. + */ + protected Map> getPreConfiguredTokenFilters() { + Map> filters = new HashMap<>(); + filters.put("standard", null); + filters.put("lowercase", null); + // TODO remove the loop below once all the tokenizers are migrated out of PreBuiltTokenFilters + for (PreBuiltTokenFilters tokenizer : PreBuiltTokenFilters.values()) { + Class luceneFactoryClass; + switch (tokenizer) { + case LOWERCASE: + // This has been migrated but has to stick around until PreBuiltTokenizers is removed. + continue; + case SNOWBALL: + case DUTCH_STEM: + case FRENCH_STEM: + case RUSSIAN_STEM: + luceneFactoryClass = SnowballPorterFilterFactory.class; + break; + case STEMMER: + luceneFactoryClass = PorterStemFilterFactory.class; + break; + case DELIMITED_PAYLOAD_FILTER: + luceneFactoryClass = org.apache.lucene.analysis.payloads.DelimitedPayloadTokenFilterFactory.class; + break; + case LIMIT: + luceneFactoryClass = org.apache.lucene.analysis.miscellaneous.LimitTokenCountFilterFactory.class; + break; + default: + luceneFactoryClass = null; + } + filters.put(tokenizer.name().toLowerCase(Locale.ROOT), luceneFactoryClass); + } + return filters; + } + protected Map> getCharFilters() { return KNOWN_CHARFILTERS; } @@ -445,18 +465,24 @@ public void testPreBuiltMultiTermAware() { expected.add(tokenizer); } } - for (Map.Entry> entry : PREBUILT_TOKENFILTERS.entrySet()) { - PreBuiltTokenFilters tokenFilter = entry.getKey(); + Map preBuiltTokenFilters = AnalysisModule.setupPreConfiguredTokenFilters(singletonList(plugin)); + for (Map.Entry> entry : getPreConfiguredTokenFilters().entrySet()) { + String name = entry.getKey(); Class luceneFactory = entry.getValue(); if (luceneFactory == Void.class) { continue; } + if (luceneFactory == null) { + luceneFactory = TokenFilterFactory.lookupClass(toCamelCase(name)); + } assertTrue(TokenFilterFactory.class.isAssignableFrom(luceneFactory)); - if (tokenFilter.getTokenFilterFactory(Version.CURRENT) instanceof MultiTermAwareComponent) { - actual.add(tokenFilter); + PreConfiguredTokenFilter filter = preBuiltTokenFilters.get(name); + assertNotNull("test claims pre built token filter [" + name + "] should be available but it wasn't", filter); + if (filter.shouldUseFilterForMultitermQueries()) { + actual.add("token filter [" + name + "]"); } if (org.apache.lucene.analysis.util.MultiTermAwareComponent.class.isAssignableFrom(luceneFactory)) { - expected.add(tokenFilter); + expected.add("token filter [" + name + "]"); } } for (Map.Entry> entry : PREBUILT_CHARFILTERS.entrySet()) { From c5bdbecc649d1af8ef2d08dbd66d36882802b5a4 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Tue, 9 May 2017 21:08:10 +0200 Subject: [PATCH 255/619] [TEST] Add test for Aggregations#fromXContent (#24524) AggregationsTests#testFromXContent verifies that parsing of aggregations works by combining multiple aggs at the same level, and also adding sub-aggregations to multi bucket and single bucket aggs, up to a maximum depth of 5. --- .../search/aggregations/Aggregations.java | 1 - .../aggregations/AggregationsTests.java | 156 ++++++++++++++++++ ...nternalMultiBucketAggregationTestCase.java | 32 ++-- ...ternalSingleBucketAggregationTestCase.java | 28 +++- .../histogram/InternalDateHistogramTests.java | 6 +- .../histogram/InternalHistogramTests.java | 7 +- .../metrics/InternalMaxTests.java | 2 +- .../cardinality/InternalCardinalityTests.java | 22 +-- .../geocentroid/InternalGeoCentroidTests.java | 2 +- .../AbstractPercentilesTestCase.java | 6 +- 10 files changed, 218 insertions(+), 44 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/Aggregations.java b/core/src/main/java/org/elasticsearch/search/aggregations/Aggregations.java index c7f84fe65772e..465cef3087713 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/Aggregations.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/Aggregations.java @@ -128,7 +128,6 @@ public XContentBuilder toXContentInternal(XContentBuilder builder, Params params return builder; } - //TODO add tests for this method public static Aggregations fromXContent(XContentParser parser) throws IOException { final List aggregations = new ArrayList<>(); XContentParser.Token token; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java new file mode 100644 index 0000000000000..84d11db3dbe09 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java @@ -0,0 +1,156 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations; + +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.rest.action.search.RestSearchAction; +import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.bucket.histogram.InternalDateHistogramTests; +import org.elasticsearch.search.aggregations.bucket.histogram.InternalHistogramTests; +import org.elasticsearch.search.aggregations.metrics.InternalExtendedStatsTests; +import org.elasticsearch.search.aggregations.metrics.InternalMaxTests; +import org.elasticsearch.search.aggregations.metrics.InternalStatsBucketTests; +import org.elasticsearch.search.aggregations.metrics.InternalStatsTests; +import org.elasticsearch.search.aggregations.metrics.avg.InternalAvgTests; +import org.elasticsearch.search.aggregations.metrics.cardinality.InternalCardinalityTests; +import org.elasticsearch.search.aggregations.metrics.geobounds.InternalGeoBoundsTests; +import org.elasticsearch.search.aggregations.metrics.geocentroid.InternalGeoCentroidTests; +import org.elasticsearch.search.aggregations.metrics.min.InternalMinTests; +import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.InternalHDRPercentilesRanksTests; +import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.InternalHDRPercentilesTests; +import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentilesRanksTests; +import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentilesTests; +import org.elasticsearch.search.aggregations.metrics.sum.InternalSumTests; +import org.elasticsearch.search.aggregations.metrics.valuecount.InternalValueCountTests; +import org.elasticsearch.search.aggregations.pipeline.InternalSimpleValueTests; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.InternalBucketMetricValueTests; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.percentile.InternalPercentilesBucketTests; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.extended.InternalExtendedStatsBucketTests; +import org.elasticsearch.search.aggregations.pipeline.derivative.InternalDerivativeTests; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.hamcrest.ElasticsearchAssertions; +import org.junit.After; +import org.junit.Before; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static java.util.Collections.singletonMap; + +/** + * This class tests that aggregations parsing works properly. It checks that we can parse + * different aggregations and adds sub-aggregations where applicable. + * + */ +public class AggregationsTests extends ESTestCase { + + private static final List aggsTests = getAggsTests(); + + private static List getAggsTests() { + List aggsTests = new ArrayList<>(); + aggsTests.add(new InternalCardinalityTests()); + aggsTests.add(new InternalTDigestPercentilesTests()); + aggsTests.add(new InternalTDigestPercentilesRanksTests()); + aggsTests.add(new InternalHDRPercentilesTests()); + aggsTests.add(new InternalHDRPercentilesRanksTests()); + aggsTests.add(new InternalPercentilesBucketTests()); + aggsTests.add(new InternalMinTests()); + aggsTests.add(new InternalMaxTests()); + aggsTests.add(new InternalAvgTests()); + aggsTests.add(new InternalSumTests()); + aggsTests.add(new InternalValueCountTests()); + aggsTests.add(new InternalSimpleValueTests()); + aggsTests.add(new InternalDerivativeTests()); + aggsTests.add(new InternalBucketMetricValueTests()); + aggsTests.add(new InternalStatsTests()); + aggsTests.add(new InternalStatsBucketTests()); + aggsTests.add(new InternalExtendedStatsTests()); + aggsTests.add(new InternalExtendedStatsBucketTests()); + aggsTests.add(new InternalGeoBoundsTests()); + aggsTests.add(new InternalGeoCentroidTests()); + aggsTests.add(new InternalHistogramTests()); + aggsTests.add(new InternalDateHistogramTests()); + return Collections.unmodifiableList(aggsTests); + } + + @Override + protected NamedXContentRegistry xContentRegistry() { + return new NamedXContentRegistry(InternalAggregationTestCase.getNamedXContents()); + } + + @Before + public void init() throws Exception { + for (InternalAggregationTestCase aggsTest : aggsTests) { + aggsTest.setUp(); + } + } + + @After + public void cleanUp() throws Exception { + for (InternalAggregationTestCase aggsTest : aggsTests) { + aggsTest.tearDown(); + } + } + + public void testFromXContent() throws IOException { + XContentType xContentType = randomFrom(XContentType.values()); + final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); + Aggregations aggregations = createTestInstance(); + BytesReference originalBytes = toShuffledXContent(aggregations, xContentType, params, randomBoolean()); + try (XContentParser parser = createParser(xContentType.xContent(), originalBytes)) { + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals(Aggregations.AGGREGATIONS_FIELD, parser.currentName()); + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + Aggregations parsedAggregations = Aggregations.fromXContent(parser); + BytesReference parsedBytes = XContentHelper.toXContent(parsedAggregations, xContentType, randomBoolean()); + ElasticsearchAssertions.assertToXContentEquivalent(originalBytes, parsedBytes, xContentType); + } + } + + private static InternalAggregations createTestInstance() { + return createTestInstance(0, 5); + } + + private static InternalAggregations createTestInstance(final int currentDepth, final int maxDepth) { + int numAggs = randomIntBetween(1, 5); + List aggs = new ArrayList<>(numAggs); + for (int i = 0; i < numAggs; i++) { + InternalAggregationTestCase testCase = randomFrom(aggsTests); + if (testCase instanceof InternalMultiBucketAggregationTestCase && currentDepth < maxDepth) { + InternalMultiBucketAggregationTestCase multiBucketAggTestCase = (InternalMultiBucketAggregationTestCase) testCase; + multiBucketAggTestCase.subAggregationsSupplier = () -> createTestInstance(currentDepth + 1, maxDepth); + } + if (testCase instanceof InternalSingleBucketAggregationTestCase && currentDepth < maxDepth) { + InternalSingleBucketAggregationTestCase singleBucketAggTestCase = (InternalSingleBucketAggregationTestCase) testCase; + singleBucketAggTestCase.subAggregationsSupplier = () -> createTestInstance(currentDepth + 1, maxDepth); + } + aggs.add(testCase.createTestInstance()); + } + return new InternalAggregations(aggs); + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java index ae293d5ac3189..64eee02dcb68e 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java @@ -21,36 +21,42 @@ import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import org.junit.Before; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.function.Supplier; +import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; public abstract class InternalMultiBucketAggregationTestCase extends InternalAggregationTestCase { - private boolean hasSubAggregations; + Supplier subAggregationsSupplier; - @Before - public void initHasSubAggregations() { - hasSubAggregations = randomBoolean(); + @Override + public void setUp() throws Exception { + super.setUp(); + if (randomBoolean()) { + subAggregationsSupplier = () -> InternalAggregations.EMPTY; + } else { + subAggregationsSupplier = () -> { + final int numAggregations = randomIntBetween(1, 3); + List aggs = new ArrayList<>(); + for (int i = 0; i < numAggregations; i++) { + aggs.add(createTestInstance(randomAlphaOfLength(5), emptyList(), emptyMap(), InternalAggregations.EMPTY)); + } + return new InternalAggregations(aggs); + }; + } } @Override protected final T createTestInstance(String name, List pipelineAggregators, Map metaData) { - List internal = new ArrayList<>(); - if (hasSubAggregations) { - final int numAggregations = randomIntBetween(1, 3); - for (int i = 0; i pipelineAggregators, diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java index 5d2e8affe7854..a31b28e7fdb60 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java @@ -29,31 +29,43 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; public abstract class InternalSingleBucketAggregationTestCase extends InternalAggregationTestCase { + private final boolean hasInternalMax = randomBoolean(); private final boolean hasInternalMin = randomBoolean(); + public Supplier subAggregationsSupplier; + + @Override + public void setUp() throws Exception { + super.setUp(); + subAggregationsSupplier = () -> { + List aggs = new ArrayList<>(); + if (hasInternalMax) { + aggs.add(new InternalMax("max", randomDouble(), randomNumericDocValueFormat(), emptyList(), emptyMap())); + } + if (hasInternalMin) { + aggs.add(new InternalMin("min", randomDouble(), randomNumericDocValueFormat(), emptyList(), emptyMap())); + } + return new InternalAggregations(aggs); + }; + } + protected abstract T createTestInstance(String name, long docCount, InternalAggregations aggregations, List pipelineAggregators, Map metaData); protected abstract void extraAssertReduced(T reduced, List inputs); @Override protected final T createTestInstance(String name, List pipelineAggregators, Map metaData) { - List internal = new ArrayList<>(); - if (hasInternalMax) { - internal.add(new InternalMax("max", randomDouble(), randomNumericDocValueFormat(), emptyList(), emptyMap())); - } - if (hasInternalMin) { - internal.add(new InternalMin("min", randomDouble(), randomNumericDocValueFormat(), emptyList(), emptyMap())); - } // we shouldn't use the full long range here since we sum doc count on reduce, and don't want to overflow the long range there long docCount = between(0, Integer.MAX_VALUE); - return createTestInstance(name, docCount, new InternalAggregations(internal), pipelineAggregators, metaData); + return createTestInstance(name, docCount, subAggregationsSupplier.get(), pipelineAggregators, metaData); } @Override diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java index c4410713ac8a0..0b71d138a574a 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java @@ -26,7 +26,6 @@ import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.joda.time.DateTime; -import org.junit.Before; import java.util.ArrayList; import java.util.List; @@ -42,8 +41,9 @@ public class InternalDateHistogramTests extends InternalMultiBucketAggregationTe private boolean keyed; private DocValueFormat format; - @Before - public void init() { + @Override + public void setUp() throws Exception { + super.setUp(); keyed = randomBoolean(); format = randomNumericDocValueFormat(); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java index bfcfe78ff7d4b..4f0fea87d6308 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java @@ -22,12 +22,10 @@ import org.apache.lucene.util.TestUtil; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.InternalMultiBucketAggregationTestCase; import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import org.junit.Before; import java.util.ArrayList; import java.util.List; @@ -39,8 +37,9 @@ public class InternalHistogramTests extends InternalMultiBucketAggregationTestCa private boolean keyed; private DocValueFormat format; - @Before - public void init() { + @Override + public void setUp() throws Exception{ + super.setUp(); keyed = randomBoolean(); format = randomNumericDocValueFormat(); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java index 53483be55c08b..1d3a8b45a11e6 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java @@ -34,7 +34,7 @@ public class InternalMaxTests extends InternalAggregationTestCase { @Override protected InternalMax createTestInstance(String name, List pipelineAggregators, Map metaData) { - double value = frequently() ? randomDouble() : randomFrom(new Double[] { Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY }); + double value = frequently() ? randomDouble() : randomFrom(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); DocValueFormat formatter = randomNumericDocValueFormat(); return new InternalMax(name, value, formatter, pipelineAggregators, metaData); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java index efa5438ae3c57..60f0179d9a1c8 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java @@ -28,7 +28,6 @@ import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.junit.After; -import org.junit.Before; import java.util.ArrayList; import java.util.List; @@ -38,12 +37,22 @@ public class InternalCardinalityTests extends InternalAggregationTestCase algos; private static int p; - @Before - public void setup() { + @Override + public void setUp() throws Exception { + super.setUp(); algos = new ArrayList<>(); p = randomIntBetween(HyperLogLogPlusPlus.MIN_PRECISION, HyperLogLogPlusPlus.MAX_PRECISION); } + @After //we force @After to have it run before ESTestCase#after otherwise it fails + @Override + public void tearDown() throws Exception { + super.tearDown(); + Releasables.close(algos); + algos.clear(); + algos = null; + } + @Override protected InternalCardinality createTestInstance(String name, List pipelineAggregators, Map metaData) { @@ -82,11 +91,4 @@ protected void assertFromXContent(InternalCardinality aggregation, ParsedAggrega assertEquals(aggregation.getValue(), parsed.getValue(), Double.MIN_VALUE); assertEquals(aggregation.getValueAsString(), parsed.getValueAsString()); } - - @After - public void cleanup() { - Releasables.close(algos); - algos.clear(); - algos = null; - } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java index 31da19bb91575..52474c4ae3bd2 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java @@ -47,7 +47,7 @@ protected InternalGeoCentroid createTestInstance(String name, List Date: Tue, 9 May 2017 15:08:11 -0400 Subject: [PATCH 256/619] Inline global checkpoints Today we rely on background syncs to relay the global checkpoint under the mandate of the primary to its replicas. This means that the global checkpoint on a replica can lag far behind the primary. The commit moves to inlining global checkpoints with replication requests. When a replication operation is performed, the primary will send the latest global checkpoint inline with the replica requests. This keeps the replicas closer in-sync with the primary. However, consider a replication request that is not followed by another replication request for an indefinite period of time. When the replicas respond to the primary with their local checkpoint, the primary will advance its global checkpoint. During this indefinite period of time, the replicas will not be notified of the advanced global checkpoint. This necessitates a need for another sync. To achieve this, we perform a global checkpoint sync when a shard falls idle. Relates #24513 --- .../replication/ReplicationOperation.java | 38 +++-- .../TransportReplicationAction.java | 125 ++++++++++++--- .../metadata/MetaDataCreateIndexService.java | 2 +- .../metadata/MetaDataIndexAliasesService.java | 3 +- .../MetaDataIndexTemplateService.java | 2 +- .../metadata/MetaDataMappingService.java | 2 +- .../common/settings/IndexScopedSettings.java | 1 - .../org/elasticsearch/index/IndexModule.java | 28 ++-- .../org/elasticsearch/index/IndexService.java | 98 +++--------- .../elasticsearch/index/IndexSettings.java | 13 -- .../seqno/GlobalCheckpointSyncAction.java | 141 +++++++---------- .../index/seqno/GlobalCheckpointTracker.java | 112 +++++++++----- .../index/seqno/SequenceNumbersService.java | 21 ++- .../elasticsearch/index/shard/IndexShard.java | 36 ++--- .../elasticsearch/indices/IndicesService.java | 35 ++--- .../cluster/IndicesClusterStateService.java | 28 ++-- .../java/org/elasticsearch/node/Node.java | 2 +- .../ReplicationOperationTests.java | 14 +- .../TransportReplicationActionTests.java | 18 ++- .../TransportWriteActionTests.java | 7 +- .../DiscoveryWithServiceDisruptionsIT.java | 1 - .../elasticsearch/index/IndexModuleTests.java | 2 +- .../index/IndexServiceTests.java | 14 -- .../index/engine/InternalEngineTests.java | 2 - .../ESIndexLevelReplicationTestCase.java | 56 ++++--- .../IndexLevelReplicationTests.java | 40 ++++- .../RecoveryDuringReplicationTests.java | 45 +++--- .../GlobalCheckpointSyncActionTests.java | 9 +- .../seqno/GlobalCheckpointTrackerTests.java | 142 +++++++++++------- .../index/shard/IndexShardIT.java | 13 +- .../index/shard/IndexShardTests.java | 4 +- ...dicesLifecycleListenerSingleNodeTests.java | 2 +- ...actIndicesClusterStateServiceTestCase.java | 7 +- .../indices/cluster/ClusterStateChanges.java | 3 +- ...ClusterStateServiceRandomUpdatesTests.java | 2 +- .../elasticsearch/recovery/RelocationIT.java | 11 +- .../elasticsearch/backwards/IndexingIT.java | 4 +- .../index/shard/IndexShardTestCase.java | 21 +-- 38 files changed, 590 insertions(+), 514 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/support/replication/ReplicationOperation.java b/core/src/main/java/org/elasticsearch/action/support/replication/ReplicationOperation.java index 7f63faac49c2e..ee8270e911ce3 100644 --- a/core/src/main/java/org/elasticsearch/action/support/replication/ReplicationOperation.java +++ b/core/src/main/java/org/elasticsearch/action/support/replication/ReplicationOperation.java @@ -35,7 +35,6 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.util.set.Sets; -import org.elasticsearch.index.engine.VersionConflictEngineException; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.rest.RestStatus; @@ -126,7 +125,7 @@ public void execute() throws Exception { markUnavailableShardsAsStale(replicaRequest, inSyncAllocationIds, shards); - performOnReplicas(replicaRequest, shards); + performOnReplicas(replicaRequest, primary.globalCheckpoint(), shards); } successfulShards.incrementAndGet(); // mark primary as successful @@ -154,7 +153,7 @@ private void markUnavailableShardsAsStale(ReplicaRequest replicaRequest, Set shards) { + private void performOnReplicas(final ReplicaRequest replicaRequest, final long globalCheckpoint, final List shards) { final String localNodeId = primary.routingEntry().currentNodeId(); // If the index gets deleted after primary operation, we skip replication for (final ShardRouting shard : shards) { @@ -166,23 +165,23 @@ private void performOnReplicas(ReplicaRequest replicaRequest, List } if (shard.currentNodeId().equals(localNodeId) == false) { - performOnReplica(shard, replicaRequest); + performOnReplica(shard, replicaRequest, globalCheckpoint); } if (shard.relocating() && shard.relocatingNodeId().equals(localNodeId) == false) { - performOnReplica(shard.getTargetRelocatingShard(), replicaRequest); + performOnReplica(shard.getTargetRelocatingShard(), replicaRequest, globalCheckpoint); } } } - private void performOnReplica(final ShardRouting shard, final ReplicaRequest replicaRequest) { + private void performOnReplica(final ShardRouting shard, final ReplicaRequest replicaRequest, final long globalCheckpoint) { if (logger.isTraceEnabled()) { logger.trace("[{}] sending op [{}] to replica {} for request [{}]", shard.shardId(), opType, shard, replicaRequest); } totalShards.incrementAndGet(); pendingActions.incrementAndGet(); - replicasProxy.performOn(shard, replicaRequest, new ActionListener() { + replicasProxy.performOn(shard, replicaRequest, globalCheckpoint, new ActionListener() { @Override public void onResponse(ReplicaResponse response) { successfulShards.incrementAndGet(); @@ -347,8 +346,20 @@ public interface Primary< */ void updateLocalCheckpointForShard(String allocationId, long checkpoint); - /** returns the local checkpoint of the primary shard */ + /** + * Returns the local checkpoint on the primary shard. + * + * @return the local checkpoint + */ long localCheckpoint(); + + /** + * Returns the global checkpoint on the primary shard. + * + * @return the global checkpoint + */ + long globalCheckpoint(); + } /** @@ -357,13 +368,14 @@ public interface Primary< public interface Replicas> { /** - * performs the the given request on the specified replica + * Performs the the specified request on the specified replica. * - * @param replica {@link ShardRouting} of the shard this request should be executed on - * @param replicaRequest operation to perform - * @param listener a callback to call once the operation has been complicated, either successfully or with an error. + * @param replica the shard this request should be executed on + * @param replicaRequest the operation to perform + * @param globalCheckpoint the global checkpoint on the primary + * @param listener callback for handling the response or failure */ - void performOn(ShardRouting replica, RequestT replicaRequest, ActionListener listener); + void performOn(ShardRouting replica, RequestT replicaRequest, long globalCheckpoint, ActionListener listener); /** * Fail the specified shard if needed, removing it from the current set diff --git a/core/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java b/core/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java index e9a26778e7006..ae107e9515731 100644 --- a/core/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java +++ b/core/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java @@ -129,7 +129,7 @@ protected TransportReplicationAction(Settings settings, String actionName, Trans new PrimaryOperationTransportHandler()); // we must never reject on because of thread pool capacity on replicas transportService.registerRequestHandler(transportReplicaAction, - () -> new ConcreteShardRequest<>(replicaRequest), + () -> new ConcreteReplicaRequest<>(replicaRequest), executor, true, true, new ReplicaOperationTransportHandler()); @@ -155,15 +155,13 @@ protected ReplicationOperation.Replicas newReplicasProxy() { protected abstract Response newResponseInstance(); /** - * Resolves derived values in the request. For example, the target shard id of the incoming request, - * if not set at request construction + * Resolves derived values in the request. For example, the target shard id of the incoming request, if not set at request construction. * Additional processing or validation of the request should be done here. * - * @param metaData cluster state metadata * @param indexMetaData index metadata of the concrete index this request is going to operate on * @param request the request to resolve */ - protected void resolveRequest(MetaData metaData, IndexMetaData indexMetaData, Request request) { + protected void resolveRequest(final IndexMetaData indexMetaData, final Request request) { if (request.waitForActiveShards() == ActiveShardCount.DEFAULT) { // if the wait for active shard count has not been set in the request, // resolve it from the index settings @@ -441,18 +439,28 @@ public void respond(ActionListener listener) { } } - class ReplicaOperationTransportHandler implements TransportRequestHandler> { + class ReplicaOperationTransportHandler implements TransportRequestHandler> { + @Override - public void messageReceived(final ConcreteShardRequest request, final TransportChannel channel) - throws Exception { + public void messageReceived( + final ConcreteReplicaRequest replicaRequest, final TransportChannel channel) throws Exception { throw new UnsupportedOperationException("the task parameter is required for this operation"); } @Override - public void messageReceived(ConcreteShardRequest requestWithAID, TransportChannel channel, Task task) + public void messageReceived( + final ConcreteReplicaRequest replicaRequest, + final TransportChannel channel, + final Task task) throws Exception { - new AsyncReplicaAction(requestWithAID.request, requestWithAID.targetAllocationID, channel, (ReplicationTask) task).run(); + new AsyncReplicaAction( + replicaRequest.getRequest(), + replicaRequest.getTargetAllocationID(), + replicaRequest.getGlobalCheckpoint(), + channel, + (ReplicationTask) task).run(); } + } public static class RetryOnReplicaException extends ElasticsearchException { @@ -471,6 +479,7 @@ private final class AsyncReplicaAction extends AbstractRunnable implements Actio private final ReplicaRequest request; // allocation id of the replica this request is meant for private final String targetAllocationID; + private final long globalCheckpoint; private final TransportChannel channel; private final IndexShard replica; /** @@ -481,11 +490,17 @@ private final class AsyncReplicaAction extends AbstractRunnable implements Actio // something we want to avoid at all costs private final ClusterStateObserver observer = new ClusterStateObserver(clusterService, null, logger, threadPool.getThreadContext()); - AsyncReplicaAction(ReplicaRequest request, String targetAllocationID, TransportChannel channel, ReplicationTask task) { + AsyncReplicaAction( + ReplicaRequest request, + String targetAllocationID, + long globalCheckpoint, + TransportChannel channel, + ReplicationTask task) { this.request = request; this.channel = channel; this.task = task; this.targetAllocationID = targetAllocationID; + this.globalCheckpoint = globalCheckpoint; final ShardId shardId = request.shardId(); assert shardId != null : "request shardId must be set"; this.replica = getIndexShard(shardId); @@ -494,12 +509,13 @@ private final class AsyncReplicaAction extends AbstractRunnable implements Actio @Override public void onResponse(Releasable releasable) { try { - ReplicaResult replicaResult = shardOperationOnReplica(request, replica); + replica.updateGlobalCheckpointOnReplica(globalCheckpoint); + final ReplicaResult replicaResult = shardOperationOnReplica(request, replica); releasable.close(); // release shard operation lock before responding to caller final TransportReplicationAction.ReplicaResponse response = new ReplicaResponse(replica.routingEntry().allocationId().getId(), replica.getLocalCheckpoint()); replicaResult.respond(new ResponseListener(response)); - } catch (Exception e) { + } catch (final Exception e) { Releasables.closeWhileHandlingException(releasable); // release shard operation lock before responding to caller AsyncReplicaAction.this.onFailure(e); } @@ -656,7 +672,7 @@ protected void doRun() { } // resolve all derived request fields, so we can route and apply it - resolveRequest(state.metaData(), indexMetaData, request); + resolveRequest(indexMetaData, request); assert request.shardId() != null : "request shardId must be set in resolveRequest"; assert request.waitForActiveShards() != ActiveShardCount.DEFAULT : "request waitForActiveShards must be set in resolveRequest"; @@ -974,6 +990,11 @@ public long localCheckpoint() { return indexShard.getLocalCheckpoint(); } + @Override + public long globalCheckpoint() { + return indexShard.getGlobalCheckpoint(); + } + } @@ -1033,16 +1054,20 @@ public String allocationId() { class ReplicasProxy implements ReplicationOperation.Replicas { @Override - public void performOn(ShardRouting replica, ReplicaRequest request, ActionListener listener) { + public void performOn( + final ShardRouting replica, + final ReplicaRequest request, + final long globalCheckpoint, + final ActionListener listener) { String nodeId = replica.currentNodeId(); final DiscoveryNode node = clusterService.state().nodes().get(nodeId); if (node == null) { listener.onFailure(new NoNodeAvailableException("unknown node [" + nodeId + "]")); return; } - final ConcreteShardRequest concreteShardRequest = - new ConcreteShardRequest<>(request, replica.allocationId().getId()); - sendReplicaRequest(concreteShardRequest, node, listener); + final ConcreteReplicaRequest replicaRequest = + new ConcreteReplicaRequest<>(request, replica.allocationId().getId(), globalCheckpoint); + sendReplicaRequest(replicaRequest, node, listener); } @Override @@ -1066,16 +1091,23 @@ public void markShardCopyAsStaleIfNeeded(ShardId shardId, String allocationId, l } } - /** sends the given replica request to the supplied nodes */ - protected void sendReplicaRequest(ConcreteShardRequest concreteShardRequest, DiscoveryNode node, - ActionListener listener) { - transportService.sendRequest(node, transportReplicaAction, concreteShardRequest, transportOptions, - // Eclipse can't handle when this is <> so we specify the type here. - new ActionListenerResponseHandler(listener, ReplicaResponse::new)); + /** + * Sends the specified replica request to the specified node. + * + * @param replicaRequest the replica request + * @param node the node to send the request to + * @param listener callback for handling the response or failure + */ + protected void sendReplicaRequest( + final ConcreteReplicaRequest replicaRequest, + final DiscoveryNode node, + final ActionListener listener) { + final ActionListenerResponseHandler handler = new ActionListenerResponseHandler<>(listener, ReplicaResponse::new); + transportService.sendRequest(node, transportReplicaAction, replicaRequest, transportOptions, handler); } /** a wrapper class to encapsulate a request when being sent to a specific allocation id **/ - public static final class ConcreteShardRequest extends TransportRequest { + public static class ConcreteShardRequest extends TransportRequest { /** {@link AllocationId#getId()} of the shard this request is sent to **/ private String targetAllocationID; @@ -1145,6 +1177,49 @@ public String toString() { } } + protected static final class ConcreteReplicaRequest extends ConcreteShardRequest { + + private long globalCheckpoint; + + public ConcreteReplicaRequest(final Supplier requestSupplier) { + super(requestSupplier); + } + + public ConcreteReplicaRequest(final R request, final String targetAllocationID, final long globalCheckpoint) { + super(request, targetAllocationID); + this.globalCheckpoint = globalCheckpoint; + } + + @Override + public void readFrom(StreamInput in) throws IOException { + super.readFrom(in); + if (in.getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { + globalCheckpoint = in.readZLong(); + } + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + if (out.getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { + out.writeZLong(globalCheckpoint); + } + } + + public long getGlobalCheckpoint() { + return globalCheckpoint; + } + + @Override + public String toString() { + return "ConcreteReplicaRequest{" + + "targetAllocationID='" + getTargetAllocationID() + '\'' + + ", request=" + getRequest() + + ", globalCheckpoint=" + globalCheckpoint + + '}'; + } + } + /** * Sets the current phase on the task if it isn't null. Pulled into its own * method because its more convenient that way. diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java index fc9ee533090a4..43c13087dd01b 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java @@ -363,7 +363,7 @@ public ClusterState execute(ClusterState currentState) throws Exception { (tmpImd.getNumberOfReplicas() + 1) + "]"); } // create the index here (on the master) to validate it can be created, as well as adding the mapping - final IndexService indexService = indicesService.createIndex(tmpImd, Collections.emptyList(), shardId -> {}); + final IndexService indexService = indicesService.createIndex(tmpImd, Collections.emptyList()); createdIndex = indexService.index(); // now add the mappings MapperService mapperService = indexService.mapperService(); diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexAliasesService.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexAliasesService.java index 81fbddce46aee..1f09e64c73c21 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexAliasesService.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexAliasesService.java @@ -19,7 +19,6 @@ package org.elasticsearch.cluster.metadata; -import com.carrotsearch.hppc.cursors.ObjectCursor; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesClusterStateUpdateRequest; @@ -140,7 +139,7 @@ ClusterState innerExecute(ClusterState currentState, Iterable actio if (indexService == null) { // temporarily create the index and add mappings so we can parse the filter try { - indexService = indicesService.createIndex(index, emptyList(), shardId -> {}); + indexService = indicesService.createIndex(index, emptyList()); indicesToClose.add(index.getIndex()); } catch (IOException e) { throw new ElasticsearchException("Failed to create temporary index for parsing the alias", e); diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java index e35fc7c837c46..c96895b94e793 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java @@ -213,7 +213,7 @@ private static void validateAndAddTemplate(final PutRequest request, IndexTempla .build(); final IndexMetaData tmpIndexMetadata = IndexMetaData.builder(temporaryIndexName).settings(dummySettings).build(); - IndexService dummyIndexService = indicesService.createIndex(tmpIndexMetadata, Collections.emptyList(), shardId -> {}); + IndexService dummyIndexService = indicesService.createIndex(tmpIndexMetadata, Collections.emptyList()); createdIndex = dummyIndexService.index(); templateBuilder.order(request.order); diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java index c0032a4b6a452..865b58c468a52 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java @@ -145,7 +145,7 @@ ClusterState executeRefresh(final ClusterState currentState, final List {}); + indexService = indicesService.createIndex(indexMetaData, Collections.emptyList()); removeIndex = true; indexService.mapperService().merge(indexMetaData, MergeReason.MAPPING_RECOVERY, true); } diff --git a/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java b/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java index ff99fb80d5298..1b3d2f249b9cc 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java +++ b/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java @@ -115,7 +115,6 @@ public final class IndexScopedSettings extends AbstractScopedSettings { IndexSettings.QUERY_STRING_LENIENT_SETTING, IndexSettings.ALLOW_UNMAPPED, IndexSettings.INDEX_CHECK_ON_STARTUP, - IndexSettings.INDEX_SEQ_NO_CHECKPOINT_SYNC_INTERVAL, LocalCheckpointTracker.SETTINGS_BIT_ARRAYS_SIZE, IndexSettings.MAX_REFRESH_LISTENERS_PER_SHARD, IndexSettings.MAX_SLICES_PER_SCROLL, diff --git a/core/src/main/java/org/elasticsearch/index/IndexModule.java b/core/src/main/java/org/elasticsearch/index/IndexModule.java index dc7021e81fcaf..dbadef4718d7d 100644 --- a/core/src/main/java/org/elasticsearch/index/IndexModule.java +++ b/core/src/main/java/org/elasticsearch/index/IndexModule.java @@ -320,19 +320,17 @@ public interface IndexSearcherWrapperFactory { } public IndexService newIndexService( - NodeEnvironment environment, - NamedXContentRegistry xContentRegistry, - IndexService.ShardStoreDeleter shardStoreDeleter, - CircuitBreakerService circuitBreakerService, - BigArrays bigArrays, - ThreadPool threadPool, - ScriptService scriptService, - ClusterService clusterService, - Client client, - IndicesQueryCache indicesQueryCache, - MapperRegistry mapperRegistry, - Consumer globalCheckpointSyncer, - IndicesFieldDataCache indicesFieldDataCache) + NodeEnvironment environment, + NamedXContentRegistry xContentRegistry, + IndexService.ShardStoreDeleter shardStoreDeleter, + CircuitBreakerService circuitBreakerService, + BigArrays bigArrays, + ThreadPool threadPool, + ScriptService scriptService, + Client client, + IndicesQueryCache indicesQueryCache, + MapperRegistry mapperRegistry, + IndicesFieldDataCache indicesFieldDataCache) throws IOException { final IndexEventListener eventListener = freeze(); IndexSearcherWrapperFactory searcherWrapperFactory = indexSearcherWrapper.get() == null @@ -365,8 +363,8 @@ public IndexService newIndexService( } return new IndexService(indexSettings, environment, xContentRegistry, new SimilarityService(indexSettings, similarities), shardStoreDeleter, analysisRegistry, engineFactory.get(), circuitBreakerService, bigArrays, threadPool, scriptService, - clusterService, client, queryCache, store, eventListener, searcherWrapperFactory, mapperRegistry, - indicesFieldDataCache, globalCheckpointSyncer, searchOperationListeners, indexOperationListeners); + client, queryCache, store, eventListener, searcherWrapperFactory, mapperRegistry, + indicesFieldDataCache, searchOperationListeners, indexOperationListeners); } /** diff --git a/core/src/main/java/org/elasticsearch/index/IndexService.java b/core/src/main/java/org/elasticsearch/index/IndexService.java index 9a24f8eb68df7..94b5782a22cbf 100644 --- a/core/src/main/java/org/elasticsearch/index/IndexService.java +++ b/core/src/main/java/org/elasticsearch/index/IndexService.java @@ -28,7 +28,6 @@ import org.elasticsearch.client.Client; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.routing.ShardRouting; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; @@ -105,7 +104,6 @@ public class IndexService extends AbstractIndexComponent implements IndicesClust private final SimilarityService similarityService; private final EngineFactory engineFactory; private final IndexWarmer warmer; - private final Consumer globalCheckpointSyncer; private volatile Map shards = emptyMap(); private final AtomicBoolean closed = new AtomicBoolean(false); private final AtomicBoolean deleted = new AtomicBoolean(false); @@ -116,36 +114,33 @@ public class IndexService extends AbstractIndexComponent implements IndicesClust private volatile AsyncTranslogFSync fsyncTask; private final ThreadPool threadPool; private final BigArrays bigArrays; - private final AsyncGlobalCheckpointTask globalCheckpointTask; private final ScriptService scriptService; - private final ClusterService clusterService; private final Client client; private Supplier indexSortSupplier; - public IndexService(IndexSettings indexSettings, NodeEnvironment nodeEnv, - NamedXContentRegistry xContentRegistry, - SimilarityService similarityService, - ShardStoreDeleter shardStoreDeleter, - AnalysisRegistry registry, - @Nullable EngineFactory engineFactory, - CircuitBreakerService circuitBreakerService, - BigArrays bigArrays, - ThreadPool threadPool, - ScriptService scriptService, - ClusterService clusterService, - Client client, - QueryCache queryCache, - IndexStore indexStore, - IndexEventListener eventListener, - IndexModule.IndexSearcherWrapperFactory wrapperFactory, - MapperRegistry mapperRegistry, - IndicesFieldDataCache indicesFieldDataCache, - Consumer globalCheckpointSyncer, - List searchOperationListeners, - List indexingOperationListeners) throws IOException { + public IndexService( + IndexSettings indexSettings, + NodeEnvironment nodeEnv, + NamedXContentRegistry xContentRegistry, + SimilarityService similarityService, + ShardStoreDeleter shardStoreDeleter, + AnalysisRegistry registry, + @Nullable EngineFactory engineFactory, + CircuitBreakerService circuitBreakerService, + BigArrays bigArrays, + ThreadPool threadPool, + ScriptService scriptService, + Client client, + QueryCache queryCache, + IndexStore indexStore, + IndexEventListener eventListener, + IndexModule.IndexSearcherWrapperFactory wrapperFactory, + MapperRegistry mapperRegistry, + IndicesFieldDataCache indicesFieldDataCache, + List searchOperationListeners, + List indexingOperationListeners) throws IOException { super(indexSettings); this.indexSettings = indexSettings; - this.globalCheckpointSyncer = globalCheckpointSyncer; this.xContentRegistry = xContentRegistry; this.similarityService = similarityService; this.mapperService = new MapperService(indexSettings, registry.build(indexSettings), xContentRegistry, similarityService, @@ -169,7 +164,6 @@ public IndexService(IndexSettings indexSettings, NodeEnvironment nodeEnv, this.bigArrays = bigArrays; this.threadPool = threadPool; this.scriptService = scriptService; - this.clusterService = clusterService; this.client = client; this.eventListener = eventListener; this.nodeEnv = nodeEnv; @@ -182,7 +176,6 @@ public IndexService(IndexSettings indexSettings, NodeEnvironment nodeEnv, this.engineFactory = engineFactory; // initialize this last -- otherwise if the wrapper requires any other member to be non-null we fail with an NPE this.searcherWrapper = wrapperFactory.newWrapper(this); - this.globalCheckpointTask = new AsyncGlobalCheckpointTask(this); this.indexingOperationListeners = Collections.unmodifiableList(indexingOperationListeners); this.searchOperationListeners = Collections.unmodifiableList(searchOperationListeners); // kick off async ops for the first shard in this index @@ -272,7 +265,7 @@ public synchronized void close(final String reason, boolean delete) throws IOExc } } } finally { - IOUtils.close(bitsetFilterCache, indexCache, indexFieldData, mapperService, refreshTask, fsyncTask, globalCheckpointTask); + IOUtils.close(bitsetFilterCache, indexCache, indexFieldData, mapperService, refreshTask, fsyncTask); } } } @@ -369,7 +362,7 @@ public synchronized IndexShard createShard(ShardRouting routing) throws IOExcept indexShard = new IndexShard(routing, this.indexSettings, path, store, indexSortSupplier, indexCache, mapperService, similarityService, indexFieldData, engineFactory, eventListener, searcherWrapper, threadPool, bigArrays, engineWarmer, - () -> globalCheckpointSyncer.accept(shardId), searchOperationListeners, indexingOperationListeners); + searchOperationListeners, indexingOperationListeners); eventListener.indexShardStateChanged(indexShard, null, indexShard.state(), "shard created"); eventListener.afterIndexShardCreated(indexShard); shards = newMapBuilder(shards).put(shardId.id(), indexShard).immutableMap(); @@ -708,31 +701,6 @@ private void maybeRefreshEngine() { } } - private void maybeUpdateGlobalCheckpoints() { - for (IndexShard shard : this.shards.values()) { - if (shard.routingEntry().primary()) { - switch (shard.state()) { - case CREATED: - case RECOVERING: - case CLOSED: - case RELOCATED: - continue; - case POST_RECOVERY: - case STARTED: - try { - shard.updateGlobalCheckpointOnPrimary(); - } catch (AlreadyClosedException ex) { - // fine - continue, the shard was concurrently closed on us. - } - continue; - default: - throw new IllegalStateException("unknown state: " + shard.state()); - } - } - } - } - - abstract static class BaseAsyncTask implements Runnable, Closeable { protected final IndexService indexService; protected final ThreadPool threadPool; @@ -877,23 +845,6 @@ public String toString() { } } - final class AsyncGlobalCheckpointTask extends BaseAsyncTask { - - AsyncGlobalCheckpointTask(IndexService indexService) { - super(indexService, indexService.getIndexSettings().getGlobalCheckpointInterval()); - } - - @Override - protected void runInternal() { - indexService.maybeUpdateGlobalCheckpoints(); - } - - @Override - public String toString() { - return "global_checkpoint"; - } - } - AsyncRefreshTask getRefreshTask() { // for tests return refreshTask; } @@ -902,7 +853,4 @@ AsyncTranslogFSync getFsyncTask() { // for tests return fsyncTask; } - AsyncGlobalCheckpointTask getGlobalCheckpointTask() { // for tests - return globalCheckpointTask; - } } diff --git a/core/src/main/java/org/elasticsearch/index/IndexSettings.java b/core/src/main/java/org/elasticsearch/index/IndexSettings.java index 05b8f509eae25..2764ffd38cc17 100644 --- a/core/src/main/java/org/elasticsearch/index/IndexSettings.java +++ b/core/src/main/java/org/elasticsearch/index/IndexSettings.java @@ -130,10 +130,6 @@ public final class IndexSettings { new ByteSizeValue(Long.MAX_VALUE, ByteSizeUnit.BYTES), new Property[]{Property.Dynamic, Property.IndexScope}); - public static final Setting INDEX_SEQ_NO_CHECKPOINT_SYNC_INTERVAL = - Setting.timeSetting("index.seq_no.checkpoint_sync_interval", new TimeValue(30, TimeUnit.SECONDS), - new TimeValue(-1, TimeUnit.MILLISECONDS), Property.Dynamic, Property.IndexScope); - /** * Index setting to enable / disable deletes garbage collection. * This setting is realtime updateable @@ -171,7 +167,6 @@ public final class IndexSettings { private volatile Translog.Durability durability; private final TimeValue syncInterval; private volatile TimeValue refreshInterval; - private final TimeValue globalCheckpointInterval; private volatile ByteSizeValue flushThresholdSize; private volatile ByteSizeValue generationThresholdSize; private final MergeSchedulerConfig mergeSchedulerConfig; @@ -269,7 +264,6 @@ public IndexSettings(final IndexMetaData indexMetaData, final Settings nodeSetti this.durability = scopedSettings.get(INDEX_TRANSLOG_DURABILITY_SETTING); syncInterval = INDEX_TRANSLOG_SYNC_INTERVAL_SETTING.get(settings); refreshInterval = scopedSettings.get(INDEX_REFRESH_INTERVAL_SETTING); - globalCheckpointInterval = scopedSettings.get(INDEX_SEQ_NO_CHECKPOINT_SYNC_INTERVAL); flushThresholdSize = scopedSettings.get(INDEX_TRANSLOG_FLUSH_THRESHOLD_SIZE_SETTING); generationThresholdSize = scopedSettings.get(INDEX_TRANSLOG_GENERATION_THRESHOLD_SIZE_SETTING); mergeSchedulerConfig = new MergeSchedulerConfig(this); @@ -470,13 +464,6 @@ public TimeValue getRefreshInterval() { return refreshInterval; } - /** - * Returns this interval in which the primary shards of this index should check and advance the global checkpoint - */ - public TimeValue getGlobalCheckpointInterval() { - return globalCheckpointInterval; - } - /** * Returns the transaction log threshold size when to forcefully flush the index and clear the transaction log. */ diff --git a/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncAction.java b/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncAction.java index 6e13573794d79..a55b920bb27a9 100644 --- a/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncAction.java +++ b/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncAction.java @@ -16,10 +16,9 @@ * specific language governing permissions and limitations * under the License. */ + package org.elasticsearch.index.seqno; -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; @@ -32,31 +31,49 @@ import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.shard.IndexEventListener; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.IndicesService; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; -import java.io.IOException; - -public class GlobalCheckpointSyncAction extends TransportReplicationAction { +/** + * Background global checkpoint sync action initiated when a shard goes inactive. This is needed because while we send the global checkpoint + * on every replication operation, after the last operation completes the global checkpoint could advance but without a follow-up operation + * the global checkpoint will never be synced to the replicas. + */ +public class GlobalCheckpointSyncAction extends TransportReplicationAction< + GlobalCheckpointSyncAction.Request, + GlobalCheckpointSyncAction.Request, + ReplicationResponse> implements IndexEventListener { public static String ACTION_NAME = "indices:admin/seq_no/global_checkpoint_sync"; @Inject - public GlobalCheckpointSyncAction(Settings settings, TransportService transportService, - ClusterService clusterService, IndicesService indicesService, - ThreadPool threadPool, ShardStateAction shardStateAction, - ActionFilters actionFilters, - IndexNameExpressionResolver indexNameExpressionResolver) { - super(settings, ACTION_NAME, transportService, clusterService, indicesService, threadPool, shardStateAction, - actionFilters, indexNameExpressionResolver, PrimaryRequest::new, ReplicaRequest::new, - ThreadPool.Names.SAME); + public GlobalCheckpointSyncAction( + final Settings settings, + final TransportService transportService, + final ClusterService clusterService, + final IndicesService indicesService, + final ThreadPool threadPool, + final ShardStateAction shardStateAction, + final ActionFilters actionFilters, + final IndexNameExpressionResolver indexNameExpressionResolver) { + super( + settings, + ACTION_NAME, + transportService, + clusterService, + indicesService, + threadPool, + shardStateAction, + actionFilters, + indexNameExpressionResolver, + Request::new, + Request::new, + ThreadPool.Names.SAME); } @Override @@ -65,97 +82,53 @@ protected ReplicationResponse newResponseInstance() { } @Override - protected void sendReplicaRequest(ConcreteShardRequest concreteShardRequest, DiscoveryNode node, - ActionListener listener) { + protected void sendReplicaRequest( + final ConcreteReplicaRequest replicaRequest, + final DiscoveryNode node, + final ActionListener listener) { if (node.getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { - super.sendReplicaRequest(concreteShardRequest, node, listener); + super.sendReplicaRequest(replicaRequest, node, listener); } else { - listener.onResponse( - new ReplicaResponse(concreteShardRequest.getTargetAllocationID(), SequenceNumbersService.UNASSIGNED_SEQ_NO)); + listener.onResponse(new ReplicaResponse(replicaRequest.getTargetAllocationID(), SequenceNumbersService.UNASSIGNED_SEQ_NO)); } } @Override - protected PrimaryResult shardOperationOnPrimary(PrimaryRequest request, IndexShard indexShard) throws Exception { - long checkpoint = indexShard.getGlobalCheckpoint(); - indexShard.getTranslog().sync(); - return new PrimaryResult(new ReplicaRequest(request, checkpoint), new ReplicationResponse()); + public void onShardInactive(final IndexShard indexShard) { + execute(new Request(indexShard.shardId())); } @Override - protected ReplicaResult shardOperationOnReplica(ReplicaRequest request, IndexShard indexShard) throws Exception { - indexShard.updateGlobalCheckpointOnReplica(request.checkpoint); + protected PrimaryResult shardOperationOnPrimary( + final Request request, final IndexShard indexShard) throws Exception { indexShard.getTranslog().sync(); - return new ReplicaResult(); + return new PrimaryResult<>(request, new ReplicationResponse()); } - public void updateCheckpointForShard(ShardId shardId) { - execute(new PrimaryRequest(shardId), new ActionListener() { - @Override - public void onResponse(ReplicationResponse replicationResponse) { - if (logger.isTraceEnabled()) { - logger.trace("{} global checkpoint successfully updated (shard info [{}])", shardId, - replicationResponse.getShardInfo()); - } - } - - @Override - public void onFailure(Exception e) { - logger.debug((Supplier) () -> new ParameterizedMessage("{} failed to update global checkpoint", shardId), e); - } - }); + @Override + protected ReplicaResult shardOperationOnReplica(final Request request, final IndexShard indexShard) throws Exception { + indexShard.getTranslog().sync(); + return new ReplicaResult(); } - public static final class PrimaryRequest extends ReplicationRequest { + public static final class Request extends ReplicationRequest { - private PrimaryRequest() { + private Request() { super(); } - public PrimaryRequest(ShardId shardId) { + public Request(final ShardId shardId) { super(shardId); } @Override public String toString() { - return "GlobalCkpSyncPrimary{" + shardId + "}"; - } - } - - public static final class ReplicaRequest extends ReplicationRequest { - - private long checkpoint; - - private ReplicaRequest() { - } - - public ReplicaRequest(PrimaryRequest primaryRequest, long checkpoint) { - super(primaryRequest.shardId()); - this.checkpoint = checkpoint; - } - - @Override - public void readFrom(StreamInput in) throws IOException { - super.readFrom(in); - checkpoint = in.readZLong(); - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - super.writeTo(out); - out.writeZLong(checkpoint); - } - - public long getCheckpoint() { - return checkpoint; - } - - @Override - public String toString() { - return "GlobalCkpSyncReplica{" + - "checkpoint=" + checkpoint + - ", shardId=" + shardId + - '}'; + return "GlobalCheckpointSyncAction.Request{" + + "shardId=" + shardId + + ", timeout=" + timeout + + ", index='" + index + '\'' + + ", waitForActiveShards=" + waitForActiveShards + + "}"; } } diff --git a/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointTracker.java b/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointTracker.java index 9c9f9a3ca4927..ff35c4291a35c 100644 --- a/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointTracker.java +++ b/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointTracker.java @@ -87,16 +87,17 @@ public class GlobalCheckpointTracker extends AbstractIndexShardComponent { /** * Notifies the service to update the local checkpoint for the shard with the provided allocation ID. If the checkpoint is lower than - * the currently known one, this is a no-op. If the allocation ID is not in sync, it is ignored. This is to prevent late arrivals from + * the currently known one, this is a no-op. If the allocation ID is not tracked, it is ignored. This is to prevent late arrivals from * shards that are removed to be re-added. * - * @param allocationId the allocation ID of the shard to update the local checkpoint for - * @param localCheckpoint the local checkpoint for the shard + * @param allocationId the allocation ID of the shard to update the local checkpoint for + * @param localCheckpoint the local checkpoint for the shard */ public synchronized void updateLocalCheckpoint(final String allocationId, final long localCheckpoint) { final boolean updated; if (updateLocalCheckpoint(allocationId, localCheckpoint, inSyncLocalCheckpoints, "in-sync")) { updated = true; + updateGlobalCheckpointOnPrimary(); } else if (updateLocalCheckpoint(allocationId, localCheckpoint, trackingLocalCheckpoints, "tracking")) { updated = true; } else { @@ -108,11 +109,25 @@ public synchronized void updateLocalCheckpoint(final String allocationId, final } } + /** + * Notify all threads waiting on the monitor on this tracker. These threads should be waiting for the local checkpoint on a specific + * allocation ID to catch up to the global checkpoint. + */ @SuppressForbidden(reason = "Object#notifyAll waiters for local checkpoint advancement") private synchronized void notifyAllWaiters() { this.notifyAll(); } + /** + * Update the local checkpoint for the specified allocation ID in the specified tracking map. If the checkpoint is lower than the + * currently known one, this is a no-op. If the allocation ID is not tracked, it is ignored. + * + * @param allocationId the allocation ID of the shard to update the local checkpoint for + * @param localCheckpoint the local checkpoint for the shard + * @param map the tracking map + * @param reason the reason for the update (used for logging) + * @return {@code true} if the local checkpoint was updated, otherwise {@code false} if this was a no-op + */ private boolean updateLocalCheckpoint( final String allocationId, final long localCheckpoint, ObjectLongMap map, final String reason) { final int index = map.indexOf(allocationId); @@ -137,19 +152,16 @@ private boolean updateLocalCheckpoint( /** * Scans through the currently known local checkpoint and updates the global checkpoint accordingly. - * - * @return {@code true} if the checkpoint has been updated or if it can not be updated since the local checkpoints of one of the active - * allocations is not known. */ - synchronized boolean updateCheckpointOnPrimary() { + private synchronized void updateGlobalCheckpointOnPrimary() { long minLocalCheckpoint = Long.MAX_VALUE; if (inSyncLocalCheckpoints.isEmpty() || !pendingInSync.isEmpty()) { - return false; + return; } for (final ObjectLongCursor localCheckpoint : inSyncLocalCheckpoints) { if (localCheckpoint.value == SequenceNumbersService.UNASSIGNED_SEQ_NO) { logger.trace("unknown local checkpoint for active allocation ID [{}], requesting a sync", localCheckpoint.key); - return true; + return; } minLocalCheckpoint = Math.min(localCheckpoint.value, minLocalCheckpoint); } @@ -163,12 +175,9 @@ synchronized boolean updateCheckpointOnPrimary() { globalCheckpoint); throw new IllegalStateException(message); } - if (globalCheckpoint != minLocalCheckpoint) { + if (minLocalCheckpoint >= 0 && globalCheckpoint != minLocalCheckpoint) { logger.trace("global checkpoint updated to [{}]", minLocalCheckpoint); globalCheckpoint = minLocalCheckpoint; - return true; - } else { - return false; } } @@ -177,7 +186,7 @@ synchronized boolean updateCheckpointOnPrimary() { * * @return the global checkpoint */ - public synchronized long getCheckpoint() { + public synchronized long getGlobalCheckpoint() { return globalCheckpoint; } @@ -235,6 +244,8 @@ public synchronized void updateAllocationIdsFromMaster( trackingLocalCheckpoints.put(a, SequenceNumbersService.UNASSIGNED_SEQ_NO); logger.trace("tracking [{}] via cluster state update from master", a); } + + updateGlobalCheckpointOnPrimary(); } /** @@ -257,47 +268,68 @@ public synchronized void markAllocationIdAsInSync(final String allocationId, fin } updateLocalCheckpoint(allocationId, localCheckpoint, trackingLocalCheckpoints, "tracking"); - waitForAllocationIdToBeInSync(allocationId); - } - - private synchronized void waitForAllocationIdToBeInSync(final String allocationId) throws InterruptedException { if (!pendingInSync.add(allocationId)) { throw new IllegalStateException("there is already a pending sync in progress for allocation ID [" + allocationId + "]"); } try { - while (true) { + waitForAllocationIdToBeInSync(allocationId); + } finally { + pendingInSync.remove(allocationId); + updateGlobalCheckpointOnPrimary(); + } + } + + /** + * Wait for knowledge of the local checkpoint for the specified allocation ID to advance to the global checkpoint. Global checkpoint + * advancement is blocked while there are any allocation IDs waiting to catch up to the global checkpoint. + * + * @param allocationId the allocation ID + * @throws InterruptedException if this thread was interrupted before of during waiting + */ + private synchronized void waitForAllocationIdToBeInSync(final String allocationId) throws InterruptedException { + while (true) { + /* + * If the allocation has been cancelled and so removed from the tracking map from a cluster state update from the master it + * means that this recovery will be cancelled; we are here on a cancellable recovery thread and so this thread will throw an + * interrupted exception as soon as it tries to wait on the monitor. + */ + final long current = trackingLocalCheckpoints.getOrDefault(allocationId, Long.MIN_VALUE); + if (current >= globalCheckpoint) { + logger.trace("marked [{}] as in-sync with local checkpoint [{}]", allocationId, current); + trackingLocalCheckpoints.remove(allocationId); /* - * If the allocation has been cancelled and so removed from the tracking map from a cluster state update from the master it - * means that this recovery will be cancelled; we are here on a cancellable recovery thread and so this thread will throw - * an interrupted exception as soon as it tries to wait on the monitor. + * This is prematurely adding the allocation ID to the in-sync map as at this point recovery is not yet finished and could + * still abort. At this point we will end up with a shard in the in-sync map holding back the global checkpoint because the + * shard never recovered and we would have to wait until either the recovery retries and completes successfully, or the + * master fails the shard and issues a cluster state update that removes the shard from the set of active allocation IDs. */ - final long current = trackingLocalCheckpoints.getOrDefault(allocationId, Long.MIN_VALUE); - if (current >= globalCheckpoint) { - logger.trace("marked [{}] as in-sync with local checkpoint [{}]", allocationId, current); - trackingLocalCheckpoints.remove(allocationId); - /* - * This is prematurely adding the allocation ID to the in-sync map as at this point recovery is not yet finished and - * could still abort. At this point we will end up with a shard in the in-sync map holding back the global checkpoint - * because the shard never recovered and we would have to wait until either the recovery retries and completes - * successfully, or the master fails the shard and issues a cluster state update that removes the shard from the set of - * active allocation IDs. - */ - inSyncLocalCheckpoints.put(allocationId, current); - break; - } else { - waitForLocalCheckpointToAdvance(); - } + inSyncLocalCheckpoints.put(allocationId, current); + break; + } else { + waitForLocalCheckpointToAdvance(); } - } finally { - pendingInSync.remove(allocationId); } } + /** + * Wait for the local checkpoint to advance to the global checkpoint. + * + * @throws InterruptedException if this thread was interrupted before of during waiting + */ @SuppressForbidden(reason = "Object#wait for local checkpoint advancement") private synchronized void waitForLocalCheckpointToAdvance() throws InterruptedException { this.wait(); } + /** + * Check if there are any recoveries pending in-sync. + * + * @return {@code true} if there is at least one shard pending in-sync, otherwise false + */ + public boolean pendingInSync() { + return !pendingInSync.isEmpty(); + } + /** * Returns the local checkpoint for the shard with the specified allocation ID, or {@link SequenceNumbersService#UNASSIGNED_SEQ_NO} if * the shard is not in-sync. diff --git a/core/src/main/java/org/elasticsearch/index/seqno/SequenceNumbersService.java b/core/src/main/java/org/elasticsearch/index/seqno/SequenceNumbersService.java index 4b14ce8fff526..4180c7e0f7d92 100644 --- a/core/src/main/java/org/elasticsearch/index/seqno/SequenceNumbersService.java +++ b/core/src/main/java/org/elasticsearch/index/seqno/SequenceNumbersService.java @@ -151,17 +151,7 @@ public long getLocalCheckpoint() { * @return the global checkpoint */ public long getGlobalCheckpoint() { - return globalCheckpointTracker.getCheckpoint(); - } - - /** - * Scans through the currently known local checkpoint and updates the global checkpoint accordingly. - * - * @return {@code true} if the checkpoint has been updated or if it can not be updated since one of the local checkpoints of one of the - * active allocations is not known. - */ - public boolean updateGlobalCheckpointOnPrimary() { - return globalCheckpointTracker.updateCheckpointOnPrimary(); + return globalCheckpointTracker.getGlobalCheckpoint(); } /** @@ -184,4 +174,13 @@ public void updateAllocationIdsFromMaster(final Set activeAllocationIds, globalCheckpointTracker.updateAllocationIdsFromMaster(activeAllocationIds, initializingAllocationIds); } + /** + * Check if there are any recoveries pending in-sync. + * + * @return {@code true} if there is at least one shard pending in-sync, otherwise false + */ + public boolean pendingInSync() { + return globalCheckpointTracker.pendingInSync(); + } + } diff --git a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java index e08011d8f296f..8e1d47a65e0b5 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -211,8 +211,6 @@ public class IndexShard extends AbstractIndexShardComponent implements IndicesCl private final IndexSearcherWrapper searcherWrapper; - private final Runnable globalCheckpointSyncer; - /** * True if this shard is still indexing (recently) and false if we've been idle for long enough (as periodically checked by {@link * IndexingMemoryController}). @@ -227,7 +225,7 @@ public IndexShard(ShardRouting shardRouting, IndexSettings indexSettings, ShardP Supplier indexSortSupplier, IndexCache indexCache, MapperService mapperService, SimilarityService similarityService, IndexFieldDataService indexFieldDataService, @Nullable EngineFactory engineFactory, IndexEventListener indexEventListener, IndexSearcherWrapper indexSearcherWrapper, ThreadPool threadPool, BigArrays bigArrays, - Engine.Warmer warmer, Runnable globalCheckpointSyncer, List searchOperationListener, List listeners) throws IOException { + Engine.Warmer warmer, List searchOperationListener, List listeners) throws IOException { super(shardRouting.shardId(), indexSettings); assert shardRouting.initializing(); this.shardRouting = shardRouting; @@ -251,7 +249,6 @@ public IndexShard(ShardRouting shardRouting, IndexSettings indexSettings, ShardP final List searchListenersList = new ArrayList<>(searchOperationListener); searchListenersList.add(searchStats); this.searchOperationListener = new SearchOperationListener.CompositeListener(searchListenersList, logger); - this.globalCheckpointSyncer = globalCheckpointSyncer; this.getService = new ShardGetService(indexSettings, this, mapperService); this.shardWarmerService = new ShardIndexWarmerService(shardId, indexSettings); this.requestCacheStats = new ShardRequestCache(); @@ -1486,6 +1483,11 @@ public void waitForOpsToComplete(final long seqNo) throws InterruptedException { public void markAllocationIdAsInSync(final String allocationId, final long localCheckpoint) throws InterruptedException { verifyPrimary(); getEngine().seqNoService().markAllocationIdAsInSync(allocationId, localCheckpoint); + /* + * We could have blocked waiting for the replica to catch up that we fell idle and there will not be a background sync to the + * replica; mark our self as active to force a future background sync. + */ + active.compareAndSet(false, true); } /** @@ -1506,17 +1508,6 @@ public long getGlobalCheckpoint() { return getEngine().seqNoService().getGlobalCheckpoint(); } - /** - * Checks whether the global checkpoint can be updated based on current knowledge of local checkpoints on the different shard copies. - * The checkpoint is updated or if more information is required from the replica, a global checkpoint sync is initiated. - */ - public void updateGlobalCheckpointOnPrimary() { - verifyPrimary(); - if (getEngine().seqNoService().updateGlobalCheckpointOnPrimary()) { - globalCheckpointSyncer.run(); - } - } - /** * Updates the global checkpoint on a replica shard after it has been updated by the primary. * @@ -1561,6 +1552,16 @@ public void updateAllocationIdsFromMaster(final Set activeAllocationIds, } } + /** + * Check if there are any recoveries pending in-sync. + * + * @return {@code true} if there is at least one shard pending in-sync, otherwise false + */ + public boolean pendingInSync() { + verifyPrimary(); + return getEngine().seqNoService().pendingInSync(); + } + /** * Should be called for each no-op update operation to increment relevant statistics. * @@ -2074,9 +2075,4 @@ protected void delete(Engine engine, Engine.Delete engineDelete) throws IOExcept } } - // for tests - Runnable getGlobalCheckpointSyncer() { - return globalCheckpointSyncer; - } - } diff --git a/core/src/main/java/org/elasticsearch/indices/IndicesService.java b/core/src/main/java/org/elasticsearch/indices/IndicesService.java index bde4438158c1b..b2843bf9f8a2c 100644 --- a/core/src/main/java/org/elasticsearch/indices/IndicesService.java +++ b/core/src/main/java/org/elasticsearch/indices/IndicesService.java @@ -182,7 +182,7 @@ protected void doStart() { } public IndicesService(Settings settings, PluginsService pluginsService, NodeEnvironment nodeEnv, NamedXContentRegistry xContentRegistry, - ClusterSettings clusterSettings, AnalysisRegistry analysisRegistry, + AnalysisRegistry analysisRegistry, IndexNameExpressionResolver indexNameExpressionResolver, MapperRegistry mapperRegistry, NamedWriteableRegistry namedWriteableRegistry, ThreadPool threadPool, IndexScopedSettings indexScopedSettings, CircuitBreakerService circuitBreakerService, @@ -373,7 +373,7 @@ public IndexService indexServiceSafe(Index index) { * @throws ResourceAlreadyExistsException if the index already exists. */ @Override - public synchronized IndexService createIndex(IndexMetaData indexMetaData, List builtInListeners, Consumer globalCheckpointSyncer) throws IOException { + public synchronized IndexService createIndex(IndexMetaData indexMetaData, List builtInListeners) throws IOException { ensureChangesAllowed(); if (indexMetaData.getIndexUUID().equals(IndexMetaData.INDEX_UUID_NA_VALUE)) { throw new IllegalArgumentException("index must have a real UUID found value: [" + indexMetaData.getIndexUUID() + "]"); @@ -398,8 +398,7 @@ public void onStoreClosed(ShardId shardId) { indicesQueryCache, indicesFieldDataCache, finalListeners, - globalCheckpointSyncer, - indexingMemoryController); + indexingMemoryController); boolean success = false; try { indexService.getIndexEventListener().afterIndexCreated(indexService); @@ -420,9 +419,7 @@ private synchronized IndexService createIndexService(final String reason, IndexMetaData indexMetaData, IndicesQueryCache indicesQueryCache, IndicesFieldDataCache indicesFieldDataCache, List builtInListeners, - Consumer globalCheckpointSyncer, IndexingOperationListener... indexingOperationListeners) throws IOException { - final Index index = indexMetaData.getIndex(); final IndexSettings idxSettings = new IndexSettings(indexMetaData, this.settings, indexScopeSetting); logger.debug("creating Index [{}], shards [{}]/[{}] - reason [{}]", indexMetaData.getIndex(), @@ -439,19 +436,17 @@ private synchronized IndexService createIndexService(final String reason, indexModule.addIndexEventListener(listener); } return indexModule.newIndexService( - nodeEnv, - xContentRegistry, - this, - circuitBreakerService, - bigArrays, - threadPool, - scriptService, - clusterService, - client, - indicesQueryCache, - mapperRegistry, - globalCheckpointSyncer, - indicesFieldDataCache); + nodeEnv, + xContentRegistry, + this, + circuitBreakerService, + bigArrays, + threadPool, + scriptService, + client, + indicesQueryCache, + mapperRegistry, + indicesFieldDataCache); } /** @@ -482,7 +477,7 @@ public synchronized void verifyIndexMetadata(IndexMetaData metaData, IndexMetaDa closeables.add(indicesQueryCache); // this will also fail if some plugin fails etc. which is nice since we can verify that early final IndexService service = - createIndexService("metadata verification", metaData, indicesQueryCache, indicesFieldDataCache, emptyList(), s -> {}); + createIndexService("metadata verification", metaData, indicesQueryCache, indicesFieldDataCache, emptyList()); closeables.add(() -> service.close("metadata verification", false)); service.mapperService().merge(metaData, MapperService.MergeReason.MAPPING_RECOVERY, true); if (metaData.equals(metaDataUpdate) == false) { diff --git a/core/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java b/core/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java index 663cdece6acb8..1b243bccd7472 100644 --- a/core/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java +++ b/core/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java @@ -99,7 +99,6 @@ public class IndicesClusterStateService extends AbstractLifecycleComponent imple private final PeerRecoveryTargetService recoveryTargetService; private final ShardStateAction shardStateAction; private final NodeMappingRefreshAction nodeMappingRefreshAction; - private final Consumer globalCheckpointSyncer; private static final ShardStateAction.Listener SHARD_STATE_ACTION_LISTENER = new ShardStateAction.Listener() { }; @@ -123,10 +122,10 @@ public IndicesClusterStateService(Settings settings, IndicesService indicesServi SearchService searchService, SyncedFlushService syncedFlushService, PeerRecoverySourceService peerRecoverySourceService, SnapshotShardsService snapshotShardsService, GlobalCheckpointSyncAction globalCheckpointSyncAction) { - this(settings, indicesService, - clusterService, threadPool, recoveryTargetService, shardStateAction, - nodeMappingRefreshAction, repositoriesService, searchService, syncedFlushService, peerRecoverySourceService, - snapshotShardsService, globalCheckpointSyncAction::updateCheckpointForShard); + this(settings, (AllocatedIndices>) indicesService, + clusterService, threadPool, recoveryTargetService, shardStateAction, + nodeMappingRefreshAction, repositoriesService, searchService, syncedFlushService, peerRecoverySourceService, + snapshotShardsService, globalCheckpointSyncAction); } // for tests @@ -139,10 +138,16 @@ public IndicesClusterStateService(Settings settings, IndicesService indicesServi RepositoriesService repositoriesService, SearchService searchService, SyncedFlushService syncedFlushService, PeerRecoverySourceService peerRecoverySourceService, SnapshotShardsService snapshotShardsService, - Consumer globalCheckpointSyncer) { + GlobalCheckpointSyncAction globalCheckpointSyncAction) { super(settings); - this.buildInIndexListener = Arrays.asList(peerRecoverySourceService, recoveryTargetService, searchService, syncedFlushService, - snapshotShardsService); + this.buildInIndexListener = + Arrays.asList( + peerRecoverySourceService, + recoveryTargetService, + searchService, + syncedFlushService, + snapshotShardsService, + globalCheckpointSyncAction); this.indicesService = indicesService; this.clusterService = clusterService; this.threadPool = threadPool; @@ -151,7 +156,6 @@ public IndicesClusterStateService(Settings settings, IndicesService indicesServi this.nodeMappingRefreshAction = nodeMappingRefreshAction; this.repositoriesService = repositoriesService; this.sendRefreshMapping = this.settings.getAsBoolean("indices.cluster.send_refresh_mapping", true); - this.globalCheckpointSyncer = globalCheckpointSyncer; } @Override @@ -432,7 +436,7 @@ private void createIndices(final ClusterState state) { AllocatedIndex indexService = null; try { - indexService = indicesService.createIndex(indexMetaData, buildInIndexListener, globalCheckpointSyncer); + indexService = indicesService.createIndex(indexMetaData, buildInIndexListener); if (indexService.updateMapping(indexMetaData) && sendRefreshMapping) { nodeMappingRefreshAction.nodeMappingRefresh(state.nodes().getMasterNode(), new NodeMappingRefreshAction.NodeMappingRefreshRequest(indexMetaData.getIndex().getName(), @@ -769,12 +773,10 @@ public interface AllocatedIndices> * @param indexMetaData the index metadata to create the index for * @param builtInIndexListener a list of built-in lifecycle {@link IndexEventListener} that should should be used along side with * the per-index listeners - * @param globalCheckpointSyncer the global checkpoint syncer * @throws ResourceAlreadyExistsException if the index already exists. */ U createIndex(IndexMetaData indexMetaData, - List builtInIndexListener, - Consumer globalCheckpointSyncer) throws IOException; + List builtInIndexListener) throws IOException; /** * Verify that the contents on disk for the given index is deleted; if not, delete the contents. diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index a8f3921b6924a..0643c6b96a170 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -392,7 +392,7 @@ protected Node(final Environment environment, Collection modules.add(new RepositoriesModule(this.environment, pluginsService.filterPlugins(RepositoryPlugin.class), xContentRegistry)); final MetaStateService metaStateService = new MetaStateService(settings, nodeEnvironment, xContentRegistry); final IndicesService indicesService = new IndicesService(settings, pluginsService, nodeEnvironment, xContentRegistry, - settingsModule.getClusterSettings(), analysisModule.getAnalysisRegistry(), + analysisModule.getAnalysisRegistry(), clusterModule.getIndexNameExpressionResolver(), indicesModule.getMapperRegistry(), namedWriteableRegistry, threadPool, settingsModule.getIndexScopedSettings(), circuitBreakerService, bigArrays, scriptModule.getScriptService(), clusterService, client, metaStateService); diff --git a/core/src/test/java/org/elasticsearch/action/support/replication/ReplicationOperationTests.java b/core/src/test/java/org/elasticsearch/action/support/replication/ReplicationOperationTests.java index 182d2f8645d3f..9fcc8c24353ba 100644 --- a/core/src/test/java/org/elasticsearch/action/support/replication/ReplicationOperationTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/replication/ReplicationOperationTests.java @@ -345,12 +345,14 @@ static class TestPrimary implements ReplicationOperation.Primary knownLocalCheckpoints = new HashMap<>(); TestPrimary(ShardRouting routing, long term) { this.routing = routing; this.term = term; this.localCheckpoint = random().nextLong(); + this.globalCheckpoint = randomNonNegativeLong(); } @Override @@ -404,6 +406,12 @@ public void updateLocalCheckpointForShard(String allocationId, long checkpoint) public long localCheckpoint() { return localCheckpoint; } + + @Override + public long globalCheckpoint() { + return globalCheckpoint; + } + } static class ReplicaResponse implements ReplicationOperation.ReplicaResponse { @@ -445,7 +453,11 @@ static class TestReplicaProxy implements ReplicationOperation.Replicas } @Override - public void performOn(ShardRouting replica, Request request, ActionListener listener) { + public void performOn( + final ShardRouting replica, + final Request request, + final long globalCheckpoint, + final ActionListener listener) { assertTrue("replica request processed twice on [" + replica + "]", request.processedOnReplicas.add(replica)); if (opFailures.containsKey(replica)) { listener.onFailure(opFailures.get(replica)); diff --git a/core/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java b/core/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java index bf15974d3e5b9..db8855aaddc02 100644 --- a/core/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java @@ -608,7 +608,9 @@ public void testReplicaProxy() throws InterruptedException, ExecutionException { PlainActionFuture listener = new PlainActionFuture<>(); proxy.performOn( TestShardRouting.newShardRouting(shardId, "NOT THERE", false, randomFrom(ShardRoutingState.values())), - new Request(), listener); + new Request(), + randomNonNegativeLong(), + listener); assertTrue(listener.isDone()); assertListenerThrows("non existent node should throw a NoNodeAvailableException", listener, NoNodeAvailableException.class); @@ -616,7 +618,7 @@ public void testReplicaProxy() throws InterruptedException, ExecutionException { final ShardRouting replica = randomFrom(shardRoutings.replicaShards().stream() .filter(ShardRouting::assignedToNode).collect(Collectors.toList())); listener = new PlainActionFuture<>(); - proxy.performOn(replica, new Request(), listener); + proxy.performOn(replica, new Request(), randomNonNegativeLong(), listener); assertFalse(listener.isDone()); CapturingTransport.CapturedRequest[] captures = transport.getCapturedRequestsAndClear(); @@ -767,8 +769,8 @@ protected ReplicaResult shardOperationOnReplica(Request request, IndexShard repl final TestAction.ReplicaOperationTransportHandler replicaOperationTransportHandler = action.new ReplicaOperationTransportHandler(); try { replicaOperationTransportHandler.messageReceived( - new TransportReplicationAction.ConcreteShardRequest<>( - new Request().setShardId(shardId), replicaRouting.allocationId().getId()), + new TransportReplicationAction.ConcreteReplicaRequest<>( + new Request().setShardId(shardId), replicaRouting.allocationId().getId(), randomNonNegativeLong()), createTransportChannel(new PlainActionFuture<>()), task); } catch (ElasticsearchException e) { assertThat(e.getMessage(), containsString("simulated")); @@ -800,13 +802,13 @@ public void testDefaultWaitForActiveShardsUsesIndexSetting() throws Exception { state = ClusterState.builder(state).metaData(metaDataBuilder).build(); setState(clusterService, state); Request request = new Request(shardId).waitForActiveShards(ActiveShardCount.DEFAULT); // set to default so index settings are used - action.resolveRequest(state.metaData(), state.metaData().index(indexName), request); + action.resolveRequest(state.metaData().index(indexName), request); assertEquals(ActiveShardCount.from(idxSettingWaitForActiveShards), request.waitForActiveShards()); // test wait_for_active_shards when default not set on the request (request value should be honored over index setting) int requestWaitForActiveShards = randomIntBetween(0, numReplicas + 1); request = new Request(shardId).waitForActiveShards(ActiveShardCount.from(requestWaitForActiveShards)); - action.resolveRequest(state.metaData(), state.metaData().index(indexName), request); + action.resolveRequest(state.metaData().index(indexName), request); assertEquals(ActiveShardCount.from(requestWaitForActiveShards), request.waitForActiveShards()); } @@ -844,7 +846,7 @@ public void testReplicaActionRejectsWrongAid() throws Exception { PlainActionFuture listener = new PlainActionFuture<>(); Request request = new Request(shardId).timeout("1ms"); action.new ReplicaOperationTransportHandler().messageReceived( - new TransportReplicationAction.ConcreteShardRequest<>(request, "_not_a_valid_aid_"), + new TransportReplicationAction.ConcreteReplicaRequest<>(request, "_not_a_valid_aid_", randomNonNegativeLong()), createTransportChannel(listener), maybeTask() ); try { @@ -888,7 +890,7 @@ protected ReplicaResult shardOperationOnReplica(Request request, IndexShard repl final Request request = new Request().setShardId(shardId); request.primaryTerm(state.metaData().getIndexSafe(shardId.getIndex()).primaryTerm(shardId.id())); replicaOperationTransportHandler.messageReceived( - new TransportReplicationAction.ConcreteShardRequest<>(request, replica.allocationId().getId()), + new TransportReplicationAction.ConcreteReplicaRequest<>(request, replica.allocationId().getId(), randomNonNegativeLong()), createTransportChannel(listener), task); if (listener.isDone()) { listener.get(); // fail with the exception if there diff --git a/core/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java b/core/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java index 3d57c27e373c1..ca16e816b47fe 100644 --- a/core/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java @@ -270,8 +270,9 @@ public void testReplicaProxy() throws InterruptedException, ExecutionException { // check that at unknown node fails PlainActionFuture listener = new PlainActionFuture<>(); proxy.performOn( - TestShardRouting.newShardRouting(shardId, "NOT THERE", false, randomFrom(ShardRoutingState.values())), - new TestRequest(), listener); + TestShardRouting.newShardRouting(shardId, "NOT THERE", false, randomFrom(ShardRoutingState.values())), + new TestRequest(), + randomNonNegativeLong(), listener); assertTrue(listener.isDone()); assertListenerThrows("non existent node should throw a NoNodeAvailableException", listener, NoNodeAvailableException.class); @@ -279,7 +280,7 @@ public void testReplicaProxy() throws InterruptedException, ExecutionException { final ShardRouting replica = randomFrom(shardRoutings.replicaShards().stream() .filter(ShardRouting::assignedToNode).collect(Collectors.toList())); listener = new PlainActionFuture<>(); - proxy.performOn(replica, new TestRequest(), listener); + proxy.performOn(replica, new TestRequest(), randomNonNegativeLong(), listener); assertFalse(listener.isDone()); CapturingTransport.CapturedRequest[] captures = transport.getCapturedRequestsAndClear(); diff --git a/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java b/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java index 9d46b35377c2c..ca1f572547db2 100644 --- a/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java +++ b/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java @@ -493,7 +493,6 @@ public void testAckedIndexing() throws Exception { .setSettings(Settings.builder() .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1 + randomInt(2)) .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, randomInt(2)) - .put(IndexSettings.INDEX_SEQ_NO_CHECKPOINT_SYNC_INTERVAL.getKey(), randomBoolean() ? "5s" : "200ms") )); ensureGreen(); diff --git a/core/src/test/java/org/elasticsearch/index/IndexModuleTests.java b/core/src/test/java/org/elasticsearch/index/IndexModuleTests.java index 209bd1648b66e..e3df8c423c886 100644 --- a/core/src/test/java/org/elasticsearch/index/IndexModuleTests.java +++ b/core/src/test/java/org/elasticsearch/index/IndexModuleTests.java @@ -147,7 +147,7 @@ public void tearDown() throws Exception { private IndexService newIndexService(IndexModule module) throws IOException { return module.newIndexService(nodeEnvironment, xContentRegistry(), deleter, circuitBreakerService, bigArrays, threadPool, - scriptService, clusterService, null, indicesQueryCache, mapperRegistry, shardId -> {}, + scriptService, null, indicesQueryCache, mapperRegistry, new IndicesFieldDataCache(settings, listener)); } diff --git a/core/src/test/java/org/elasticsearch/index/IndexServiceTests.java b/core/src/test/java/org/elasticsearch/index/IndexServiceTests.java index c202db1470e10..6a6f726230882 100644 --- a/core/src/test/java/org/elasticsearch/index/IndexServiceTests.java +++ b/core/src/test/java/org/elasticsearch/index/IndexServiceTests.java @@ -179,20 +179,6 @@ public void testFsyncTaskIsRunning() throws IOException { assertNull(indexService.getFsyncTask()); } - public void testGlobalCheckpointTaskIsRunning() throws IOException { - IndexService indexService = createIndex("test", Settings.EMPTY); - IndexService.AsyncGlobalCheckpointTask task = indexService.getGlobalCheckpointTask(); - assertNotNull(task); - assertEquals(IndexSettings.INDEX_SEQ_NO_CHECKPOINT_SYNC_INTERVAL.getDefault(Settings.EMPTY), task.getInterval()); - assertTrue(task.mustReschedule()); - assertTrue(task.isScheduled()); - - indexService.close("simon says", false); - assertFalse(task.isScheduled()); - assertTrue(task.isClosed()); - } - - public void testRefreshActuallyWorks() throws Exception { IndexService indexService = createIndex("test", Settings.EMPTY); ensureGreen("test"); diff --git a/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java b/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java index 1b40316fca1f3..7dc94d972c02b 100644 --- a/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java +++ b/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java @@ -2071,13 +2071,11 @@ public void testSeqNoAndCheckpoints() throws IOException { if (rarely()) { localCheckpoint = primarySeqNo; maxSeqNo = primarySeqNo; - initialEngine.seqNoService().updateGlobalCheckpointOnPrimary(); initialEngine.flush(true, true); } } logger.info("localcheckpoint {}, global {}", replicaLocalCheckpoint, primarySeqNo); - initialEngine.seqNoService().updateGlobalCheckpointOnPrimary(); globalCheckpoint = initialEngine.seqNoService().getGlobalCheckpoint(); assertEquals(primarySeqNo, initialEngine.seqNoService().getMaxSeqNo()); diff --git a/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java b/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java index dc518dba45238..0f68dc3c50a76 100644 --- a/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java +++ b/core/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java @@ -59,6 +59,7 @@ import org.elasticsearch.index.translog.Translog; import org.elasticsearch.indices.recovery.RecoveryState; import org.elasticsearch.indices.recovery.RecoveryTarget; +import org.elasticsearch.transport.TransportRequest; import java.io.IOException; import java.util.ArrayList; @@ -123,7 +124,7 @@ protected class ReplicationGroup implements AutoCloseable, Iterable ReplicationGroup(final IndexMetaData indexMetaData) throws IOException { final ShardRouting primaryRouting = this.createShardRouting("s0", true); - primary = newShard(primaryRouting, indexMetaData, null, this::syncGlobalCheckpoint, getEngineFactory(primaryRouting)); + primary = newShard(primaryRouting, indexMetaData, null, getEngineFactory(primaryRouting)); replicas = new ArrayList<>(); this.indexMetaData = indexMetaData; updateAllocationIDsOnPrimary(); @@ -152,7 +153,6 @@ public int indexDocs(final int numOfDoc) throws Exception { assertEquals(DocWriteResponse.Result.CREATED, response.getResponse().getResult()); } } - primary.updateGlobalCheckpointOnPrimary(); return numOfDoc; } @@ -166,7 +166,6 @@ public int appendDocs(final int numOfDoc) throws Exception { assertEquals(DocWriteResponse.Result.CREATED, response.getResponse().getResult()); } } - primary.updateGlobalCheckpointOnPrimary(); return numOfDoc; } @@ -208,16 +207,16 @@ public void startPrimary() throws IOException { primary.markAsRecovering("store", new RecoveryState(primary.routingEntry(), pNode, null)); primary.recoverFromStore(); primary.updateRoutingEntry(ShardRoutingHelper.moveToStarted(primary.routingEntry())); + updateAllocationIDsOnPrimary(); for (final IndexShard replica : replicas) { recoverReplica(replica); } - updateAllocationIDsOnPrimary(); } public IndexShard addReplica() throws IOException { final ShardRouting replicaRouting = createShardRouting("s" + replicaId.incrementAndGet(), false); final IndexShard replica = - newShard(replicaRouting, indexMetaData, null, this::syncGlobalCheckpoint, getEngineFactory(replicaRouting)); + newShard(replicaRouting, indexMetaData, null, getEngineFactory(replicaRouting)); addReplica(replica); return replica; } @@ -240,7 +239,7 @@ public synchronized IndexShard addReplicaWithExistingPath(final ShardPath shardP RecoverySource.PeerRecoverySource.INSTANCE); final IndexShard newReplica = newShard(shardRouting, shardPath, indexMetaData, null, - this::syncGlobalCheckpoint, getEngineFactory(shardRouting)); + getEngineFactory(shardRouting)); replicas.add(newReplica); updateAllocationIDsOnPrimary(); return newReplica; @@ -358,7 +357,7 @@ public IndexShard getPrimary() { return primary; } - private void syncGlobalCheckpoint() { + public void syncGlobalCheckpoint() { PlainActionFuture listener = new PlainActionFuture<>(); try { new GlobalCheckpointSync(listener, this).execute(); @@ -390,8 +389,7 @@ abstract class ReplicationAction, private final ReplicationGroup replicationGroup; private final String opType; - ReplicationAction(Request request, ActionListener listener, - ReplicationGroup group, String opType) { + ReplicationAction(Request request, ActionListener listener, ReplicationGroup group, String opType) { this.request = request; this.listener = listener; this.replicationGroup = group; @@ -462,18 +460,26 @@ public void updateLocalCheckpointForShard(String allocationId, long checkpoint) public long localCheckpoint() { return replicationGroup.getPrimary().getLocalCheckpoint(); } + + @Override + public long globalCheckpoint() { + return replicationGroup.getPrimary().getGlobalCheckpoint(); + } + } class ReplicasRef implements ReplicationOperation.Replicas { @Override public void performOn( - ShardRouting replicaRouting, - ReplicaRequest request, - ActionListener listener) { + final ShardRouting replicaRouting, + final ReplicaRequest request, + final long globalCheckpoint, + final ActionListener listener) { try { IndexShard replica = replicationGroup.replicas.stream() .filter(s -> replicaRouting.isSameAllocation(s.routingEntry())).findFirst().get(); + replica.updateGlobalCheckpointOnReplica(globalCheckpoint); performOnReplica(request, replica); listener.onResponse(new ReplicaResponse(replica.routingEntry().allocationId().getId(), replica.getLocalCheckpoint())); } catch (Exception e) { @@ -578,26 +584,30 @@ void indexOnReplica(BulkShardRequest request, IndexShard replica) throws Excepti executeShardBulkOnReplica(replica, request); } - class GlobalCheckpointSync extends ReplicationAction { + class GlobalCheckpointSync extends ReplicationAction< + GlobalCheckpointSyncAction.Request, + GlobalCheckpointSyncAction.Request, + ReplicationResponse> { - GlobalCheckpointSync(ActionListener listener, ReplicationGroup replicationGroup) { - super(new GlobalCheckpointSyncAction.PrimaryRequest(replicationGroup.getPrimary().shardId()), listener, - replicationGroup, "global_ckp"); + GlobalCheckpointSync(final ActionListener listener, final ReplicationGroup replicationGroup) { + super( + new GlobalCheckpointSyncAction.Request(replicationGroup.getPrimary().shardId()), + listener, + replicationGroup, + "global_checkpoint_sync"); } @Override - protected PrimaryResult performOnPrimary(IndexShard primary, - GlobalCheckpointSyncAction.PrimaryRequest request) throws Exception { + protected PrimaryResult performOnPrimary( + final IndexShard primary, final GlobalCheckpointSyncAction.Request request) throws Exception { primary.getTranslog().sync(); - return new PrimaryResult(new GlobalCheckpointSyncAction.ReplicaRequest(request, primary.getGlobalCheckpoint()), - new ReplicationResponse()); + return new PrimaryResult(request, new ReplicationResponse()); } @Override - protected void performOnReplica(GlobalCheckpointSyncAction.ReplicaRequest request, IndexShard replica) throws IOException { - replica.updateGlobalCheckpointOnReplica(request.getCheckpoint()); + protected void performOnReplica(final GlobalCheckpointSyncAction.Request request, final IndexShard replica) throws IOException { replica.getTranslog().sync(); } } + } diff --git a/core/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java b/core/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java index 3e037bc9c7b37..c08e3976bde1a 100644 --- a/core/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java +++ b/core/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java @@ -38,11 +38,14 @@ import org.elasticsearch.index.engine.SegmentsStats; import org.elasticsearch.index.engine.VersionConflictEngineException; import org.elasticsearch.index.seqno.SeqNoStats; +import org.elasticsearch.index.seqno.SequenceNumbersService; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.IndexShardTests; import org.elasticsearch.index.store.Store; import org.elasticsearch.index.translog.Translog; import org.elasticsearch.indices.recovery.RecoveryTarget; +import org.elasticsearch.test.junit.annotations.TestLogging; +import org.hamcrest.Matcher; import java.io.IOException; import java.util.Collections; @@ -50,6 +53,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; +import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; @@ -148,18 +152,40 @@ public void testCheckpointsAdvance() throws Exception { startedShards = shards.startReplicas(randomIntBetween(1, 2)); } while (startedShards > 0); - if (numDocs == 0 || randomBoolean()) { - // in the case we have no indexing, we simulate the background global checkpoint sync - shards.getPrimary().updateGlobalCheckpointOnPrimary(); - } + final long unassignedSeqNo = SequenceNumbersService.UNASSIGNED_SEQ_NO; for (IndexShard shard : shards) { final SeqNoStats shardStats = shard.seqNoStats(); final ShardRouting shardRouting = shard.routingEntry(); - logger.debug("seq_no stats for {}: {}", shardRouting, XContentHelper.toString(shardStats, - new ToXContent.MapParams(Collections.singletonMap("pretty", "false")))); assertThat(shardRouting + " local checkpoint mismatch", shardStats.getLocalCheckpoint(), equalTo(numDocs - 1L)); + /* + * After the last indexing operation completes, the primary will advance its global checkpoint. Without an other indexing + * operation, or a background sync, the primary will not have broadcast this global checkpoint to its replicas. However, a + * shard could have recovered from the primary in which case its global checkpoint will be in-sync with the primary. + * Therefore, we can only assert that the global checkpoint is number of docs minus one (matching the primary, in case of a + * recovery), or number of docs minus two (received indexing operations but has not received a global checkpoint sync after + * the last operation completed). + */ + final Matcher globalCheckpointMatcher; + if (shardRouting.primary()) { + globalCheckpointMatcher = numDocs == 0 ? equalTo(unassignedSeqNo) : equalTo(numDocs - 1L); + } else { + globalCheckpointMatcher = numDocs == 0 ? equalTo(unassignedSeqNo) : anyOf(equalTo(numDocs - 1L), equalTo(numDocs - 2L)); + } + assertThat(shardRouting + " global checkpoint mismatch", shardStats.getGlobalCheckpoint(), globalCheckpointMatcher); + assertThat(shardRouting + " max seq no mismatch", shardStats.getMaxSeqNo(), equalTo(numDocs - 1L)); + } + + // simulate a background global checkpoint sync at which point we expect the global checkpoint to advance on the replicas + shards.syncGlobalCheckpoint(); - assertThat(shardRouting + " global checkpoint mismatch", shardStats.getGlobalCheckpoint(), equalTo(numDocs - 1L)); + for (IndexShard shard : shards) { + final SeqNoStats shardStats = shard.seqNoStats(); + final ShardRouting shardRouting = shard.routingEntry(); + assertThat(shardRouting + " local checkpoint mismatch", shardStats.getLocalCheckpoint(), equalTo(numDocs - 1L)); + assertThat( + shardRouting + " global checkpoint mismatch", + shardStats.getGlobalCheckpoint(), + numDocs == 0 ? equalTo(unassignedSeqNo) : equalTo(numDocs - 1L)); assertThat(shardRouting + " max seq no mismatch", shardStats.getMaxSeqNo(), equalTo(numDocs - 1L)); } } diff --git a/core/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java b/core/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java index 7ee40c5c90eac..ddd69c084950f 100644 --- a/core/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java +++ b/core/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java @@ -29,6 +29,7 @@ import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.ShardRouting; +import org.elasticsearch.common.util.concurrent.CountDown; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.engine.EngineConfig; @@ -51,6 +52,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.lessThan; @@ -84,7 +86,6 @@ public void testRecoveryOfDisconnectedReplica() throws Exception { shards.startAll(); int docs = shards.indexDocs(randomInt(50)); shards.flush(); - shards.getPrimary().updateGlobalCheckpointOnPrimary(); final IndexShard originalReplica = shards.getReplicas().get(0); long replicaCommittedLocalCheckpoint = docs - 1; boolean replicaHasDocsSinceLastFlushedCheckpoint = false; @@ -101,23 +102,17 @@ public void testRecoveryOfDisconnectedReplica() throws Exception { replicaHasDocsSinceLastFlushedCheckpoint = false; replicaCommittedLocalCheckpoint = docs - 1; } - - final boolean sync = randomBoolean(); - if (sync) { - shards.getPrimary().updateGlobalCheckpointOnPrimary(); - } } + // simulate a background global checkpoint sync at which point we expect the global checkpoint to advance on the replicas + shards.syncGlobalCheckpoint(); + shards.removeReplica(originalReplica); final int missingOnReplica = shards.indexDocs(randomInt(5)); docs += missingOnReplica; replicaHasDocsSinceLastFlushedCheckpoint |= missingOnReplica > 0; - if (randomBoolean()) { - shards.getPrimary().updateGlobalCheckpointOnPrimary(); - } - final boolean flushPrimary = randomBoolean(); if (flushPrimary) { shards.flush(); @@ -234,6 +229,8 @@ protected EngineFactory getEngineFactory(ShardRouting routing) { }) { shards.startAll(); int docs = shards.indexDocs(randomIntBetween(1, 10)); + // simulate a background global checkpoint sync at which point we expect the global checkpoint to advance on the replicas + shards.syncGlobalCheckpoint(); IndexShard replica = shards.getReplicas().get(0); shards.removeReplica(replica); closeShards(replica); @@ -330,6 +327,7 @@ protected EngineFactory getEngineFactory(final ShardRouting routing) { logger.info("indexed [{}] docs", docs); final CountDownLatch pendingDocDone = new CountDownLatch(1); final CountDownLatch pendingDocActiveWithExtraDocIndexed = new CountDownLatch(1); + final CountDownLatch phaseTwoStartLatch = new CountDownLatch(1); final IndexShard replica = shards.addReplica(); final Future recoveryFuture = shards.asyncRecoverReplica( replica, @@ -353,17 +351,15 @@ public long indexTranslogOperations(final List operations, f // unblock indexing for the next doc replicaEngineFactory.allowIndexing(); shards.index(new IndexRequest(index.getName(), "type", "completed").source("{}", XContentType.JSON)); - /* - * We want to test that the global checkpoint is blocked from advancing on the primary when a replica shard - * is pending being marked in-sync. We also want to test the the global checkpoint does not advance on the - * replica when its local checkpoint is behind the global checkpoint on the primary. Finally, advancing the - * global checkpoint here forces recovery to block until the pending doc is indexing on the replica. - */ - shards.getPrimary().updateGlobalCheckpointOnPrimary(); pendingDocActiveWithExtraDocIndexed.countDown(); } catch (final Exception e) { throw new AssertionError(e); } + try { + phaseTwoStartLatch.await(); + } catch (InterruptedException e) { + throw new AssertionError(e); + } return super.indexTranslogOperations(operations, totalTranslogOps); } }); @@ -372,18 +368,23 @@ public long indexTranslogOperations(final List operations, f { final long expectedDocs = docs + 2L; assertThat(shards.getPrimary().getLocalCheckpoint(), equalTo(expectedDocs - 1)); - // recovery has not completed, therefore the global checkpoint can have advance on the primary + // recovery has not completed, therefore the global checkpoint can have advanced on the primary assertThat(shards.getPrimary().getGlobalCheckpoint(), equalTo(expectedDocs - 1)); // the pending document is not done, the checkpoints can not have advanced on the replica assertThat(replica.getLocalCheckpoint(), lessThan(expectedDocs - 1)); assertThat(replica.getGlobalCheckpoint(), lessThan(expectedDocs - 1)); } - shards.getPrimary().updateGlobalCheckpointOnPrimary(); + // wait for recovery to enter the translog phase + phaseTwoStartLatch.countDown(); + + // wait for the translog phase to complete and the recovery to block global checkpoint advancement + awaitBusy(() -> shards.getPrimary().pendingInSync()); { - final long expectedDocs = docs + 3L; shards.index(new IndexRequest(index.getName(), "type", "last").source("{}", XContentType.JSON)); + final long expectedDocs = docs + 3L; assertThat(shards.getPrimary().getLocalCheckpoint(), equalTo(expectedDocs - 1)); + // recovery is now in the process of being completed, therefore the global checkpoint can not have advanced on the primary assertThat(shards.getPrimary().getGlobalCheckpoint(), equalTo(expectedDocs - 2)); assertThat(replica.getLocalCheckpoint(), lessThan(expectedDocs - 2)); assertThat(replica.getGlobalCheckpoint(), lessThan(expectedDocs - 2)); @@ -392,14 +393,14 @@ public long indexTranslogOperations(final List operations, f replicaEngineFactory.releaseLatchedIndexers(); pendingDocDone.await(); recoveryFuture.get(); - shards.getPrimary().updateGlobalCheckpointOnPrimary(); { final long expectedDocs = docs + 3L; assertBusy(() -> { assertThat(shards.getPrimary().getLocalCheckpoint(), equalTo(expectedDocs - 1)); assertThat(shards.getPrimary().getGlobalCheckpoint(), equalTo(expectedDocs - 1)); assertThat(replica.getLocalCheckpoint(), equalTo(expectedDocs - 1)); - assertThat(replica.getGlobalCheckpoint(), equalTo(expectedDocs - 1)); + // the global checkpoint advances can only advance here if a background global checkpoint sync fires + assertThat(replica.getGlobalCheckpoint(), anyOf(equalTo(expectedDocs - 1), equalTo(expectedDocs - 2))); }); } } diff --git a/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncActionTests.java b/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncActionTests.java index 0e12795b6d4f9..a03b506cba583 100644 --- a/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncActionTests.java +++ b/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncActionTests.java @@ -83,6 +83,9 @@ public void testTranslogSyncAfterGlobalCheckpointSync() throws Exception { final IndexShard indexShard = mock(IndexShard.class); when(indexService.getShard(id)).thenReturn(indexShard); + final ShardId shardId = new ShardId(index, id); + when(indexShard.shardId()).thenReturn(shardId); + final Translog translog = mock(Translog.class); when(indexShard.getTranslog()).thenReturn(translog); @@ -95,13 +98,11 @@ public void testTranslogSyncAfterGlobalCheckpointSync() throws Exception { shardStateAction, new ActionFilters(Collections.emptySet()), new IndexNameExpressionResolver(Settings.EMPTY)); - final ShardId shardId = new ShardId(index, id); - final GlobalCheckpointSyncAction.PrimaryRequest primaryRequest = new GlobalCheckpointSyncAction.PrimaryRequest(shardId); + final GlobalCheckpointSyncAction.Request primaryRequest = new GlobalCheckpointSyncAction.Request(indexShard.shardId()); if (randomBoolean()) { action.shardOperationOnPrimary(primaryRequest, indexShard); } else { - action.shardOperationOnReplica( - new GlobalCheckpointSyncAction.ReplicaRequest(primaryRequest, randomNonNegativeLong()), indexShard); + action.shardOperationOnReplica(new GlobalCheckpointSyncAction.Request(indexShard.shardId()), indexShard); } verify(translog).sync(); diff --git a/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java b/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java index 3f5882e6c577e..4898c55328179 100644 --- a/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java +++ b/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -44,10 +45,8 @@ import java.util.stream.Stream; import static org.elasticsearch.index.seqno.SequenceNumbersService.UNASSIGNED_SEQ_NO; -import static org.hamcrest.Matchers.comparesEqualTo; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.not; public class GlobalCheckpointTrackerTests extends ESTestCase { @@ -66,8 +65,7 @@ public void setUp() throws Exception { } public void testEmptyShards() { - assertFalse("checkpoint shouldn't be updated when the are no active shards", tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); + assertThat(tracker.getGlobalCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); } private final AtomicInteger aIdGenerator = new AtomicInteger(); @@ -92,9 +90,9 @@ public void testGlobalCheckpointUpdate() { // note: allocations can never be empty in practice as we always have at least one primary shard active/in sync // it is however nice not to assume this on this level and check we do the right thing. - final long maxLocalCheckpoint = allocations.values().stream().min(Long::compare).orElse(UNASSIGNED_SEQ_NO); + final long minLocalCheckpoint = allocations.values().stream().min(Long::compare).orElse(UNASSIGNED_SEQ_NO); - assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); + assertThat(tracker.getGlobalCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); logger.info("--> using allocations"); allocations.keySet().forEach(aId -> { @@ -110,41 +108,35 @@ public void testGlobalCheckpointUpdate() { }); tracker.updateAllocationIdsFromMaster(active, initializing); - initializing.forEach(aId -> markAllocationIdAsInSyncQuietly(tracker, aId, tracker.getCheckpoint())); + initializing.forEach(aId -> markAllocationIdAsInSyncQuietly(tracker, aId, tracker.getGlobalCheckpoint())); allocations.keySet().forEach(aId -> tracker.updateLocalCheckpoint(aId, allocations.get(aId))); - - assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); - - assertThat(tracker.updateCheckpointOnPrimary(), equalTo(maxLocalCheckpoint != UNASSIGNED_SEQ_NO)); - assertThat(tracker.getCheckpoint(), equalTo(maxLocalCheckpoint)); + assertThat(tracker.getGlobalCheckpoint(), equalTo(minLocalCheckpoint)); // increment checkpoints active.forEach(aId -> allocations.put(aId, allocations.get(aId) + 1 + randomInt(4))); initializing.forEach(aId -> allocations.put(aId, allocations.get(aId) + 1 + randomInt(4))); allocations.keySet().forEach(aId -> tracker.updateLocalCheckpoint(aId, allocations.get(aId))); + final long minLocalCheckpointAfterUpdates = + allocations.entrySet().stream().map(Map.Entry::getValue).min(Long::compareTo).orElse(UNASSIGNED_SEQ_NO); + // now insert an unknown active/insync id , the checkpoint shouldn't change but a refresh should be requested. final String extraId = "extra_" + randomAlphaOfLength(5); // first check that adding it without the master blessing doesn't change anything. - tracker.updateLocalCheckpoint(extraId, maxLocalCheckpoint + 1 + randomInt(4)); + tracker.updateLocalCheckpoint(extraId, minLocalCheckpointAfterUpdates + 1 + randomInt(4)); assertThat(tracker.getLocalCheckpointForAllocationId(extraId), equalTo(UNASSIGNED_SEQ_NO)); Set newActive = new HashSet<>(active); newActive.add(extraId); tracker.updateAllocationIdsFromMaster(newActive, initializing); - // we should ask for a refresh , but not update the checkpoint - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), equalTo(maxLocalCheckpoint)); - // now notify for the new id - tracker.updateLocalCheckpoint(extraId, maxLocalCheckpoint + 1 + randomInt(4)); + tracker.updateLocalCheckpoint(extraId, minLocalCheckpointAfterUpdates + 1 + randomInt(4)); // now it should be incremented - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), greaterThan(maxLocalCheckpoint)); + assertThat(tracker.getGlobalCheckpoint(), greaterThan(minLocalCheckpoint)); } public void testMissingActiveIdsPreventAdvance() { @@ -153,43 +145,36 @@ public void testMissingActiveIdsPreventAdvance() { final Map assigned = new HashMap<>(); assigned.putAll(active); assigned.putAll(initializing); + final String maxActiveID = active.entrySet().stream().max(Comparator.comparing(Map.Entry::getValue)).get().getKey(); tracker.updateAllocationIdsFromMaster( - new HashSet<>(randomSubsetOf(randomInt(active.size() - 1), active.keySet())), - initializing.keySet()); - randomSubsetOf(initializing.keySet()).forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getCheckpoint())); + active.entrySet().stream().filter(e -> !e.getKey().equals(maxActiveID)).map(Map.Entry::getKey).collect(Collectors.toSet()), + initializing.keySet()); + randomSubsetOf(initializing.keySet()).forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getGlobalCheckpoint())); assigned.forEach(tracker::updateLocalCheckpoint); // now mark all active shards tracker.updateAllocationIdsFromMaster(active.keySet(), initializing.keySet()); - // global checkpoint can't be advanced, but we need a sync - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); - // update again assigned.forEach(tracker::updateLocalCheckpoint); - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), not(equalTo(UNASSIGNED_SEQ_NO))); + assertThat(tracker.getGlobalCheckpoint(), not(equalTo(UNASSIGNED_SEQ_NO))); } public void testMissingInSyncIdsPreventAdvance() { final Map active = randomAllocationsWithLocalCheckpoints(0, 5); final Map initializing = randomAllocationsWithLocalCheckpoints(1, 5); tracker.updateAllocationIdsFromMaster(active.keySet(), initializing.keySet()); - initializing.keySet().forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getCheckpoint())); + initializing.keySet().forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getGlobalCheckpoint())); randomSubsetOf(randomInt(initializing.size() - 1), initializing.keySet()).forEach(aId -> tracker.updateLocalCheckpoint(aId, initializing.get(aId))); active.forEach(tracker::updateLocalCheckpoint); - // global checkpoint can't be advanced, but we need a sync - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); + assertThat(tracker.getGlobalCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); // update again initializing.forEach(tracker::updateLocalCheckpoint); - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), not(equalTo(UNASSIGNED_SEQ_NO))); + assertThat(tracker.getGlobalCheckpoint(), not(equalTo(UNASSIGNED_SEQ_NO))); } public void testInSyncIdsAreIgnoredIfNotValidatedByMaster() { @@ -197,16 +182,14 @@ public void testInSyncIdsAreIgnoredIfNotValidatedByMaster() { final Map initializing = randomAllocationsWithLocalCheckpoints(1, 5); final Map nonApproved = randomAllocationsWithLocalCheckpoints(1, 5); tracker.updateAllocationIdsFromMaster(active.keySet(), initializing.keySet()); - initializing.keySet().forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getCheckpoint())); - nonApproved.keySet().forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getCheckpoint())); + initializing.keySet().forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getGlobalCheckpoint())); + nonApproved.keySet().forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getGlobalCheckpoint())); List> allocations = Arrays.asList(active, initializing, nonApproved); Collections.shuffle(allocations, random()); allocations.forEach(a -> a.forEach(tracker::updateLocalCheckpoint)); - // global checkpoint can be advanced, but we need a sync - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), not(equalTo(UNASSIGNED_SEQ_NO))); + assertThat(tracker.getGlobalCheckpoint(), not(equalTo(UNASSIGNED_SEQ_NO))); } public void testInSyncIdsAreRemovedIfNotValidatedByMaster() { @@ -227,17 +210,14 @@ public void testInSyncIdsAreRemovedIfNotValidatedByMaster() { } tracker.updateAllocationIdsFromMaster(active, initializing); if (randomBoolean()) { - initializingToStay.keySet().forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getCheckpoint())); + initializingToStay.keySet().forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getGlobalCheckpoint())); } else { - initializing.forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getCheckpoint())); + initializing.forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getGlobalCheckpoint())); } if (randomBoolean()) { allocations.forEach(tracker::updateLocalCheckpoint); } - // global checkpoint may be advanced, but we need a sync in any case - assertTrue(tracker.updateCheckpointOnPrimary()); - // now remove shards if (randomBoolean()) { tracker.updateAllocationIdsFromMaster(activeToStay.keySet(), initializingToStay.keySet()); @@ -250,9 +230,7 @@ public void testInSyncIdsAreRemovedIfNotValidatedByMaster() { final long checkpoint = Stream.concat(activeToStay.values().stream(), initializingToStay.values().stream()) .min(Long::compare).get() + 10; // we added 10 to make sure it's advanced in the second time - // global checkpoint is advanced and we need a sync - assertTrue(tracker.updateCheckpointOnPrimary()); - assertThat(tracker.getCheckpoint(), equalTo(checkpoint)); + assertThat(tracker.getGlobalCheckpoint(), equalTo(checkpoint)); } public void testWaitForAllocationIdToBeInSync() throws BrokenBarrierException, InterruptedException { @@ -264,7 +242,6 @@ public void testWaitForAllocationIdToBeInSync() throws BrokenBarrierException, I final String trackingAllocationId = randomAlphaOfLength(16); tracker.updateAllocationIdsFromMaster(Collections.singleton(inSyncAllocationId), Collections.singleton(trackingAllocationId)); tracker.updateLocalCheckpoint(inSyncAllocationId, globalCheckpoint); - tracker.updateCheckpointOnPrimary(); final Thread thread = new Thread(() -> { try { // synchronize starting with the test thread @@ -313,7 +290,6 @@ public void testWaitForAllocationIdToBeInSyncCanBeInterrupted() throws BrokenBar final String trackingAllocationId = randomAlphaOfLength(32); tracker.updateAllocationIdsFromMaster(Collections.singleton(inSyncAllocationId), Collections.singleton(trackingAllocationId)); tracker.updateLocalCheckpoint(inSyncAllocationId, globalCheckpoint); - tracker.updateCheckpointOnPrimary(); final Thread thread = new Thread(() -> { try { // synchronize starting with the test thread @@ -426,9 +402,8 @@ public void testUpdateAllocationIdsFromMaster() throws Exception { .entrySet() .stream() .allMatch(e -> tracker.trackingLocalCheckpoints.get(e.getKey()) == e.getValue())); - assertTrue(tracker.updateCheckpointOnPrimary()); final long minimumActiveLocalCheckpoint = (long) activeLocalCheckpoints.values().stream().min(Integer::compareTo).get(); - assertThat(tracker.getCheckpoint(), equalTo(minimumActiveLocalCheckpoint)); + assertThat(tracker.getGlobalCheckpoint(), equalTo(minimumActiveLocalCheckpoint)); final long minimumInitailizingLocalCheckpoint = (long) initializingLocalCheckpoints.values().stream().min(Integer::compareTo).get(); // now we are going to add a new allocation ID and bring it in sync which should move it to the in-sync allocation IDs @@ -477,6 +452,69 @@ public void testUpdateAllocationIdsFromMaster() throws Exception { assertTrue(tracker.inSyncLocalCheckpoints.containsKey(newSyncingAllocationId)); } + /** + * If we do not update the global checkpoint in {@link GlobalCheckpointTracker#markAllocationIdAsInSync(String, long)} after adding the + * allocation ID to the in-sync set and removing it from pending, the local checkpoint update that freed the thread waiting for the + * local checkpoint to advance could miss updating the global checkpoint in a race if the the waiting thread did not add the allocation + * ID to the in-sync set and remove it from the pending set before the local checkpoint updating thread executed the global checkpoint + * update. This test fails without an additional to {@link GlobalCheckpointTracker#updateGlobalCheckpointOnPrimary()} after removing + * the allocation ID from the pending set in {@link GlobalCheckpointTracker#markAllocationIdAsInSync(String, long)} (even if a call is + * added after notifying all waiters in {@link GlobalCheckpointTracker#updateLocalCheckpoint(String, long)}). + * + * @throws InterruptedException if the main test thread was interrupted while waiting + * @throws BrokenBarrierException if the barrier was broken while the main test thread was waiting + */ + public void testRaceUpdatingGlobalCheckpoint() throws InterruptedException, BrokenBarrierException { + + final String active = randomAlphaOfLength(16); + final String initializing = randomAlphaOfLength(32); + tracker.updateAllocationIdsFromMaster(Collections.singleton(active), Collections.singleton(initializing)); + + final CyclicBarrier barrier = new CyclicBarrier(4); + + final int activeLocalCheckpoint = randomIntBetween(0, Integer.MAX_VALUE - 1); + tracker.updateLocalCheckpoint(active, activeLocalCheckpoint); + final int nextActiveLocalCheckpoint = randomIntBetween(activeLocalCheckpoint + 1, Integer.MAX_VALUE); + final Thread activeThread = new Thread(() -> { + try { + barrier.await(); + } catch (final BrokenBarrierException | InterruptedException e) { + throw new RuntimeException(e); + } + tracker.updateLocalCheckpoint(active, nextActiveLocalCheckpoint); + }); + + final int initializingLocalCheckpoint = randomIntBetween(0, nextActiveLocalCheckpoint - 1); + final Thread initializingThread = new Thread(() -> { + try { + barrier.await(); + } catch (final BrokenBarrierException | InterruptedException e) { + throw new RuntimeException(e); + } + tracker.updateLocalCheckpoint(initializing, nextActiveLocalCheckpoint); + }); + + final Thread markingThread = new Thread(() -> { + try { + barrier.await(); + tracker.markAllocationIdAsInSync(initializing, initializingLocalCheckpoint - 1); + } catch (final BrokenBarrierException | InterruptedException e) { + throw new RuntimeException(e); + } + }); + + activeThread.start(); + initializingThread.start(); + markingThread.start(); + barrier.await(); + + activeThread.join(); + initializingThread.join(); + markingThread.join(); + + assertThat(tracker.getGlobalCheckpoint(), equalTo((long) nextActiveLocalCheckpoint)); + + } private void markAllocationIdAsInSyncQuietly( final GlobalCheckpointTracker tracker, final String allocationId, final long localCheckpoint) { diff --git a/core/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java b/core/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java index bcaccabd37869..338634d977e97 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java +++ b/core/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java @@ -161,21 +161,23 @@ public void testMarkAsInactiveTriggersSyncedFlush() throws Exception { assertNotNull(indexStats.getShards()[0].getCommitStats().getUserData().get(Engine.SYNC_COMMIT_ID)); } - public void testDurableFlagHasEffect() { + public void testDurableFlagHasEffect() throws Exception { createIndex("test"); ensureGreen(); client().prepareIndex("test", "bar", "1").setSource("{}", XContentType.JSON).get(); IndicesService indicesService = getInstanceFromNode(IndicesService.class); IndexService test = indicesService.indexService(resolveIndex("test")); IndexShard shard = test.getShardOrNull(0); + shard.checkIdle(Long.MIN_VALUE); setDurability(shard, Translog.Durability.REQUEST); - assertFalse(ShardUtilsTests.getShardEngine(shard).getTranslog().syncNeeded()); + assertBusy(() -> assertFalse(ShardUtilsTests.getShardEngine(shard).getTranslog().syncNeeded())); setDurability(shard, Translog.Durability.ASYNC); client().prepareIndex("test", "bar", "2").setSource("{}", XContentType.JSON).get(); assertTrue(ShardUtilsTests.getShardEngine(shard).getTranslog().syncNeeded()); setDurability(shard, Translog.Durability.REQUEST); client().prepareDelete("test", "bar", "1").get(); - assertFalse(ShardUtilsTests.getShardEngine(shard).getTranslog().syncNeeded()); + shard.checkIdle(Long.MIN_VALUE); + assertBusy(() -> assertFalse(ShardUtilsTests.getShardEngine(shard).getTranslog().syncNeeded())); setDurability(shard, Translog.Durability.ASYNC); client().prepareDelete("test", "bar", "2").get(); @@ -184,7 +186,8 @@ public void testDurableFlagHasEffect() { assertNoFailures(client().prepareBulk() .add(client().prepareIndex("test", "bar", "3").setSource("{}", XContentType.JSON)) .add(client().prepareDelete("test", "bar", "1")).get()); - assertFalse(ShardUtilsTests.getShardEngine(shard).getTranslog().syncNeeded()); + shard.checkIdle(Long.MIN_VALUE); + assertBusy(() -> assertFalse(ShardUtilsTests.getShardEngine(shard).getTranslog().syncNeeded())); setDurability(shard, Translog.Durability.ASYNC); assertNoFailures(client().prepareBulk() @@ -532,7 +535,7 @@ public static final IndexShard newIndexShard(IndexService indexService, IndexSh IndexShard newShard = new IndexShard(initializingShardRouting, indexService.getIndexSettings(), shard.shardPath(), shard.store(), indexService.getIndexSortSupplier(), indexService.cache(), indexService.mapperService(), indexService.similarityService(), indexService.fieldData(), shard.getEngineFactory(), indexService.getIndexEventListener(), wrapper, - indexService.getThreadPool(), indexService.getBigArrays(), null, () -> {}, Collections.emptyList(), Arrays.asList(listeners)); + indexService.getThreadPool(), indexService.getBigArrays(), null, Collections.emptyList(), Arrays.asList(listeners)); return newShard; } diff --git a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java index b8e621df8872d..ab898985e43af 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java +++ b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java @@ -1102,7 +1102,7 @@ public IndexSearcher wrap(IndexSearcher searcher) throws EngineException { }; closeShards(shard); IndexShard newShard = newShard(ShardRoutingHelper.reinitPrimary(shard.routingEntry()), - shard.shardPath(), shard.indexSettings().getIndexMetaData(), wrapper, () -> {}, null); + shard.shardPath(), shard.indexSettings().getIndexMetaData(), wrapper, null); recoveryShardFromStore(newShard); @@ -1243,7 +1243,7 @@ public IndexSearcher wrap(IndexSearcher searcher) throws EngineException { closeShards(shard); IndexShard newShard = newShard(ShardRoutingHelper.reinitPrimary(shard.routingEntry()), - shard.shardPath(), shard.indexSettings().getIndexMetaData(), wrapper, () -> {}, null); + shard.shardPath(), shard.indexSettings().getIndexMetaData(), wrapper, null); recoveryShardFromStore(newShard); diff --git a/core/src/test/java/org/elasticsearch/indices/IndicesLifecycleListenerSingleNodeTests.java b/core/src/test/java/org/elasticsearch/indices/IndicesLifecycleListenerSingleNodeTests.java index a2a90a8c753ac..1fe4def962339 100644 --- a/core/src/test/java/org/elasticsearch/indices/IndicesLifecycleListenerSingleNodeTests.java +++ b/core/src/test/java/org/elasticsearch/indices/IndicesLifecycleListenerSingleNodeTests.java @@ -120,7 +120,7 @@ public void afterIndexRemoved(Index index, IndexSettings indexSettings, IndexRem }; indicesService.removeIndex(idx, DELETED, "simon says"); try { - IndexService index = indicesService.createIndex(metaData, Arrays.asList(countingListener), s -> {}); + IndexService index = indicesService.createIndex(metaData, Arrays.asList(countingListener)); assertEquals(3, counter.get()); idx = index.index(); ShardRouting newRouting = shardRouting; diff --git a/core/src/test/java/org/elasticsearch/indices/cluster/AbstractIndicesClusterStateServiceTestCase.java b/core/src/test/java/org/elasticsearch/indices/cluster/AbstractIndicesClusterStateServiceTestCase.java index e1657955d90e0..b9e1b23bda190 100644 --- a/core/src/test/java/org/elasticsearch/indices/cluster/AbstractIndicesClusterStateServiceTestCase.java +++ b/core/src/test/java/org/elasticsearch/indices/cluster/AbstractIndicesClusterStateServiceTestCase.java @@ -52,13 +52,11 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; -import java.util.function.Consumer; import java.util.stream.Collectors; import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; import static org.elasticsearch.common.collect.MapBuilder.newMapBuilder; -import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; @@ -191,9 +189,8 @@ protected class MockIndicesService implements AllocatedIndices buildInIndexListener, - Consumer globalCheckPointSyncer) throws IOException { + IndexMetaData indexMetaData, + List buildInIndexListener) throws IOException { MockIndexService indexService = new MockIndexService(new IndexSettings(indexMetaData, Settings.EMPTY)); indices = newMapBuilder(indices).put(indexMetaData.getIndexUUID(), indexService).immutableMap(); return indexService; diff --git a/core/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java b/core/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java index e7c3024cfdc6b..feff696e94201 100644 --- a/core/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java +++ b/core/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java @@ -141,8 +141,7 @@ public ClusterStateChanges(NamedXContentRegistry xContentRegistry, ThreadPool th // MetaDataCreateIndexService creates indices using its IndicesService instance to check mappings -> fake it here try { @SuppressWarnings("unchecked") final List listeners = anyList(); - @SuppressWarnings("unchecked") final Consumer globalCheckpointSyncer = any(Consumer.class); - when(indicesService.createIndex(any(IndexMetaData.class), listeners, globalCheckpointSyncer)) + when(indicesService.createIndex(any(IndexMetaData.class), listeners)) .then(invocationOnMock -> { IndexService indexService = mock(IndexService.class); IndexMetaData indexMetaData = (IndexMetaData)invocationOnMock.getArguments()[0]; diff --git a/core/src/test/java/org/elasticsearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java b/core/src/test/java/org/elasticsearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java index f2608f68ee57c..adfc6609d8f93 100644 --- a/core/src/test/java/org/elasticsearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java +++ b/core/src/test/java/org/elasticsearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java @@ -420,7 +420,7 @@ private IndicesClusterStateService createIndicesClusterStateService(DiscoveryNod null, null, null, - shardId -> {}); + null); } private class RecordingIndicesService extends MockIndicesService { diff --git a/core/src/test/java/org/elasticsearch/recovery/RelocationIT.java b/core/src/test/java/org/elasticsearch/recovery/RelocationIT.java index 8848a61c97653..dca450c8a1df7 100644 --- a/core/src/test/java/org/elasticsearch/recovery/RelocationIT.java +++ b/core/src/test/java/org/elasticsearch/recovery/RelocationIT.java @@ -84,7 +84,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Stream; -import static org.elasticsearch.index.IndexSettings.INDEX_SEQ_NO_CHECKPOINT_SYNC_INTERVAL; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; @@ -104,12 +103,6 @@ protected Collection> nodePlugins() { return Arrays.asList(MockTransportService.TestPlugin.class, MockIndexEventListener.TestPlugin.class); } - @Override - public Settings indexSettings() { - return Settings.builder().put(super.indexSettings()) - .put(INDEX_SEQ_NO_CHECKPOINT_SYNC_INTERVAL.getKey(), "200ms").build(); - } - @Override protected void beforeIndexDeletion() throws Exception { super.beforeIndexDeletion(); @@ -131,7 +124,6 @@ protected void beforeIndexDeletion() throws Exception { final SeqNoStats seqNoStats = shardStats.getSeqNoStats(); assertThat(shardStats.getShardRouting() + " local checkpoint mismatch", seqNoStats.getLocalCheckpoint(), equalTo(primarySeqNoStats.getLocalCheckpoint())); - assertThat(shardStats.getShardRouting() + " global checkpoint mismatch", seqNoStats.getGlobalCheckpoint(), equalTo(primarySeqNoStats.getGlobalCheckpoint())); assertThat(shardStats.getShardRouting() + " max seq no mismatch", @@ -375,6 +367,9 @@ public void indexShardStateChanged(IndexShard indexShard, @Nullable IndexShardSt } } + // refresh is a replication action so this forces a global checkpoint sync which is needed as these are asserted on in tear down + client().admin().indices().prepareRefresh("test").get(); + } } diff --git a/qa/backwards-5.0/src/test/java/org/elasticsearch/backwards/IndexingIT.java b/qa/backwards-5.0/src/test/java/org/elasticsearch/backwards/IndexingIT.java index 6ef40a7778236..0954682094cb3 100644 --- a/qa/backwards-5.0/src/test/java/org/elasticsearch/backwards/IndexingIT.java +++ b/qa/backwards-5.0/src/test/java/org/elasticsearch/backwards/IndexingIT.java @@ -210,9 +210,6 @@ public void testSeqNoCheckpoints() throws Exception { final boolean checkGlobalCheckpoints = nodes.getMaster().getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED); logger.info("master version is [{}], global checkpoints will be [{}]", nodes.getMaster().getVersion(), checkGlobalCheckpoints ? "checked" : "not be checked"); - if (checkGlobalCheckpoints) { - settings.put(IndexSettings.INDEX_SEQ_NO_CHECKPOINT_SYNC_INTERVAL.getKey(), "100ms"); - } final String index = "test"; createIndex(index, settings.build()); try (RestClient newNodeClient = buildClient(restClientSettings(), @@ -242,6 +239,7 @@ public void testSeqNoCheckpoints() throws Exception { logger.info("indexing [{}] docs after moving primary", numberOfDocsAfterMovingPrimary); numDocsOnNewPrimary += indexDocs(index, numDocs, numberOfDocsAfterMovingPrimary); numDocs += numberOfDocsAfterMovingPrimary; + assertOK(client().performRequest("POST", index + "/_refresh")); // this forces a global checkpoint sync assertSeqNoOnShards(index, nodes, checkGlobalCheckpoints, numDocsOnNewPrimary, newNodeClient); /* * Dropping the number of replicas to zero, and then increasing it to one triggers a recovery thus exercising any BWC-logic in diff --git a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java index f0c4a9c44d0a5..dbfa77635ab33 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java @@ -190,7 +190,7 @@ protected IndexShard newShard(ShardId shardId, boolean primary, String nodeId, I @Nullable IndexSearcherWrapper searcherWrapper) throws IOException { ShardRouting shardRouting = TestShardRouting.newShardRouting(shardId, nodeId, primary, ShardRoutingState.INITIALIZING, primary ? RecoverySource.StoreRecoverySource.EMPTY_STORE_INSTANCE : RecoverySource.PeerRecoverySource.INSTANCE); - return newShard(shardRouting, indexMetaData, searcherWrapper, () -> {}, null); + return newShard(shardRouting, indexMetaData, searcherWrapper, null); } /** @@ -206,7 +206,7 @@ protected IndexShard newShard(ShardId shardId, boolean primary, String nodeId, I @Nullable IndexSearcherWrapper searcherWrapper) throws IOException { ShardRouting shardRouting = TestShardRouting.newShardRouting(shardId, nodeId, primary, ShardRoutingState.INITIALIZING, primary ? RecoverySource.StoreRecoverySource.EMPTY_STORE_INSTANCE : RecoverySource.PeerRecoverySource.INSTANCE); - return newShard(shardRouting, indexMetaData, searcherWrapper, globalCheckpointSyncer, null); + return newShard(shardRouting, indexMetaData, searcherWrapper, null); } @@ -220,21 +220,19 @@ protected IndexShard newShard(ShardId shardId, boolean primary, String nodeId, I */ protected IndexShard newShard(ShardRouting routing, IndexMetaData indexMetaData, IndexingOperationListener... listeners) throws IOException { - return newShard(routing, indexMetaData, null, () -> {}, null, listeners); + return newShard(routing, indexMetaData, null, null, listeners); } /** * creates a new initializing shard. The shard will will be put in its proper path under the * current node id the shard is assigned to. - * - * @param routing shard routing to use + * @param routing shard routing to use * @param indexMetaData indexMetaData for the shard, including any mapping * @param indexSearcherWrapper an optional wrapper to be used during searchers - * @param globalCheckpointSyncer an runnable to run when the global check point needs syncing * @param listeners an optional set of listeners to add to the shard */ protected IndexShard newShard(ShardRouting routing, IndexMetaData indexMetaData, - @Nullable IndexSearcherWrapper indexSearcherWrapper, Runnable globalCheckpointSyncer, + @Nullable IndexSearcherWrapper indexSearcherWrapper, @Nullable EngineFactory engineFactory, IndexingOperationListener... listeners) throws IOException { @@ -242,12 +240,11 @@ protected IndexShard newShard(ShardRouting routing, IndexMetaData indexMetaData, final ShardId shardId = routing.shardId(); final NodeEnvironment.NodePath nodePath = new NodeEnvironment.NodePath(createTempDir()); ShardPath shardPath = new ShardPath(false, nodePath.resolve(shardId), nodePath.resolve(shardId), shardId); - return newShard(routing, shardPath, indexMetaData, indexSearcherWrapper, globalCheckpointSyncer, engineFactory, listeners); + return newShard(routing, shardPath, indexMetaData, indexSearcherWrapper, engineFactory, listeners); } /** * creates a new initializing shard. - * * @param routing shard routing to use * @param shardPath path to use for shard data * @param indexMetaData indexMetaData for the shard, including any mapping @@ -256,7 +253,6 @@ protected IndexShard newShard(ShardRouting routing, IndexMetaData indexMetaData, */ protected IndexShard newShard(ShardRouting routing, ShardPath shardPath, IndexMetaData indexMetaData, @Nullable IndexSearcherWrapper indexSearcherWrapper, - Runnable globalCheckpointSyncer, @Nullable EngineFactory engineFactory, IndexingOperationListener... listeners) throws IOException { final Settings nodeSettings = Settings.builder().put("node.name", routing.currentNodeId()).build(); @@ -280,7 +276,7 @@ protected IndexShard newShard(ShardRouting routing, ShardPath shardPath, IndexMe new NoneCircuitBreakerService(), mapperService); indexShard = new IndexShard(routing, indexSettings, shardPath, store, () ->null, indexCache, mapperService, similarityService, indexFieldDataService, engineFactory, indexEventListener, indexSearcherWrapper, threadPool, - BigArrays.NON_RECYCLING_INSTANCE, warmer, globalCheckpointSyncer, Collections.emptyList(), Arrays.asList(listeners)); + BigArrays.NON_RECYCLING_INSTANCE, warmer, Collections.emptyList(), Arrays.asList(listeners)); success = true; } finally { if (success == false) { @@ -310,8 +306,7 @@ protected IndexShard reinitShard(IndexShard current, IndexingOperationListener.. */ protected IndexShard reinitShard(IndexShard current, ShardRouting routing, IndexingOperationListener... listeners) throws IOException { closeShards(current); - return newShard(routing, current.shardPath(), current.indexSettings().getIndexMetaData(), null, - current.getGlobalCheckpointSyncer(), current.engineFactory, listeners); + return newShard(routing, current.shardPath(), current.indexSettings().getIndexMetaData(), null, current.engineFactory, listeners); } /** From 09c5fbfd0009e1cc08ea564fd1f0399db4dbc2c2 Mon Sep 17 00:00:00 2001 From: Suhas Karanth Date: Wed, 10 May 2017 00:48:43 +0530 Subject: [PATCH 257/619] Docs: Correct description of example (#24541) Copy and paste error. --- .../pipeline/extended-stats-bucket-aggregation.asciidoc | 2 +- .../pipeline/percentiles-bucket-aggregation.asciidoc | 2 +- .../aggregations/pipeline/stats-bucket-aggregation.asciidoc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/aggregations/pipeline/extended-stats-bucket-aggregation.asciidoc b/docs/reference/aggregations/pipeline/extended-stats-bucket-aggregation.asciidoc index a7c1ab6b3dc0b..c6a3bb56765a8 100644 --- a/docs/reference/aggregations/pipeline/extended-stats-bucket-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/extended-stats-bucket-aggregation.asciidoc @@ -33,7 +33,7 @@ A `extended_stats_bucket` aggregation looks like this in isolation: |`sigma` |The number of standard deviations above/below the mean to display |Optional | 2 |=== -The following snippet calculates the sum of all the total monthly `sales` buckets: +The following snippet calculates the extended stats for monthly `sales` bucket: [source,js] -------------------------------------------------- diff --git a/docs/reference/aggregations/pipeline/percentiles-bucket-aggregation.asciidoc b/docs/reference/aggregations/pipeline/percentiles-bucket-aggregation.asciidoc index b65f8c3be7930..f386af209b1a0 100644 --- a/docs/reference/aggregations/pipeline/percentiles-bucket-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/percentiles-bucket-aggregation.asciidoc @@ -31,7 +31,7 @@ A `percentiles_bucket` aggregation looks like this in isolation: |`percents` |The list of percentiles to calculate |Optional | `[ 1, 5, 25, 50, 75, 95, 99 ]` |=== -The following snippet calculates the sum of all the total monthly `sales` buckets: +The following snippet calculates the percentiles for the total monthly `sales` buckets: [source,js] -------------------------------------------------- diff --git a/docs/reference/aggregations/pipeline/stats-bucket-aggregation.asciidoc b/docs/reference/aggregations/pipeline/stats-bucket-aggregation.asciidoc index 5cbfe53f90eb7..b4131ef494441 100644 --- a/docs/reference/aggregations/pipeline/stats-bucket-aggregation.asciidoc +++ b/docs/reference/aggregations/pipeline/stats-bucket-aggregation.asciidoc @@ -30,7 +30,7 @@ A `stats_bucket` aggregation looks like this in isolation: |`format` |format to apply to the output value of this aggregation |Optional | `null` |=== -The following snippet calculates the sum of all the total monthly `sales` buckets: +The following snippet calculates the stats for monthly `sales`: [source,js] -------------------------------------------------- From 37dc165e7f8a5607d83d62e46260d6c1a389a8e3 Mon Sep 17 00:00:00 2001 From: javanna Date: Tue, 9 May 2017 21:23:00 +0200 Subject: [PATCH 258/619] [TEST] add test to verify that no aggs have been forgotten in AggregationsTests --- .../search/aggregations/AggregationsTests.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java index 84d11db3dbe09..f986f614fa172 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java @@ -58,6 +58,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import static java.util.Collections.singletonMap; @@ -116,6 +118,14 @@ public void cleanUp() throws Exception { } } + public void testAllAggsAreBeingTested() { + assertEquals(InternalAggregationTestCase.getNamedXContents().size(), aggsTests.size()); + Set aggs = aggsTests.stream().map((testCase) -> testCase.createTestInstance().getType()).collect(Collectors.toSet()); + for (NamedXContentRegistry.Entry entry : InternalAggregationTestCase.getNamedXContents()) { + assertTrue(aggs.contains(entry.name.getPreferredName())); + } + } + public void testFromXContent() throws IOException { XContentType xContentType = randomFrom(XContentType.values()); final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); From 3f54ae62648b2f0f71f09e83af89e5ec0479c68f Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 9 May 2017 15:36:51 -0400 Subject: [PATCH 259/619] Fix handling of spaces in plugin script on Windows A previous fix for handling spaces in the Windows scripts should also be applied to the plugin script. This commit does that. Closes #24750 --- .../src/main/resources/bin/elasticsearch-plugin.bat | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/distribution/src/main/resources/bin/elasticsearch-plugin.bat b/distribution/src/main/resources/bin/elasticsearch-plugin.bat index 58b749d6b83ba..88552089346e0 100644 --- a/distribution/src/main/resources/bin/elasticsearch-plugin.bat +++ b/distribution/src/main/resources/bin/elasticsearch-plugin.bat @@ -3,11 +3,11 @@ SETLOCAL enabledelayedexpansion IF DEFINED JAVA_HOME ( - set JAVA=%JAVA_HOME%\bin\java.exe + set JAVA="%JAVA_HOME%\bin\java.exe" ) ELSE ( FOR %%I IN (java.exe) DO set JAVA=%%~$PATH:I ) -IF NOT EXIST "%JAVA%" ( +IF NOT EXIST %JAVA% ( ECHO Could not find any executable java binary. Please install java in your PATH or set JAVA_HOME 1>&2 EXIT /B 1 ) @@ -25,6 +25,6 @@ IF DEFINED CONF_DIR ( SET args=%* SET HOSTNAME=%COMPUTERNAME% -"%JAVA%" %ES_JAVA_OPTS% !path_props! -cp "%ES_HOME%/lib/*;" "org.elasticsearch.plugins.PluginCli" !args! +%JAVA% %ES_JAVA_OPTS% !path_props! -cp "%ES_HOME%/lib/*;" "org.elasticsearch.plugins.PluginCli" !args! ENDLOCAL From b6906702ccabc52b67a56e97a059d864424c0f89 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 9 May 2017 17:04:52 -0400 Subject: [PATCH 260/619] Remove global checkpoint constraint A constraint on the global checkpoint was inadvertently committed from the inlining global checkpoint work. Namely, the constraint prevents the global checkpoint from advancing to no ops performed, a situation that can occur when shards are started but empty. --- .../elasticsearch/index/seqno/GlobalCheckpointTracker.java | 2 +- .../index/seqno/GlobalCheckpointTrackerTests.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointTracker.java b/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointTracker.java index ff35c4291a35c..ea6edef7a12fa 100644 --- a/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointTracker.java +++ b/core/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointTracker.java @@ -175,7 +175,7 @@ private synchronized void updateGlobalCheckpointOnPrimary() { globalCheckpoint); throw new IllegalStateException(message); } - if (minLocalCheckpoint >= 0 && globalCheckpoint != minLocalCheckpoint) { + if (globalCheckpoint != minLocalCheckpoint) { logger.trace("global checkpoint updated to [{}]", minLocalCheckpoint); globalCheckpoint = minLocalCheckpoint; } diff --git a/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java b/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java index 4898c55328179..b99ac13a0d44a 100644 --- a/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java +++ b/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java @@ -457,9 +457,9 @@ public void testUpdateAllocationIdsFromMaster() throws Exception { * allocation ID to the in-sync set and removing it from pending, the local checkpoint update that freed the thread waiting for the * local checkpoint to advance could miss updating the global checkpoint in a race if the the waiting thread did not add the allocation * ID to the in-sync set and remove it from the pending set before the local checkpoint updating thread executed the global checkpoint - * update. This test fails without an additional to {@link GlobalCheckpointTracker#updateGlobalCheckpointOnPrimary()} after removing - * the allocation ID from the pending set in {@link GlobalCheckpointTracker#markAllocationIdAsInSync(String, long)} (even if a call is - * added after notifying all waiters in {@link GlobalCheckpointTracker#updateLocalCheckpoint(String, long)}). + * update. This test fails without an additional call to {@link GlobalCheckpointTracker#updateGlobalCheckpointOnPrimary()} after + * removing the allocation ID from the pending set in {@link GlobalCheckpointTracker#markAllocationIdAsInSync(String, long)} (even if a + * call is added after notifying all waiters in {@link GlobalCheckpointTracker#updateLocalCheckpoint(String, long)}). * * @throws InterruptedException if the main test thread was interrupted while waiting * @throws BrokenBarrierException if the barrier was broken while the main test thread was waiting From d2416a3a408d3d3d02742890ad8b96ac9591270c Mon Sep 17 00:00:00 2001 From: Tal Levy Date: Tue, 9 May 2017 14:33:43 -0700 Subject: [PATCH 261/619] modify ingest timestamp format check to remove timezone --- .../ingest/60_pipeline_timestamp_date_mapping.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml index e172ad9f4ce64..99c7671659313 100644 --- a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml +++ b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml @@ -24,11 +24,23 @@ "value": "{{ _ingest.timestamp }}" } }, + { + "grok" : { + "field" : "my_time", + "patterns": ["%{DAY:day} %{MONTH:month} %{MONTHDAY:monthday} %{TIME:time} %{TZ} %{YEAR:year}"] + } + }, + { + "set": { + "field": "my_time", + "value": "{{day}} {{month}} {{monthday}} {{time}} {{year}}" + } + }, { "date" : { "field" : "my_time", "target_field": "my_time", - "formats": ["EEE MMM dd HH:mm:ss zzz yyyy"] + "formats": ["EEE MMM dd HH:mm:ss yyyy"] } } ] From 53f6d9412e3e313e15aa8df94adf2f79ba549182 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 9 May 2017 14:52:28 -0700 Subject: [PATCH 262/619] Fix ids query test when none or ALL type is used See #24460 --- .../org/elasticsearch/index/query/IdsQueryBuilderTests.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/test/java/org/elasticsearch/index/query/IdsQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/IdsQueryBuilderTests.java index 9c4758b52bfe5..fa5562474bdb9 100644 --- a/core/src/test/java/org/elasticsearch/index/query/IdsQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/IdsQueryBuilderTests.java @@ -25,6 +25,7 @@ import org.apache.lucene.search.Query; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.ParsingException; +import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.test.AbstractQueryTestCase; @@ -73,7 +74,8 @@ protected IdsQueryBuilder doCreateTestQueryBuilder() { @Override protected void doAssertLuceneQuery(IdsQueryBuilder queryBuilder, Query query, SearchContext context) throws IOException { - if (queryBuilder.ids().size() == 0) { + if (queryBuilder.ids().size() == 0 || queryBuilder.types().length == 0 || + (queryBuilder.types().length == 1 && queryBuilder.types()[0].equals(MetaData.ALL))) { assertThat(query, instanceOf(MatchNoDocsQuery.class)); } else { assertThat(query, instanceOf(TermInSetQuery.class)); From 945b3cd4ca5cba36e3dddcc5d8b1d795f4c788cc Mon Sep 17 00:00:00 2001 From: Tal Levy Date: Tue, 9 May 2017 15:31:32 -0700 Subject: [PATCH 263/619] fix ingest pipeline timestamp timezone format check to be even more generic --- .../test/ingest/60_pipeline_timestamp_date_mapping.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml index 99c7671659313..3f51cda6fb115 100644 --- a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml +++ b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml @@ -27,7 +27,7 @@ { "grok" : { "field" : "my_time", - "patterns": ["%{DAY:day} %{MONTH:month} %{MONTHDAY:monthday} %{TIME:time} %{TZ} %{YEAR:year}"] + "patterns": ["%{DAY:day} %{MONTH:month} %{MONTHDAY:monthday} %{TIME:time} %{WORD} %{YEAR:year}"] } }, { From ebd3e5f73f1ac8849510bbe14f07f25ea591bbbc Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 9 May 2017 16:14:57 -0700 Subject: [PATCH 264/619] Scripting: Deprecate file script settings (#24555) File scripts have 2 related settings: the path of file scripts, and whether they can be dynamically reloaded. This commit deprecates those settings. relates #21798 --- core/src/main/java/org/elasticsearch/env/Environment.java | 3 ++- .../main/java/org/elasticsearch/script/ScriptService.java | 4 ++-- .../java/org/elasticsearch/script/FileScriptTests.java | 7 +++++-- .../java/org/elasticsearch/script/NativeScriptTests.java | 1 - .../java/org/elasticsearch/script/ScriptContextTests.java | 5 +++++ .../search/aggregations/AggregatorFactoriesTests.java | 3 +-- .../metrics/scripted/InternalScriptedMetricTests.java | 2 -- .../metrics/scripted/ScriptedMetricAggregatorTests.java | 4 +--- .../java/org/elasticsearch/tribe/TribeServiceTests.java | 4 ++++ .../org/elasticsearch/bootstrap/EvilSecurityTests.java | 2 ++ .../org/elasticsearch/ingest/AbstractScriptTestCase.java | 1 - .../java/org/elasticsearch/test/AbstractQueryTestCase.java | 3 --- .../src/main/java/org/elasticsearch/test/ESTestCase.java | 2 -- 13 files changed, 22 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/env/Environment.java b/core/src/main/java/org/elasticsearch/env/Environment.java index 0d30f7b576c06..d1ad668e0eb31 100644 --- a/core/src/main/java/org/elasticsearch/env/Environment.java +++ b/core/src/main/java/org/elasticsearch/env/Environment.java @@ -55,7 +55,8 @@ public class Environment { public static final Setting DEFAULT_PATH_CONF_SETTING = Setting.simpleString("default.path.conf", Property.NodeScope); public static final Setting PATH_CONF_SETTING = new Setting<>("path.conf", DEFAULT_PATH_CONF_SETTING, Function.identity(), Property.NodeScope); - public static final Setting PATH_SCRIPTS_SETTING = Setting.simpleString("path.scripts", Property.NodeScope); + public static final Setting PATH_SCRIPTS_SETTING = + Setting.simpleString("path.scripts", Property.NodeScope, Property.Deprecated); public static final Setting> DEFAULT_PATH_DATA_SETTING = Setting.listSetting("default.path.data", Collections.emptyList(), Function.identity(), Property.NodeScope); public static final Setting> PATH_DATA_SETTING = diff --git a/core/src/main/java/org/elasticsearch/script/ScriptService.java b/core/src/main/java/org/elasticsearch/script/ScriptService.java index 3e612915a7b07..7a65fee327aaa 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptService.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptService.java @@ -86,7 +86,7 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust public static final Setting SCRIPT_CACHE_EXPIRE_SETTING = Setting.positiveTimeSetting("script.cache.expire", TimeValue.timeValueMillis(0), Property.NodeScope); public static final Setting SCRIPT_AUTO_RELOAD_ENABLED_SETTING = - Setting.boolSetting("script.auto_reload_enabled", true, Property.NodeScope); + Setting.boolSetting("script.auto_reload_enabled", true, Property.NodeScope, Property.Deprecated); public static final Setting SCRIPT_MAX_SIZE_IN_BYTES = Setting.intSetting("script.max_size_in_bytes", 65535, Property.NodeScope); public static final Setting SCRIPT_MAX_COMPILATIONS_PER_MINUTE = @@ -162,7 +162,7 @@ public ScriptService(Settings settings, Environment env, FileWatcher fileWatcher = new FileWatcher(scriptsDirectory); fileWatcher.addListener(new ScriptChangesListener()); - if (SCRIPT_AUTO_RELOAD_ENABLED_SETTING.get(settings)) { + if (SCRIPT_AUTO_RELOAD_ENABLED_SETTING.get(settings) && resourceWatcherService != null) { // automatic reload is enabled - register scripts resourceWatcherService.add(fileWatcher); } else { diff --git a/core/src/test/java/org/elasticsearch/script/FileScriptTests.java b/core/src/test/java/org/elasticsearch/script/FileScriptTests.java index c502bd5f03020..af32e8abec797 100644 --- a/core/src/test/java/org/elasticsearch/script/FileScriptTests.java +++ b/core/src/test/java/org/elasticsearch/script/FileScriptTests.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.script; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.script.MockScriptEngine.MockCompiledScript; @@ -59,7 +60,8 @@ public void testFileScriptFound() throws Exception { assertNotNull(compiledScript); MockCompiledScript executable = (MockCompiledScript) compiledScript.compiled(); assertEquals("script1.mockscript", executable.getName()); - assertWarnings("File scripts are deprecated. Use stored or inline scripts instead."); + assertSettingDeprecationsAndWarnings(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}, + "File scripts are deprecated. Use stored or inline scripts instead."); } public void testAllOpsDisabled() throws Exception { @@ -79,6 +81,7 @@ public void testAllOpsDisabled() throws Exception { assertTrue(e.getMessage(), e.getMessage().contains("scripts of type [file], operation [" + context.getKey() + "] and lang [" + MockScriptEngine.NAME + "] are disabled")); } } - assertWarnings("File scripts are deprecated. Use stored or inline scripts instead."); + assertSettingDeprecationsAndWarnings(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}, + "File scripts are deprecated. Use stored or inline scripts instead."); } } diff --git a/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java b/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java index aa2e260c7c2e1..0dfda8f7a1602 100644 --- a/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java +++ b/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java @@ -45,7 +45,6 @@ public void testNativeScript() throws InterruptedException { Settings settings = Settings.builder() .put("node.name", "testNativeScript") .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) - .put(ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING.getKey(), false) .build(); ScriptModule scriptModule = new ScriptModule(settings, new Environment(settings), null, singletonList(new NativeScriptEngineService(settings, singletonMap("my", new MyNativeScriptFactory()))), emptyList()); diff --git a/core/src/test/java/org/elasticsearch/script/ScriptContextTests.java b/core/src/test/java/org/elasticsearch/script/ScriptContextTests.java index e25335e5d68e4..83608a010b1ff 100644 --- a/core/src/test/java/org/elasticsearch/script/ScriptContextTests.java +++ b/core/src/test/java/org/elasticsearch/script/ScriptContextTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.MetaData; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.test.ESTestCase; @@ -77,6 +78,7 @@ public void testCustomGlobalScriptContextSettings() throws Exception { assertThat(e.getMessage(), containsString("scripts of type [" + scriptType + "], operation [" + PLUGIN_NAME + "_custom_globally_disabled_op] and lang [" + MockScriptEngine.NAME + "] are disabled")); } } + assertSettingDeprecationsAndWarnings(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}); } public void testCustomScriptContextSettings() throws Exception { @@ -93,6 +95,7 @@ public void testCustomScriptContextSettings() throws Exception { assertNotNull(scriptService.compile(script, ScriptContext.Standard.AGGS)); assertNotNull(scriptService.compile(script, ScriptContext.Standard.SEARCH)); assertNotNull(scriptService.compile(script, new ScriptContext.Plugin(PLUGIN_NAME, "custom_op"))); + assertSettingDeprecationsAndWarnings(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}); } public void testUnknownPluginScriptContext() throws Exception { @@ -106,6 +109,7 @@ public void testUnknownPluginScriptContext() throws Exception { assertTrue(e.getMessage(), e.getMessage().contains("script context [" + PLUGIN_NAME + "_unknown] not supported")); } } + assertSettingDeprecationsAndWarnings(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}); } public void testUnknownCustomScriptContext() throws Exception { @@ -125,6 +129,7 @@ public String getKey() { assertTrue(e.getMessage(), e.getMessage().contains("script context [test] not supported")); } } + assertSettingDeprecationsAndWarnings(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}); } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorFactoriesTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorFactoriesTests.java index 180e639eb345f..a3ffd4108f4ef 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorFactoriesTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorFactoriesTests.java @@ -52,8 +52,7 @@ public void setUp() throws Exception { // we have to prefer CURRENT since with the range of versions we support // it's rather unlikely to get the current actually. Settings settings = Settings.builder().put("node.name", AbstractQueryTestCase.class.toString()) - .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) - .put(ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING.getKey(), false).build(); + .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()).build(); // create some random type with some default field, those types will // stick around for all of the subclasses currentTypes = new String[randomIntBetween(0, 5)]; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java index be06bdd56b46d..9d8ff94c47c2f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java @@ -69,8 +69,6 @@ protected InternalScriptedMetric createTestInstance(String name, List[] {Environment.PATH_SCRIPTS_SETTING}); // plugins: ro assertExactPermissions(new FilePermission(environment.pluginsFile().toString(), "read,readlink"), permissions); diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/java/org/elasticsearch/ingest/AbstractScriptTestCase.java b/qa/smoke-test-ingest-with-all-dependencies/src/test/java/org/elasticsearch/ingest/AbstractScriptTestCase.java index 95810e089a275..1ca4c0252c39a 100644 --- a/qa/smoke-test-ingest-with-all-dependencies/src/test/java/org/elasticsearch/ingest/AbstractScriptTestCase.java +++ b/qa/smoke-test-ingest-with-all-dependencies/src/test/java/org/elasticsearch/ingest/AbstractScriptTestCase.java @@ -40,7 +40,6 @@ public abstract class AbstractScriptTestCase extends ESTestCase { public void init() throws Exception { Settings settings = Settings.builder() .put("path.home", createTempDir()) - .put(ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING.getKey(), false) .build(); ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(Arrays.asList(new MustacheScriptEngineService())); ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Collections.emptyList()); diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java index 4cd321b4fb8a8..79d813940fa60 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java @@ -178,7 +178,6 @@ public static void beforeClass() { nodeSettings = Settings.builder() .put("node.name", AbstractQueryTestCase.class.toString()) .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) - .put(ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING.getKey(), false) .build(); indexSettings = Settings.builder() .put(IndexMetaData.SETTING_VERSION_CREATED, indexVersionCreated) @@ -1091,8 +1090,6 @@ ScriptModule createScriptModule(List scriptPlugins) { Settings settings = Settings.builder() .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) - // no file watching, so we don't need a ResourceWatcherService - .put(ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING.getKey(), false) .build(); Environment environment = new Environment(settings); return ScriptModule.create(settings, environment, null, scriptPlugins); diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index 58d67ea3b98ad..ed8f271bab14e 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -1172,8 +1172,6 @@ public static TestAnalysis createTestAnalysis(IndexSettings indexSettings, Setti public static ScriptModule newTestScriptModule() { Settings settings = Settings.builder() .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) - // no file watching, so we don't need a ResourceWatcherService - .put(ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING.getKey(), false) .build(); Environment environment = new Environment(settings); MockScriptEngine scriptEngine = new MockScriptEngine(MockScriptEngine.NAME, Collections.singletonMap("1", script -> "1")); From 5242d3aeb0f5ffcab119710065660455b04ce26f Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 9 May 2017 16:53:19 -0700 Subject: [PATCH 265/619] Fix ids query builder test to use the same logic for matchnodocs as the builder itself --- .../org/elasticsearch/index/query/IdsQueryBuilderTests.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/index/query/IdsQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/IdsQueryBuilderTests.java index fa5562474bdb9..27c1558a7dde5 100644 --- a/core/src/test/java/org/elasticsearch/index/query/IdsQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/IdsQueryBuilderTests.java @@ -74,8 +74,7 @@ protected IdsQueryBuilder doCreateTestQueryBuilder() { @Override protected void doAssertLuceneQuery(IdsQueryBuilder queryBuilder, Query query, SearchContext context) throws IOException { - if (queryBuilder.ids().size() == 0 || queryBuilder.types().length == 0 || - (queryBuilder.types().length == 1 && queryBuilder.types()[0].equals(MetaData.ALL))) { + if (queryBuilder.ids().size() == 0 || context.getQueryShardContext().fieldMapper(UidFieldMapper.NAME) == null) { assertThat(query, instanceOf(MatchNoDocsQuery.class)); } else { assertThat(query, instanceOf(TermInSetQuery.class)); From 9ca7d28552535b6efbe2d5f8fb1a0ced91c8de80 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 10 May 2017 00:47:33 -0700 Subject: [PATCH 266/619] Scripting: Remove "service" from ScriptEngine interface name (#24574) This commit renames ScriptEngineService to ScriptEngine. It is often confusing because we have the ScriptService, and then ScriptEngineService implementations, but the latter are not services as we see in other places in elasticsearch. --- .../elasticsearch/plugins/ScriptPlugin.java | 6 +-- ...neService.java => NativeScriptEngine.java} | 4 +- ...ptEngineService.java => ScriptEngine.java} | 11 ++++- .../script/ScriptEngineRegistry.java | 26 ++++++------ .../org/elasticsearch/script/ScriptModes.java | 2 +- .../elasticsearch/script/ScriptModule.java | 16 ++++--- .../elasticsearch/script/ScriptService.java | 42 +++++++++---------- .../elasticsearch/script/ScriptSettings.java | 4 +- .../org/elasticsearch/script/ScriptType.java | 6 +-- .../script/NativeScriptTests.java | 8 ++-- .../script/ScriptModesTests.java | 16 +++---- .../script/ScriptServiceTests.java | 32 +++++++------- .../script/ScriptSettingsTests.java | 5 +-- .../search/aggregations/metrics/AvgIT.java | 10 ++--- .../search/aggregations/metrics/SumIT.java | 10 ++--- .../aggregations/metrics/ValueCountIT.java | 6 +-- .../search/sort/AbstractSortTestCase.java | 4 +- .../search/suggest/SuggestSearchIT.java | 7 ++-- .../update/UpdateByNativeScriptIT.java | 4 +- .../org/elasticsearch/update/UpdateIT.java | 21 ++++------ .../script/expression/ExpressionPlugin.java | 8 ++-- ...rvice.java => ExpressionScriptEngine.java} | 6 +-- .../script/expression/ExpressionTests.java | 4 +- .../expression/MoreExpressionTests.java | 12 +++--- .../expression/StoredExpressionTests.java | 4 +- .../script/mustache/MustachePlugin.java | 6 +-- ...Service.java => MustacheScriptEngine.java} | 6 +-- .../TransportSearchTemplateAction.java | 5 +-- .../mustache/CustomMustacheFactoryTests.java | 15 ++++--- .../mustache/MustacheScriptEngineTests.java | 4 +- .../script/mustache/MustacheTests.java | 4 +- .../script/mustache/SearchTemplateIT.java | 24 +++++------ .../org/elasticsearch/painless/Location.java | 2 +- .../painless/PainlessPlugin.java | 6 +-- .../painless/PainlessScript.java | 4 +- ...Service.java => PainlessScriptEngine.java} | 10 ++--- .../painless/NeedsScoreTests.java | 2 +- .../painless/ScriptTestCase.java | 4 +- .../ingest/AbstractScriptTestCase.java | 4 +- .../script/MockScriptEngine.java | 2 +- .../script/MockScriptPlugin.java | 2 +- 41 files changed, 184 insertions(+), 190 deletions(-) rename core/src/main/java/org/elasticsearch/script/{NativeScriptEngineService.java => NativeScriptEngine.java} (94%) rename core/src/main/java/org/elasticsearch/script/{ScriptEngineService.java => ScriptEngine.java} (88%) rename modules/lang-expression/src/main/java/org/elasticsearch/script/expression/{ExpressionScriptEngineService.java => ExpressionScriptEngine.java} (98%) rename modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/{MustacheScriptEngineService.java => MustacheScriptEngine.java} (97%) rename modules/lang-painless/src/main/java/org/elasticsearch/painless/{PainlessScriptEngineService.java => PainlessScriptEngine.java} (96%) diff --git a/core/src/main/java/org/elasticsearch/plugins/ScriptPlugin.java b/core/src/main/java/org/elasticsearch/plugins/ScriptPlugin.java index c1e2a43c95365..546fbdd24f64a 100644 --- a/core/src/main/java/org/elasticsearch/plugins/ScriptPlugin.java +++ b/core/src/main/java/org/elasticsearch/plugins/ScriptPlugin.java @@ -21,7 +21,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.script.NativeScriptFactory; import org.elasticsearch.script.ScriptContext; -import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptEngine; import java.util.Collections; import java.util.List; @@ -32,9 +32,9 @@ public interface ScriptPlugin { /** - * Returns a {@link ScriptEngineService} instance or null if this plugin doesn't add a new script engine + * Returns a {@link ScriptEngine} instance or null if this plugin doesn't add a new script engine */ - default ScriptEngineService getScriptEngineService(Settings settings) { + default ScriptEngine getScriptEngine(Settings settings) { return null; } diff --git a/core/src/main/java/org/elasticsearch/script/NativeScriptEngineService.java b/core/src/main/java/org/elasticsearch/script/NativeScriptEngine.java similarity index 94% rename from core/src/main/java/org/elasticsearch/script/NativeScriptEngineService.java rename to core/src/main/java/org/elasticsearch/script/NativeScriptEngine.java index 69c8b92ba1b83..7bd2f62e15145 100644 --- a/core/src/main/java/org/elasticsearch/script/NativeScriptEngineService.java +++ b/core/src/main/java/org/elasticsearch/script/NativeScriptEngine.java @@ -33,13 +33,13 @@ /** * A native script engine service. */ -public class NativeScriptEngineService extends AbstractComponent implements ScriptEngineService { +public class NativeScriptEngine extends AbstractComponent implements ScriptEngine { public static final String NAME = "native"; private final Map scripts; - public NativeScriptEngineService(Settings settings, Map scripts) { + public NativeScriptEngine(Settings settings, Map scripts) { super(settings); this.scripts = unmodifiableMap(scripts); } diff --git a/core/src/main/java/org/elasticsearch/script/ScriptEngineService.java b/core/src/main/java/org/elasticsearch/script/ScriptEngine.java similarity index 88% rename from core/src/main/java/org/elasticsearch/script/ScriptEngineService.java rename to core/src/main/java/org/elasticsearch/script/ScriptEngine.java index 1760dbaa3adbc..89ded962567aa 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptEngineService.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptEngine.java @@ -25,10 +25,19 @@ import java.io.Closeable; import java.util.Map; -public interface ScriptEngineService extends Closeable { +/** + * A script language implementation. + */ +public interface ScriptEngine extends Closeable { + /** + * The language name used in the script APIs to refer to this scripting backend. + */ String getType(); + /** + * The extension for file scripts in this language. + */ String getExtension(); /** diff --git a/core/src/main/java/org/elasticsearch/script/ScriptEngineRegistry.java b/core/src/main/java/org/elasticsearch/script/ScriptEngineRegistry.java index f65d694aa3181..4881d45f6c8af 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptEngineRegistry.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptEngineRegistry.java @@ -21,23 +21,21 @@ import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Objects; -import org.elasticsearch.common.Strings; public class ScriptEngineRegistry { - private final Map, String> registeredScriptEngineServices; - private final Map registeredLanguages; + private final Map, String> registeredScriptEngineServices; + private final Map registeredLanguages; private final Map defaultInlineScriptEnableds; - public ScriptEngineRegistry(Iterable registrations) { + public ScriptEngineRegistry(Iterable registrations) { Objects.requireNonNull(registrations); - Map, String> registeredScriptEngineServices = new HashMap<>(); - Map registeredLanguages = new HashMap<>(); + Map, String> registeredScriptEngineServices = new HashMap<>(); + Map registeredLanguages = new HashMap<>(); Map inlineScriptEnableds = new HashMap<>(); - for (ScriptEngineService service : registrations) { + for (ScriptEngine service : registrations) { String oldLanguage = registeredScriptEngineServices.putIfAbsent(service.getClass(), service.getType()); if (oldLanguage != null) { @@ -45,11 +43,11 @@ public ScriptEngineRegistry(Iterable registrations) { "] already registered for language [" + oldLanguage + "]"); } String language = service.getType(); - ScriptEngineService scriptEngineService = + ScriptEngine scriptEngine = registeredLanguages.putIfAbsent(language, service); - if (scriptEngineService != null) { + if (scriptEngine != null) { throw new IllegalArgumentException("scripting language [" + language + "] already registered for script engine service [" + - scriptEngineService.getClass().getCanonicalName() + "]"); + scriptEngine.getClass().getCanonicalName() + "]"); } inlineScriptEnableds.put(language, service.isInlineScriptEnabled()); } @@ -59,16 +57,16 @@ public ScriptEngineRegistry(Iterable registrations) { this.defaultInlineScriptEnableds = Collections.unmodifiableMap(inlineScriptEnableds); } - Iterable> getRegisteredScriptEngineServices() { + Iterable> getRegisteredScriptEngineServices() { return registeredScriptEngineServices.keySet(); } - String getLanguage(Class scriptEngineService) { + String getLanguage(Class scriptEngineService) { Objects.requireNonNull(scriptEngineService); return registeredScriptEngineServices.get(scriptEngineService); } - public Map getRegisteredLanguages() { + public Map getRegisteredLanguages() { return registeredLanguages; } diff --git a/core/src/main/java/org/elasticsearch/script/ScriptModes.java b/core/src/main/java/org/elasticsearch/script/ScriptModes.java index 15393948d6654..ae12deaec97f5 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptModes.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptModes.java @@ -57,7 +57,7 @@ public class ScriptModes { */ public boolean getScriptEnabled(String lang, ScriptType scriptType, ScriptContext scriptContext) { //native scripts are always enabled as they are static by definition - if (NativeScriptEngineService.NAME.equals(lang)) { + if (NativeScriptEngine.NAME.equals(lang)) { return true; } Boolean scriptMode = scriptEnabled.get(getKey(lang, scriptType, scriptContext)); diff --git a/core/src/main/java/org/elasticsearch/script/ScriptModule.java b/core/src/main/java/org/elasticsearch/script/ScriptModule.java index 30f84bc6960bc..ff03c2526563b 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptModule.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptModule.java @@ -27,8 +27,6 @@ import org.elasticsearch.watcher.ResourceWatcherService; import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -50,23 +48,23 @@ public static ScriptModule create(Settings settings, Environment environment, ResourceWatcherService resourceWatcherService, List scriptPlugins) { Map factoryMap = scriptPlugins.stream().flatMap(x -> x.getNativeScripts().stream()) .collect(Collectors.toMap(NativeScriptFactory::getName, Function.identity())); - NativeScriptEngineService nativeScriptEngineService = new NativeScriptEngineService(settings, factoryMap); - List scriptEngineServices = scriptPlugins.stream().map(x -> x.getScriptEngineService(settings)) + NativeScriptEngine nativeScriptEngineService = new NativeScriptEngine(settings, factoryMap); + List scriptEngines = scriptPlugins.stream().map(x -> x.getScriptEngine(settings)) .filter(Objects::nonNull).collect(Collectors.toList()); - scriptEngineServices.add(nativeScriptEngineService); + scriptEngines.add(nativeScriptEngineService); List plugins = scriptPlugins.stream().map(x -> x.getCustomScriptContexts()).filter(Objects::nonNull) .collect(Collectors.toList()); - return new ScriptModule(settings, environment, resourceWatcherService, scriptEngineServices, plugins); + return new ScriptModule(settings, environment, resourceWatcherService, scriptEngines, plugins); } /** - * Build {@linkplain ScriptEngineService} and {@linkplain ScriptContext.Plugin}. + * Build {@linkplain ScriptEngine} and {@linkplain ScriptContext.Plugin}. */ public ScriptModule(Settings settings, Environment environment, - ResourceWatcherService resourceWatcherService, List scriptEngineServices, + ResourceWatcherService resourceWatcherService, List scriptEngines, List customScriptContexts) { ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(customScriptContexts); - ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(scriptEngineServices); + ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(scriptEngines); scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry); try { scriptService = new ScriptService(settings, environment, resourceWatcherService, scriptEngineRegistry, scriptContextRegistry, diff --git a/core/src/main/java/org/elasticsearch/script/ScriptService.java b/core/src/main/java/org/elasticsearch/script/ScriptService.java index 7a65fee327aaa..b52e135be5b93 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptService.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptService.java @@ -92,9 +92,9 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust public static final Setting SCRIPT_MAX_COMPILATIONS_PER_MINUTE = Setting.intSetting("script.max_compilations_per_minute", 15, 0, Property.Dynamic, Property.NodeScope); - private final Collection scriptEngines; - private final Map scriptEnginesByLang; - private final Map scriptEnginesByExt; + private final Collection scriptEngines; + private final Map scriptEnginesByLang; + private final Map scriptEnginesByExt; private final ConcurrentMap staticCache = ConcurrentCollections.newConcurrentMap(); @@ -142,9 +142,9 @@ public ScriptService(Settings settings, Environment env, logger.debug("using script cache with max_size [{}], expire [{}]", cacheMaxSize, cacheExpire); this.cache = cacheBuilder.removalListener(new ScriptCacheRemovalListener()).build(); - Map enginesByLangBuilder = new HashMap<>(); - Map enginesByExtBuilder = new HashMap<>(); - for (ScriptEngineService scriptEngine : scriptEngines) { + Map enginesByLangBuilder = new HashMap<>(); + Map enginesByExtBuilder = new HashMap<>(); + for (ScriptEngine scriptEngine : scriptEngines) { String language = scriptEngineRegistry.getLanguage(scriptEngine.getClass()); enginesByLangBuilder.put(language, scriptEngine); enginesByExtBuilder.put(scriptEngine.getExtension(), scriptEngine); @@ -183,20 +183,20 @@ public void close() throws IOException { IOUtils.close(scriptEngines); } - private ScriptEngineService getScriptEngineServiceForLang(String lang) { - ScriptEngineService scriptEngineService = scriptEnginesByLang.get(lang); - if (scriptEngineService == null) { + private ScriptEngine getScriptEngineServiceForLang(String lang) { + ScriptEngine scriptEngine = scriptEnginesByLang.get(lang); + if (scriptEngine == null) { throw new IllegalArgumentException("script_lang not supported [" + lang + "]"); } - return scriptEngineService; + return scriptEngine; } - private ScriptEngineService getScriptEngineServiceForFileExt(String fileExtension) { - ScriptEngineService scriptEngineService = scriptEnginesByExt.get(fileExtension); - if (scriptEngineService == null) { + private ScriptEngine getScriptEngineServiceForFileExt(String fileExtension) { + ScriptEngine scriptEngine = scriptEnginesByExt.get(fileExtension); + if (scriptEngine == null) { throw new IllegalArgumentException("script file extension not supported [" + fileExtension + "]"); } - return scriptEngineService; + return scriptEngine; } void setMaxCompilationsPerMinute(Integer newMaxPerMinute) { @@ -258,7 +258,7 @@ public CompiledScript compile(Script script, ScriptContext scriptContext) { " operation [" + scriptContext.getKey() + "] and lang [" + lang + "] are not supported"); } - ScriptEngineService scriptEngineService = getScriptEngineServiceForLang(lang); + ScriptEngine scriptEngine = getScriptEngineServiceForLang(lang); if (canExecuteScript(lang, type, scriptContext) == false) { throw new IllegalStateException("scripts of type [" + script.getType() + "]," + @@ -304,7 +304,7 @@ public CompiledScript compile(Script script, ScriptContext scriptContext) { } // Check whether too many compilations have happened checkCompilationLimit(); - compiledScript = new CompiledScript(type, id, lang, scriptEngineService.compile(id, idOrCode, options)); + compiledScript = new CompiledScript(type, id, lang, scriptEngine.compile(id, idOrCode, options)); } catch (ScriptException good) { // TODO: remove this try-catch completely, when all script engines have good exceptions! throw good; // its already good @@ -404,10 +404,10 @@ public void putStoredScript(ClusterService clusterService, PutStoredScriptReques } try { - ScriptEngineService scriptEngineService = getScriptEngineServiceForLang(source.getLang()); + ScriptEngine scriptEngine = getScriptEngineServiceForLang(source.getLang()); if (isAnyScriptContextEnabled(source.getLang(), ScriptType.STORED)) { - Object compiled = scriptEngineService.compile(request.id(), source.getCode(), Collections.emptyMap()); + Object compiled = scriptEngine.compile(request.id(), source.getCode(), Collections.emptyMap()); if (compiled == null) { throw new IllegalArgumentException("failed to parse/compile stored script [" + request.id() + "]" + @@ -528,7 +528,7 @@ public void clusterChanged(ClusterChangedEvent event) { /** * A small listener for the script cache that calls each - * {@code ScriptEngineService}'s {@code scriptRemoved} method when the + * {@code ScriptEngine}'s {@code scriptRemoved} method when the * script has been removed from the cache */ private class ScriptCacheRemovalListener implements RemovalListener { @@ -571,7 +571,7 @@ public void onFileInit(Path file) { logger.trace("Loading script file : [{}]", file); } - ScriptEngineService engineService = getScriptEngineServiceForFileExt(scriptNameExt.v2()); + ScriptEngine engineService = getScriptEngineServiceForFileExt(scriptNameExt.v2()); if (engineService == null) { logger.warn("No script engine found for [{}]", scriptNameExt.v2()); } else { @@ -629,7 +629,7 @@ public void onFileCreated(Path file) { public void onFileDeleted(Path file) { Tuple scriptNameExt = getScriptNameExt(file); if (scriptNameExt != null) { - ScriptEngineService engineService = getScriptEngineServiceForFileExt(scriptNameExt.v2()); + ScriptEngine engineService = getScriptEngineServiceForFileExt(scriptNameExt.v2()); assert engineService != null; logger.info("removing script file [{}]", file.toAbsolutePath()); staticCache.remove(new CacheKey(engineService.getType(), scriptNameExt.v1(), null)); diff --git a/core/src/main/java/org/elasticsearch/script/ScriptSettings.java b/core/src/main/java/org/elasticsearch/script/ScriptSettings.java index e4387aa52dd8e..cc928a8b5977c 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptSettings.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptSettings.java @@ -72,8 +72,8 @@ private static List> languageSettings(Map> scriptModeSettings = new ArrayList<>(); - for (final Class scriptEngineService : scriptEngineRegistry.getRegisteredScriptEngineServices()) { - if (scriptEngineService == NativeScriptEngineService.class) { + for (final Class scriptEngineService : scriptEngineRegistry.getRegisteredScriptEngineServices()) { + if (scriptEngineService == NativeScriptEngine.class) { // native scripts are always enabled, and their settings can not be changed continue; } diff --git a/core/src/main/java/org/elasticsearch/script/ScriptType.java b/core/src/main/java/org/elasticsearch/script/ScriptType.java index 35698e36b498c..e8265b644dae2 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptType.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptType.java @@ -37,7 +37,7 @@ public enum ScriptType implements Writeable { * INLINE scripts are specified in numerous queries and compiled on-the-fly. * They will be cached based on the lang and code of the script. * They are turned off by default because most languages are insecure - * (Groovy and others), but can be overridden by the specific {@link ScriptEngineService} + * (Groovy and others), but can be overridden by the specific {@link ScriptEngine} * if the language is naturally secure (Painless, Mustache, and Expressions). */ INLINE ( 0 , new ParseField("inline") , false ), @@ -46,7 +46,7 @@ public enum ScriptType implements Writeable { * STORED scripts are saved as part of the {@link org.elasticsearch.cluster.ClusterState} * based on user requests. They will be cached when they are first used in a query. * They are turned off by default because most languages are insecure - * (Groovy and others), but can be overridden by the specific {@link ScriptEngineService} + * (Groovy and others), but can be overridden by the specific {@link ScriptEngine} * if the language is naturally secure (Painless, Mustache, and Expressions). */ STORED ( 1 , new ParseField("stored", "id") , false ), @@ -123,7 +123,7 @@ public ParseField getParseField() { /** * @return Whether or not a {@link ScriptType} can be run by default. Note - * this can be potentially overridden by any {@link ScriptEngineService}. + * this can be potentially overridden by any {@link ScriptEngine}. */ public boolean isDefaultEnabled() { return defaultEnabled; diff --git a/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java b/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java index 0dfda8f7a1602..7f56f3de4b373 100644 --- a/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java +++ b/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java @@ -47,11 +47,11 @@ public void testNativeScript() throws InterruptedException { .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) .build(); ScriptModule scriptModule = new ScriptModule(settings, new Environment(settings), null, - singletonList(new NativeScriptEngineService(settings, singletonMap("my", new MyNativeScriptFactory()))), emptyList()); + singletonList(new NativeScriptEngine(settings, singletonMap("my", new MyNativeScriptFactory()))), emptyList()); List> scriptSettings = scriptModule.getSettings(); scriptSettings.add(InternalSettingsPlugin.VERSION_CREATED); - Script script = new Script(ScriptType.INLINE, NativeScriptEngineService.NAME, "my", Collections.emptyMap()); + Script script = new Script(ScriptType.INLINE, NativeScriptEngine.NAME, "my", Collections.emptyMap()); CompiledScript compiledScript = scriptModule.getScriptService().compile(script, ScriptContext.Standard.SEARCH); ExecutableScript executable = scriptModule.getScriptService().executable(compiledScript, script.getParams()); assertThat(executable.run().toString(), equalTo("test")); @@ -71,7 +71,7 @@ public void testFineGrainedSettingsDontAffectNativeScripts() throws IOException ResourceWatcherService resourceWatcherService = new ResourceWatcherService(settings, null); Map nativeScriptFactoryMap = new HashMap<>(); nativeScriptFactoryMap.put("my", new MyNativeScriptFactory()); - ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(Collections.singleton(new NativeScriptEngineService(settings, + ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(Collections.singleton(new NativeScriptEngine(settings, nativeScriptFactoryMap))); ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(new ArrayList<>()); ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry); @@ -79,7 +79,7 @@ public void testFineGrainedSettingsDontAffectNativeScripts() throws IOException scriptContextRegistry, scriptSettings); for (ScriptContext scriptContext : scriptContextRegistry.scriptContexts()) { - assertThat(scriptService.compile(new Script(ScriptType.INLINE, NativeScriptEngineService.NAME, "my", Collections.emptyMap()), + assertThat(scriptService.compile(new Script(ScriptType.INLINE, NativeScriptEngine.NAME, "my", Collections.emptyMap()), scriptContext), notNullValue()); } } diff --git a/core/src/test/java/org/elasticsearch/script/ScriptModesTests.java b/core/src/test/java/org/elasticsearch/script/ScriptModesTests.java index 2289e040f57d7..a2db206858c3d 100644 --- a/core/src/test/java/org/elasticsearch/script/ScriptModesTests.java +++ b/core/src/test/java/org/elasticsearch/script/ScriptModesTests.java @@ -43,7 +43,7 @@ public class ScriptModesTests extends ESTestCase { ScriptSettings scriptSettings; ScriptContextRegistry scriptContextRegistry; private ScriptContext[] scriptContexts; - private Map scriptEngines; + private Map scriptEngines; private ScriptModes scriptModes; private Set checkedSettings; private boolean assertAllSettingsWereChecked; @@ -65,8 +65,8 @@ public void setupScriptEngines() { scriptContexts = scriptContextRegistry.scriptContexts().toArray(new ScriptContext[scriptContextRegistry.scriptContexts().size()]); scriptEngines = buildScriptEnginesByLangMap(newHashSet( //add the native engine just to make sure it gets filtered out - new NativeScriptEngineService(Settings.EMPTY, Collections.emptyMap()), - new CustomScriptEngineService())); + new NativeScriptEngine(Settings.EMPTY, Collections.emptyMap()), + new CustomScriptEngine())); ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(scriptEngines.values()); scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry); checkedSettings = new HashSet<>(); @@ -77,7 +77,7 @@ public void setupScriptEngines() { @After public void assertNativeScriptsAreAlwaysAllowed() { if (assertScriptModesNonNull) { - assertThat(scriptModes.getScriptEnabled(NativeScriptEngineService.NAME, randomFrom(ScriptType.values()), randomFrom(scriptContexts)), equalTo(true)); + assertThat(scriptModes.getScriptEnabled(NativeScriptEngine.NAME, randomFrom(ScriptType.values()), randomFrom(scriptContexts)), equalTo(true)); } } @@ -216,16 +216,16 @@ private ScriptContext[] complementOf(ScriptContext... scriptContexts) { return copy.values().toArray(new ScriptContext[copy.size()]); } - static Map buildScriptEnginesByLangMap(Set scriptEngines) { - Map builder = new HashMap<>(); - for (ScriptEngineService scriptEngine : scriptEngines) { + static Map buildScriptEnginesByLangMap(Set scriptEngines) { + Map builder = new HashMap<>(); + for (ScriptEngine scriptEngine : scriptEngines) { String type = scriptEngine.getType(); builder.put(type, scriptEngine); } return unmodifiableMap(builder); } - private static class CustomScriptEngineService implements ScriptEngineService { + private static class CustomScriptEngine implements ScriptEngine { public static final String NAME = "custom"; diff --git a/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java b/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java index 026751f64436a..6928d565487cd 100644 --- a/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java +++ b/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java @@ -55,9 +55,9 @@ public class ScriptServiceTests extends ESTestCase { private ResourceWatcherService resourceWatcherService; - private ScriptEngineService scriptEngineService; - private ScriptEngineService dangerousScriptEngineService; - private Map scriptEnginesByLangMap; + private ScriptEngine scriptEngine; + private ScriptEngine dangerousScriptEngine; + private Map scriptEnginesByLangMap; private ScriptEngineRegistry scriptEngineRegistry; private ScriptContextRegistry scriptContextRegistry; private ScriptSettings scriptSettings; @@ -83,11 +83,11 @@ public void setup() throws IOException { .put(ScriptService.SCRIPT_MAX_COMPILATIONS_PER_MINUTE.getKey(), 10000) .build(); resourceWatcherService = new ResourceWatcherService(baseSettings, null); - scriptEngineService = new TestEngineService(); - dangerousScriptEngineService = new TestDangerousEngineService(); - TestEngineService defaultScriptServiceEngine = new TestEngineService(Script.DEFAULT_SCRIPT_LANG) {}; + scriptEngine = new TestEngine(); + dangerousScriptEngine = new TestDangerousEngine(); + TestEngine defaultScriptServiceEngine = new TestEngine(Script.DEFAULT_SCRIPT_LANG) {}; scriptEnginesByLangMap = ScriptModesTests.buildScriptEnginesByLangMap( - new HashSet<>(Arrays.asList(scriptEngineService, defaultScriptServiceEngine))); + new HashSet<>(Arrays.asList(scriptEngine, defaultScriptServiceEngine))); //randomly register custom script contexts int randomInt = randomIntBetween(0, 3); //prevent duplicates using map @@ -104,7 +104,7 @@ public void setup() throws IOException { String context = plugin + "_" + operation; contexts.put(context, new ScriptContext.Plugin(plugin, operation)); } - scriptEngineRegistry = new ScriptEngineRegistry(Arrays.asList(scriptEngineService, dangerousScriptEngineService, + scriptEngineRegistry = new ScriptEngineRegistry(Arrays.asList(scriptEngine, dangerousScriptEngine, defaultScriptServiceEngine)); scriptContextRegistry = new ScriptContextRegistry(contexts.values()); scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry); @@ -259,7 +259,7 @@ public void testFineGrainedSettings() throws IOException { do { ScriptType scriptType = randomFrom(ScriptType.values()); ScriptContext scriptContext = randomFrom(this.scriptContexts); - settingKey = scriptEngineService.getType() + "." + scriptType + "." + scriptContext.getKey(); + settingKey = scriptEngine.getType() + "." + scriptType + "." + scriptContext.getKey(); } while (engineSettings.containsKey(settingKey)); engineSettings.put(settingKey, randomBoolean()); } @@ -301,7 +301,7 @@ public void testFineGrainedSettings() throws IOException { String script = scriptType == ScriptType.FILE ? "file_script" : "script"; for (ScriptContext scriptContext : this.scriptContexts) { //fallback mechanism: 1) engine specific settings 2) op based settings 3) source based settings - Boolean scriptEnabled = engineSettings.get(dangerousScriptEngineService.getType() + "." + scriptType + "." + scriptContext.getKey()); + Boolean scriptEnabled = engineSettings.get(dangerousScriptEngine.getType() + "." + scriptType + "." + scriptContext.getKey()); if (scriptEnabled == null) { scriptEnabled = scriptContextSettings.get(scriptContext); } @@ -312,7 +312,7 @@ public void testFineGrainedSettings() throws IOException { scriptEnabled = DEFAULT_SCRIPT_ENABLED.get(scriptType); } - String lang = dangerousScriptEngineService.getType(); + String lang = dangerousScriptEngine.getType(); if (scriptEnabled) { assertCompileAccepted(lang, script, scriptType, scriptContext); } else { @@ -332,7 +332,7 @@ public void testCompileNonRegisteredContext() throws IOException { unknownContext = randomAlphaOfLength(randomIntBetween(1, 30)); } while(scriptContextRegistry.isSupportedContext(new ScriptContext.Plugin(pluginName, unknownContext))); - String type = scriptEngineService.getType(); + String type = scriptEngine.getType(); try { scriptService.compile(new Script(randomFrom(ScriptType.values()), type, "test", Collections.emptyMap()), new ScriptContext.Plugin(pluginName, unknownContext)); @@ -482,17 +482,17 @@ private void assertCompileAccepted(String lang, String script, ScriptType script ); } - public static class TestEngineService implements ScriptEngineService { + public static class TestEngine implements ScriptEngine { public static final String NAME = "test"; private final String name; - public TestEngineService() { + public TestEngine() { this(NAME); } - public TestEngineService(String name) { + public TestEngine(String name) { this.name = name; } @@ -532,7 +532,7 @@ public boolean isInlineScriptEnabled() { } } - public static class TestDangerousEngineService implements ScriptEngineService { + public static class TestDangerousEngine implements ScriptEngine { public static final String NAME = "dtest"; diff --git a/core/src/test/java/org/elasticsearch/script/ScriptSettingsTests.java b/core/src/test/java/org/elasticsearch/script/ScriptSettingsTests.java index abda0376a2cfa..6eea1fa8010b7 100644 --- a/core/src/test/java/org/elasticsearch/script/ScriptSettingsTests.java +++ b/core/src/test/java/org/elasticsearch/script/ScriptSettingsTests.java @@ -29,14 +29,13 @@ import java.util.Iterator; import java.util.Map; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; public class ScriptSettingsTests extends ESTestCase { public void testSettingsAreProperlyPropogated() { ScriptEngineRegistry scriptEngineRegistry = - new ScriptEngineRegistry(Collections.singletonList(new CustomScriptEngineService())); + new ScriptEngineRegistry(Collections.singletonList(new CustomScriptEngine())); ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Collections.emptyList()); ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry); boolean enabled = randomBoolean(); @@ -50,7 +49,7 @@ public void testSettingsAreProperlyPropogated() { } } - private static class CustomScriptEngineService implements ScriptEngineService { + private static class CustomScriptEngine implements ScriptEngine { public static final String NAME = "custom"; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AvgIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AvgIT.java index b7739d6c816a1..c952f43eb3032 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AvgIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AvgIT.java @@ -28,7 +28,7 @@ import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.LeafSearchScript; import org.elasticsearch.script.Script; -import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.script.ScriptType; import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.aggregations.InternalAggregation; @@ -397,7 +397,7 @@ public void testDontCacheScripts() throws Exception { */ public static class ExtractFieldScriptPlugin extends Plugin implements ScriptPlugin { @Override - public ScriptEngineService getScriptEngineService(Settings settings) { + public ScriptEngine getScriptEngine(Settings settings) { return new ExtractFieldScriptEngine(); } } @@ -405,7 +405,7 @@ public ScriptEngineService getScriptEngineService(Settings settings) { /** * This mock script returns the field that is specified by name in the script body */ - public static class ExtractFieldScriptEngine implements ScriptEngineService { + public static class ExtractFieldScriptEngine implements ScriptEngine { public static final String NAME = "extract_field"; @@ -502,7 +502,7 @@ public boolean needsScores() { */ public static class FieldValueScriptPlugin extends Plugin implements ScriptPlugin { @Override - public ScriptEngineService getScriptEngineService(Settings settings) { + public ScriptEngine getScriptEngine(Settings settings) { return new FieldValueScriptEngine(); } } @@ -510,7 +510,7 @@ public ScriptEngineService getScriptEngineService(Settings settings) { /** * This mock script returns the field value and adds one month to the returned date */ - public static class FieldValueScriptEngine implements ScriptEngineService { + public static class FieldValueScriptEngine implements ScriptEngine { public static final String NAME = "field_value"; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java index 16d345c7b842d..227ffc7251bb3 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java @@ -28,7 +28,7 @@ import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.LeafSearchScript; import org.elasticsearch.script.Script; -import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.script.ScriptType; import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.aggregations.InternalAggregation; @@ -396,7 +396,7 @@ public void testDontCacheScripts() throws Exception { */ public static class ExtractFieldScriptPlugin extends Plugin implements ScriptPlugin { @Override - public ScriptEngineService getScriptEngineService(Settings settings) { + public ScriptEngine getScriptEngine(Settings settings) { return new ExtractFieldScriptEngine(); } } @@ -405,7 +405,7 @@ public ScriptEngineService getScriptEngineService(Settings settings) { * This mock script returns the field that is specified by name in the * script body */ - public static class ExtractFieldScriptEngine implements ScriptEngineService { + public static class ExtractFieldScriptEngine implements ScriptEngine { public static final String NAME = "extract_field"; @@ -508,7 +508,7 @@ public boolean isInlineScriptEnabled() { */ public static class FieldValueScriptPlugin extends Plugin implements ScriptPlugin { @Override - public ScriptEngineService getScriptEngineService(Settings settings) { + public ScriptEngine getScriptEngine(Settings settings) { return new FieldValueScriptEngine(); } } @@ -517,7 +517,7 @@ public ScriptEngineService getScriptEngineService(Settings settings) { * This mock script returns the field value and adds one to the returned * value */ - public static class FieldValueScriptEngine implements ScriptEngineService { + public static class FieldValueScriptEngine implements ScriptEngine { public static final String NAME = "field_value"; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java index 784635bb1d61d..2cfb34431059c 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java @@ -28,7 +28,7 @@ import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.LeafSearchScript; import org.elasticsearch.script.Script; -import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.script.ScriptType; import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.aggregations.InternalAggregation; @@ -251,7 +251,7 @@ public void testDontCacheScripts() throws Exception { */ public static class FieldValueScriptPlugin extends Plugin implements ScriptPlugin { @Override - public ScriptEngineService getScriptEngineService(Settings settings) { + public ScriptEngine getScriptEngine(Settings settings) { return new FieldValueScriptEngine(); } } @@ -259,7 +259,7 @@ public ScriptEngineService getScriptEngineService(Settings settings) { /** * This mock script returns the field value. If the parameter map contains a parameter "s", the corresponding is used as field name. */ - public static class FieldValueScriptEngine implements ScriptEngineService { + public static class FieldValueScriptEngine implements ScriptEngine { public static final String NAME = "field_value"; diff --git a/core/src/test/java/org/elasticsearch/search/sort/AbstractSortTestCase.java b/core/src/test/java/org/elasticsearch/search/sort/AbstractSortTestCase.java index 6f1087561c42f..32676d06a28b8 100644 --- a/core/src/test/java/org/elasticsearch/search/sort/AbstractSortTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/sort/AbstractSortTestCase.java @@ -56,7 +56,7 @@ import org.elasticsearch.script.ScriptContextRegistry; import org.elasticsearch.script.ScriptEngineRegistry; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.script.ScriptServiceTests.TestEngineService; +import org.elasticsearch.script.ScriptServiceTests.TestEngine; import org.elasticsearch.script.ScriptSettings; import org.elasticsearch.script.ScriptType; import org.elasticsearch.search.DocValueFormat; @@ -91,7 +91,7 @@ public static void init() throws IOException { .build(); Environment environment = new Environment(baseSettings); ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Collections.emptyList()); - ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(Collections.singletonList(new TestEngineService())); + ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(Collections.singletonList(new TestEngine())); ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry); scriptService = new ScriptService(baseSettings, environment, new ResourceWatcherService(baseSettings, null), scriptEngineRegistry, scriptContextRegistry, scriptSettings) { diff --git a/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java b/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java index 92fb2cd2777f6..82b1dc3656c98 100644 --- a/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java +++ b/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java @@ -22,7 +22,6 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.search.ReduceSearchPhaseException; import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; @@ -34,7 +33,7 @@ import org.elasticsearch.plugins.ScriptPlugin; import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; -import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.lookup.SearchLookup; import org.elasticsearch.search.suggest.phrase.DirectCandidateGeneratorBuilder; @@ -1112,12 +1111,12 @@ protected Collection> nodePlugins() { public static class DummyTemplatePlugin extends Plugin implements ScriptPlugin { @Override - public ScriptEngineService getScriptEngineService(Settings settings) { + public ScriptEngine getScriptEngine(Settings settings) { return new DummyTemplateScriptEngine(); } } - public static class DummyTemplateScriptEngine implements ScriptEngineService { + public static class DummyTemplateScriptEngine implements ScriptEngine { // The collate query setter is hard coded to use mustache, so lets lie in this test about the script plugin, // which makes the collate code thinks mustache is evaluating the query. diff --git a/core/src/test/java/org/elasticsearch/update/UpdateByNativeScriptIT.java b/core/src/test/java/org/elasticsearch/update/UpdateByNativeScriptIT.java index 2d28f75ff93cf..463d9e0d290c0 100644 --- a/core/src/test/java/org/elasticsearch/update/UpdateByNativeScriptIT.java +++ b/core/src/test/java/org/elasticsearch/update/UpdateByNativeScriptIT.java @@ -23,7 +23,7 @@ import org.elasticsearch.plugins.ScriptPlugin; import org.elasticsearch.script.AbstractExecutableScript; import org.elasticsearch.script.ExecutableScript; -import org.elasticsearch.script.NativeScriptEngineService; +import org.elasticsearch.script.NativeScriptEngine; import org.elasticsearch.script.NativeScriptFactory; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; @@ -57,7 +57,7 @@ public void testThatUpdateUsingNativeScriptWorks() throws Exception { Map params = new HashMap<>(); params.put("foo", "SETVALUE"); client().prepareUpdate("test", "type", "1") - .setScript(new Script(ScriptType.INLINE, NativeScriptEngineService.NAME, "custom", params)).get(); + .setScript(new Script(ScriptType.INLINE, NativeScriptEngine.NAME, "custom", params)).get(); Map data = client().prepareGet("test", "type", "1").get().getSource(); assertThat(data, hasKey("foo")); diff --git a/core/src/test/java/org/elasticsearch/update/UpdateIT.java b/core/src/test/java/org/elasticsearch/update/UpdateIT.java index 10d235d3a855d..d274399387ab5 100644 --- a/core/src/test/java/org/elasticsearch/update/UpdateIT.java +++ b/core/src/test/java/org/elasticsearch/update/UpdateIT.java @@ -34,17 +34,14 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.MergePolicyConfig; -import org.elasticsearch.index.VersionType; import org.elasticsearch.index.engine.DocumentMissingException; -import org.elasticsearch.index.engine.VersionConflictEngineException; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.ScriptPlugin; import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.Script; -import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.script.ScriptType; import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.lookup.SearchLookup; @@ -76,12 +73,12 @@ public class UpdateIT extends ESIntegTestCase { public static class PutFieldValuesScriptPlugin extends Plugin implements ScriptPlugin { @Override - public ScriptEngineService getScriptEngineService(Settings settings) { + public ScriptEngine getScriptEngine(Settings settings) { return new PutFieldValuesScriptEngine(); } } - public static class PutFieldValuesScriptEngine implements ScriptEngineService { + public static class PutFieldValuesScriptEngine implements ScriptEngine { public static final String NAME = "put_values"; @@ -149,12 +146,12 @@ public boolean isInlineScriptEnabled() { public static class FieldIncrementScriptPlugin extends Plugin implements ScriptPlugin { @Override - public ScriptEngineService getScriptEngineService(Settings settings) { + public ScriptEngine getScriptEngine(Settings settings) { return new FieldIncrementScriptEngine(); } } - public static class FieldIncrementScriptEngine implements ScriptEngineService { + public static class FieldIncrementScriptEngine implements ScriptEngine { public static final String NAME = "field_inc"; @@ -215,12 +212,12 @@ public boolean isInlineScriptEnabled() { public static class ScriptedUpsertScriptPlugin extends Plugin implements ScriptPlugin { @Override - public ScriptEngineService getScriptEngineService(Settings settings) { + public ScriptEngine getScriptEngine(Settings settings) { return new ScriptedUpsertScriptEngine(); } } - public static class ScriptedUpsertScriptEngine implements ScriptEngineService { + public static class ScriptedUpsertScriptEngine implements ScriptEngine { public static final String NAME = "scripted_upsert"; @@ -282,12 +279,12 @@ public boolean isInlineScriptEnabled() { public static class ExtractContextInSourceScriptPlugin extends Plugin implements ScriptPlugin { @Override - public ScriptEngineService getScriptEngineService(Settings settings) { + public ScriptEngine getScriptEngine(Settings settings) { return new ExtractContextInSourceScriptEngine(); } } - public static class ExtractContextInSourceScriptEngine implements ScriptEngineService { + public static class ExtractContextInSourceScriptEngine implements ScriptEngine { public static final String NAME = "extract_ctx"; diff --git a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionPlugin.java b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionPlugin.java index 1a5702c2090c0..071e5cf666d0c 100644 --- a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionPlugin.java +++ b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionPlugin.java @@ -22,14 +22,12 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.ScriptPlugin; -import org.elasticsearch.script.ScriptEngineRegistry; -import org.elasticsearch.script.ScriptEngineService; -import org.elasticsearch.script.ScriptModule; +import org.elasticsearch.script.ScriptEngine; public class ExpressionPlugin extends Plugin implements ScriptPlugin { @Override - public ScriptEngineService getScriptEngineService(Settings settings) { - return new ExpressionScriptEngineService(settings); + public ScriptEngine getScriptEngine(Settings settings) { + return new ExpressionScriptEngine(settings); } } diff --git a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngineService.java b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngine.java similarity index 98% rename from modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngineService.java rename to modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngine.java index 07ba6668fff22..815ff37e1265d 100644 --- a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngineService.java +++ b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngine.java @@ -39,7 +39,7 @@ import org.elasticsearch.script.ClassPermission; import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; -import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.script.ScriptException; import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.lookup.SearchLookup; @@ -56,11 +56,11 @@ * Provides the infrastructure for Lucene expressions as a scripting language for Elasticsearch. Only * {@link SearchScript}s are supported. */ -public class ExpressionScriptEngineService extends AbstractComponent implements ScriptEngineService { +public class ExpressionScriptEngine extends AbstractComponent implements ScriptEngine { public static final String NAME = "expression"; - public ExpressionScriptEngineService(Settings settings) { + public ExpressionScriptEngine(Settings settings) { super(settings); } diff --git a/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/ExpressionTests.java b/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/ExpressionTests.java index b9c628926e3ac..49b5fa03957de 100644 --- a/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/ExpressionTests.java +++ b/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/ExpressionTests.java @@ -32,14 +32,14 @@ import java.util.Collections; public class ExpressionTests extends ESSingleNodeTestCase { - ExpressionScriptEngineService service; + ExpressionScriptEngine service; SearchLookup lookup; @Override public void setUp() throws Exception { super.setUp(); IndexService index = createIndex("test", Settings.EMPTY, "type", "d", "type=double"); - service = new ExpressionScriptEngineService(Settings.EMPTY); + service = new ExpressionScriptEngine(Settings.EMPTY); lookup = new SearchLookup(index.mapperService(), index.fieldData(), null); } diff --git a/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/MoreExpressionTests.java b/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/MoreExpressionTests.java index 190a7745add67..59f691e8894ca 100644 --- a/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/MoreExpressionTests.java +++ b/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/MoreExpressionTests.java @@ -444,15 +444,15 @@ public void testSpecialValueVariable() throws Exception { .addAggregation( AggregationBuilders.stats("int_agg").field("x") .script(new Script(ScriptType.INLINE, - ExpressionScriptEngineService.NAME, "_value * 3", Collections.emptyMap()))) + ExpressionScriptEngine.NAME, "_value * 3", Collections.emptyMap()))) .addAggregation( AggregationBuilders.stats("double_agg").field("y") .script(new Script(ScriptType.INLINE, - ExpressionScriptEngineService.NAME, "_value - 1.1", Collections.emptyMap()))) + ExpressionScriptEngine.NAME, "_value - 1.1", Collections.emptyMap()))) .addAggregation( AggregationBuilders.stats("const_agg").field("x") // specifically to test a script w/o _value .script(new Script(ScriptType.INLINE, - ExpressionScriptEngineService.NAME, "3.0", Collections.emptyMap())) + ExpressionScriptEngine.NAME, "3.0", Collections.emptyMap())) ); SearchResponse rsp = req.get(); @@ -487,7 +487,7 @@ public void testStringSpecialValueVariable() throws Exception { .addAggregation( AggregationBuilders.terms("term_agg").field("text") .script( - new Script(ScriptType.INLINE, ExpressionScriptEngineService.NAME, "_value", Collections.emptyMap()))); + new Script(ScriptType.INLINE, ExpressionScriptEngine.NAME, "_value", Collections.emptyMap()))); String message; try { @@ -577,7 +577,7 @@ public void testInvalidUpdateScript() throws Exception { UpdateRequestBuilder urb = client().prepareUpdate().setIndex("test_index"); urb.setType("doc"); urb.setId("1"); - urb.setScript(new Script(ScriptType.INLINE, ExpressionScriptEngineService.NAME, "0", Collections.emptyMap())); + urb.setScript(new Script(ScriptType.INLINE, ExpressionScriptEngine.NAME, "0", Collections.emptyMap())); urb.get(); fail("Expression scripts should not be allowed to run as update scripts."); } catch (Exception e) { @@ -609,7 +609,7 @@ public void testPipelineAggregationScript() throws Exception { .subAggregation(sum("fourSum").field("four")) .subAggregation(bucketScript("totalSum", new Script(ScriptType.INLINE, - ExpressionScriptEngineService.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), + ExpressionScriptEngine.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), "twoSum", "threeSum", "fourSum"))) .execute().actionGet(); diff --git a/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/StoredExpressionTests.java b/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/StoredExpressionTests.java index 697dbf625752b..67bbce5f6f50f 100644 --- a/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/StoredExpressionTests.java +++ b/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/StoredExpressionTests.java @@ -52,14 +52,14 @@ protected Collection> nodePlugins() { public void testAllOpsDisabledIndexedScripts() throws IOException { client().admin().cluster().preparePutStoredScript() - .setLang(ExpressionScriptEngineService.NAME) + .setLang(ExpressionScriptEngine.NAME) .setId("script1") .setContent(new BytesArray("{\"script\":\"2\"}"), XContentType.JSON) .get(); client().prepareIndex("test", "scriptTest", "1").setSource("{\"theField\":\"foo\"}", XContentType.JSON).get(); try { client().prepareUpdate("test", "scriptTest", "1") - .setScript(new Script(ScriptType.STORED, ExpressionScriptEngineService.NAME, "script1", Collections.emptyMap())).get(); + .setScript(new Script(ScriptType.STORED, ExpressionScriptEngine.NAME, "script1", Collections.emptyMap())).get(); fail("update script should have been rejected"); } catch(Exception e) { assertThat(e.getMessage(), containsString("failed to execute script")); diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustachePlugin.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustachePlugin.java index 9315a0fbd4b1f..7b5c97aa9a793 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustachePlugin.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustachePlugin.java @@ -33,7 +33,7 @@ import org.elasticsearch.plugins.SearchPlugin; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptEngine; import java.util.Arrays; import java.util.List; @@ -44,8 +44,8 @@ public class MustachePlugin extends Plugin implements ScriptPlugin, ActionPlugin, SearchPlugin { @Override - public ScriptEngineService getScriptEngineService(Settings settings) { - return new MustacheScriptEngineService(); + public ScriptEngine getScriptEngine(Settings settings) { + return new MustacheScriptEngine(); } @Override diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngineService.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngine.java similarity index 97% rename from modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngineService.java rename to modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngine.java index 2d39eb080e004..18189fc99bbc0 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngineService.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngine.java @@ -34,7 +34,7 @@ import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.GeneralScriptException; import org.elasticsearch.script.Script; -import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.lookup.SearchLookup; @@ -53,8 +53,8 @@ * process: First compile the string representing the template, the resulting * {@link Mustache} object can then be re-used for subsequent executions. */ -public final class MustacheScriptEngineService implements ScriptEngineService { - private static final Logger logger = ESLoggerFactory.getLogger(MustacheScriptEngineService.class); +public final class MustacheScriptEngine implements ScriptEngine { + private static final Logger logger = ESLoggerFactory.getLogger(MustacheScriptEngine.class); public static final String NAME = "mustache"; diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java index 60435e72a4beb..10922a94a8917 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java @@ -19,7 +19,6 @@ package org.elasticsearch.script.mustache; -import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; @@ -35,8 +34,6 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryParseContext; -import org.elasticsearch.script.CompiledScript; -import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.builder.SearchSourceBuilder; @@ -50,7 +47,7 @@ public class TransportSearchTemplateAction extends HandledTransportAction { - private static final String TEMPLATE_LANG = MustacheScriptEngineService.NAME; + private static final String TEMPLATE_LANG = MustacheScriptEngine.NAME; private final ScriptService scriptService; private final TransportSearchAction searchAction; diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java index 265edb7d53bd4..f456f3d6f98c3 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java @@ -21,11 +21,10 @@ import com.github.mustachejava.Mustache; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.Script; -import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.test.ESTestCase; import java.util.Map; @@ -62,11 +61,11 @@ public void testCreateEncoder() { } public void testJsonEscapeEncoder() { - final ScriptEngineService engine = new MustacheScriptEngineService(); + final ScriptEngine engine = new MustacheScriptEngine(); final Map params = randomBoolean() ? singletonMap(Script.CONTENT_TYPE_OPTION, JSON_MIME_TYPE) : emptyMap(); Mustache script = (Mustache) engine.compile(null, "{\"field\": \"{{value}}\"}", params); - CompiledScript compiled = new CompiledScript(INLINE, null, MustacheScriptEngineService.NAME, script); + CompiledScript compiled = new CompiledScript(INLINE, null, MustacheScriptEngine.NAME, script); ExecutableScript executable = engine.executable(compiled, singletonMap("value", "a \"value\"")); BytesReference result = (BytesReference) executable.run(); @@ -74,11 +73,11 @@ public void testJsonEscapeEncoder() { } public void testDefaultEncoder() { - final ScriptEngineService engine = new MustacheScriptEngineService(); + final ScriptEngine engine = new MustacheScriptEngine(); final Map params = singletonMap(Script.CONTENT_TYPE_OPTION, PLAIN_TEXT_MIME_TYPE); Mustache script = (Mustache) engine.compile(null, "{\"field\": \"{{value}}\"}", params); - CompiledScript compiled = new CompiledScript(INLINE, null, MustacheScriptEngineService.NAME, script); + CompiledScript compiled = new CompiledScript(INLINE, null, MustacheScriptEngine.NAME, script); ExecutableScript executable = engine.executable(compiled, singletonMap("value", "a \"value\"")); BytesReference result = (BytesReference) executable.run(); @@ -86,11 +85,11 @@ public void testDefaultEncoder() { } public void testUrlEncoder() { - final ScriptEngineService engine = new MustacheScriptEngineService(); + final ScriptEngine engine = new MustacheScriptEngine(); final Map params = singletonMap(Script.CONTENT_TYPE_OPTION, X_WWW_FORM_URLENCODED_MIME_TYPE); Mustache script = (Mustache) engine.compile(null, "{\"field\": \"{{value}}\"}", params); - CompiledScript compiled = new CompiledScript(INLINE, null, MustacheScriptEngineService.NAME, script); + CompiledScript compiled = new CompiledScript(INLINE, null, MustacheScriptEngine.NAME, script); ExecutableScript executable = engine.executable(compiled, singletonMap("value", "tilde~ AND date:[2016 FROM*]")); BytesReference result = (BytesReference) executable.run(); diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheScriptEngineTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheScriptEngineTests.java index 12f6f54fa04fb..a92f34b84923c 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheScriptEngineTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheScriptEngineTests.java @@ -42,12 +42,12 @@ * Mustache based templating test */ public class MustacheScriptEngineTests extends ESTestCase { - private MustacheScriptEngineService qe; + private MustacheScriptEngine qe; private MustacheFactory factory; @Before public void setup() { - qe = new MustacheScriptEngineService(); + qe = new MustacheScriptEngine(); factory = new CustomMustacheFactory(); } diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheTests.java index 0d527590e478c..0becd11380a64 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheTests.java @@ -26,7 +26,7 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; -import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.test.ESTestCase; import org.hamcrest.Matcher; @@ -55,7 +55,7 @@ public class MustacheTests extends ESTestCase { - private ScriptEngineService engine = new MustacheScriptEngineService(); + private ScriptEngine engine = new MustacheScriptEngine(); public void testBasics() { String template = "GET _search {\"query\": " + "{\"boosting\": {" diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java index 15065d441ad64..b79257ebe2f99 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java @@ -151,7 +151,7 @@ public void testTemplateQueryAsEscapedStringWithConditionalClauseAtEnd() throws public void testIndexedTemplateClient() throws Exception { assertAcked(client().admin().cluster().preparePutStoredScript() - .setLang(MustacheScriptEngineService.NAME) + .setLang(MustacheScriptEngine.NAME) .setId("testTemplate") .setContent(new BytesArray("{" + "\"template\":{" + @@ -164,7 +164,7 @@ public void testIndexedTemplateClient() throws Exception { assertAcked(client().admin().cluster().preparePutStoredScript() - .setLang(MustacheScriptEngineService.NAME) + .setLang(MustacheScriptEngine.NAME) .setId("testTemplate").setContent(new BytesArray("{" + "\"template\":{" + " \"query\":{" + @@ -175,7 +175,7 @@ public void testIndexedTemplateClient() throws Exception { "}"), XContentType.JSON)); GetStoredScriptResponse getResponse = client().admin().cluster() - .prepareGetStoredScript(MustacheScriptEngineService.NAME, "testTemplate").get(); + .prepareGetStoredScript(MustacheScriptEngine.NAME, "testTemplate").get(); assertNotNull(getResponse.getSource()); BulkRequestBuilder bulkRequestBuilder = client().prepareBulk(); @@ -197,10 +197,10 @@ public void testIndexedTemplateClient() throws Exception { assertHitCount(searchResponse.getResponse(), 4); assertAcked(client().admin().cluster() - .prepareDeleteStoredScript(MustacheScriptEngineService.NAME, "testTemplate")); + .prepareDeleteStoredScript(MustacheScriptEngine.NAME, "testTemplate")); getResponse = client().admin().cluster() - .prepareGetStoredScript(MustacheScriptEngineService.NAME, "testTemplate").get(); + .prepareGetStoredScript(MustacheScriptEngine.NAME, "testTemplate").get(); assertNull(getResponse.getSource()); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new SearchTemplateRequestBuilder(client()) @@ -212,7 +212,7 @@ public void testIndexedTemplateClient() throws Exception { public void testIndexedTemplate() throws Exception { assertAcked(client().admin().cluster().preparePutStoredScript() - .setLang(MustacheScriptEngineService.NAME) + .setLang(MustacheScriptEngine.NAME) .setId("1a") .setContent(new BytesArray("{" + "\"template\":{" + @@ -225,7 +225,7 @@ public void testIndexedTemplate() throws Exception { ), XContentType.JSON) ); assertAcked(client().admin().cluster().preparePutStoredScript() - .setLang(MustacheScriptEngineService.NAME) + .setLang(MustacheScriptEngine.NAME) .setId("2") .setContent(new BytesArray("{" + "\"template\":{" + @@ -237,7 +237,7 @@ public void testIndexedTemplate() throws Exception { "}"), XContentType.JSON) ); assertAcked(client().admin().cluster().preparePutStoredScript() - .setLang(MustacheScriptEngineService.NAME) + .setLang(MustacheScriptEngine.NAME) .setId("3") .setContent(new BytesArray("{" + "\"template\":{" + @@ -313,13 +313,13 @@ public void testIndexedTemplateOverwrite() throws Exception { int iterations = randomIntBetween(2, 11); for (int i = 1; i < iterations; i++) { assertAcked(client().admin().cluster().preparePutStoredScript() - .setLang(MustacheScriptEngineService.NAME) + .setLang(MustacheScriptEngine.NAME) .setId("git01") .setContent(new BytesArray("{\"template\":{\"query\": {\"match\": {\"searchtext\": {\"query\": \"{{P_Keyword1}}\"," + "\"type\": \"ooophrase_prefix\"}}}}}"), XContentType.JSON)); GetStoredScriptResponse getResponse = client().admin().cluster() - .prepareGetStoredScript(MustacheScriptEngineService.NAME, "git01").get(); + .prepareGetStoredScript(MustacheScriptEngine.NAME, "git01").get(); assertNotNull(getResponse.getSource()); Map templateParams = new HashMap<>(); @@ -333,7 +333,7 @@ public void testIndexedTemplateOverwrite() throws Exception { assertWarnings("Deprecated field [type] used, replaced by [match_phrase and match_phrase_prefix query]"); assertAcked(client().admin().cluster().preparePutStoredScript() - .setLang(MustacheScriptEngineService.NAME) + .setLang(MustacheScriptEngine.NAME) .setId("git01") .setContent(new BytesArray("{\"query\": {\"match\": {\"searchtext\": {\"query\": \"{{P_Keyword1}}\"," + "\"type\": \"phrase_prefix\"}}}}"), XContentType.JSON)); @@ -351,7 +351,7 @@ public void testIndexedTemplateWithArray() throws Exception { String multiQuery = "{\"query\":{\"terms\":{\"theField\":[\"{{#fieldParam}}\",\"{{.}}\",\"{{/fieldParam}}\"]}}}"; assertAcked( client().admin().cluster().preparePutStoredScript() - .setLang(MustacheScriptEngineService.NAME) + .setLang(MustacheScriptEngine.NAME) .setId("4") .setContent(jsonBuilder().startObject().field("template", multiQuery).endObject().bytes(), XContentType.JSON) ); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Location.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Location.java index 4a4803844a4c4..f64200d972996 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Location.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Location.java @@ -72,7 +72,7 @@ public RuntimeException createError(RuntimeException exception) { /** Computes the file name (mostly important for stacktraces) */ public static String computeSourceName(String scriptName, String source) { StringBuilder fileName = new StringBuilder(); - if (scriptName.equals(PainlessScriptEngineService.INLINE_NAME)) { + if (scriptName.equals(PainlessScriptEngine.INLINE_NAME)) { // its an anonymous script, include at least a portion of the source to help identify which one it is // but don't create stacktraces with filenames that contain newlines or huge names. diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessPlugin.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessPlugin.java index c00dc64310237..62ea89acb06e2 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessPlugin.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessPlugin.java @@ -24,7 +24,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.ScriptPlugin; -import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptEngine; import java.util.Arrays; import java.util.List; @@ -40,8 +40,8 @@ public final class PainlessPlugin extends Plugin implements ScriptPlugin { } @Override - public ScriptEngineService getScriptEngineService(Settings settings) { - return new PainlessScriptEngineService(settings); + public ScriptEngine getScriptEngine(Settings settings) { + return new PainlessScriptEngine(settings); } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScript.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScript.java index b4e7c157f1fe2..643837d8db018 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScript.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScript.java @@ -93,12 +93,12 @@ protected final ScriptException convertToScriptException(Throwable t, Map> entry : extraMetadata.entrySet()) { scriptException.addMetadata(entry.getKey(), entry.getValue()); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngineService.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java similarity index 96% rename from modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngineService.java rename to modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java index a8dc045674a49..296e9b2c4b80d 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngineService.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java @@ -27,7 +27,7 @@ import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.LeafSearchScript; -import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.script.ScriptException; import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.lookup.SearchLookup; @@ -46,7 +46,7 @@ /** * Implementation of a ScriptEngine for the Painless language. */ -public final class PainlessScriptEngineService extends AbstractComponent implements ScriptEngineService { +public final class PainlessScriptEngine extends AbstractComponent implements ScriptEngine { /** * Standard name of the Painless language. @@ -71,7 +71,7 @@ public final class PainlessScriptEngineService extends AbstractComponent impleme /** * Default compiler settings to be used. Note that {@link CompilerSettings} is mutable but this instance shouldn't be mutated outside - * of {@link PainlessScriptEngineService#PainlessScriptEngineService(Settings)}. + * of {@link PainlessScriptEngine#PainlessScriptEngine(Settings)}. */ private final CompilerSettings defaultCompilerSettings = new CompilerSettings(); @@ -79,7 +79,7 @@ public final class PainlessScriptEngineService extends AbstractComponent impleme * Constructor. * @param settings The settings to initialize the engine with. */ - public PainlessScriptEngineService(final Settings settings) { + public PainlessScriptEngine(final Settings settings) { super(settings); defaultCompilerSettings.setRegexesEnabled(CompilerSettings.REGEX_ENABLED.get(settings)); } @@ -262,7 +262,7 @@ private ScriptException convertToScriptException(String scriptName, String scrip break; } } - throw new ScriptException("compile error", t, scriptStack, scriptSource, PainlessScriptEngineService.NAME); + throw new ScriptException("compile error", t, scriptStack, scriptSource, PainlessScriptEngine.NAME); } // very simple heuristic: +/- 25 chars. can be improved later. diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/NeedsScoreTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/NeedsScoreTests.java index 2de25ba54b08e..4611893b645d0 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/NeedsScoreTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/NeedsScoreTests.java @@ -38,7 +38,7 @@ public class NeedsScoreTests extends ESSingleNodeTestCase { public void testNeedsScores() { IndexService index = createIndex("test", Settings.EMPTY, "type", "d", "type=double"); - PainlessScriptEngineService service = new PainlessScriptEngineService(Settings.EMPTY); + PainlessScriptEngine service = new PainlessScriptEngine(Settings.EMPTY); SearchLookup lookup = new SearchLookup(index.mapperService(), index.fieldData(), null); Object compiled = service.compile(null, "1.2", Collections.emptyMap()); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java index 334d311c49cb5..27d7c5c94b71e 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java @@ -43,11 +43,11 @@ * Typically just asserts the output of {@code exec()} */ public abstract class ScriptTestCase extends ESTestCase { - protected PainlessScriptEngineService scriptEngine; + protected PainlessScriptEngine scriptEngine; @Before public void setup() { - scriptEngine = new PainlessScriptEngineService(scriptEngineSettings()); + scriptEngine = new PainlessScriptEngine(scriptEngineSettings()); } /** diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/java/org/elasticsearch/ingest/AbstractScriptTestCase.java b/qa/smoke-test-ingest-with-all-dependencies/src/test/java/org/elasticsearch/ingest/AbstractScriptTestCase.java index 1ca4c0252c39a..5c5dd7cfe01ea 100644 --- a/qa/smoke-test-ingest-with-all-dependencies/src/test/java/org/elasticsearch/ingest/AbstractScriptTestCase.java +++ b/qa/smoke-test-ingest-with-all-dependencies/src/test/java/org/elasticsearch/ingest/AbstractScriptTestCase.java @@ -25,7 +25,7 @@ import org.elasticsearch.script.ScriptEngineRegistry; import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptSettings; -import org.elasticsearch.script.mustache.MustacheScriptEngineService; +import org.elasticsearch.script.mustache.MustacheScriptEngine; import org.elasticsearch.test.ESTestCase; import org.junit.Before; @@ -41,7 +41,7 @@ public void init() throws Exception { Settings settings = Settings.builder() .put("path.home", createTempDir()) .build(); - ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(Arrays.asList(new MustacheScriptEngineService())); + ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(Arrays.asList(new MustacheScriptEngine())); ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Collections.emptyList()); ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry); diff --git a/test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java b/test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java index 62e3d51ad10b4..12fa092de5036 100644 --- a/test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java +++ b/test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java @@ -46,7 +46,7 @@ * * The function is used to provide the result of the script execution and can return anything. */ -public class MockScriptEngine implements ScriptEngineService { +public class MockScriptEngine implements ScriptEngine { public static final String NAME = "mockscript"; diff --git a/test/framework/src/main/java/org/elasticsearch/script/MockScriptPlugin.java b/test/framework/src/main/java/org/elasticsearch/script/MockScriptPlugin.java index dc397bd0b14a3..ff022e1d73173 100644 --- a/test/framework/src/main/java/org/elasticsearch/script/MockScriptPlugin.java +++ b/test/framework/src/main/java/org/elasticsearch/script/MockScriptPlugin.java @@ -34,7 +34,7 @@ public abstract class MockScriptPlugin extends Plugin implements ScriptPlugin { public static final String NAME = MockScriptEngine.NAME; @Override - public ScriptEngineService getScriptEngineService(Settings settings) { + public ScriptEngine getScriptEngine(Settings settings) { return new MockScriptEngine(pluginScriptLang(), pluginScripts()); } From 3f1ef488cd67a4f4946b443e2efa7c33a2befaf3 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 10 May 2017 01:04:02 -0700 Subject: [PATCH 267/619] fix checkstyle after script engine rename --- buildSrc/src/main/resources/checkstyle_suppressions.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 4b43b766d38f1..3e0028032134a 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -750,7 +750,7 @@ - + From 3c66ac06aee2401db8f6554a7a6a0a6282b7f187 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Wed, 10 May 2017 10:17:25 +0200 Subject: [PATCH 268/619] Add parsing for String/Long/Double Terms aggregations (#24521) --- .../search/aggregations/Aggregations.java | 2 +- .../ParsedMultiBucketAggregation.java | 42 +++-- .../bucket/histogram/ParsedDateHistogram.java | 29 ++-- .../bucket/histogram/ParsedHistogram.java | 23 ++- .../bucket/terms/ParsedDoubleTerms.java | 85 ++++++++++ .../bucket/terms/ParsedLongTerms.java | 85 ++++++++++ .../bucket/terms/ParsedStringTerms.java | 85 ++++++++++ .../bucket/terms/ParsedTerms.java | 151 ++++++++++++++++++ .../percentiles/ParsedPercentiles.java | 3 +- .../InternalAggregationTestCase.java | 19 ++- .../bucket/terms/DoubleTermsTests.java | 23 +-- .../bucket/terms/InternalTermsTestCase.java | 32 +++- .../bucket/terms/LongTermsTests.java | 22 +-- .../bucket/terms/StringTermsTests.java | 20 ++- 14 files changed, 542 insertions(+), 79 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedDoubleTerms.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedLongTerms.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedStringTerms.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedTerms.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/Aggregations.java b/core/src/main/java/org/elasticsearch/search/aggregations/Aggregations.java index 465cef3087713..05521e488315d 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/Aggregations.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/Aggregations.java @@ -47,7 +47,7 @@ public class Aggregations implements Iterable, ToXContent { protected Aggregations() { } - protected Aggregations(List aggregations) { + public Aggregations(List aggregations) { this.aggregations = aggregations; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java index b2823669a75f3..02e29a1746faa 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java @@ -19,6 +19,7 @@ package org.elasticsearch.search.aggregations; +import org.elasticsearch.common.CheckedBiConsumer; import org.elasticsearch.common.CheckedFunction; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -33,10 +34,11 @@ import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; -public abstract class ParsedMultiBucketAggregation extends ParsedAggregation implements MultiBucketsAggregation { +public abstract class ParsedMultiBucketAggregation + extends ParsedAggregation implements MultiBucketsAggregation { - protected final List> buckets = new ArrayList<>(); - protected boolean keyed; + protected final List buckets = new ArrayList<>(); + protected boolean keyed = false; @Override protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { @@ -45,7 +47,7 @@ protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) } else { builder.startArray(CommonFields.BUCKETS.getPreferredName()); } - for (ParsedBucket bucket : buckets) { + for (B bucket : buckets) { bucket.toXContent(builder, params); } if (keyed) { @@ -57,8 +59,8 @@ protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) } protected static void declareMultiBucketAggregationFields(final ObjectParser objectParser, - final CheckedFunction, IOException> bucketParser, - final CheckedFunction, IOException> keyedBucketParser) { + final CheckedFunction bucketParser, + final CheckedFunction keyedBucketParser) { declareAggregationFields(objectParser); objectParser.declareField((parser, aggregation, context) -> { XContentParser.Token token = parser.currentToken(); @@ -76,23 +78,13 @@ protected static void declareMultiBucketAggregationFields(final ObjectParser implements MultiBucketsAggregation.Bucket { + public static abstract class ParsedBucket implements MultiBucketsAggregation.Bucket { private Aggregations aggregations; - private T key; private String keyAsString; private long docCount; private boolean keyed; - protected void setKey(T key) { - this.key = key; - } - - @Override - public Object getKey() { - return key; - } - protected void setKeyAsString(String keyAsString) { this.keyAsString = keyAsString; } @@ -137,17 +129,21 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (keyAsString != null) { builder.field(CommonFields.KEY_AS_STRING.getPreferredName(), getKeyAsString()); } - builder.field(CommonFields.KEY.getPreferredName(), key); + keyToXContent(builder); builder.field(CommonFields.DOC_COUNT.getPreferredName(), docCount); aggregations.toXContentInternal(builder, params); builder.endObject(); return builder; } - protected static > B parseXContent(final XContentParser parser, - final boolean keyed, - final Supplier bucketSupplier, - final CheckedFunction keyParser) + protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException { + return builder.field(CommonFields.KEY.getPreferredName(), getKey()); + } + + protected static B parseXContent(final XContentParser parser, + final boolean keyed, + final Supplier bucketSupplier, + final CheckedBiConsumer keyConsumer) throws IOException { final B bucket = bucketSupplier.get(); bucket.setKeyed(keyed); @@ -166,7 +162,7 @@ protected static > B parseXContent(final XContentPa if (CommonFields.KEY_AS_STRING.getPreferredName().equals(currentFieldName)) { bucket.setKeyAsString(parser.text()); } else if (CommonFields.KEY.getPreferredName().equals(currentFieldName)) { - bucket.setKey(keyParser.apply(parser)); + keyConsumer.accept(parser, bucket); } else if (CommonFields.DOC_COUNT.getPreferredName().equals(currentFieldName)) { bucket.setDocCount(parser.longValue()); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedDateHistogram.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedDateHistogram.java index 27ba2c029d2d5..9efc478aff1aa 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedDateHistogram.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedDateHistogram.java @@ -20,17 +20,16 @@ package org.elasticsearch.search.aggregations.bucket.histogram; import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import java.io.IOException; import java.util.List; -import java.util.stream.Collectors; -public class ParsedDateHistogram extends ParsedMultiBucketAggregation implements Histogram { +public class ParsedDateHistogram extends ParsedMultiBucketAggregation implements Histogram { @Override protected String getType() { @@ -39,7 +38,7 @@ protected String getType() { @Override public List getBuckets() { - return buckets.stream().map(bucket -> (Histogram.Bucket) bucket).collect(Collectors.toList()); + return buckets; } private static ObjectParser PARSER = @@ -56,11 +55,16 @@ public static ParsedDateHistogram fromXContent(XContentParser parser, String nam return aggregation; } - public static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements Histogram.Bucket { + public static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements Histogram.Bucket { + + private Long key; @Override public Object getKey() { - return new DateTime(super.getKey(), DateTimeZone.UTC); + if (key != null) { + return new DateTime(key, DateTimeZone.UTC); + } + return null; } @Override @@ -68,13 +72,20 @@ public String getKeyAsString() { String keyAsString = super.getKeyAsString(); if (keyAsString != null) { return keyAsString; - } else { - return DocValueFormat.RAW.format((Long) super.getKey()); } + if (key != null) { + return Long.toString(key); + } + return null; + } + + @Override + protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException { + return builder.field(CommonFields.KEY.getPreferredName(), key); } static ParsedBucket fromXContent(XContentParser parser, boolean keyed) throws IOException { - return parseXContent(parser, keyed, ParsedBucket::new, XContentParser::longValue); + return parseXContent(parser, keyed, ParsedBucket::new, (p, bucket) -> bucket.key = p.longValue()); } } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedHistogram.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedHistogram.java index 2b6730df2cc02..eeec1cd81ba7f 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedHistogram.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedHistogram.java @@ -21,14 +21,12 @@ import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import java.io.IOException; import java.util.List; -import java.util.stream.Collectors; -public class ParsedHistogram extends ParsedMultiBucketAggregation implements Histogram { +public class ParsedHistogram extends ParsedMultiBucketAggregation implements Histogram { @Override protected String getType() { @@ -37,7 +35,7 @@ protected String getType() { @Override public List getBuckets() { - return buckets.stream().map(bucket -> (Histogram.Bucket) bucket).collect(Collectors.toList()); + return buckets; } private static ObjectParser PARSER = @@ -54,20 +52,29 @@ public static ParsedHistogram fromXContent(XContentParser parser, String name) t return aggregation; } - static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements Histogram.Bucket { + static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements Histogram.Bucket { + + private Double key; + + @Override + public Object getKey() { + return key; + } @Override public String getKeyAsString() { String keyAsString = super.getKeyAsString(); if (keyAsString != null) { return keyAsString; - } else { - return DocValueFormat.RAW.format((Double) getKey()); } + if (key != null) { + return Double.toString(key); + } + return null; } static ParsedBucket fromXContent(XContentParser parser, boolean keyed) throws IOException { - return parseXContent(parser, keyed, ParsedBucket::new, XContentParser::doubleValue); + return parseXContent(parser, keyed, ParsedBucket::new, (p, bucket) -> bucket.key = p.doubleValue()); } } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedDoubleTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedDoubleTerms.java new file mode 100644 index 0000000000000..3401cd021bde6 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedDoubleTerms.java @@ -0,0 +1,85 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.terms; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; + +public class ParsedDoubleTerms extends ParsedTerms { + + @Override + protected String getType() { + return DoubleTerms.NAME; + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedDoubleTerms.class.getSimpleName(), true, ParsedDoubleTerms::new); + static { + declareParsedTermsFields(PARSER, ParsedBucket::fromXContent); + } + + public static ParsedDoubleTerms fromXContent(XContentParser parser, String name) throws IOException { + ParsedDoubleTerms aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } + + public static class ParsedBucket extends ParsedTerms.ParsedBucket { + + private Double key; + + @Override + public Object getKey() { + return key; + } + + @Override + public String getKeyAsString() { + String keyAsString = super.getKeyAsString(); + if (keyAsString != null) { + return keyAsString; + } + if (key != null) { + return Double.toString(key); + } + return null; + } + + public Number getKeyAsNumber() { + return key; + } + + @Override + protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException { + builder.field(CommonFields.KEY.getPreferredName(), key); + if (super.getKeyAsString() != null) { + builder.field(CommonFields.KEY_AS_STRING.getPreferredName(), getKeyAsString()); + } + return builder; + } + + static ParsedBucket fromXContent(XContentParser parser) throws IOException { + return parseTermsBucketXContent(parser, ParsedBucket::new, (p, bucket) -> bucket.key = p.doubleValue()); + } + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedLongTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedLongTerms.java new file mode 100644 index 0000000000000..6de25dfefe1de --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedLongTerms.java @@ -0,0 +1,85 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.terms; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; + +public class ParsedLongTerms extends ParsedTerms { + + @Override + protected String getType() { + return LongTerms.NAME; + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedLongTerms.class.getSimpleName(), true, ParsedLongTerms::new); + static { + declareParsedTermsFields(PARSER, ParsedBucket::fromXContent); + } + + public static ParsedLongTerms fromXContent(XContentParser parser, String name) throws IOException { + ParsedLongTerms aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } + + public static class ParsedBucket extends ParsedTerms.ParsedBucket { + + private Long key; + + @Override + public Object getKey() { + return key; + } + + @Override + public String getKeyAsString() { + String keyAsString = super.getKeyAsString(); + if (keyAsString != null) { + return keyAsString; + } + if (key != null) { + return Long.toString(key); + } + return null; + } + + public Number getKeyAsNumber() { + return key; + } + + @Override + protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException { + builder.field(CommonFields.KEY.getPreferredName(), key); + if (super.getKeyAsString() != null) { + builder.field(CommonFields.KEY_AS_STRING.getPreferredName(), getKeyAsString()); + } + return builder; + } + + static ParsedBucket fromXContent(XContentParser parser) throws IOException { + return parseTermsBucketXContent(parser, ParsedBucket::new, (p, bucket) -> bucket.key = p.longValue()); + } + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedStringTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedStringTerms.java new file mode 100644 index 0000000000000..f258f7f847ed0 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedStringTerms.java @@ -0,0 +1,85 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.terms; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; + +public class ParsedStringTerms extends ParsedTerms { + + @Override + protected String getType() { + return StringTerms.NAME; + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedStringTerms.class.getSimpleName(), true, ParsedStringTerms::new); + static { + declareParsedTermsFields(PARSER, ParsedBucket::fromXContent); + } + + public static ParsedStringTerms fromXContent(XContentParser parser, String name) throws IOException { + ParsedStringTerms aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } + + public static class ParsedBucket extends ParsedTerms.ParsedBucket { + + private BytesRef key; + + @Override + public Object getKey() { + return getKeyAsString(); + } + + @Override + public String getKeyAsString() { + String keyAsString = super.getKeyAsString(); + if (keyAsString != null) { + return keyAsString; + } + if (key != null) { + return key.utf8ToString(); + } + return null; + } + + public Number getKeyAsNumber() { + if (key != null) { + return Double.parseDouble(key.utf8ToString()); + } + return null; + } + + @Override + protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException { + return builder.field(CommonFields.KEY.getPreferredName(), getKey()); + } + + static ParsedBucket fromXContent(XContentParser parser) throws IOException { + return parseTermsBucketXContent(parser, ParsedBucket::new, (p, bucket) -> bucket.key = p.utf8BytesOrNull()); + } + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedTerms.java new file mode 100644 index 0000000000000..6e24bd00383a7 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedTerms.java @@ -0,0 +1,151 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.terms; + +import org.elasticsearch.common.CheckedBiConsumer; +import org.elasticsearch.common.CheckedFunction; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParserUtils; +import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.aggregations.Aggregations; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +import static org.elasticsearch.search.aggregations.bucket.terms.InternalTerms.DOC_COUNT_ERROR_UPPER_BOUND_FIELD_NAME; +import static org.elasticsearch.search.aggregations.bucket.terms.InternalTerms.SUM_OF_OTHER_DOC_COUNTS; + +public abstract class ParsedTerms extends ParsedMultiBucketAggregation implements Terms { + + protected long docCountErrorUpperBound; + protected long sumOtherDocCount; + + @Override + public long getDocCountError() { + return docCountErrorUpperBound; + } + + @Override + public long getSumOfOtherDocCounts() { + return sumOtherDocCount; + } + + @Override + public List getBuckets() { + return buckets; + } + + @Override + public Terms.Bucket getBucketByKey(String term) { + for (Terms.Bucket bucket : getBuckets()) { + if (bucket.getKeyAsString().equals(term)) { + return bucket; + } + } + return null; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + builder.field(DOC_COUNT_ERROR_UPPER_BOUND_FIELD_NAME.getPreferredName(), getDocCountError()); + builder.field(SUM_OF_OTHER_DOC_COUNTS.getPreferredName(), getSumOfOtherDocCounts()); + builder.startArray(CommonFields.BUCKETS.getPreferredName()); + for (Terms.Bucket bucket : getBuckets()) { + bucket.toXContent(builder, params); + } + builder.endArray(); + return builder; + } + + static void declareParsedTermsFields(final ObjectParser objectParser, + final CheckedFunction bucketParser) { + declareMultiBucketAggregationFields(objectParser, bucketParser::apply, bucketParser::apply); + objectParser.declareLong((parsedTerms, value) -> parsedTerms.docCountErrorUpperBound = value , + DOC_COUNT_ERROR_UPPER_BOUND_FIELD_NAME); + objectParser.declareLong((parsedTerms, value) -> parsedTerms.sumOtherDocCount = value, + SUM_OF_OTHER_DOC_COUNTS); + } + + public abstract static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements Terms.Bucket { + + boolean showDocCountError = false; + protected long docCountError; + + @Override + public int compareTerm(Terms.Bucket other) { + throw new UnsupportedOperationException(); + } + + @Override + public long getDocCountError() { + return docCountError; + } + + @Override + public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + keyToXContent(builder); + builder.field(CommonFields.DOC_COUNT.getPreferredName(), getDocCount()); + if (showDocCountError) { + builder.field(DOC_COUNT_ERROR_UPPER_BOUND_FIELD_NAME.getPreferredName(), getDocCountError()); + } + getAggregations().toXContentInternal(builder, params); + builder.endObject(); + return builder; + } + + + static B parseTermsBucketXContent(final XContentParser parser, final Supplier bucketSupplier, + final CheckedBiConsumer keyConsumer) + throws IOException { + + final B bucket = bucketSupplier.get(); + final List aggregations = new ArrayList<>(); + + XContentParser.Token token; + String currentFieldName = parser.currentName(); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (CommonFields.KEY_AS_STRING.getPreferredName().equals(currentFieldName)) { + bucket.setKeyAsString(parser.text()); + } else if (CommonFields.KEY.getPreferredName().equals(currentFieldName)) { + keyConsumer.accept(parser, bucket); + } else if (CommonFields.DOC_COUNT.getPreferredName().equals(currentFieldName)) { + bucket.setDocCount(parser.longValue()); + } else if (DOC_COUNT_ERROR_UPPER_BOUND_FIELD_NAME.getPreferredName().equals(currentFieldName)) { + bucket.docCountError = parser.longValue(); + bucket.showDocCountError = true; + } + } else if (token == XContentParser.Token.START_OBJECT) { + aggregations.add(XContentParserUtils.parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Aggregation.class)); + } + } + bucket.setAggregations(new Aggregations(aggregations)); + return bucket; + } + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentiles.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentiles.java index a96fb2cd13cc3..48f3dccecefa9 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentiles.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentiles.java @@ -22,7 +22,6 @@ import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.ParsedAggregation; import java.io.IOException; @@ -60,7 +59,7 @@ protected String getPercentileAsString(double percent) { } Double value = getPercentile(percent); if (value != null) { - return DocValueFormat.RAW.format(value); + return Double.toString(value); } return null; } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index 782e98e888555..a766dcbf5c882 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParserUtils; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.rest.action.search.RestSearchAction; @@ -38,6 +39,12 @@ import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram; import org.elasticsearch.search.aggregations.bucket.histogram.ParsedHistogram; +import org.elasticsearch.search.aggregations.bucket.terms.DoubleTerms; +import org.elasticsearch.search.aggregations.bucket.terms.LongTerms; +import org.elasticsearch.search.aggregations.bucket.terms.ParsedDoubleTerms; +import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms; +import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms; +import org.elasticsearch.search.aggregations.bucket.terms.StringTerms; import org.elasticsearch.search.aggregations.metrics.avg.AvgAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.avg.ParsedAvg; import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder; @@ -127,6 +134,9 @@ static List getNamedXContents() { namedXContents.put(GeoCentroidAggregationBuilder.NAME, (p, c) -> ParsedGeoCentroid.fromXContent(p, (String) c)); namedXContents.put(HistogramAggregationBuilder.NAME, (p, c) -> ParsedHistogram.fromXContent(p, (String) c)); namedXContents.put(DateHistogramAggregationBuilder.NAME, (p, c) -> ParsedDateHistogram.fromXContent(p, (String) c)); + namedXContents.put(StringTerms.NAME, (p, c) -> ParsedStringTerms.fromXContent(p, (String) c)); + namedXContents.put(LongTerms.NAME, (p, c) -> ParsedLongTerms.fromXContent(p, (String) c)); + namedXContents.put(DoubleTerms.NAME, (p, c) -> ParsedDoubleTerms.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) @@ -259,12 +269,7 @@ protected

P parseAndAssert(final InternalAggregati assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); - String currentName = parser.currentName(); - int i = currentName.indexOf(InternalAggregation.TYPED_KEYS_DELIMITER); - String aggType = currentName.substring(0, i); - String aggName = currentName.substring(i + 1); - - parsedAggregation = parser.namedObject(Aggregation.class, aggType, aggName); + parsedAggregation = XContentParserUtils.parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Aggregation.class); assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken()); assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); @@ -277,7 +282,7 @@ protected

P parseAndAssert(final InternalAggregati assertEquals(aggregation.getType(), ((ParsedAggregation) parsedAggregation).getType()); } - BytesReference parsedBytes = toXContent((ToXContent) parsedAggregation, xContentType, params, humanReadable); + BytesReference parsedBytes = toXContent(parsedAggregation, xContentType, params, humanReadable); assertToXContentEquivalent(originalBytes, parsedBytes, xContentType); return (P) parsedAggregation; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTermsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTermsTests.java index 757d5647a12d8..45531e27dde4f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTermsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTermsTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.ArrayList; @@ -33,17 +34,17 @@ public class DoubleTermsTests extends InternalTermsTestCase { @Override - protected InternalTerms createTestInstance( - String name, - List pipelineAggregators, - Map metaData) { + protected InternalTerms createTestInstance(String name, + List pipelineAggregators, + Map metaData, + InternalAggregations aggregations, + boolean showTermDocCountError, + long docCountError) { Terms.Order order = Terms.Order.count(false); long minDocCount = 1; int requiredSize = 3; int shardSize = requiredSize + 2; - DocValueFormat format = DocValueFormat.RAW; - boolean showTermDocCountError = false; - long docCountError = -1; + DocValueFormat format = randomNumericDocValueFormat(); long otherDocCount = 0; List buckets = new ArrayList<>(); final int numBuckets = randomInt(shardSize); @@ -51,8 +52,7 @@ public class DoubleTermsTests extends InternalTermsTestCase { for (int i = 0; i < numBuckets; ++i) { double term = randomValueOtherThanMany(d -> terms.add(d) == false, random()::nextDouble); int docCount = randomIntBetween(1, 100); - buckets.add(new DoubleTerms.Bucket(term, docCount, InternalAggregations.EMPTY, - showTermDocCountError, docCountError, format)); + buckets.add(new DoubleTerms.Bucket(term, docCount, aggregations, showTermDocCountError, docCountError, format)); } return new DoubleTerms(name, order, requiredSize, minDocCount, pipelineAggregators, metaData, format, shardSize, showTermDocCountError, otherDocCount, buckets, docCountError); @@ -63,4 +63,9 @@ public class DoubleTermsTests extends InternalTermsTestCase { return DoubleTerms::new; } + @Override + protected Class implementationClass() { + return ParsedDoubleTerms.class; + } + } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTermsTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTermsTestCase.java index 03031633f746d..cfd7a82da1a96 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTermsTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTermsTestCase.java @@ -19,18 +19,44 @@ package org.elasticsearch.search.aggregations.bucket.terms; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.InternalMultiBucketAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.junit.Before; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Map.Entry; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; -public abstract class InternalTermsTestCase extends InternalAggregationTestCase> { +public abstract class InternalTermsTestCase extends InternalMultiBucketAggregationTestCase> { + + private boolean showDocCount; + private long docCountError; + + @Before + public void init() { + showDocCount = randomBoolean(); + docCountError = showDocCount ? randomInt(1000) : -1; + } + + @Override + protected InternalTerms createTestInstance(String name, + List pipelineAggregators, + Map metaData, + InternalAggregations aggregations) { + return createTestInstance(name, pipelineAggregators, metaData, aggregations, showDocCount, docCountError); + } + + protected abstract InternalTerms createTestInstance(String name, + List pipelineAggregators, + Map metaData, + InternalAggregations aggregations, + boolean showTermDocCountError, + long docCountError); @Override protected InternalTerms createUnmappedInstance( diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsTests.java index ff95984bc3206..cc97e4989a916 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.ArrayList; @@ -33,17 +34,17 @@ public class LongTermsTests extends InternalTermsTestCase { @Override - protected InternalTerms createTestInstance( - String name, - List pipelineAggregators, - Map metaData) { + protected InternalTerms createTestInstance(String name, + List pipelineAggregators, + Map metaData, + InternalAggregations aggregations, + boolean showTermDocCountError, + long docCountError) { Terms.Order order = Terms.Order.count(false); long minDocCount = 1; int requiredSize = 3; int shardSize = requiredSize + 2; - DocValueFormat format = DocValueFormat.RAW; - boolean showTermDocCountError = false; - long docCountError = -1; + DocValueFormat format = randomNumericDocValueFormat(); long otherDocCount = 0; List buckets = new ArrayList<>(); final int numBuckets = randomInt(shardSize); @@ -51,8 +52,7 @@ public class LongTermsTests extends InternalTermsTestCase { for (int i = 0; i < numBuckets; ++i) { long term = randomValueOtherThanMany(l -> terms.add(l) == false, random()::nextLong); int docCount = randomIntBetween(1, 100); - buckets.add(new LongTerms.Bucket(term, docCount, InternalAggregations.EMPTY, - showTermDocCountError, docCountError, format)); + buckets.add(new LongTerms.Bucket(term, docCount, aggregations, showTermDocCountError, docCountError, format)); } return new LongTerms(name, order, requiredSize, minDocCount, pipelineAggregators, metaData, format, shardSize, showTermDocCountError, otherDocCount, buckets, docCountError); @@ -63,4 +63,8 @@ public class LongTermsTests extends InternalTermsTestCase { return LongTerms::new; } + @Override + protected Class implementationClass() { + return ParsedLongTerms.class; + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsTests.java index 64e814bd8192a..e909358be5e5f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.ArrayList; @@ -34,17 +35,17 @@ public class StringTermsTests extends InternalTermsTestCase { @Override - protected InternalTerms createTestInstance( - String name, - List pipelineAggregators, - Map metaData) { + protected InternalTerms createTestInstance(String name, + List pipelineAggregators, + Map metaData, + InternalAggregations aggregations, + boolean showTermDocCountError, + long docCountError) { Terms.Order order = Terms.Order.count(false); long minDocCount = 1; int requiredSize = 3; int shardSize = requiredSize + 2; DocValueFormat format = DocValueFormat.RAW; - boolean showTermDocCountError = false; - long docCountError = -1; long otherDocCount = 0; List buckets = new ArrayList<>(); final int numBuckets = randomInt(shardSize); @@ -52,8 +53,7 @@ public class StringTermsTests extends InternalTermsTestCase { for (int i = 0; i < numBuckets; ++i) { BytesRef term = randomValueOtherThanMany(b -> terms.add(b) == false, () -> new BytesRef(randomAlphaOfLength(10))); int docCount = randomIntBetween(1, 100); - buckets.add(new StringTerms.Bucket(term, docCount, InternalAggregations.EMPTY, - showTermDocCountError, docCountError, format)); + buckets.add(new StringTerms.Bucket(term, docCount, aggregations, showTermDocCountError, docCountError, format)); } return new StringTerms(name, order, requiredSize, minDocCount, pipelineAggregators, metaData, format, shardSize, showTermDocCountError, otherDocCount, buckets, docCountError); @@ -64,4 +64,8 @@ public class StringTermsTests extends InternalTermsTestCase { return StringTerms::new; } + @Override + protected Class implementationClass() { + return ParsedStringTerms.class; + } } From b24326271e6778d5d595005e7e1e4258e7e7ee24 Mon Sep 17 00:00:00 2001 From: Matt Weber Date: Wed, 10 May 2017 01:35:11 -0700 Subject: [PATCH 269/619] Add ICUCollationFieldMapper (#24126) Adds a new "icu_collation" field type that exposes lucene's ICUCollationDocValuesField. ICUCollationDocValuesField is the replacement for ICUCollationKeyFilter which has been deprecated since Lucene 5. --- .../index/mapper/StringFieldType.java | 6 +- docs/plugins/analysis-icu.asciidoc | 100 ++- plugins/analysis-icu/build.gradle | 2 +- .../ICUCollationKeywordFieldMapper.java | 746 ++++++++++++++++++ .../analysis/icu/AnalysisICUPlugin.java | 29 +- .../index/mapper/CollationFieldTypeTests.java | 145 ++++ .../ICUCollationKeywordFieldMapperIT.java | 443 +++++++++++ .../ICUCollationKeywordFieldMapperTests.java | 342 ++++++++ .../index/mapper/FieldTypeTestCase.java | 6 +- 9 files changed, 1774 insertions(+), 45 deletions(-) create mode 100644 plugins/analysis-icu/src/main/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapper.java create mode 100644 plugins/analysis-icu/src/test/java/org/elasticsearch/index/mapper/CollationFieldTypeTests.java create mode 100644 plugins/analysis-icu/src/test/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapperIT.java create mode 100644 plugins/analysis-icu/src/test/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapperTests.java rename {core/src/test => test/framework/src/main}/java/org/elasticsearch/index/mapper/FieldTypeTestCase.java (99%) diff --git a/core/src/main/java/org/elasticsearch/index/mapper/StringFieldType.java b/core/src/main/java/org/elasticsearch/index/mapper/StringFieldType.java index a7d59fcfb4285..37834b93a1e0f 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/StringFieldType.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/StringFieldType.java @@ -57,7 +57,7 @@ public Query termsQuery(List values, QueryShardContext context) { } @Override - public final Query fuzzyQuery(Object value, Fuzziness fuzziness, int prefixLength, int maxExpansions, + public Query fuzzyQuery(Object value, Fuzziness fuzziness, int prefixLength, int maxExpansions, boolean transpositions) { failIfNotIndexed(); return new FuzzyQuery(new Term(name(), indexedValueForSearch(value)), @@ -65,7 +65,7 @@ public final Query fuzzyQuery(Object value, Fuzziness fuzziness, int prefixLengt } @Override - public final Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) { + public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) { failIfNotIndexed(); PrefixQuery query = new PrefixQuery(new Term(name(), indexedValueForSearch(value))); if (method != null) { @@ -75,7 +75,7 @@ public final Query prefixQuery(String value, MultiTermQuery.RewriteMethod method } @Override - public final Query regexpQuery(String value, int flags, int maxDeterminizedStates, + public Query regexpQuery(String value, int flags, int maxDeterminizedStates, MultiTermQuery.RewriteMethod method, QueryShardContext context) { failIfNotIndexed(); RegexpQuery query = new RegexpQuery(new Term(name(), indexedValueForSearch(value)), flags, maxDeterminizedStates); diff --git a/docs/plugins/analysis-icu.asciidoc b/docs/plugins/analysis-icu.asciidoc index fd322c112e697..d95766bb1902c 100644 --- a/docs/plugins/analysis-icu.asciidoc +++ b/docs/plugins/analysis-icu.asciidoc @@ -302,50 +302,46 @@ PUT icu_sample -------------------------------------------------- // CONSOLE + [[analysis-icu-collation]] ==== ICU Collation Token Filter +[WARNING] +====== +This token filter has been deprecated since Lucene 5.0. Please use +<>. +====== + +[[analysis-icu-collation-keyword-field]] +==== ICU Collation Keyword Field + Collations are used for sorting documents in a language-specific word order. -The `icu_collation` token filter is available to all indices and defaults to -using the -{defguide}/sorting-collations.html#uca[DUCET collation], +The `icu_collation_keyword` field type is available to all indices and will encode +the terms directly as bytes in a doc values field and a single indexed token just +like a standard {ref}/keyword.html[Keyword Field]. + +Defaults to using {defguide}/sorting-collations.html#uca[DUCET collation], which is a best-effort attempt at language-neutral sorting. Below is an example of how to set up a field for sorting German names in ``phonebook'' order: [source,js] --------------------------------------------------- -PUT /my_index +-------------------------- +PUT my_index { - "settings": { - "analysis": { - "filter": { - "german_phonebook": { - "type": "icu_collation", - "language": "de", - "country": "DE", - "variant": "@collation=phonebook" - } - }, - "analyzer": { - "german_phonebook": { - "tokenizer": "keyword", - "filter": [ "german_phonebook" ] - } - } - } - }, "mappings": { "user": { "properties": { - "name": { <1> + "name": { <1> "type": "text", "fields": { - "sort": { <2> - "type": "text", - "fielddata": true, - "analyzer": "german_phonebook" + "sort": { <2> + "type": "icu_collation_keyword", + "index": false, + "language": "de", + "country": "DE", + "variant": "@collation=phonebook" } } } @@ -364,15 +360,47 @@ GET _search <3> "sort": "name.sort" } --------------------------------------------------- +-------------------------- // CONSOLE <1> The `name` field uses the `standard` analyzer, and so support full text queries. -<2> The `name.sort` field uses the `keyword` analyzer to preserve the name as - a single token, and applies the `german_phonebook` token filter to index - the value in German phonebook sort order. +<2> The `name.sort` field is an `icu_collation_keyword` field that will preserve the name as + a single token doc values, and applies the German ``phonebook'' order. <3> An example query which searches the `name` field and sorts on the `name.sort` field. +==== Parameters for ICU Collation Keyword Fields + +The following parameters are accepted by `icu_collation_keyword` fields: + +[horizontal] + +`doc_values`:: + + Should the field be stored on disk in a column-stride fashion, so that it + can later be used for sorting, aggregations, or scripting? Accepts `true` + (default) or `false`. + +`index`:: + + Should the field be searchable? Accepts `true` (default) or `false`. + +`null_value`:: + + Accepts a string value which is substituted for any explicit `null` + values. Defaults to `null`, which means the field is treated as missing. + +`store`:: + + Whether the field value should be stored and retrievable separately from + the {ref}/mapping-source-field.html[`_source`] field. Accepts `true` or `false` + (default). + +`fields`:: + + Multi-fields allow the same string value to be indexed in multiple ways for + different purposes, such as one field for search and a multi-field for + sorting and aggregations. + ===== Collation options `strength`:: @@ -404,14 +432,14 @@ Possible values: `shifted` or `non-ignorable`. Sets the alternate handling for strength `quaternary` to be either shifted or non-ignorable. Which boils down to ignoring punctuation and whitespace. -`caseLevel`:: +`case_level`:: Possible values: `true` or `false` (default). Whether case level sorting is required. When strength is set to `primary` this will ignore accent differences. -`caseFirst`:: +`case_first`:: Possible values: `lower` or `upper`. Useful to control which case is sorted first when case is not ignored for strength `tertiary`. The default depends on @@ -424,11 +452,11 @@ according to their numeric representation. For example the value `egg-9` is sorted before the value `egg-21`. -`variableTop`:: +`variable_top`:: Single character or contraction. Controls what is variable for `alternate`. -`hiraganaQuaternaryMode`:: +`hiragana_quaternary_mode`:: Possible values: `true` or `false`. Distinguishing between Katakana and Hiragana characters in `quaternary` strength. diff --git a/plugins/analysis-icu/build.gradle b/plugins/analysis-icu/build.gradle index 9ed155b5fc4b8..53f2747c0a277 100644 --- a/plugins/analysis-icu/build.gradle +++ b/plugins/analysis-icu/build.gradle @@ -29,4 +29,4 @@ dependencies { dependencyLicenses { mapping from: /lucene-.*/, to: 'lucene' -} +} \ No newline at end of file diff --git a/plugins/analysis-icu/src/main/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapper.java b/plugins/analysis-icu/src/main/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapper.java new file mode 100644 index 0000000000000..408ad0a4543ac --- /dev/null +++ b/plugins/analysis-icu/src/main/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapper.java @@ -0,0 +1,746 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.index.mapper; + +import com.ibm.icu.text.Collator; +import com.ibm.icu.text.RawCollationKey; +import com.ibm.icu.text.RuleBasedCollator; +import com.ibm.icu.util.ULocale; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.SortedDocValuesField; +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.search.MultiTermQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.lucene.Lucene; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.Fuzziness; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.support.XContentMapValues; +import org.elasticsearch.index.analysis.IndexableBinaryStringTools; +import org.elasticsearch.index.fielddata.IndexFieldData; +import org.elasticsearch.index.fielddata.plain.DocValuesIndexFieldData; +import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.search.DocValueFormat; +import org.joda.time.DateTimeZone; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.LongSupplier; + +public class ICUCollationKeywordFieldMapper extends FieldMapper { + + public static final String CONTENT_TYPE = "icu_collation_keyword"; + + public static class Defaults { + public static final MappedFieldType FIELD_TYPE = new CollationFieldType(); + + static { + FIELD_TYPE.setTokenized(false); + FIELD_TYPE.setOmitNorms(true); + FIELD_TYPE.setIndexOptions(IndexOptions.DOCS); + FIELD_TYPE.freeze(); + } + + public static final String NULL_VALUE = null; + } + + public static final class CollationFieldType extends StringFieldType { + private Collator collator = null; + + public CollationFieldType() { + setIndexAnalyzer(Lucene.KEYWORD_ANALYZER); + setSearchAnalyzer(Lucene.KEYWORD_ANALYZER); + } + + protected CollationFieldType(CollationFieldType ref) { + super(ref); + this.collator = ref.collator; + } + + public CollationFieldType clone() { + return new CollationFieldType(this); + } + + @Override + public boolean equals(Object o) { + return super.equals(o) && Objects.equals(collator, ((CollationFieldType) o).collator); + } + + @Override + public void checkCompatibility(MappedFieldType otherFT, List conflicts, boolean strict) { + super.checkCompatibility(otherFT, conflicts, strict); + CollationFieldType other = (CollationFieldType) otherFT; + if (!Objects.equals(collator, other.collator)) { + conflicts.add("mapper [" + name() + "] has different [collator]"); + } + } + + @Override + public int hashCode() { + return 31 * super.hashCode() + Objects.hashCode(collator); + } + + @Override + public String typeName() { + return CONTENT_TYPE; + } + + public Collator collator() { + return collator; + } + + public void setCollator(Collator collator) { + checkIfFrozen(); + this.collator = collator.isFrozen() ? collator : collator.freeze(); + } + + @Override + public Query nullValueQuery() { + if (nullValue() == null) { + return null; + } + return termQuery(nullValue(), null); + } + + @Override + public IndexFieldData.Builder fielddataBuilder() { + failIfNoDocValues(); + return new DocValuesIndexFieldData.Builder(); + } + + @Override + protected BytesRef indexedValueForSearch(Object value) { + if (value == null) { + return null; + } + if (value instanceof BytesRef) { + value = ((BytesRef) value).utf8ToString(); + } + + if (collator != null) { + RawCollationKey key = collator.getRawCollationKey(value.toString(), null); + return new BytesRef(key.bytes, 0, key.size); + } else { + throw new IllegalStateException("collator is null"); + } + } + + @Override + public Query fuzzyQuery(Object value, Fuzziness fuzziness, int prefixLength, int maxExpansions, + boolean transpositions) { + throw new UnsupportedOperationException(); + } + + @Override + public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) { + throw new UnsupportedOperationException(); + } + + @Override + public Query regexpQuery(String value, int flags, int maxDeterminizedStates, + MultiTermQuery.RewriteMethod method, QueryShardContext context) { + throw new UnsupportedOperationException(); + } + + public static DocValueFormat COLLATE_FORMAT = new DocValueFormat() { + @Override + public String getWriteableName() { + return "collate"; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + } + + @Override + public String format(long value) { + throw new UnsupportedOperationException(); + } + + @Override + public String format(double value) { + throw new UnsupportedOperationException(); + } + + @Override + public String format(BytesRef value) { + int encodedLength = IndexableBinaryStringTools.getEncodedLength(value.bytes, value.offset, value.length); + char[] encoded = new char[encodedLength]; + IndexableBinaryStringTools.encode(value.bytes, value.offset, value.length, encoded, 0, encodedLength); + return new String(encoded, 0, encodedLength); + } + + @Override + public long parseLong(String value, boolean roundUp, LongSupplier now) { + throw new UnsupportedOperationException(); + } + + @Override + public double parseDouble(String value, boolean roundUp, LongSupplier now) { + throw new UnsupportedOperationException(); + } + + @Override + public BytesRef parseBytesRef(String value) { + char[] encoded = value.toCharArray(); + int decodedLength = IndexableBinaryStringTools.getDecodedLength(encoded, 0, encoded.length); + byte[] decoded = new byte[decodedLength]; + IndexableBinaryStringTools.decode(encoded, 0, encoded.length, decoded, 0, decodedLength); + return new BytesRef(decoded); + } + }; + + @Override + public DocValueFormat docValueFormat(final String format, final DateTimeZone timeZone) { + return COLLATE_FORMAT; + } + } + + public static class Builder extends FieldMapper.Builder { + private String rules = null; + private String language = null; + private String country = null; + private String variant = null; + private String strength = null; + private String decomposition = null; + private String alternate = null; + private boolean caseLevel = false; + private String caseFirst = null; + private boolean numeric = false; + private String variableTop = null; + private boolean hiraganaQuaternaryMode = false; + private String nullValue = Defaults.NULL_VALUE; + + public Builder(String name) { + super(name, Defaults.FIELD_TYPE, Defaults.FIELD_TYPE); + builder = this; + } + + @Override + public CollationFieldType fieldType() { + return (CollationFieldType) super.fieldType(); + } + + @Override + public Builder indexOptions(IndexOptions indexOptions) { + if (indexOptions.compareTo(IndexOptions.DOCS_AND_FREQS) > 0) { + throw new IllegalArgumentException("The [" + CONTENT_TYPE + "] field does not support positions, got [index_options]=" + + indexOptionToString(indexOptions)); + } + + return super.indexOptions(indexOptions); + } + + public String rules() { + return rules; + } + + public Builder rules(final String rules) { + this.rules = rules; + return this; + } + + public String language() { + return language; + } + + public Builder language(final String language) { + this.language = language; + return this; + } + + public String country() { + return country; + } + + public Builder country(final String country) { + this.country = country; + return this; + } + + public String variant() { + return variant; + } + + public Builder variant(final String variant) { + this.variant = variant; + return this; + } + + public String strength() { + return strength; + } + + public Builder strength(final String strength) { + this.strength = strength; + return this; + } + + public String decomposition() { + return decomposition; + } + + public Builder decomposition(final String decomposition) { + this.decomposition = decomposition; + return this; + } + + public String alternate() { + return alternate; + } + + public Builder alternate(final String alternate) { + this.alternate = alternate; + return this; + } + + public boolean caseLevel() { + return caseLevel; + } + + public Builder caseLevel(final boolean caseLevel) { + this.caseLevel = caseLevel; + return this; + } + + public String caseFirst() { + return caseFirst; + } + + public Builder caseFirst(final String caseFirst) { + this.caseFirst = caseFirst; + return this; + } + + public boolean numeric() { + return numeric; + } + + public Builder numeric(final boolean numeric) { + this.numeric = numeric; + return this; + } + + public String variableTop() { + return variableTop; + } + + public Builder variableTop(final String variableTop) { + this.variableTop = variableTop; + return this; + } + + public boolean hiraganaQuaternaryMode() { + return hiraganaQuaternaryMode; + } + + public Builder hiraganaQuaternaryMode(final boolean hiraganaQuaternaryMode) { + this.hiraganaQuaternaryMode = hiraganaQuaternaryMode; + return this; + } + + public Collator buildCollator() { + Collator collator; + if (rules != null) { + try { + collator = new RuleBasedCollator(rules); + } catch (Exception e) { + throw new IllegalArgumentException("Failed to parse collation rules", e); + } + } else { + if (language != null) { + ULocale locale; + if (country != null) { + if (variant != null) { + locale = new ULocale(language, country, variant); + } else { + locale = new ULocale(language, country); + } + } else { + locale = new ULocale(language); + } + collator = Collator.getInstance(locale); + } else { + collator = Collator.getInstance(); + } + } + + // set the strength flag, otherwise it will be the default. + if (strength != null) { + if (strength.equalsIgnoreCase("primary")) { + collator.setStrength(Collator.PRIMARY); + } else if (strength.equalsIgnoreCase("secondary")) { + collator.setStrength(Collator.SECONDARY); + } else if (strength.equalsIgnoreCase("tertiary")) { + collator.setStrength(Collator.TERTIARY); + } else if (strength.equalsIgnoreCase("quaternary")) { + collator.setStrength(Collator.QUATERNARY); + } else if (strength.equalsIgnoreCase("identical")) { + collator.setStrength(Collator.IDENTICAL); + } else { + throw new IllegalArgumentException("Invalid strength: " + strength); + } + } + + // set the decomposition flag, otherwise it will be the default. + if (decomposition != null) { + if (decomposition.equalsIgnoreCase("no")) { + collator.setDecomposition(Collator.NO_DECOMPOSITION); + } else if (decomposition.equalsIgnoreCase("canonical")) { + collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION); + } else { + throw new IllegalArgumentException("Invalid decomposition: " + decomposition); + } + } + + // expert options: concrete subclasses are always a RuleBasedCollator + RuleBasedCollator rbc = (RuleBasedCollator) collator; + if (alternate != null) { + if (alternate.equalsIgnoreCase("shifted")) { + rbc.setAlternateHandlingShifted(true); + } else if (alternate.equalsIgnoreCase("non-ignorable")) { + rbc.setAlternateHandlingShifted(false); + } else { + throw new IllegalArgumentException("Invalid alternate: " + alternate); + } + } + + if (caseLevel) { + rbc.setCaseLevel(true); + } + + if (caseFirst != null) { + if (caseFirst.equalsIgnoreCase("lower")) { + rbc.setLowerCaseFirst(true); + } else if (caseFirst.equalsIgnoreCase("upper")) { + rbc.setUpperCaseFirst(true); + } else { + throw new IllegalArgumentException("Invalid caseFirst: " + caseFirst); + } + } + + if (numeric) { + rbc.setNumericCollation(true); + } + + if (variableTop != null) { + rbc.setVariableTop(variableTop); + } + + if (hiraganaQuaternaryMode) { + rbc.setHiraganaQuaternary(true); + } + + // freeze so thread-safe + return collator.freeze(); + } + + @Override + public ICUCollationKeywordFieldMapper build(BuilderContext context) { + final Collator collator = buildCollator(); + fieldType().setCollator(collator); + setupFieldType(context); + return new ICUCollationKeywordFieldMapper(name, fieldType, defaultFieldType, context.indexSettings(), + multiFieldsBuilder.build(this, context), copyTo, rules, language, country, variant, strength, decomposition, + alternate, caseLevel, caseFirst, numeric, variableTop, hiraganaQuaternaryMode, collator); + } + } + + public static class TypeParser implements Mapper.TypeParser { + @Override + public Mapper.Builder parse(String name, Map node, ParserContext parserContext) + throws MapperParsingException { + Builder builder = new Builder(name); + TypeParsers.parseField(builder, name, node, parserContext); + for (Iterator> iterator = node.entrySet().iterator(); iterator.hasNext(); ) { + Map.Entry entry = iterator.next(); + String fieldName = entry.getKey(); + Object fieldNode = entry.getValue(); + switch (fieldName) { + case "null_value": + if (fieldNode == null) { + throw new MapperParsingException("Property [null_value] cannot be null."); + } + builder.nullValue(fieldNode.toString()); + iterator.remove(); + break; + case "norms": + builder.omitNorms(!XContentMapValues.nodeBooleanValue(fieldNode, "norms")); + iterator.remove(); + break; + case "rules": + builder.rules(XContentMapValues.nodeStringValue(fieldNode, null)); + iterator.remove(); + break; + case "language": + builder.language(XContentMapValues.nodeStringValue(fieldNode, null)); + iterator.remove(); + break; + case "country": + builder.country(XContentMapValues.nodeStringValue(fieldNode, null)); + iterator.remove(); + break; + case "variant": + builder.variant(XContentMapValues.nodeStringValue(fieldNode, null)); + iterator.remove(); + break; + case "strength": + builder.strength(XContentMapValues.nodeStringValue(fieldNode, null)); + iterator.remove(); + break; + case "decomposition": + builder.decomposition(XContentMapValues.nodeStringValue(fieldNode, null)); + iterator.remove(); + break; + case "alternate": + builder.alternate(XContentMapValues.nodeStringValue(fieldNode, null)); + iterator.remove(); + break; + case "case_level": + builder.caseLevel(XContentMapValues.nodeBooleanValue(fieldNode, false)); + iterator.remove(); + break; + case "case_first": + builder.caseFirst(XContentMapValues.nodeStringValue(fieldNode, null)); + iterator.remove(); + break; + case "numeric": + builder.numeric(XContentMapValues.nodeBooleanValue(fieldNode, false)); + iterator.remove(); + break; + case "variable_top": + builder.variableTop(XContentMapValues.nodeStringValue(fieldNode, null)); + iterator.remove(); + break; + case "hiragana_quaternary_mode": + builder.hiraganaQuaternaryMode(XContentMapValues.nodeBooleanValue(fieldNode, false)); + iterator.remove(); + break; + default: + break; + } + } + + return builder; + } + } + + private final String rules; + private final String language; + private final String country; + private final String variant; + private final String strength; + private final String decomposition; + private final String alternate; + private final boolean caseLevel; + private final String caseFirst; + private final boolean numeric; + private final String variableTop; + private final boolean hiraganaQuaternaryMode; + private final Collator collator; + + protected ICUCollationKeywordFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType, + Settings indexSettings, MultiFields multiFields, CopyTo copyTo, String rules, String language, + String country, String variant, + String strength, String decomposition, String alternate, boolean caseLevel, String caseFirst, + boolean numeric, String variableTop, boolean hiraganaQuaternaryMode, Collator collator) { + super(simpleName, fieldType, defaultFieldType, indexSettings, multiFields, copyTo); + assert collator.isFrozen(); + this.rules = rules; + this.language = language; + this.country = country; + this.variant = variant; + this.strength = strength; + this.decomposition = decomposition; + this.alternate = alternate; + this.caseLevel = caseLevel; + this.caseFirst = caseFirst; + this.numeric = numeric; + this.variableTop = variableTop; + this.hiraganaQuaternaryMode = hiraganaQuaternaryMode; + this.collator = collator; + } + + @Override + public CollationFieldType fieldType() { + return (CollationFieldType) super.fieldType(); + } + + @Override + protected String contentType() { + return CONTENT_TYPE; + } + + @Override + protected void doMerge(Mapper mergeWith, boolean updateAllTypes) { + super.doMerge(mergeWith, updateAllTypes); + + List conflicts = new ArrayList<>(); + ICUCollationKeywordFieldMapper icuMergeWith = (ICUCollationKeywordFieldMapper) mergeWith; + + if (!Objects.equals(rules, icuMergeWith.rules)) { + conflicts.add("Cannot update rules setting for [" + CONTENT_TYPE + "]"); + } + + if (!Objects.equals(language, icuMergeWith.language)) { + conflicts.add("Cannot update language setting for [" + CONTENT_TYPE + "]"); + } + + if (!Objects.equals(country, icuMergeWith.country)) { + conflicts.add("Cannot update country setting for [" + CONTENT_TYPE + "]"); + } + + if (!Objects.equals(variant, icuMergeWith.variant)) { + conflicts.add("Cannot update variant setting for [" + CONTENT_TYPE + "]"); + } + + if (!Objects.equals(strength, icuMergeWith.strength)) { + conflicts.add("Cannot update strength setting for [" + CONTENT_TYPE + "]"); + } + + if (!Objects.equals(decomposition, icuMergeWith.decomposition)) { + conflicts.add("Cannot update decomposition setting for [" + CONTENT_TYPE + "]"); + } + + if (!Objects.equals(alternate, icuMergeWith.alternate)) { + conflicts.add("Cannot update alternate setting for [" + CONTENT_TYPE + "]"); + } + + if (caseLevel != icuMergeWith.caseLevel) { + conflicts.add("Cannot update case_level setting for [" + CONTENT_TYPE + "]"); + } + + if (!Objects.equals(caseFirst, icuMergeWith.caseFirst)) { + conflicts.add("Cannot update case_first setting for [" + CONTENT_TYPE + "]"); + } + + if (numeric != icuMergeWith.numeric) { + conflicts.add("Cannot update numeric setting for [" + CONTENT_TYPE + "]"); + } + + if (!Objects.equals(variableTop, icuMergeWith.variableTop)) { + conflicts.add("Cannot update variable_top setting for [" + CONTENT_TYPE + "]"); + } + + if (hiraganaQuaternaryMode != icuMergeWith.hiraganaQuaternaryMode) { + conflicts.add("Cannot update hiragana_quaternary_mode setting for [" + CONTENT_TYPE + "]"); + } + + if (!conflicts.isEmpty()) { + throw new IllegalArgumentException("Can't merge because of conflicts: " + conflicts); + } + } + + @Override + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + super.doXContentBody(builder, includeDefaults, params); + + if (includeDefaults || fieldType().nullValue() != null) { + builder.field("null_value", fieldType().nullValue()); + } + + if (includeDefaults || rules != null) { + builder.field("rules", rules); + } + + if (includeDefaults || language != null) { + builder.field("language", language); + } + + if (includeDefaults || country != null) { + builder.field("country", country); + } + + if (includeDefaults || variant != null) { + builder.field("variant", variant); + } + + if (includeDefaults || strength != null) { + builder.field("strength", strength); + } + + if (includeDefaults || decomposition != null) { + builder.field("decomposition", decomposition); + } + + if (includeDefaults || alternate != null) { + builder.field("alternate", alternate); + } + + if (includeDefaults || caseLevel) { + builder.field("case_level", caseLevel); + } + + if (includeDefaults || caseFirst != null) { + builder.field("case_first", caseFirst); + } + + if (includeDefaults || numeric) { + builder.field("numeric", numeric); + } + + if (includeDefaults || variableTop != null) { + builder.field("variable_top", variableTop); + } + + if (includeDefaults || hiraganaQuaternaryMode) { + builder.field("hiragana_quaternary_mode", hiraganaQuaternaryMode); + } + } + + @Override + protected void parseCreateField(ParseContext context, List fields) throws IOException { + final String value; + if (context.externalValueSet()) { + value = context.externalValue().toString(); + } else { + XContentParser parser = context.parser(); + if (parser.currentToken() == XContentParser.Token.VALUE_NULL) { + value = fieldType().nullValueAsString(); + } else { + value = parser.textOrNull(); + } + } + + if (value == null) { + return; + } + + RawCollationKey key = collator.getRawCollationKey(value, null); + final BytesRef binaryValue = new BytesRef(key.bytes, 0, key.size); + + if (fieldType().indexOptions() != IndexOptions.NONE || fieldType().stored()) { + Field field = new Field(fieldType().name(), binaryValue, fieldType()); + fields.add(field); + } + + if (fieldType().hasDocValues()) { + fields.add(new SortedDocValuesField(fieldType().name(), binaryValue)); + } + } +} diff --git a/plugins/analysis-icu/src/main/java/org/elasticsearch/plugin/analysis/icu/AnalysisICUPlugin.java b/plugins/analysis-icu/src/main/java/org/elasticsearch/plugin/analysis/icu/AnalysisICUPlugin.java index 059dabb4f4625..58ebdc8e2a801 100644 --- a/plugins/analysis-icu/src/main/java/org/elasticsearch/plugin/analysis/icu/AnalysisICUPlugin.java +++ b/plugins/analysis-icu/src/main/java/org/elasticsearch/plugin/analysis/icu/AnalysisICUPlugin.java @@ -19,6 +19,9 @@ package org.elasticsearch.plugin.analysis.icu; +import static java.util.Collections.singletonMap; + +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.index.analysis.CharFilterFactory; import org.elasticsearch.index.analysis.IcuCollationTokenFilterFactory; import org.elasticsearch.index.analysis.IcuFoldingTokenFilterFactory; @@ -28,16 +31,20 @@ import org.elasticsearch.index.analysis.IcuTransformTokenFilterFactory; import org.elasticsearch.index.analysis.TokenFilterFactory; import org.elasticsearch.index.analysis.TokenizerFactory; +import org.elasticsearch.index.mapper.ICUCollationKeywordFieldMapper; +import org.elasticsearch.index.mapper.Mapper; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; import org.elasticsearch.plugins.AnalysisPlugin; +import org.elasticsearch.plugins.MapperPlugin; import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.search.DocValueFormat; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; -import static java.util.Collections.singletonMap; - -public class AnalysisICUPlugin extends Plugin implements AnalysisPlugin { +public class AnalysisICUPlugin extends Plugin implements AnalysisPlugin, MapperPlugin { @Override public Map> getCharFilters() { return singletonMap("icu_normalizer", IcuNormalizerCharFilterFactory::new); @@ -57,4 +64,20 @@ public Map> getTokenFilters() { public Map> getTokenizers() { return singletonMap("icu_tokenizer", IcuTokenizerFactory::new); } + + @Override + public Map getMappers() { + return Collections.singletonMap(ICUCollationKeywordFieldMapper.CONTENT_TYPE, new ICUCollationKeywordFieldMapper.TypeParser()); + } + + @Override + public List getNamedWriteables() { + return Collections.singletonList( + new NamedWriteableRegistry.Entry( + DocValueFormat.class, + ICUCollationKeywordFieldMapper.CollationFieldType.COLLATE_FORMAT.getWriteableName(), + in -> ICUCollationKeywordFieldMapper.CollationFieldType.COLLATE_FORMAT + ) + ); + } } diff --git a/plugins/analysis-icu/src/test/java/org/elasticsearch/index/mapper/CollationFieldTypeTests.java b/plugins/analysis-icu/src/test/java/org/elasticsearch/index/mapper/CollationFieldTypeTests.java new file mode 100644 index 0000000000000..94634fc79c893 --- /dev/null +++ b/plugins/analysis-icu/src/test/java/org/elasticsearch/index/mapper/CollationFieldTypeTests.java @@ -0,0 +1,145 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.index.mapper; + +import com.carrotsearch.randomizedtesting.generators.RandomStrings; +import com.ibm.icu.text.Collator; +import com.ibm.icu.text.RawCollationKey; +import com.ibm.icu.util.ULocale; +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.TermInSetQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.TermRangeQuery; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.unit.Fuzziness; +import org.elasticsearch.index.mapper.ICUCollationKeywordFieldMapper.CollationFieldType; +import org.elasticsearch.index.mapper.MappedFieldType.Relation; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CollationFieldTypeTests extends FieldTypeTestCase { + @Override + protected MappedFieldType createDefaultFieldType() { + return new CollationFieldType(); + } + + public void testIsFieldWithinQuery() throws IOException { + CollationFieldType ft = new CollationFieldType(); + // current impl ignores args and shourd always return INTERSECTS + assertEquals(Relation.INTERSECTS, ft.isFieldWithinQuery(null, + RandomStrings.randomAsciiOfLengthBetween(random(), 0, 5), + RandomStrings.randomAsciiOfLengthBetween(random(), 0, 5), + randomBoolean(), randomBoolean(), null, null, null)); + } + + public void testTermQuery() { + MappedFieldType ft = createDefaultFieldType(); + ft.setName("field"); + ft.setIndexOptions(IndexOptions.DOCS); + + Collator collator = Collator.getInstance(new ULocale("tr")); + collator.setStrength(Collator.PRIMARY); + collator.freeze(); + ((CollationFieldType) ft).setCollator(collator); + + RawCollationKey key = collator.getRawCollationKey("ı will use turkish casıng", null); + BytesRef expected = new BytesRef(key.bytes, 0, key.size); + + assertEquals(new TermQuery(new Term("field", expected)), ft.termQuery("I WİLL USE TURKİSH CASING", null)); + + ft.setIndexOptions(IndexOptions.NONE); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> ft.termQuery("bar", null)); + assertEquals("Cannot search on field [field] since it is not indexed.", e.getMessage()); + } + + public void testTermsQuery() { + MappedFieldType ft = createDefaultFieldType(); + ft.setName("field"); + ft.setIndexOptions(IndexOptions.DOCS); + + Collator collator = Collator.getInstance().freeze(); + ((CollationFieldType) ft).setCollator(collator); + + RawCollationKey fooKey = collator.getRawCollationKey("foo", null); + RawCollationKey barKey = collator.getRawCollationKey("bar", null); + + List terms = new ArrayList<>(); + terms.add(new BytesRef(fooKey.bytes, 0, fooKey.size)); + terms.add(new BytesRef(barKey.bytes, 0, barKey.size)); + + assertEquals(new TermInSetQuery("field", terms), + ft.termsQuery(Arrays.asList("foo", "bar"), null)); + + ft.setIndexOptions(IndexOptions.NONE); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> ft.termsQuery(Arrays.asList("foo", "bar"), null)); + assertEquals("Cannot search on field [field] since it is not indexed.", e.getMessage()); + } + + public void testRegexpQuery() { + MappedFieldType ft = createDefaultFieldType(); + ft.setName("field"); + ft.setIndexOptions(IndexOptions.DOCS); + expectThrows(UnsupportedOperationException.class, + () -> ft.regexpQuery("foo.*", 0, 10, null, null)); + } + + public void testFuzzyQuery() { + MappedFieldType ft = createDefaultFieldType(); + ft.setName("field"); + ft.setIndexOptions(IndexOptions.DOCS); + expectThrows(UnsupportedOperationException.class, + () -> ft.fuzzyQuery("foo", Fuzziness.fromEdits(2), 1, 50, true)); + } + + public void testPrefixQuery() { + MappedFieldType ft = createDefaultFieldType(); + ft.setName("field"); + ft.setIndexOptions(IndexOptions.DOCS); + expectThrows(UnsupportedOperationException.class, + () -> ft.prefixQuery("prefix", null, null)); + } + + public void testRangeQuery() { + MappedFieldType ft = createDefaultFieldType(); + ft.setName("field"); + ft.setIndexOptions(IndexOptions.DOCS); + + Collator collator = Collator.getInstance().freeze(); + ((CollationFieldType) ft).setCollator(collator); + + RawCollationKey aKey = collator.getRawCollationKey("a", null); + RawCollationKey bKey = collator.getRawCollationKey("b", null); + + TermRangeQuery expected = new TermRangeQuery("field", new BytesRef(aKey.bytes, 0, aKey.size), + new BytesRef(bKey.bytes, 0, bKey.size), false, false); + + assertEquals(expected, ft.rangeQuery("a", "b", false, false, null)); + + ft.setIndexOptions(IndexOptions.NONE); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> ft.rangeQuery("a", "b", false, false, null)); + assertEquals("Cannot search on field [field] since it is not indexed.", e.getMessage()); + } +} diff --git a/plugins/analysis-icu/src/test/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapperIT.java b/plugins/analysis-icu/src/test/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapperIT.java new file mode 100644 index 0000000000000..8a6e9b49ac974 --- /dev/null +++ b/plugins/analysis-icu/src/test/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapperIT.java @@ -0,0 +1,443 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.index.mapper; + +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertOrderedSearchHits; + +import com.ibm.icu.text.Collator; +import com.ibm.icu.text.RuleBasedCollator; +import com.ibm.icu.util.ULocale; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.plugin.analysis.icu.AnalysisICUPlugin; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.sort.SortOrder; +import org.elasticsearch.test.ESIntegTestCase; + +import java.util.Collection; +import java.util.Collections; + +public class ICUCollationKeywordFieldMapperIT extends ESIntegTestCase { + + @Override + protected Collection> nodePlugins() { + return Collections.singletonList(AnalysisICUPlugin.class); + } + + /* + * Turkish has some funny casing. + * This test shows how you can solve this kind of thing easily with collation. + * Instead of using LowerCaseFilter, use a turkish collator with primary strength. + * Then things will sort and match correctly. + */ + public void testBasicUsage() throws Exception { + String index = "foo"; + String type = "mytype"; + + String[] equilavent = {"I WİLL USE TURKİSH CASING", "ı will use turkish casıng"}; + + XContentBuilder builder = jsonBuilder() + .startObject().startObject("properties") + .startObject("collate") + .field("type", "icu_collation_keyword") + .field("language", "tr") + .field("strength", "primary") + .endObject() + .endObject().endObject(); + + assertAcked(client().admin().indices().prepareCreate(index).addMapping(type, builder)); + + // both values should collate to same value + indexRandom(true, + client().prepareIndex(index, type, "1").setSource("{\"collate\":\"" + equilavent[0] + "\"}", XContentType.JSON), + client().prepareIndex(index, type, "2").setSource("{\"collate\":\"" + equilavent[1] + "\"}", XContentType.JSON) + ); + + // searching for either of the terms should return both results since they collate to the same value + SearchRequest request = new SearchRequest() + .indices(index) + .types(type) + .source(new SearchSourceBuilder() + .fetchSource(false) + .query(QueryBuilders.termQuery("collate", randomBoolean() ? equilavent[0] : equilavent[1])) + .sort("collate") + .sort("_uid", SortOrder.DESC) // secondary sort should kick in because both will collate to same value + ); + + SearchResponse response = client().search(request).actionGet(); + assertNoFailures(response); + assertHitCount(response, 2L); + assertOrderedSearchHits(response, "2", "1"); + } + + /* + * Test usage of the decomposition option for unicode normalization. + */ + public void testNormalization() throws Exception { + String index = "foo"; + String type = "mytype"; + + String[] equilavent = {"I W\u0049\u0307LL USE TURKİSH CASING", "ı will use turkish casıng"}; + + XContentBuilder builder = jsonBuilder() + .startObject().startObject("properties") + .startObject("collate") + .field("type", "icu_collation_keyword") + .field("language", "tr") + .field("strength", "primary") + .field("decomposition", "canonical") + .endObject() + .endObject().endObject(); + + assertAcked(client().admin().indices().prepareCreate(index).addMapping(type, builder)); + + indexRandom(true, + client().prepareIndex(index, type, "1").setSource("{\"collate\":\"" + equilavent[0] + "\"}", XContentType.JSON), + client().prepareIndex(index, type, "2").setSource("{\"collate\":\"" + equilavent[1] + "\"}", XContentType.JSON) + ); + + // searching for either of the terms should return both results since they collate to the same value + SearchRequest request = new SearchRequest() + .indices(index) + .types(type) + .source(new SearchSourceBuilder() + .fetchSource(false) + .query(QueryBuilders.termQuery("collate", randomBoolean() ? equilavent[0] : equilavent[1])) + .sort("collate") + .sort("_uid", SortOrder.DESC) // secondary sort should kick in because both will collate to same value + ); + + SearchResponse response = client().search(request).actionGet(); + assertNoFailures(response); + assertHitCount(response, 2L); + assertOrderedSearchHits(response, "2", "1"); + } + + /* + * Test secondary strength, for english case is not significant. + */ + public void testSecondaryStrength() throws Exception { + String index = "foo"; + String type = "mytype"; + + String[] equilavent = {"TESTING", "testing"}; + + XContentBuilder builder = jsonBuilder() + .startObject().startObject("properties") + .startObject("collate") + .field("type", "icu_collation_keyword") + .field("language", "en") + .field("strength", "secondary") + .field("decomposition", "no") + .endObject() + .endObject().endObject(); + + assertAcked(client().admin().indices().prepareCreate(index).addMapping(type, builder)); + + indexRandom(true, + client().prepareIndex(index, type, "1").setSource("{\"collate\":\"" + equilavent[0] + "\"}", XContentType.JSON), + client().prepareIndex(index, type, "2").setSource("{\"collate\":\"" + equilavent[1] + "\"}", XContentType.JSON) + ); + + SearchRequest request = new SearchRequest() + .indices(index) + .types(type) + .source(new SearchSourceBuilder() + .fetchSource(false) + .query(QueryBuilders.termQuery("collate", randomBoolean() ? equilavent[0] : equilavent[1])) + .sort("collate") + .sort("_uid", SortOrder.DESC) // secondary sort should kick in because both will collate to same value + ); + + SearchResponse response = client().search(request).actionGet(); + assertNoFailures(response); + assertHitCount(response, 2L); + assertOrderedSearchHits(response, "2", "1"); + } + + /* + * Setting alternate=shifted to shift whitespace, punctuation and symbols + * to quaternary level + */ + public void testIgnorePunctuation() throws Exception { + String index = "foo"; + String type = "mytype"; + + String[] equilavent = {"foo-bar", "foo bar"}; + + XContentBuilder builder = jsonBuilder() + .startObject().startObject("properties") + .startObject("collate") + .field("type", "icu_collation_keyword") + .field("language", "en") + .field("strength", "primary") + .field("alternate", "shifted") + .endObject() + .endObject().endObject(); + + assertAcked(client().admin().indices().prepareCreate(index).addMapping(type, builder)); + + indexRandom(true, + client().prepareIndex(index, type, "1").setSource("{\"collate\":\"" + equilavent[0] + "\"}", XContentType.JSON), + client().prepareIndex(index, type, "2").setSource("{\"collate\":\"" + equilavent[1] + "\"}", XContentType.JSON) + ); + + SearchRequest request = new SearchRequest() + .indices(index) + .types(type) + .source(new SearchSourceBuilder() + .fetchSource(false) + .query(QueryBuilders.termQuery("collate", randomBoolean() ? equilavent[0] : equilavent[1])) + .sort("collate") + .sort("_uid", SortOrder.DESC) // secondary sort should kick in because both will collate to same value + ); + + SearchResponse response = client().search(request).actionGet(); + assertNoFailures(response); + assertHitCount(response, 2L); + assertOrderedSearchHits(response, "2", "1"); + } + + /* + * Setting alternate=shifted and variableTop to shift whitespace, but not + * punctuation or symbols, to quaternary level + */ + public void testIgnoreWhitespace() throws Exception { + String index = "foo"; + String type = "mytype"; + + XContentBuilder builder = jsonBuilder() + .startObject().startObject("properties") + .startObject("collate") + .field("type", "icu_collation_keyword") + .field("language", "en") + .field("strength", "primary") + .field("alternate", "shifted") + .field("variable_top", " ") + .field("index", false) + .endObject() + .endObject().endObject(); + + assertAcked(client().admin().indices().prepareCreate(index).addMapping(type, builder)); + + indexRandom(true, + client().prepareIndex(index, type, "1").setSource("{\"collate\":\"foo bar\"}", XContentType.JSON), + client().prepareIndex(index, type, "2").setSource("{\"collate\":\"foobar\"}", XContentType.JSON), + client().prepareIndex(index, type, "3").setSource("{\"collate\":\"foo-bar\"}", XContentType.JSON) + ); + + SearchRequest request = new SearchRequest() + .indices(index) + .types(type) + .source(new SearchSourceBuilder() + .fetchSource(false) + .sort("collate", SortOrder.ASC) + .sort("_uid", SortOrder.ASC) // secondary sort should kick in on docs 1 and 3 because same value collate value + ); + + SearchResponse response = client().search(request).actionGet(); + assertNoFailures(response); + assertHitCount(response, 3L); + assertOrderedSearchHits(response, "3", "1", "2"); + } + + /* + * Setting numeric to encode digits with numeric value, so that + * foobar-9 sorts before foobar-10 + */ + public void testNumerics() throws Exception { + String index = "foo"; + String type = "mytype"; + + XContentBuilder builder = jsonBuilder() + .startObject().startObject("properties") + .startObject("collate") + .field("type", "icu_collation_keyword") + .field("language", "en") + .field("numeric", true) + .field("index", false) + .endObject() + .endObject().endObject(); + + assertAcked(client().admin().indices().prepareCreate(index).addMapping(type, builder)); + + indexRandom(true, + client().prepareIndex(index, type, "1").setSource("{\"collate\":\"foobar-10\"}", XContentType.JSON), + client().prepareIndex(index, type, "2").setSource("{\"collate\":\"foobar-9\"}", XContentType.JSON) + ); + + SearchRequest request = new SearchRequest() + .indices(index) + .types(type) + .source(new SearchSourceBuilder() + .fetchSource(false) + .sort("collate", SortOrder.ASC) + ); + + SearchResponse response = client().search(request).actionGet(); + assertNoFailures(response); + assertHitCount(response, 2L); + assertOrderedSearchHits(response, "2", "1"); + } + + /* + * Setting caseLevel=true to create an additional case level between + * secondary and tertiary + */ + public void testIgnoreAccentsButNotCase() throws Exception { + String index = "foo"; + String type = "mytype"; + + XContentBuilder builder = jsonBuilder() + .startObject().startObject("properties") + .startObject("collate") + .field("type", "icu_collation_keyword") + .field("language", "en") + .field("strength", "primary") + .field("case_level", true) + .field("index", false) + .endObject() + .endObject().endObject(); + + assertAcked(client().admin().indices().prepareCreate(index).addMapping(type, builder)); + + indexRandom(true, + client().prepareIndex(index, type, "1").setSource("{\"collate\":\"résumé\"}", XContentType.JSON), + client().prepareIndex(index, type, "2").setSource("{\"collate\":\"Resume\"}", XContentType.JSON), + client().prepareIndex(index, type, "3").setSource("{\"collate\":\"resume\"}", XContentType.JSON), + client().prepareIndex(index, type, "4").setSource("{\"collate\":\"Résumé\"}", XContentType.JSON) + ); + + SearchRequest request = new SearchRequest() + .indices(index) + .types(type) + .source(new SearchSourceBuilder() + .fetchSource(false) + .sort("collate", SortOrder.ASC) + .sort("_uid", SortOrder.DESC) + ); + + SearchResponse response = client().search(request).actionGet(); + assertNoFailures(response); + assertHitCount(response, 4L); + assertOrderedSearchHits(response, "3", "1", "4", "2"); + } + + /* + * Setting caseFirst=upper to cause uppercase strings to sort + * before lowercase ones. + */ + public void testUpperCaseFirst() throws Exception { + String index = "foo"; + String type = "mytype"; + + XContentBuilder builder = jsonBuilder() + .startObject().startObject("properties") + .startObject("collate") + .field("type", "icu_collation_keyword") + .field("language", "en") + .field("strength", "tertiary") + .field("case_first", "upper") + .field("index", false) + .endObject() + .endObject().endObject(); + + assertAcked(client().admin().indices().prepareCreate(index).addMapping(type, builder)); + + indexRandom(true, + client().prepareIndex(index, type, "1").setSource("{\"collate\":\"resume\"}", XContentType.JSON), + client().prepareIndex(index, type, "2").setSource("{\"collate\":\"Resume\"}", XContentType.JSON) + ); + + SearchRequest request = new SearchRequest() + .indices(index) + .types(type) + .source(new SearchSourceBuilder() + .fetchSource(false) + .sort("collate", SortOrder.ASC) + ); + + SearchResponse response = client().search(request).actionGet(); + assertNoFailures(response); + assertHitCount(response, 2L); + assertOrderedSearchHits(response, "2", "1"); + } + + /* + * For german, you might want oe to sort and match with o umlaut. + * This is not the default, but you can make a customized ruleset to do this. + * + * The default is DIN 5007-1, this shows how to tailor a collator to get DIN 5007-2 behavior. + * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4423383 + */ + public void testCustomRules() throws Exception { + String index = "foo"; + String type = "mytype"; + + RuleBasedCollator baseCollator = (RuleBasedCollator) Collator.getInstance(new ULocale("de_DE")); + String DIN5007_2_tailorings = + "& ae , a\u0308 & AE , A\u0308" + + "& oe , o\u0308 & OE , O\u0308" + + "& ue , u\u0308 & UE , u\u0308"; + + RuleBasedCollator tailoredCollator = new RuleBasedCollator(baseCollator.getRules() + DIN5007_2_tailorings); + String tailoredRules = tailoredCollator.getRules(); + + String[] equilavent = {"Töne", "Toene"}; + + XContentBuilder builder = jsonBuilder() + .startObject().startObject("properties") + .startObject("collate") + .field("type", "icu_collation_keyword") + .field("rules", tailoredRules) + .field("strength", "primary") + .endObject() + .endObject().endObject(); + + assertAcked(client().admin().indices().prepareCreate(index).addMapping(type, builder)); + + indexRandom(true, + client().prepareIndex(index, type, "1").setSource("{\"collate\":\"" + equilavent[0] + "\"}", XContentType.JSON), + client().prepareIndex(index, type, "2").setSource("{\"collate\":\"" + equilavent[1] + "\"}", XContentType.JSON) + ); + + SearchRequest request = new SearchRequest() + .indices(index) + .types(type) + .source(new SearchSourceBuilder() + .fetchSource(false) + .query(QueryBuilders.termQuery("collate", randomBoolean() ? equilavent[0] : equilavent[1])) + .sort("collate", SortOrder.ASC) + .sort("_uid", SortOrder.DESC) // secondary sort should kick in because both will collate to same value + ); + + SearchResponse response = client().search(request).actionGet(); + assertNoFailures(response); + assertHitCount(response, 2L); + assertOrderedSearchHits(response, "2", "1"); + } +} diff --git a/plugins/analysis-icu/src/test/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapperTests.java b/plugins/analysis-icu/src/test/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapperTests.java new file mode 100644 index 0000000000000..ebe909837e999 --- /dev/null +++ b/plugins/analysis-icu/src/test/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapperTests.java @@ -0,0 +1,342 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.index.mapper; + +import static org.hamcrest.Matchers.equalTo; + +import com.ibm.icu.text.Collator; +import com.ibm.icu.text.RawCollationKey; +import com.ibm.icu.util.ULocale; +import org.apache.lucene.index.DocValuesType; +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.IndexableFieldType; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.compress.CompressedXContent; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.IndexService; +import org.elasticsearch.index.mapper.MapperService.MergeReason; +import org.elasticsearch.plugin.analysis.icu.AnalysisICUPlugin; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.test.ESSingleNodeTestCase; +import org.elasticsearch.test.InternalSettingsPlugin; +import org.junit.Before; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; + +public class ICUCollationKeywordFieldMapperTests extends ESSingleNodeTestCase { + + private static final String FIELD_TYPE = "icu_collation_keyword"; + + @Override + protected Collection> getPlugins() { + return Arrays.asList(AnalysisICUPlugin.class, InternalSettingsPlugin.class); + } + + IndexService indexService; + DocumentMapperParser parser; + + @Before + public void setup() { + indexService = createIndex("test"); + parser = indexService.mapperService().documentMapperParser(); + } + + public void testDefaults() throws Exception { + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties").startObject("field").field("type", FIELD_TYPE).endObject().endObject() + .endObject().endObject().string(); + + DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping)); + + assertEquals(mapping, mapper.mappingSource().toString()); + + ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder() + .startObject() + .field("field", "1234") + .endObject() + .bytes(), + XContentType.JSON)); + + IndexableField[] fields = doc.rootDoc().getFields("field"); + assertEquals(2, fields.length); + + Collator collator = Collator.getInstance(); + RawCollationKey key = collator.getRawCollationKey("1234", null); + BytesRef expected = new BytesRef(key.bytes, 0, key.size); + + assertEquals(expected, fields[0].binaryValue()); + IndexableFieldType fieldType = fields[0].fieldType(); + assertThat(fieldType.omitNorms(), equalTo(true)); + assertFalse(fieldType.tokenized()); + assertFalse(fieldType.stored()); + assertThat(fieldType.indexOptions(), equalTo(IndexOptions.DOCS)); + assertThat(fieldType.storeTermVectors(), equalTo(false)); + assertThat(fieldType.storeTermVectorOffsets(), equalTo(false)); + assertThat(fieldType.storeTermVectorPositions(), equalTo(false)); + assertThat(fieldType.storeTermVectorPayloads(), equalTo(false)); + assertEquals(DocValuesType.NONE, fieldType.docValuesType()); + + assertEquals(expected, fields[1].binaryValue()); + fieldType = fields[1].fieldType(); + assertThat(fieldType.indexOptions(), equalTo(IndexOptions.NONE)); + assertEquals(DocValuesType.SORTED, fieldType.docValuesType()); + } + + public void testNullValue() throws IOException { + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties").startObject("field").field("type", FIELD_TYPE).endObject().endObject() + .endObject().endObject().string(); + + DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping)); + assertEquals(mapping, mapper.mappingSource().toString()); + + ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder() + .startObject() + .nullField("field") + .endObject() + .bytes(), + XContentType.JSON)); + assertArrayEquals(new IndexableField[0], doc.rootDoc().getFields("field")); + + mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties").startObject("field").field("type", FIELD_TYPE) + .field("null_value", "1234").endObject().endObject() + .endObject().endObject().string(); + + mapper = parser.parse("type", new CompressedXContent(mapping)); + + assertEquals(mapping, mapper.mappingSource().toString()); + + doc = mapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder() + .startObject() + .endObject() + .bytes(), + XContentType.JSON)); + + IndexableField[] fields = doc.rootDoc().getFields("field"); + assertEquals(0, fields.length); + + doc = mapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder() + .startObject() + .nullField("field") + .endObject() + .bytes(), + XContentType.JSON)); + + Collator collator = Collator.getInstance(); + RawCollationKey key = collator.getRawCollationKey("1234", null); + BytesRef expected = new BytesRef(key.bytes, 0, key.size); + + fields = doc.rootDoc().getFields("field"); + assertEquals(2, fields.length); + assertEquals(expected, fields[0].binaryValue()); + } + + public void testEnableStore() throws IOException { + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties").startObject("field").field("type", FIELD_TYPE) + .field("store", true).endObject().endObject() + .endObject().endObject().string(); + + DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping)); + + assertEquals(mapping, mapper.mappingSource().toString()); + + ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder() + .startObject() + .field("field", "1234") + .endObject() + .bytes(), + XContentType.JSON)); + + IndexableField[] fields = doc.rootDoc().getFields("field"); + assertEquals(2, fields.length); + assertTrue(fields[0].fieldType().stored()); + } + + public void testDisableIndex() throws IOException { + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties").startObject("field").field("type", FIELD_TYPE) + .field("index", false).endObject().endObject() + .endObject().endObject().string(); + + DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping)); + + assertEquals(mapping, mapper.mappingSource().toString()); + + ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder() + .startObject() + .field("field", "1234") + .endObject() + .bytes(), + XContentType.JSON)); + + IndexableField[] fields = doc.rootDoc().getFields("field"); + assertEquals(1, fields.length); + assertEquals(IndexOptions.NONE, fields[0].fieldType().indexOptions()); + assertEquals(DocValuesType.SORTED, fields[0].fieldType().docValuesType()); + } + + public void testDisableDocValues() throws IOException { + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties").startObject("field").field("type", FIELD_TYPE) + .field("doc_values", false).endObject().endObject() + .endObject().endObject().string(); + + DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping)); + + assertEquals(mapping, mapper.mappingSource().toString()); + + ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder() + .startObject() + .field("field", "1234") + .endObject() + .bytes(), + XContentType.JSON)); + + IndexableField[] fields = doc.rootDoc().getFields("field"); + assertEquals(1, fields.length); + assertEquals(DocValuesType.NONE, fields[0].fieldType().docValuesType()); + } + + public void testIndexOptions() throws IOException { + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties").startObject("field").field("type", FIELD_TYPE) + .field("index_options", "freqs").endObject().endObject() + .endObject().endObject().string(); + + DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping)); + + assertEquals(mapping, mapper.mappingSource().toString()); + + ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder() + .startObject() + .field("field", "1234") + .endObject() + .bytes(), + XContentType.JSON)); + + IndexableField[] fields = doc.rootDoc().getFields("field"); + assertEquals(2, fields.length); + assertEquals(IndexOptions.DOCS_AND_FREQS, fields[0].fieldType().indexOptions()); + + for (String indexOptions : Arrays.asList("positions", "offsets")) { + final String mapping2 = XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties").startObject("field").field("type", FIELD_TYPE) + .field("index_options", indexOptions).endObject().endObject() + .endObject().endObject().string(); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> parser.parse("type", new CompressedXContent(mapping2))); + assertEquals("The [" + FIELD_TYPE + "] field does not support positions, got [index_options]=" + indexOptions, + e.getMessage()); + } + } + + public void testEnableNorms() throws IOException { + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties").startObject("field").field("type", FIELD_TYPE) + .field("norms", true).endObject().endObject() + .endObject().endObject().string(); + + DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping)); + + assertEquals(mapping, mapper.mappingSource().toString()); + + ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder() + .startObject() + .field("field", "1234") + .endObject() + .bytes(), + XContentType.JSON)); + + IndexableField[] fields = doc.rootDoc().getFields("field"); + assertEquals(2, fields.length); + assertFalse(fields[0].fieldType().omitNorms()); + } + + public void testCollator() throws IOException { + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties").startObject("field") + .field("type", FIELD_TYPE) + .field("language", "tr") + .field("strength", "primary") + .endObject().endObject().endObject().endObject().string(); + + DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping)); + + assertEquals(mapping, mapper.mappingSource().toString()); + + ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder() + .startObject() + .field("field", "I WİLL USE TURKİSH CASING") + .endObject() + .bytes(), + XContentType.JSON)); + + Collator collator = Collator.getInstance(new ULocale("tr")); + collator.setStrength(Collator.PRIMARY); + RawCollationKey key = collator.getRawCollationKey("ı will use turkish casıng", null); // should collate to same value + BytesRef expected = new BytesRef(key.bytes, 0, key.size); + + IndexableField[] fields = doc.rootDoc().getFields("field"); + assertEquals(2, fields.length); + + assertEquals(expected, fields[0].binaryValue()); + IndexableFieldType fieldType = fields[0].fieldType(); + assertThat(fieldType.omitNorms(), equalTo(true)); + assertFalse(fieldType.tokenized()); + assertFalse(fieldType.stored()); + assertThat(fieldType.indexOptions(), equalTo(IndexOptions.DOCS)); + assertThat(fieldType.storeTermVectors(), equalTo(false)); + assertThat(fieldType.storeTermVectorOffsets(), equalTo(false)); + assertThat(fieldType.storeTermVectorPositions(), equalTo(false)); + assertThat(fieldType.storeTermVectorPayloads(), equalTo(false)); + assertEquals(DocValuesType.NONE, fieldType.docValuesType()); + + assertEquals(expected, fields[1].binaryValue()); + fieldType = fields[1].fieldType(); + assertThat(fieldType.indexOptions(), equalTo(IndexOptions.NONE)); + assertEquals(DocValuesType.SORTED, fieldType.docValuesType()); + } + + public void testUpdateCollator() throws IOException { + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties").startObject("field") + .field("type", FIELD_TYPE) + .field("language", "tr") + .field("strength", "primary") + .endObject().endObject().endObject().endObject().string(); + indexService.mapperService().merge("type", new CompressedXContent(mapping), MergeReason.MAPPING_UPDATE, randomBoolean()); + + String mapping2 = XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties").startObject("field") + .field("type", FIELD_TYPE) + .field("language", "en") + .endObject().endObject().endObject().endObject().string(); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> indexService.mapperService().merge("type", + new CompressedXContent(mapping2), MergeReason.MAPPING_UPDATE, randomBoolean())); + assertEquals("Can't merge because of conflicts: [Cannot update language setting for [" + FIELD_TYPE + + "], Cannot update strength setting for [" + FIELD_TYPE + "]]", e.getMessage()); + } +} diff --git a/core/src/test/java/org/elasticsearch/index/mapper/FieldTypeTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/mapper/FieldTypeTestCase.java similarity index 99% rename from core/src/test/java/org/elasticsearch/index/mapper/FieldTypeTestCase.java rename to test/framework/src/main/java/org/elasticsearch/index/mapper/FieldTypeTestCase.java index 5d86602c4ca09..ae91a791535d9 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/FieldTypeTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/mapper/FieldTypeTestCase.java @@ -175,13 +175,15 @@ MappedFieldType createNamedDefaultFieldType() { // TODO: remove this once toString is no longer final on FieldType... protected void assertFieldTypeEquals(String property, MappedFieldType ft1, MappedFieldType ft2) { if (ft1.equals(ft2) == false) { - fail("Expected equality, testing property " + property + "\nexpected: " + toString(ft1) + "; \nactual: " + toString(ft2) + "\n"); + fail("Expected equality, testing property " + property + "\nexpected: " + toString(ft1) + "; \nactual: " + toString(ft2) + + "\n"); } } protected void assertFieldTypeNotEquals(String property, MappedFieldType ft1, MappedFieldType ft2) { if (ft1.equals(ft2)) { - fail("Expected inequality, testing property " + property + "\nfirst: " + toString(ft1) + "; \nsecond: " + toString(ft2) + "\n"); + fail("Expected inequality, testing property " + property + "\nfirst: " + toString(ft1) + "; \nsecond: " + toString(ft2) + + "\n"); } } From 51c74ce5476ed4d8b0f2ed3469fee955ff1e5fd1 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Tue, 9 May 2017 15:04:28 +0200 Subject: [PATCH 270/619] Added unit tests for InternalMatrixStats. Also moved InternalAggregationTestCase to test-framework module in order to make use of it from other modules than core. Relates to #22278 --- ...ternalSingleBucketAggregationTestCase.java | 2 +- .../geogrid/InternalGeoHashGridTests.java | 2 +- .../histogram/InternalDateHistogramTests.java | 4 +- .../histogram/InternalHistogramTests.java | 2 +- .../range/InternalBinaryRangeTests.java | 2 +- .../InternalSignificantTermsTestCase.java | 2 +- .../bucket/terms/InternalTermsTestCase.java | 4 +- .../metrics/InternalExtendedStatsTests.java | 2 +- .../metrics/InternalMaxTests.java | 2 +- .../metrics/InternalStatsTests.java | 2 +- .../metrics/avg/InternalAvgTests.java | 3 +- .../cardinality/InternalCardinalityTests.java | 2 +- .../geobounds/InternalGeoBoundsTests.java | 2 +- .../geocentroid/InternalGeoCentroidTests.java | 2 +- .../metrics/min/InternalMinTests.java | 3 +- .../InternalPercentilesTestCase.java | 2 +- .../hdr/InternalHDRPercentilesRanksTests.java | 3 +- .../InternalTDigestPercentilesRanksTests.java | 2 +- .../scripted/InternalScriptedMetricTests.java | 2 +- .../metrics/sum/InternalSumTests.java | 2 +- .../metrics/tophits/InternalTopHitsTests.java | 6 +- .../valuecount/InternalValueCountTests.java | 2 +- .../pipeline/InternalSimpleValueTests.java | 2 +- .../InternalPercentilesBucketTests.java | 2 +- .../derivative/InternalDerivativeTests.java | 2 +- .../matrix/stats/InternalMatrixStats.java | 19 ++- .../matrix/stats/MatrixStatsResults.java | 15 ++ .../matrix/stats/RunningStats.java | 25 ++- .../matrix/stats/BaseMatrixStatsTestCase.java | 124 +------------- .../stats/InternalMatrixStatsTests.java | 103 ++++++++++++ .../matrix/stats/MultiPassStats.java | 155 ++++++++++++++++++ .../test}/InternalAggregationTestCase.java | 4 +- 32 files changed, 345 insertions(+), 161 deletions(-) create mode 100644 modules/aggs-matrix-stats/src/test/java/org/elasticsearch/search/aggregations/matrix/stats/InternalMatrixStatsTests.java create mode 100644 modules/aggs-matrix-stats/src/test/java/org/elasticsearch/search/aggregations/matrix/stats/MultiPassStats.java rename {core/src/test/java/org/elasticsearch/search/aggregations => test/framework/src/main/java/org/elasticsearch/test}/InternalAggregationTestCase.java (98%) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java index 5d2e8affe7854..8da1f0a90e95d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java @@ -20,11 +20,11 @@ package org.elasticsearch.search.aggregations.bucket; import org.elasticsearch.search.aggregations.InternalAggregation; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.metrics.max.InternalMax; import org.elasticsearch.search.aggregations.metrics.min.InternalMin; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.ArrayList; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/InternalGeoHashGridTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/InternalGeoHashGridTests.java index ace22b244a35a..a27b47946f9b9 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/InternalGeoHashGridTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/InternalGeoHashGridTests.java @@ -21,9 +21,9 @@ import org.apache.lucene.index.IndexWriter; import org.elasticsearch.common.geo.GeoHashUtils; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.ArrayList; import java.util.HashMap; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java index 40f268e6556e8..aafad557bc681 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java @@ -19,13 +19,11 @@ package org.elasticsearch.search.aggregations.bucket.histogram; -import org.apache.lucene.util.TestUtil; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import org.joda.time.DateTime; import java.util.ArrayList; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java index 093496738fd61..0dd4a163f3894 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java @@ -22,9 +22,9 @@ import org.apache.lucene.util.TestUtil; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.ArrayList; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalBinaryRangeTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalBinaryRangeTests.java index 2f8d4f9e65fa4..6d83b74c828bb 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalBinaryRangeTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalBinaryRangeTests.java @@ -22,9 +22,9 @@ import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import org.junit.Before; import java.util.ArrayList; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTermsTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTermsTestCase.java index dde1562e1afb8..f1c54c4b503dc 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTermsTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTermsTestCase.java @@ -19,8 +19,8 @@ package org.elasticsearch.search.aggregations.bucket.significant; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.Arrays; import java.util.HashMap; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTermsTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTermsTestCase.java index 03031633f746d..118f190bcabe2 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTermsTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTermsTestCase.java @@ -19,14 +19,14 @@ package org.elasticsearch.search.aggregations.bucket.terms; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Map.Entry; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalExtendedStatsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalExtendedStatsTests.java index 83e1815f3984d..2e4f2e058726f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalExtendedStatsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalExtendedStatsTests.java @@ -21,9 +21,9 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.metrics.stats.extended.InternalExtendedStats; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import org.junit.Before; import java.util.Collections; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java index e2149b24549aa..f3b43d40a2aa4 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java @@ -20,9 +20,9 @@ package org.elasticsearch.search.aggregations.metrics; import org.elasticsearch.common.io.stream.Writeable.Reader; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.metrics.max.InternalMax; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.List; import java.util.Map; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java index c99d208581c13..c4da54ac6b619 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java @@ -20,9 +20,9 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.metrics.stats.InternalStats; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.Collections; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java index 1cfb93d6dcc08..ca902ae5684f3 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java @@ -20,9 +20,8 @@ package org.elasticsearch.search.aggregations.metrics.avg; import org.elasticsearch.common.io.stream.Writeable.Reader; -import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.List; import java.util.Map; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java index 7c5809f323bdf..336def450a0ba 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/cardinality/InternalCardinalityTests.java @@ -24,8 +24,8 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import org.junit.After; import org.junit.Before; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBoundsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBoundsTests.java index cd5d4d43d1719..8ec1d924b565b 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBoundsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geobounds/InternalGeoBoundsTests.java @@ -20,8 +20,8 @@ package org.elasticsearch.search.aggregations.metrics.geobounds; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.Collections; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java index 3bbe1a1b462af..d7c4328579c79 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/geocentroid/InternalGeoCentroidTests.java @@ -21,8 +21,8 @@ import org.apache.lucene.geo.GeoEncodingUtils; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import org.elasticsearch.test.geo.RandomGeoGenerator; import java.util.Collections; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java index d75f03a5de34a..a40276e64c6e4 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java @@ -20,9 +20,8 @@ package org.elasticsearch.search.aggregations.metrics.min; import org.elasticsearch.common.io.stream.Writeable.Reader; -import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.List; import java.util.Map; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesTestCase.java index e94cf753205b0..52462fbdaf73b 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/InternalPercentilesTestCase.java @@ -21,8 +21,8 @@ import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregation; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import org.junit.Before; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesRanksTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesRanksTests.java index 15207e1d314ea..e57e7ff11645f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesRanksTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesRanksTests.java @@ -22,9 +22,8 @@ import org.HdrHistogram.DoubleHistogram; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; -import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.InternalHDRPercentileRanks; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.List; import java.util.Map; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java index bc1df930f6a4f..c4bdf907280d0 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java @@ -21,8 +21,8 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.List; import java.util.Map; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java index 9d8ff94c47c2f..75975d5a39ff8 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java @@ -30,8 +30,8 @@ import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptSettings; import org.elasticsearch.script.ScriptType; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.io.IOException; import java.util.Collections; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/sum/InternalSumTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/sum/InternalSumTests.java index 44e5ea940adeb..cdaef89e42fd4 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/sum/InternalSumTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/sum/InternalSumTests.java @@ -20,8 +20,8 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.List; import java.util.Map; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/tophits/InternalTopHitsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/tophits/InternalTopHitsTests.java index 22479eb43490f..db68e8537dffc 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/tophits/InternalTopHitsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/tophits/InternalTopHitsTests.java @@ -30,11 +30,11 @@ import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.text.Text; -import org.elasticsearch.search.SearchHitField; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; -import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHitField; import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.ArrayList; import java.util.Arrays; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/valuecount/InternalValueCountTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/valuecount/InternalValueCountTests.java index 3de87898778d4..e67d6fbf1daf7 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/valuecount/InternalValueCountTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/valuecount/InternalValueCountTests.java @@ -20,8 +20,8 @@ package org.elasticsearch.search.aggregations.metrics.valuecount; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.List; import java.util.Map; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/InternalSimpleValueTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/InternalSimpleValueTests.java index 4dae0a8d653c5..b3791b26ae876 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/InternalSimpleValueTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/InternalSimpleValueTests.java @@ -21,7 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.Collections; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucketTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucketTests.java index 0ea28f9838474..4aaae089b3c88 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucketTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucketTests.java @@ -21,9 +21,9 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.Collections; import java.util.Iterator; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/derivative/InternalDerivativeTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/derivative/InternalDerivativeTests.java index d2e4f3cb72017..34082f77a8d01 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/derivative/InternalDerivativeTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/derivative/InternalDerivativeTests.java @@ -21,8 +21,8 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.Collections; import java.util.List; diff --git a/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/matrix/stats/InternalMatrixStats.java b/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/matrix/stats/InternalMatrixStats.java index 0914ea2910c2b..5b7d2cf288d8b 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/matrix/stats/InternalMatrixStats.java +++ b/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/matrix/stats/InternalMatrixStats.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import static java.util.Collections.emptyMap; @@ -41,7 +42,7 @@ public class InternalMatrixStats extends InternalAggregation implements MatrixSt private final MatrixStatsResults results; /** per shard ctor */ - protected InternalMatrixStats(String name, long count, RunningStats multiFieldStatsResults, MatrixStatsResults results, + InternalMatrixStats(String name, long count, RunningStats multiFieldStatsResults, MatrixStatsResults results, List pipelineAggregators, Map metaData) { super(name, pipelineAggregators, metaData); assert count >= 0; @@ -138,6 +139,10 @@ public double getCorrelation(String fieldX, String fieldY) { return results.getCorrelation(fieldX, fieldY); } + MatrixStatsResults getResults() { + return results; + } + static class Fields { public static final String FIELDS = "fields"; public static final String NAME = "name"; @@ -238,4 +243,16 @@ public InternalAggregation doReduce(List aggregations, Redu return new InternalMatrixStats(name, results.getDocCount(), runningStats, results, pipelineAggregators(), getMetaData()); } + + @Override + protected int doHashCode() { + return Objects.hash(stats, results); + } + + @Override + protected boolean doEquals(Object obj) { + InternalMatrixStats other = (InternalMatrixStats) obj; + return Objects.equals(this.stats, other.stats) && + Objects.equals(this.results, other.results); + } } diff --git a/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/matrix/stats/MatrixStatsResults.java b/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/matrix/stats/MatrixStatsResults.java index f82c6df73be38..4da8b7ca617b6 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/matrix/stats/MatrixStatsResults.java +++ b/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/matrix/stats/MatrixStatsResults.java @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * Descriptive stats gathered per shard. Coordinating node computes final pearson product coefficient @@ -228,4 +229,18 @@ private void compute() { correlation.put(rowName, corRow); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MatrixStatsResults that = (MatrixStatsResults) o; + return Objects.equals(results, that.results) && + Objects.equals(correlation, that.correlation); + } + + @Override + public int hashCode() { + return Objects.hash(results, correlation); + } } diff --git a/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/matrix/stats/RunningStats.java b/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/matrix/stats/RunningStats.java index 81d0d0a49434b..1be3279e8eaf1 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/matrix/stats/RunningStats.java +++ b/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/matrix/stats/RunningStats.java @@ -28,6 +28,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * Descriptive stats gathered per shard. Coordinating node computes final correlation and covariance stats @@ -53,11 +54,11 @@ public class RunningStats implements Writeable, Cloneable { /** covariance values */ protected HashMap> covariances; - public RunningStats() { + RunningStats() { init(); } - public RunningStats(final String[] fieldNames, final double[] fieldVals) { + RunningStats(final String[] fieldNames, final double[] fieldVals) { if (fieldVals != null && fieldVals.length > 0) { init(); this.add(fieldNames, fieldVals); @@ -309,4 +310,24 @@ public RunningStats clone() { throw new ElasticsearchException("Error trying to create a copy of RunningStats"); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RunningStats that = (RunningStats) o; + return docCount == that.docCount && + Objects.equals(fieldSum, that.fieldSum) && + Objects.equals(counts, that.counts) && + Objects.equals(means, that.means) && + Objects.equals(variances, that.variances) && + Objects.equals(skewness, that.skewness) && + Objects.equals(kurtosis, that.kurtosis) && + Objects.equals(covariances, that.covariances); + } + + @Override + public int hashCode() { + return Objects.hash(docCount, fieldSum, counts, means, variances, skewness, kurtosis, covariances); + } } diff --git a/modules/aggs-matrix-stats/src/test/java/org/elasticsearch/search/aggregations/matrix/stats/BaseMatrixStatsTestCase.java b/modules/aggs-matrix-stats/src/test/java/org/elasticsearch/search/aggregations/matrix/stats/BaseMatrixStatsTestCase.java index 81c9d5146364c..091235bf82e03 100644 --- a/modules/aggs-matrix-stats/src/test/java/org/elasticsearch/search/aggregations/matrix/stats/BaseMatrixStatsTestCase.java +++ b/modules/aggs-matrix-stats/src/test/java/org/elasticsearch/search/aggregations/matrix/stats/BaseMatrixStatsTestCase.java @@ -22,15 +22,12 @@ import org.junit.Before; import java.util.ArrayList; -import java.util.HashMap; - -import static org.hamcrest.Matchers.equalTo; public abstract class BaseMatrixStatsTestCase extends ESTestCase { protected final int numObs = atLeast(10000); protected final ArrayList fieldA = new ArrayList<>(numObs); protected final ArrayList fieldB = new ArrayList<>(numObs); - protected final MultiPassStats actualStats = new MultiPassStats(); + protected final MultiPassStats actualStats = new MultiPassStats(fieldAKey, fieldBKey); protected static final String fieldAKey = "fieldA"; protected static final String fieldBKey = "fieldB"; @@ -47,123 +44,4 @@ public void createStats() { actualStats.computeStats(fieldA, fieldB); } - static class MultiPassStats { - long count; - HashMap means = new HashMap<>(); - HashMap variances = new HashMap<>(); - HashMap skewness = new HashMap<>(); - HashMap kurtosis = new HashMap<>(); - HashMap> covariances = new HashMap<>(); - HashMap> correlations = new HashMap<>(); - - @SuppressWarnings("unchecked") - void computeStats(final ArrayList fieldA, final ArrayList fieldB) { - // set count - count = fieldA.size(); - double meanA = 0d; - double meanB = 0d; - - // compute mean - for (int n = 0; n < count; ++n) { - // fieldA - meanA += fieldA.get(n); - meanB += fieldB.get(n); - } - means.put(fieldAKey, meanA/count); - means.put(fieldBKey, meanB/count); - - // compute variance, skewness, and kurtosis - double dA; - double dB; - double skewA = 0d; - double skewB = 0d; - double kurtA = 0d; - double kurtB = 0d; - double varA = 0d; - double varB = 0d; - double cVar = 0d; - for (int n = 0; n < count; ++n) { - dA = fieldA.get(n) - means.get(fieldAKey); - varA += dA * dA; - skewA += dA * dA * dA; - kurtA += dA * dA * dA * dA; - dB = fieldB.get(n) - means.get(fieldBKey); - varB += dB * dB; - skewB += dB * dB * dB; - kurtB += dB * dB * dB * dB; - cVar += dA * dB; - } - variances.put(fieldAKey, varA / (count - 1)); - final double stdA = Math.sqrt(variances.get(fieldAKey)); - variances.put(fieldBKey, varB / (count - 1)); - final double stdB = Math.sqrt(variances.get(fieldBKey)); - skewness.put(fieldAKey, skewA / ((count - 1) * variances.get(fieldAKey) * stdA)); - skewness.put(fieldBKey, skewB / ((count - 1) * variances.get(fieldBKey) * stdB)); - kurtosis.put(fieldAKey, kurtA / ((count - 1) * variances.get(fieldAKey) * variances.get(fieldAKey))); - kurtosis.put(fieldBKey, kurtB / ((count - 1) * variances.get(fieldBKey) * variances.get(fieldBKey))); - - // compute covariance - final HashMap fieldACovar = new HashMap<>(2); - fieldACovar.put(fieldAKey, 1d); - cVar /= count - 1; - fieldACovar.put(fieldBKey, cVar); - covariances.put(fieldAKey, fieldACovar); - final HashMap fieldBCovar = new HashMap<>(2); - fieldBCovar.put(fieldAKey, cVar); - fieldBCovar.put(fieldBKey, 1d); - covariances.put(fieldBKey, fieldBCovar); - - // compute correlation - final HashMap fieldACorr = new HashMap<>(); - fieldACorr.put(fieldAKey, 1d); - double corr = covariances.get(fieldAKey).get(fieldBKey); - corr /= stdA * stdB; - fieldACorr.put(fieldBKey, corr); - correlations.put(fieldAKey, fieldACorr); - final HashMap fieldBCorr = new HashMap<>(); - fieldBCorr.put(fieldAKey, corr); - fieldBCorr.put(fieldBKey, 1d); - correlations.put(fieldBKey, fieldBCorr); - } - - public void assertNearlyEqual(MatrixStatsResults stats) { - assertThat(count, equalTo(stats.getDocCount())); - assertThat(count, equalTo(stats.getFieldCount(fieldAKey))); - assertThat(count, equalTo(stats.getFieldCount(fieldBKey))); - // means - assertTrue(nearlyEqual(means.get(fieldAKey), stats.getMean(fieldAKey), 1e-7)); - assertTrue(nearlyEqual(means.get(fieldBKey), stats.getMean(fieldBKey), 1e-7)); - // variances - assertTrue(nearlyEqual(variances.get(fieldAKey), stats.getVariance(fieldAKey), 1e-7)); - assertTrue(nearlyEqual(variances.get(fieldBKey), stats.getVariance(fieldBKey), 1e-7)); - // skewness (multi-pass is more susceptible to round-off error so we need to slightly relax the tolerance) - assertTrue(nearlyEqual(skewness.get(fieldAKey), stats.getSkewness(fieldAKey), 1e-4)); - assertTrue(nearlyEqual(skewness.get(fieldBKey), stats.getSkewness(fieldBKey), 1e-4)); - // kurtosis (multi-pass is more susceptible to round-off error so we need to slightly relax the tolerance) - assertTrue(nearlyEqual(kurtosis.get(fieldAKey), stats.getKurtosis(fieldAKey), 1e-4)); - assertTrue(nearlyEqual(kurtosis.get(fieldBKey), stats.getKurtosis(fieldBKey), 1e-4)); - // covariances - assertTrue(nearlyEqual(covariances.get(fieldAKey).get(fieldBKey), stats.getCovariance(fieldAKey, fieldBKey), 1e-7)); - assertTrue(nearlyEqual(covariances.get(fieldBKey).get(fieldAKey), stats.getCovariance(fieldBKey, fieldAKey), 1e-7)); - // correlation - assertTrue(nearlyEqual(correlations.get(fieldAKey).get(fieldBKey), stats.getCorrelation(fieldAKey, fieldBKey), 1e-7)); - assertTrue(nearlyEqual(correlations.get(fieldBKey).get(fieldAKey), stats.getCorrelation(fieldBKey, fieldAKey), 1e-7)); - } - } - - private static boolean nearlyEqual(double a, double b, double epsilon) { - final double absA = Math.abs(a); - final double absB = Math.abs(b); - final double diff = Math.abs(a - b); - - if (a == b) { // shortcut, handles infinities - return true; - } else if (a == 0 || b == 0 || diff < Double.MIN_NORMAL) { - // a or b is zero or both are extremely close to it - // relative error is less meaningful here - return diff < (epsilon * Double.MIN_NORMAL); - } else { // use relative error - return diff / Math.min((absA + absB), Double.MAX_VALUE) < epsilon; - } - } } diff --git a/modules/aggs-matrix-stats/src/test/java/org/elasticsearch/search/aggregations/matrix/stats/InternalMatrixStatsTests.java b/modules/aggs-matrix-stats/src/test/java/org/elasticsearch/search/aggregations/matrix/stats/InternalMatrixStatsTests.java new file mode 100644 index 0000000000000..277006da90d50 --- /dev/null +++ b/modules/aggs-matrix-stats/src/test/java/org/elasticsearch/search/aggregations/matrix/stats/InternalMatrixStatsTests.java @@ -0,0 +1,103 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations.matrix.stats; + +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; +import org.elasticsearch.script.ScriptService; +import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class InternalMatrixStatsTests extends InternalAggregationTestCase { + + @Override + protected InternalMatrixStats createTestInstance(String name, List pipelineAggregators, + Map metaData) { + int numFields = randomInt(128); + String[] fieldNames = new String[numFields]; + double[] fieldValues = new double[numFields]; + for (int i = 0; i < numFields; i++) { + fieldNames[i] = Integer.toString(i); + fieldValues[i] = randomDouble(); + } + RunningStats runningStats = new RunningStats(); + runningStats.add(fieldNames, fieldValues); + MatrixStatsResults matrixStatsResults = randomBoolean() ? new MatrixStatsResults(runningStats) : null; + return new InternalMatrixStats("_name", 1L, runningStats, matrixStatsResults, Collections.emptyList(), Collections.emptyMap()); + } + + @Override + protected Writeable.Reader instanceReader() { + return InternalMatrixStats::new; + } + + @Override + public void testReduceRandom() { + int numValues = 10000; + int numShards = randomIntBetween(1, 20); + int valuesPerShard = (int) Math.floor(numValues / numShards); + + List aValues = new ArrayList<>(); + List bValues = new ArrayList<>(); + + RunningStats runningStats = new RunningStats(); + List shardResults = new ArrayList<>(); + + int valuePerShardCounter = 0; + for (int i = 0; i < numValues; i++) { + double valueA = randomDouble(); + aValues.add(valueA); + double valueB = randomDouble(); + bValues.add(valueB); + + runningStats.add(new String[]{"a", "b"}, new double[]{valueA, valueB}); + if (++valuePerShardCounter == valuesPerShard) { + shardResults.add(new InternalMatrixStats("_name", 1L, runningStats, null, Collections.emptyList(), Collections.emptyMap())); + runningStats = new RunningStats(); + valuePerShardCounter = 0; + } + } + + if (valuePerShardCounter != 0) { + shardResults.add(new InternalMatrixStats("_name", 1L, runningStats, null, Collections.emptyList(), Collections.emptyMap())); + } + MultiPassStats multiPassStats = new MultiPassStats("a", "b"); + multiPassStats.computeStats(aValues, bValues); + + ScriptService mockScriptService = mockScriptService(); + MockBigArrays bigArrays = new MockBigArrays(Settings.EMPTY, new NoneCircuitBreakerService()); + InternalAggregation.ReduceContext context = + new InternalAggregation.ReduceContext(bigArrays, mockScriptService, true); + InternalMatrixStats reduced = (InternalMatrixStats) shardResults.get(0).reduce(shardResults, context); + multiPassStats.assertNearlyEqual(reduced.getResults()); + } + + @Override + protected void assertReduced(InternalMatrixStats reduced, List inputs) { + throw new UnsupportedOperationException(); + } +} diff --git a/modules/aggs-matrix-stats/src/test/java/org/elasticsearch/search/aggregations/matrix/stats/MultiPassStats.java b/modules/aggs-matrix-stats/src/test/java/org/elasticsearch/search/aggregations/matrix/stats/MultiPassStats.java new file mode 100644 index 0000000000000..70e2172ce92d9 --- /dev/null +++ b/modules/aggs-matrix-stats/src/test/java/org/elasticsearch/search/aggregations/matrix/stats/MultiPassStats.java @@ -0,0 +1,155 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations.matrix.stats; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +class MultiPassStats { + + private final String fieldAKey; + private final String fieldBKey; + + private long count; + private Map means = new HashMap<>(); + private Map variances = new HashMap<>(); + private Map skewness = new HashMap<>(); + private Map kurtosis = new HashMap<>(); + private Map> covariances = new HashMap<>(); + private Map> correlations = new HashMap<>(); + + MultiPassStats(String fieldAName, String fieldBName) { + this.fieldAKey = fieldAName; + this.fieldBKey = fieldBName; + } + + @SuppressWarnings("unchecked") + void computeStats(final List fieldA, final List fieldB) { + // set count + count = fieldA.size(); + double meanA = 0d; + double meanB = 0d; + + // compute mean + for (int n = 0; n < count; ++n) { + // fieldA + meanA += fieldA.get(n); + meanB += fieldB.get(n); + } + means.put(fieldAKey, meanA / count); + means.put(fieldBKey, meanB / count); + + // compute variance, skewness, and kurtosis + double dA; + double dB; + double skewA = 0d; + double skewB = 0d; + double kurtA = 0d; + double kurtB = 0d; + double varA = 0d; + double varB = 0d; + double cVar = 0d; + for (int n = 0; n < count; ++n) { + dA = fieldA.get(n) - means.get(fieldAKey); + varA += dA * dA; + skewA += dA * dA * dA; + kurtA += dA * dA * dA * dA; + dB = fieldB.get(n) - means.get(fieldBKey); + varB += dB * dB; + skewB += dB * dB * dB; + kurtB += dB * dB * dB * dB; + cVar += dA * dB; + } + variances.put(fieldAKey, varA / (count - 1)); + final double stdA = Math.sqrt(variances.get(fieldAKey)); + variances.put(fieldBKey, varB / (count - 1)); + final double stdB = Math.sqrt(variances.get(fieldBKey)); + skewness.put(fieldAKey, skewA / ((count - 1) * variances.get(fieldAKey) * stdA)); + skewness.put(fieldBKey, skewB / ((count - 1) * variances.get(fieldBKey) * stdB)); + kurtosis.put(fieldAKey, kurtA / ((count - 1) * variances.get(fieldAKey) * variances.get(fieldAKey))); + kurtosis.put(fieldBKey, kurtB / ((count - 1) * variances.get(fieldBKey) * variances.get(fieldBKey))); + + // compute covariance + final HashMap fieldACovar = new HashMap<>(2); + fieldACovar.put(fieldAKey, 1d); + cVar /= count - 1; + fieldACovar.put(fieldBKey, cVar); + covariances.put(fieldAKey, fieldACovar); + final HashMap fieldBCovar = new HashMap<>(2); + fieldBCovar.put(fieldAKey, cVar); + fieldBCovar.put(fieldBKey, 1d); + covariances.put(fieldBKey, fieldBCovar); + + // compute correlation + final HashMap fieldACorr = new HashMap<>(); + fieldACorr.put(fieldAKey, 1d); + double corr = covariances.get(fieldAKey).get(fieldBKey); + corr /= stdA * stdB; + fieldACorr.put(fieldBKey, corr); + correlations.put(fieldAKey, fieldACorr); + final HashMap fieldBCorr = new HashMap<>(); + fieldBCorr.put(fieldAKey, corr); + fieldBCorr.put(fieldBKey, 1d); + correlations.put(fieldBKey, fieldBCorr); + } + + void assertNearlyEqual(MatrixStatsResults stats) { + assertEquals(count, stats.getDocCount()); + assertEquals(count, stats.getFieldCount(fieldAKey)); + assertEquals(count, stats.getFieldCount(fieldBKey)); + // means + assertTrue(nearlyEqual(means.get(fieldAKey), stats.getMean(fieldAKey), 1e-7)); + assertTrue(nearlyEqual(means.get(fieldBKey), stats.getMean(fieldBKey), 1e-7)); + // variances + assertTrue(nearlyEqual(variances.get(fieldAKey), stats.getVariance(fieldAKey), 1e-7)); + assertTrue(nearlyEqual(variances.get(fieldBKey), stats.getVariance(fieldBKey), 1e-7)); + // skewness (multi-pass is more susceptible to round-off error so we need to slightly relax the tolerance) + assertTrue(nearlyEqual(skewness.get(fieldAKey), stats.getSkewness(fieldAKey), 1e-4)); + assertTrue(nearlyEqual(skewness.get(fieldBKey), stats.getSkewness(fieldBKey), 1e-4)); + // kurtosis (multi-pass is more susceptible to round-off error so we need to slightly relax the tolerance) + assertTrue(nearlyEqual(kurtosis.get(fieldAKey), stats.getKurtosis(fieldAKey), 1e-4)); + assertTrue(nearlyEqual(kurtosis.get(fieldBKey), stats.getKurtosis(fieldBKey), 1e-4)); + // covariances + assertTrue(nearlyEqual(covariances.get(fieldAKey).get(fieldBKey),stats.getCovariance(fieldAKey, fieldBKey), 1e-7)); + assertTrue(nearlyEqual(covariances.get(fieldBKey).get(fieldAKey),stats.getCovariance(fieldBKey, fieldAKey), 1e-7)); + // correlation + assertTrue(nearlyEqual(correlations.get(fieldAKey).get(fieldBKey), stats.getCorrelation(fieldAKey, fieldBKey), 1e-7)); + assertTrue(nearlyEqual(correlations.get(fieldBKey).get(fieldAKey), stats.getCorrelation(fieldBKey, fieldAKey), 1e-7)); + } + + private static boolean nearlyEqual(double a, double b, double epsilon) { + final double absA = Math.abs(a); + final double absB = Math.abs(b); + final double diff = Math.abs(a - b); + + if (a == b) { // shortcut, handles infinities + return true; + } else if (a == 0 || b == 0 || diff < Double.MIN_NORMAL) { + // a or b is zero or both are extremely close to it + // relative error is less meaningful here + return diff < (epsilon * Double.MIN_NORMAL); + } else { // use relative error + return diff / Math.min((absA + absB), Double.MAX_VALUE) < epsilon; + } + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java similarity index 98% rename from core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java rename to test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java index 02abb394d0f2a..15385f566218d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.search.aggregations; +package org.elasticsearch.test; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Settings; @@ -26,8 +26,8 @@ import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.SearchModule; +import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import org.elasticsearch.test.AbstractWireSerializingTestCase; import java.util.ArrayList; import java.util.Collections; From bd559d96d43e0f418818ea1c04c85066859a1192 Mon Sep 17 00:00:00 2001 From: Isabel Drost-Fromm Date: Thu, 1 Dec 2016 12:33:53 +0100 Subject: [PATCH 271/619] This adds max_concurrent_searches to multi-search-template endpoint. Closes #20912 --- .../mustache/MultiSearchTemplateRequest.java | 23 +++++++++++++++++++ .../MultiSearchTemplateRequestBuilder.java | 8 +++++++ .../RestMultiSearchTemplateAction.java | 4 ++++ .../MultiSearchTemplateRequestTests.java | 9 ++++++++ .../rest-api-spec/api/msearch_template.json | 4 ++++ 5 files changed, 48 insertions(+) diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java index 178eb3e290da1..ca4b4dc8e1c97 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java @@ -34,6 +34,7 @@ public class MultiSearchTemplateRequest extends ActionRequest implements CompositeIndicesRequest { + private int maxConcurrentSearchRequests = 0; private List requests = new ArrayList<>(); private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpenAndForbidClosed(); @@ -56,6 +57,26 @@ public MultiSearchTemplateRequest add(SearchTemplateRequest request) { return this; } + + /** + * Returns the amount of search requests specified in this multi search requests are allowed to be ran concurrently. + */ + public int maxConcurrentSearchRequests() { + return maxConcurrentSearchRequests; + } + + /** + * Sets how many search requests specified in this multi search requests are allowed to be ran concurrently. + */ + public MultiSearchTemplateRequest maxConcurrentSearchRequests(int maxConcurrentSearchRequests) { + if (maxConcurrentSearchRequests < 1) { + throw new IllegalArgumentException("maxConcurrentSearchRequests must be positive"); + } + + this.maxConcurrentSearchRequests = maxConcurrentSearchRequests; + return this; + } + public List requests() { return this.requests; } @@ -90,12 +111,14 @@ public MultiSearchTemplateRequest indicesOptions(IndicesOptions indicesOptions) @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); + maxConcurrentSearchRequests = in.readVInt(); requests = in.readStreamableList(SearchTemplateRequest::new); } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); + out.writeVInt(maxConcurrentSearchRequests); out.writeStreamableList(requests); } } diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequestBuilder.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequestBuilder.java index 4624e8caa2433..5f5ab400f6b96 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequestBuilder.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequestBuilder.java @@ -58,4 +58,12 @@ public MultiSearchTemplateRequestBuilder setIndicesOptions(IndicesOptions indice request().indicesOptions(indicesOptions); return this; } + + /** + * Sets how many search requests specified in this multi search requests are allowed to be ran concurrently. + */ + public MultiSearchTemplateRequestBuilder setMaxConcurrentSearchRequests(int maxConcurrentSearchRequests) { + request().maxConcurrentSearchRequests(maxConcurrentSearchRequests); + return this; + } } diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateAction.java index 91d89d1253d42..f8bac3236d40b 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateAction.java @@ -70,6 +70,10 @@ public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client */ public static MultiSearchTemplateRequest parseRequest(RestRequest restRequest, boolean allowExplicitIndex) throws IOException { MultiSearchTemplateRequest multiRequest = new MultiSearchTemplateRequest(); + if (restRequest.hasParam("max_concurrent_searches")) { + multiRequest.maxConcurrentSearchRequests(restRequest.paramAsInt("max_concurrent_searches", 0)); + } + RestMultiSearchAction.parseMultiLineRequest(restRequest, multiRequest.indicesOptions(), allowExplicitIndex, (searchRequest, bytes) -> { try { diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequestTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequestTests.java index e907a988e5676..13a02b0bc48f8 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequestTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequestTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.script.mustache; +import org.elasticsearch.action.search.MultiSearchRequest; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.RestRequest; @@ -90,4 +91,12 @@ public void testParseWithCarriageReturn() throws Exception { assertEquals("{\"query\":{\"match_{{template}}\":{}}}", request.requests().get(0).getScript()); assertEquals(1, request.requests().get(0).getScriptParams().size()); } + + public void testMaxConcurrentSearchRequests() { + MultiSearchTemplateRequest request = new MultiSearchTemplateRequest(); + request.maxConcurrentSearchRequests(randomIntBetween(1, Integer.MAX_VALUE)); + expectThrows(IllegalArgumentException.class, () -> + request.maxConcurrentSearchRequests(randomIntBetween(Integer.MIN_VALUE, 0))); + } + } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json index 2e7de08e7687a..e742093293255 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json @@ -24,6 +24,10 @@ "typed_keys": { "type" : "boolean", "description" : "Specify whether aggregation and suggester names should be prefixed by their respective types in the response" + }, + "max_concurrent_searches" : { + "type" : "number", + "description" : "Controls the maximum number of concurrent searches the multi search api will execute" } } }, From 760e5fce770f654fb789158eb39bc64ac2fa5af5 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Fri, 21 Apr 2017 20:39:36 +0200 Subject: [PATCH 272/619] Rewrite multi search template api to delegate to multi search api instead of to search template api. The max concurrent searches logic is complex and we shouldn't duplicate that in multi search template api, so we should template each individual template search request and then delegate to multi search api. --- .../mustache/MultiSearchTemplateRequest.java | 2 +- .../TransportMultiSearchTemplateAction.java | 82 ++++++++++++------- .../TransportSearchTemplateAction.java | 55 +++++++------ 3 files changed, 85 insertions(+), 54 deletions(-) diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java index ca4b4dc8e1c97..b93e4ef77b6e1 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java @@ -34,7 +34,7 @@ public class MultiSearchTemplateRequest extends ActionRequest implements CompositeIndicesRequest { - private int maxConcurrentSearchRequests = 0; + private int maxConcurrentSearchRequests = 1; private List requests = new ArrayList<>(); private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpenAndForbidClosed(); diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportMultiSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportMultiSearchTemplateAction.java index a613f15972b73..167190d8f5d04 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportMultiSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportMultiSearchTemplateAction.java @@ -20,59 +20,81 @@ package org.elasticsearch.script.mustache; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.search.MultiSearchRequest; +import org.elasticsearch.action.search.MultiSearchResponse; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.TransportMultiSearchAction; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.util.concurrent.AtomicArray; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.script.ScriptService; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.ArrayList; +import java.util.List; + +import static org.elasticsearch.script.mustache.TransportSearchTemplateAction.convert; public class TransportMultiSearchTemplateAction extends HandledTransportAction { - private final TransportSearchTemplateAction searchTemplateAction; + private final ScriptService scriptService; + private final NamedXContentRegistry xContentRegistry; + private final TransportMultiSearchAction multiSearchAction; @Inject public TransportMultiSearchTemplateAction(Settings settings, ThreadPool threadPool, TransportService transportService, ActionFilters actionFilters, IndexNameExpressionResolver resolver, - TransportSearchTemplateAction searchTemplateAction) { + ScriptService scriptService, NamedXContentRegistry xContentRegistry, + TransportMultiSearchAction multiSearchAction) { super(settings, MultiSearchTemplateAction.NAME, threadPool, transportService, actionFilters, resolver, MultiSearchTemplateRequest::new); - this.searchTemplateAction = searchTemplateAction; + this.scriptService = scriptService; + this.xContentRegistry = xContentRegistry; + this.multiSearchAction = multiSearchAction; } @Override protected void doExecute(MultiSearchTemplateRequest request, ActionListener listener) { - final AtomicArray responses = new AtomicArray<>(request.requests().size()); - final AtomicInteger counter = new AtomicInteger(responses.length()); - - for (int i = 0; i < responses.length(); i++) { - final int index = i; - searchTemplateAction.execute(request.requests().get(i), new ActionListener() { - @Override - public void onResponse(SearchTemplateResponse searchTemplateResponse) { - responses.set(index, new MultiSearchTemplateResponse.Item(searchTemplateResponse, null)); - if (counter.decrementAndGet() == 0) { - finishHim(); - } - } + List originalSlots = new ArrayList<>(); + MultiSearchRequest multiSearchRequest = new MultiSearchRequest(); + multiSearchRequest.indicesOptions(request.indicesOptions()); + if (request.maxConcurrentSearchRequests() != 0) { + multiSearchRequest.maxConcurrentSearchRequests(request.maxConcurrentSearchRequests()); + } - @Override - public void onFailure(Exception e) { - responses.set(index, new MultiSearchTemplateResponse.Item(null, e)); - if (counter.decrementAndGet() == 0) { - finishHim(); - } - } + MultiSearchTemplateResponse.Item[] items = new MultiSearchTemplateResponse.Item[request.requests().size()]; + for (int i = 0; i < items.length; i++) { + SearchTemplateRequest searchTemplateRequest = request.requests().get(i); + SearchTemplateResponse searchTemplateResponse = new SearchTemplateResponse(); + SearchRequest searchRequest; + try { + searchRequest = convert(searchTemplateRequest, searchTemplateResponse, scriptService, xContentRegistry); + } catch (Exception e) { + items[i] = new MultiSearchTemplateResponse.Item(null, e); + continue; + } + items[i] = new MultiSearchTemplateResponse.Item(searchTemplateResponse, null); + if (searchRequest != null) { + multiSearchRequest.add(searchRequest); + originalSlots.add(i); + } + } - private void finishHim() { - MultiSearchTemplateResponse.Item[] items = responses.toArray(new MultiSearchTemplateResponse.Item[responses.length()]); - listener.onResponse(new MultiSearchTemplateResponse(items)); + multiSearchAction.execute(multiSearchRequest, ActionListener.wrap(r -> { + for (int i = 0; i < r.getResponses().length; i++) { + MultiSearchResponse.Item item = r.getResponses()[i]; + int originalSlot = originalSlots.get(i); + if (item.isFailure()) { + items[originalSlot] = new MultiSearchTemplateResponse.Item(null, item.getFailure()); + } else { + items[originalSlot].getResponse().setResponse(item.getResponse()); } - }); - } + } + listener.onResponse(new MultiSearchTemplateResponse(items)); + }, listener::onFailure)); } } diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java index 10922a94a8917..8c3e01359128c 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java @@ -38,9 +38,11 @@ import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.template.CompiledTemplate; +import org.elasticsearch.template.CompiledTemplate; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; +import java.io.IOException; import java.util.Collections; import static org.elasticsearch.script.ScriptContext.Standard.SEARCH; @@ -69,27 +71,8 @@ public TransportSearchTemplateAction(Settings settings, ThreadPool threadPool, T protected void doExecute(SearchTemplateRequest request, ActionListener listener) { final SearchTemplateResponse response = new SearchTemplateResponse(); try { - Script script = new Script(request.getScriptType(), TEMPLATE_LANG, request.getScript(), - request.getScriptParams() == null ? Collections.emptyMap() : request.getScriptParams()); - CompiledTemplate compiledScript = scriptService.compileTemplate(script, SEARCH); - BytesReference source = compiledScript.run(script.getParams()); - response.setSource(source); - - if (request.isSimulate()) { - listener.onResponse(response); - return; - } - - // Executes the search - SearchRequest searchRequest = request.getRequest(); - //we can assume the template is always json as we convert it before compiling it - try (XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(xContentRegistry, source)) { - SearchSourceBuilder builder = SearchSourceBuilder.searchSource(); - builder.parseXContent(new QueryParseContext(parser)); - builder.explain(request.isExplain()); - builder.profile(request.isProfile()); - searchRequest.source(builder); - + SearchRequest searchRequest = convert(request, response, scriptService, xContentRegistry); + if (searchRequest != null) { searchAction.execute(searchRequest, new ActionListener() { @Override public void onResponse(SearchResponse searchResponse) { @@ -106,9 +89,35 @@ public void onFailure(Exception t) { listener.onFailure(t); } }); + } else { + listener.onResponse(response); } - } catch (Exception t) { - listener.onFailure(t); + } catch (IOException e) { + listener.onFailure(e); + } + } + + static SearchRequest convert(SearchTemplateRequest searchTemplateRequest, SearchTemplateResponse response, ScriptService scriptService, + NamedXContentRegistry xContentRegistry) throws IOException { + Script script = new Script(searchTemplateRequest.getScriptType(), TEMPLATE_LANG, searchTemplateRequest.getScript(), + searchTemplateRequest.getScriptParams() == null ? Collections.emptyMap() : searchTemplateRequest.getScriptParams()); + CompiledTemplate compiledScript = scriptService.compileTemplate(script, SEARCH); + BytesReference source = compiledScript.run(script.getParams()); + response.setSource(source); + + SearchRequest searchRequest = searchTemplateRequest.getRequest(); + response.setSource(source); + if (searchTemplateRequest.isSimulate()) { + return null; + } + + try (XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(xContentRegistry, source)) { + SearchSourceBuilder builder = SearchSourceBuilder.searchSource(); + builder.parseXContent(new QueryParseContext(parser)); + builder.explain(searchTemplateRequest.isExplain()); + builder.profile(searchTemplateRequest.isProfile()); + searchRequest.source(builder); } + return searchRequest; } } From 21784913873c6ff821e357b99cedb1eeba8cd599 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Wed, 10 May 2017 11:34:34 +0200 Subject: [PATCH 273/619] Fix checkstyle violation --- .../search/aggregations/ParsedMultiBucketAggregation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java index 02e29a1746faa..52b5fad53798e 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java @@ -78,7 +78,7 @@ protected static void declareMultiBucketAggregationFields(final ObjectParser Date: Wed, 10 May 2017 11:37:20 +0200 Subject: [PATCH 274/619] [Test] Reference Terms aggregations in AggregationsTests --- .../search/aggregations/AggregationsTests.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java index f986f614fa172..4ac6c62f30f69 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java @@ -29,6 +29,9 @@ import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; import org.elasticsearch.search.aggregations.bucket.histogram.InternalDateHistogramTests; import org.elasticsearch.search.aggregations.bucket.histogram.InternalHistogramTests; +import org.elasticsearch.search.aggregations.bucket.terms.DoubleTermsTests; +import org.elasticsearch.search.aggregations.bucket.terms.LongTermsTests; +import org.elasticsearch.search.aggregations.bucket.terms.StringTermsTests; import org.elasticsearch.search.aggregations.metrics.InternalExtendedStatsTests; import org.elasticsearch.search.aggregations.metrics.InternalMaxTests; import org.elasticsearch.search.aggregations.metrics.InternalStatsBucketTests; @@ -96,6 +99,9 @@ private static List getAggsTests() { aggsTests.add(new InternalGeoCentroidTests()); aggsTests.add(new InternalHistogramTests()); aggsTests.add(new InternalDateHistogramTests()); + aggsTests.add(new LongTermsTests()); + aggsTests.add(new DoubleTermsTests()); + aggsTests.add(new StringTermsTests()); return Collections.unmodifiableList(aggsTests); } From 0ff5933a55379e32768d5ac2d9dbb7a8fa3a1af1 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Wed, 10 May 2017 11:47:26 +0200 Subject: [PATCH 275/619] Rewrite multi search template api to delegate to multi search api instead of to search template api. The max concurrent searches logic is complex and we shouldn't duplicate that in multi search template api, so we should template each individual template search request and then delegate to multi search api. --- .../script/mustache/MultiSearchTemplateRequest.java | 11 ++++++++--- .../mustache/MultiSearchTemplateRequestBuilder.java | 4 ---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java index b93e4ef77b6e1..fceb6fb98d9f5 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java @@ -19,6 +19,7 @@ package org.elasticsearch.script.mustache; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.CompositeIndicesRequest; @@ -34,7 +35,7 @@ public class MultiSearchTemplateRequest extends ActionRequest implements CompositeIndicesRequest { - private int maxConcurrentSearchRequests = 1; + private int maxConcurrentSearchRequests = 0; private List requests = new ArrayList<>(); private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpenAndForbidClosed(); @@ -111,14 +112,18 @@ public MultiSearchTemplateRequest indicesOptions(IndicesOptions indicesOptions) @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); - maxConcurrentSearchRequests = in.readVInt(); + if (in.getVersion().onOrAfter(Version.V_5_5_0_UNRELEASED)) { + maxConcurrentSearchRequests = in.readVInt(); + } requests = in.readStreamableList(SearchTemplateRequest::new); } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); - out.writeVInt(maxConcurrentSearchRequests); + if (out.getVersion().onOrAfter(Version.V_5_5_0_UNRELEASED)) { + out.writeVInt(maxConcurrentSearchRequests); + } out.writeStreamableList(requests); } } diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequestBuilder.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequestBuilder.java index 5f5ab400f6b96..4ef6c593d9a41 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequestBuilder.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequestBuilder.java @@ -30,10 +30,6 @@ protected MultiSearchTemplateRequestBuilder(ElasticsearchClient client, MultiSea super(client, action, new MultiSearchTemplateRequest()); } - public MultiSearchTemplateRequestBuilder(ElasticsearchClient client) { - this(client, MultiSearchTemplateAction.INSTANCE); - } - public MultiSearchTemplateRequestBuilder add(SearchTemplateRequest request) { if (request.getRequest().indicesOptions() == IndicesOptions.strictExpandOpenAndForbidClosed() && request().indicesOptions() != IndicesOptions.strictExpandOpenAndForbidClosed()) { From 2fe53be0db07f799a1c700a2d320da59bb41de0e Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Wed, 10 May 2017 12:22:52 +0200 Subject: [PATCH 276/619] [Test] Add unit tests for Range aggregations (#24569) Related to #22278 --- .../bucket/range/InternalRange.java | 38 +++++++- .../bucket/range/InternalRangeTestCase.java | 68 +++++++++++++ .../bucket/range/InternalRangeTests.java | 82 ++++++++++++++++ .../range/date/InternalDateRangeTests.java | 97 +++++++++++++++++++ .../geodistance/InternalGeoDistanceTests.java | 81 ++++++++++++++++ 5 files changed, 364 insertions(+), 2 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTestCase.java create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTests.java create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/date/InternalDateRangeTests.java create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/InternalGeoDistanceTests.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/InternalRange.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/InternalRange.java index 3d0cf1b5a8c7d..f5bb0e25c66d5 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/InternalRange.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/InternalRange.java @@ -34,6 +34,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; public class InternalRange> extends InternalMultiBucketAggregation implements Range { @@ -172,6 +173,27 @@ protected String generateKey(double from, double to, DocValueFormat formatter) { @Override public void writeTo(StreamOutput out) throws IOException { } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + Bucket that = (Bucket) other; + return Objects.equals(from, that.from) + && Objects.equals(to, that.to) + && Objects.equals(docCount, that.docCount) + && Objects.equals(aggregations, that.aggregations) + && Objects.equals(key, that.key); + } + + @Override + public int hashCode() { + return Objects.hash(getClass(), from, to, docCount, aggregations, key); + } } public static class Factory> { @@ -245,8 +267,8 @@ protected void doWriteTo(StreamOutput out) throws IOException { out.writeVInt(ranges.size()); for (B bucket : ranges) { out.writeOptionalString(((Bucket) bucket).key); - out.writeDouble(((Bucket) bucket).from); - out.writeDouble(((Bucket) bucket).to); + out.writeDouble(bucket.from); + out.writeDouble(bucket.to); out.writeVLong(((Bucket) bucket).docCount); bucket.aggregations.writeTo(out); } @@ -317,4 +339,16 @@ public XContentBuilder doXContentBody(XContentBuilder builder, Params params) th return builder; } + @Override + protected int doHashCode() { + return Objects.hash(ranges, format, keyed); + } + + @Override + protected boolean doEquals(Object obj) { + InternalRange that = (InternalRange) obj; + return Objects.equals(ranges, that.ranges) + && Objects.equals(format, that.format) + && Objects.equals(keyed, that.keyed); + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTestCase.java new file mode 100644 index 0000000000000..c00a4c20fc7e7 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTestCase.java @@ -0,0 +1,68 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.range; + +import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.junit.Before; + +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public abstract class InternalRangeTestCase extends InternalAggregationTestCase { + + private boolean keyed; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + keyed = randomBoolean(); + } + + @Override + protected T createTestInstance(String name, List pipelineAggregators, Map metaData) { + return createTestInstance(name, pipelineAggregators, metaData, keyed); + } + + protected abstract T createTestInstance(String name, + List pipelineAggregators, + Map metaData, + boolean keyed); + @Override + protected void assertReduced(T reduced, List inputs) { + final Map expectedCounts = new TreeMap<>(); + for (T input : inputs) { + for (Range.Bucket bucket : input.getBuckets()) { + expectedCounts.compute(bucket.getKeyAsString(), + (key, oldValue) -> (oldValue == null ? 0 : oldValue) + bucket.getDocCount()); + + } + } + final Map actualCounts = new TreeMap<>(); + for (Range.Bucket bucket : reduced.getBuckets()) { + actualCounts.compute(bucket.getKeyAsString(), + (key, oldValue) -> (oldValue == null ? 0 : oldValue) + bucket.getDocCount()); + } + assertEquals(expectedCounts, actualCounts); + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTests.java new file mode 100644 index 0000000000000..9264028d0734a --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTests.java @@ -0,0 +1,82 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.range; + +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.junit.Before; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class InternalRangeTests extends InternalRangeTestCase { + + private DocValueFormat format; + private List> ranges; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + format = randomNumericDocValueFormat(); + + final int interval = randomFrom(1, 5, 10, 25, 50, 100); + final int numRanges = 1;//randomIntBetween(1, 10); + + List> listOfRanges = new ArrayList<>(numRanges); + for (int i = 0; i < numRanges; i++) { + double from = i * interval; + double to = from + interval; + listOfRanges.add(Tuple.tuple(from, to)); + } + if (randomBoolean()) { + // Add some overlapping ranges + double max = (double) numRanges * interval; + listOfRanges.add(Tuple.tuple(0.0, max)); + listOfRanges.add(Tuple.tuple(0.0, max / 2)); + listOfRanges.add(Tuple.tuple(max / 3, max / 3 * 2)); + } + ranges = Collections.unmodifiableList(listOfRanges); + } + + @Override + protected InternalRange createTestInstance(String name, List pipelineAggregators, Map metaData, + boolean keyed) { + final List buckets = new ArrayList<>(); + for (int i = 0; i < ranges.size(); ++i) { + Tuple range = ranges.get(i); + int docCount = randomIntBetween(0, 1000); + double from = range.v1(); + double to = range.v2(); + buckets.add( new InternalRange.Bucket("range_" + i, from, to, docCount, InternalAggregations.EMPTY, keyed, format)); + } + return new InternalRange<>(name, buckets, format, keyed, pipelineAggregators, Collections.emptyMap()); + } + + @Override + protected Writeable.Reader instanceReader() { + return InternalRange::new; + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/date/InternalDateRangeTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/date/InternalDateRangeTests.java new file mode 100644 index 0000000000000..bbfcdf7463aae --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/date/InternalDateRangeTests.java @@ -0,0 +1,97 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.range.date; + +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.bucket.range.InternalRangeTestCase; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.junit.Before; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +public class InternalDateRangeTests extends InternalRangeTestCase { + + private DocValueFormat format; + private List> dateRanges; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + format = randomNumericDocValueFormat(); + + Function interval = randomFrom(dateTime -> dateTime.plusSeconds(1), dateTime -> dateTime.plusMinutes(1), + dateTime -> dateTime.plusHours(1), dateTime -> dateTime.plusDays(1), dateTime -> dateTime.plusMonths(1), dateTime -> + dateTime.plusYears(1)); + + final int numRanges = randomIntBetween(1, 10); + final List> listOfRanges = new ArrayList<>(numRanges); + + DateTime date = new DateTime(DateTimeZone.UTC); + double start = date.getMillis(); + double end = 0; + for (int i = 0; i < numRanges; i++) { + double from = date.getMillis(); + date = interval.apply(date); + double to = date.getMillis(); + listOfRanges.add(Tuple.tuple(from, to)); + if (to > end) { + end = to; + } + } + if (randomBoolean()) { + final int randomOverlaps = randomIntBetween(1, 5); + for (int i = 0; i < randomOverlaps; i++) { + listOfRanges.add(Tuple.tuple(start, randomDoubleBetween(start, end, false))); + } + } + dateRanges = Collections.unmodifiableList(listOfRanges); + } + + @Override + protected InternalDateRange createTestInstance(String name, + List pipelineAggregators, + Map metaData, + boolean keyed) { + final List buckets = new ArrayList<>(); + for (int i = 0; i < dateRanges.size(); ++i) { + Tuple range = dateRanges.get(i); + int docCount = randomIntBetween(0, 1000); + double from = range.v1(); + double to = range.v2(); + buckets.add( new InternalDateRange.Bucket("range_" + i, from, to, docCount, InternalAggregations.EMPTY, keyed, format)); + } + return new InternalDateRange(name, buckets, format, keyed, pipelineAggregators, metaData); + } + + @Override + protected Writeable.Reader instanceReader() { + return InternalDateRange::new; + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/InternalGeoDistanceTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/InternalGeoDistanceTests.java new file mode 100644 index 0000000000000..9dd2a7a67c75b --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/InternalGeoDistanceTests.java @@ -0,0 +1,81 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.range.geodistance; + +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.bucket.range.InternalRangeTestCase; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.junit.Before; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class InternalGeoDistanceTests extends InternalRangeTestCase { + + private List> geoDistanceRanges; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + + final int interval = randomFrom(1, 5, 10, 25, 50, 100); + final int numRanges = randomIntBetween(1, 10); + + List> listOfRanges = new ArrayList<>(numRanges); + for (int i = 0; i < numRanges; i++) { + double from = i * interval; + double to = from + interval; + listOfRanges.add(Tuple.tuple(from, to)); + } + if (randomBoolean()) { + // Add some overlapping ranges + double max = (double) numRanges * interval; + listOfRanges.add(Tuple.tuple(0.0, max)); + listOfRanges.add(Tuple.tuple(0.0, max / 2)); + listOfRanges.add(Tuple.tuple(max / 3, max / 3 * 2)); + } + geoDistanceRanges = Collections.unmodifiableList(listOfRanges); + } + @Override + protected Writeable.Reader instanceReader() { + return InternalGeoDistance::new; + } + + @Override + protected InternalGeoDistance createTestInstance(String name, + List pipelineAggregators, + Map metaData, + boolean keyed) { + final List buckets = new ArrayList<>(); + for (int i = 0; i < geoDistanceRanges.size(); ++i) { + Tuple range = geoDistanceRanges.get(i); + int docCount = randomIntBetween(0, 1000); + double from = range.v1(); + double to = range.v2(); + buckets.add(new InternalGeoDistance.Bucket("range_" + i, from, to, docCount, InternalAggregations.EMPTY, keyed)); + } + return new InternalGeoDistance(name, buckets, keyed, pipelineAggregators, metaData); + } +} From 3e4406f044676d50aab0e7135d2a289a929ea84b Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Wed, 10 May 2017 12:31:28 +0200 Subject: [PATCH 277/619] [Test] Fix wrong import --- .../search/aggregations/bucket/range/InternalRangeTestCase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTestCase.java index c00a4c20fc7e7..1edaea2f8ecf7 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTestCase.java @@ -20,8 +20,8 @@ package org.elasticsearch.search.aggregations.bucket.range; import org.elasticsearch.search.aggregations.InternalAggregation; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import org.junit.Before; import java.util.List; From 2486086980663bce5ff25f5ed7eb25d6a2cfc7c7 Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Wed, 10 May 2017 14:04:36 +0200 Subject: [PATCH 278/619] Deprecate the Tribe node The Tribe node is deprecated in favour of Cross Cluster Search and will be removed in 7.0. --- docs/reference/modules/tribe.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference/modules/tribe.asciidoc b/docs/reference/modules/tribe.asciidoc index a1240a28f84f9..ded8bc43a076e 100644 --- a/docs/reference/modules/tribe.asciidoc +++ b/docs/reference/modules/tribe.asciidoc @@ -1,6 +1,8 @@ [[modules-tribe]] == Tribe node +deprecated[5.4.0,The `tribe` node is deprecated in favour of <> and will be removed in Elasticsearch 7.0.] + The _tribes_ feature allows a _tribe node_ to act as a federated client across multiple clusters. From 3201e2271032e348d82c0725b381066e149a0e37 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Wed, 10 May 2017 14:05:43 +0200 Subject: [PATCH 279/619] Fix merging conflicts --- .../aggregations/InternalAggregation.java | 2 +- .../search/aggregations/ParsedAggregation.java | 2 +- .../bucket/histogram/ParsedDateHistogram.java | 2 +- .../bucket/histogram/ParsedHistogram.java | 2 +- .../bucket/sampler/InternalSampler.java | 2 +- .../significant/UnmappedSignificantTerms.java | 2 +- .../bucket/terms/ParsedDoubleTerms.java | 2 +- .../bucket/terms/ParsedLongTerms.java | 2 +- .../bucket/terms/ParsedStringTerms.java | 2 +- .../bucket/terms/UnmappedTerms.java | 2 +- .../aggregations/metrics/avg/ParsedAvg.java | 2 +- .../metrics/cardinality/ParsedCardinality.java | 2 +- .../metrics/geocentroid/ParsedGeoCentroid.java | 2 +- .../aggregations/metrics/max/ParsedMax.java | 2 +- .../aggregations/metrics/min/ParsedMin.java | 2 +- .../hdr/ParsedHDRPercentileRanks.java | 2 +- .../percentiles/hdr/ParsedHDRPercentiles.java | 2 +- .../tdigest/ParsedTDigestPercentileRanks.java | 2 +- .../tdigest/ParsedTDigestPercentiles.java | 2 +- .../metrics/stats/ParsedStats.java | 2 +- .../stats/extended/ParsedExtendedStats.java | 2 +- .../aggregations/metrics/sum/ParsedSum.java | 2 +- .../metrics/valuecount/ParsedValueCount.java | 2 +- .../pipeline/ParsedSimpleValue.java | 2 +- .../bucketmetrics/ParsedBucketMetricValue.java | 2 +- .../bucketmetrics/stats/ParsedStatsBucket.java | 2 +- .../extended/ParsedExtendedStatsBucket.java | 2 +- .../pipeline/derivative/ParsedDerivative.java | 2 +- .../search/aggregations/AggregationsTests.java | 1 + ...InternalMultiBucketAggregationTestCase.java | 1 + .../aggregations/ParsedAggregationTests.java | 4 ++-- .../metrics/avg/InternalAvgTests.java | 2 +- .../AbstractPercentilesTestCase.java | 2 +- .../InternalTDigestPercentilesRanksTests.java | 2 +- .../InternalBucketMetricValueTests.java | 2 +- .../test/InternalAggregationTestCase.java | 18 +++++++++++++++--- 36 files changed, 51 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/InternalAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/InternalAggregation.java index 0618e5cb29d23..c86d8afed5b3a 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/InternalAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/InternalAggregation.java @@ -174,7 +174,7 @@ public List pipelineAggregators() { * the aggregation name in the response, so that it can later be used by REST clients * to determine the internal type of the aggregation. */ - protected String getType() { + public String getType() { return getWriteableName(); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java index 6942b6aec5d1f..3ff91d8d36d70 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java @@ -63,7 +63,7 @@ public final Map getMetaData() { * to determine the internal type of the aggregation. */ //TODO it may make sense to move getType to the Aggregation interface given that we are duplicating it in both implementations - protected abstract String getType(); + public abstract String getType(); @Override public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedDateHistogram.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedDateHistogram.java index 9efc478aff1aa..ace0cb59907a8 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedDateHistogram.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedDateHistogram.java @@ -32,7 +32,7 @@ public class ParsedDateHistogram extends ParsedMultiBucketAggregation implements Histogram { @Override - protected String getType() { + public String getType() { return DateHistogramAggregationBuilder.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedHistogram.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedHistogram.java index eeec1cd81ba7f..6037c1558867a 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedHistogram.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ParsedHistogram.java @@ -29,7 +29,7 @@ public class ParsedHistogram extends ParsedMultiBucketAggregation implements Histogram { @Override - protected String getType() { + public String getType() { return HistogramAggregationBuilder.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSampler.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSampler.java index 0c02f1ae935ff..3b5b42d59fdd5 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSampler.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSampler.java @@ -48,7 +48,7 @@ public String getWriteableName() { } @Override - protected String getType() { + public String getType() { return "sampler"; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/UnmappedSignificantTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/UnmappedSignificantTerms.java index f9d8375af8c5b..e79b01abe4bbf 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/UnmappedSignificantTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/UnmappedSignificantTerms.java @@ -76,7 +76,7 @@ public String getWriteableName() { } @Override - protected String getType() { + public String getType() { return SignificantStringTerms.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedDoubleTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedDoubleTerms.java index 3401cd021bde6..d3afe5c17603f 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedDoubleTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedDoubleTerms.java @@ -28,7 +28,7 @@ public class ParsedDoubleTerms extends ParsedTerms { @Override - protected String getType() { + public String getType() { return DoubleTerms.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedLongTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedLongTerms.java index 6de25dfefe1de..b5869fc6ee2d8 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedLongTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedLongTerms.java @@ -28,7 +28,7 @@ public class ParsedLongTerms extends ParsedTerms { @Override - protected String getType() { + public String getType() { return LongTerms.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedStringTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedStringTerms.java index f258f7f847ed0..792365d6b1aa2 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedStringTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/ParsedStringTerms.java @@ -29,7 +29,7 @@ public class ParsedStringTerms extends ParsedTerms { @Override - protected String getType() { + public String getType() { return StringTerms.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/UnmappedTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/UnmappedTerms.java index 40cbacd37e698..6aaccd22d72c8 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/UnmappedTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/UnmappedTerms.java @@ -73,7 +73,7 @@ public String getWriteableName() { } @Override - protected String getType() { + public String getType() { return StringTerms.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java index d77fd2bb3ad35..16d91bd08f0d3 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java @@ -34,7 +34,7 @@ public double getValue() { } @Override - protected String getType() { + public String getType() { return AvgAggregationBuilder.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java index 77356db519705..82104fc9db627 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java @@ -50,7 +50,7 @@ public long getValue() { } @Override - protected String getType() { + public String getType() { return CardinalityAggregationBuilder.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/ParsedGeoCentroid.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/ParsedGeoCentroid.java index ed09c281868d4..7ce1f5d86feb3 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/ParsedGeoCentroid.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/ParsedGeoCentroid.java @@ -46,7 +46,7 @@ public long count() { } @Override - protected String getType() { + public String getType() { return GeoCentroidAggregationBuilder.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java index 86eab0025acd8..f6a3190cd04d4 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java @@ -34,7 +34,7 @@ public double getValue() { } @Override - protected String getType() { + public String getType() { return MaxAggregationBuilder.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java index c50cd5c3615be..9b214bb346201 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java @@ -34,7 +34,7 @@ public double getValue() { } @Override - protected String getType() { + public String getType() { return MinAggregationBuilder.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java index 5d00d8dc8e3f0..f5fd7717e04bf 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentileRanks.java @@ -31,7 +31,7 @@ public class ParsedHDRPercentileRanks extends ParsedPercentileRanks { @Override - protected String getType() { + public String getType() { return InternalHDRPercentileRanks.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentiles.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentiles.java index 0b6da9f00e112..1b1ba906aa087 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentiles.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/ParsedHDRPercentiles.java @@ -29,7 +29,7 @@ public class ParsedHDRPercentiles extends ParsedPercentiles implements Percentiles { @Override - protected String getType() { + public String getType() { return InternalHDRPercentiles.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java index 34fd92ad4fb2d..01929f374d486 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentileRanks.java @@ -31,7 +31,7 @@ public class ParsedTDigestPercentileRanks extends ParsedPercentileRanks { @Override - protected String getType() { + public String getType() { return InternalTDigestPercentileRanks.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentiles.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentiles.java index bdd2d6b810a2c..cbae25d61e046 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentiles.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/ParsedTDigestPercentiles.java @@ -29,7 +29,7 @@ public class ParsedTDigestPercentiles extends ParsedPercentiles implements Percentiles { @Override - protected String getType() { + public String getType() { return InternalTDigestPercentiles.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java index 1711e4ee15df2..239548ecdebc6 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java @@ -87,7 +87,7 @@ public String getSumAsString() { } @Override - protected String getType() { + public String getType() { return StatsAggregationBuilder.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java index 0eb0bdb215de9..59311127368f5 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ParsedExtendedStats.java @@ -44,7 +44,7 @@ public class ParsedExtendedStats extends ParsedStats implements ExtendedStats { protected double avg; @Override - protected String getType() { + public String getType() { return ExtendedStatsAggregationBuilder.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java index 86d3e1c4295e2..a51f03d356549 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java @@ -34,7 +34,7 @@ public double getValue() { } @Override - protected String getType() { + public String getType() { return SumAggregationBuilder.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ParsedValueCount.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ParsedValueCount.java index 012323f0041e8..7430bca08de32 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ParsedValueCount.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ParsedValueCount.java @@ -48,7 +48,7 @@ public String getValueAsString() { } @Override - protected String getType() { + public String getType() { return ValueCountAggregationBuilder.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/ParsedSimpleValue.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/ParsedSimpleValue.java index 7d1bcec3bc7ba..7449ce66666e6 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/ParsedSimpleValue.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/ParsedSimpleValue.java @@ -29,7 +29,7 @@ public class ParsedSimpleValue extends ParsedSingleValueNumericMetricsAggregation implements SimpleValue { @Override - protected String getType() { + public String getType() { return InternalSimpleValue.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/ParsedBucketMetricValue.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/ParsedBucketMetricValue.java index 9008c56c5caaa..69e99352636b6 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/ParsedBucketMetricValue.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/ParsedBucketMetricValue.java @@ -38,7 +38,7 @@ public String[] keys() { } @Override - protected String getType() { + public String getType() { return InternalBucketMetricValue.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/ParsedStatsBucket.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/ParsedStatsBucket.java index 4738ad7d9daa6..c7ddcc6ee9686 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/ParsedStatsBucket.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/ParsedStatsBucket.java @@ -27,7 +27,7 @@ public class ParsedStatsBucket extends ParsedStats implements StatsBucket { @Override - protected String getType() { + public String getType() { return StatsBucketPipelineAggregationBuilder.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/extended/ParsedExtendedStatsBucket.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/extended/ParsedExtendedStatsBucket.java index 08bfbfe587b36..d292249242396 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/extended/ParsedExtendedStatsBucket.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/stats/extended/ParsedExtendedStatsBucket.java @@ -27,7 +27,7 @@ public class ParsedExtendedStatsBucket extends ParsedExtendedStats implements ExtendedStatsBucket { @Override - protected String getType() { + public String getType() { return ExtendedStatsBucketPipelineAggregationBuilder.NAME; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java index 5469b8b65cfe8..2b871a99d9a6a 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java @@ -42,7 +42,7 @@ public double normalizedValue() { } @Override - protected String getType() { + public String getType() { return DerivativePipelineAggregationBuilder.NAME; } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java index 4ac6c62f30f69..99129529e2c80 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java @@ -53,6 +53,7 @@ import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.extended.InternalExtendedStatsBucketTests; import org.elasticsearch.search.aggregations.pipeline.derivative.InternalDerivativeTests; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.InternalAggregationTestCase; import org.elasticsearch.test.hamcrest.ElasticsearchAssertions; import org.junit.After; import org.junit.Before; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java index 64eee02dcb68e..b16b7f49d7e02 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java @@ -21,6 +21,7 @@ import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.io.IOException; import java.util.ArrayList; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java index f56ae175a86a9..bfb8712752d4e 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java @@ -94,7 +94,7 @@ private static class TestParsedAggregation extends ParsedAggregation { } @Override - protected String getType() { + public String getType() { return "type"; } @@ -122,7 +122,7 @@ public String getWriteableName() { } @Override - protected String getType() { + public String getType() { return "type"; } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java index 4e05b1bd687a8..9cb248401d517 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java @@ -21,7 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.test.InternalAggregationTestCase; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java index b3cfbcea6cb6e..e7d808a9b3d7b 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java @@ -21,8 +21,8 @@ import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregation; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.io.IOException; import java.util.Arrays; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java index 189091433aced..f8698fda2cb7a 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/InternalTDigestPercentilesRanksTests.java @@ -21,9 +21,9 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.metrics.percentiles.InternalPercentilesRanksTestCase; import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentiles; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import org.elasticsearch.test.InternalAggregationTestCase; import java.util.Arrays; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/InternalBucketMetricValueTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/InternalBucketMetricValueTests.java index 8de1700141ba3..e58538864a81f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/InternalBucketMetricValueTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/InternalBucketMetricValueTests.java @@ -20,9 +20,9 @@ package org.elasticsearch.search.aggregations.pipeline.bucketmetrics; import org.elasticsearch.common.io.stream.Writeable.Reader; -import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; import java.util.Collections; import java.util.List; diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java index 22c16cf9afb15..64b09a0245ff5 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java @@ -35,6 +35,9 @@ import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.SearchModule; +import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram; @@ -76,7 +79,16 @@ import org.elasticsearch.search.aggregations.pipeline.InternalSimpleValue; import org.elasticsearch.search.aggregations.pipeline.ParsedSimpleValue; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import org.elasticsearch.test.AbstractWireSerializingTestCase; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.InternalBucketMetricValue; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.ParsedBucketMetricValue; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.percentile.ParsedPercentilesBucket; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.percentile.PercentilesBucketPipelineAggregationBuilder; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.ParsedStatsBucket; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.StatsBucketPipelineAggregationBuilder; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.extended.ExtendedStatsBucketPipelineAggregationBuilder; +import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.extended.ParsedExtendedStatsBucket; +import org.elasticsearch.search.aggregations.pipeline.derivative.DerivativePipelineAggregationBuilder; +import org.elasticsearch.search.aggregations.pipeline.derivative.ParsedDerivative; import java.io.IOException; import java.util.ArrayList; @@ -99,7 +111,7 @@ public abstract class InternalAggregationTestCase private final NamedXContentRegistry namedXContentRegistry = new NamedXContentRegistry(getNamedXContents()); - static List getNamedXContents() { + public static List getNamedXContents() { Map> namedXContents = new HashMap<>(); namedXContents.put(CardinalityAggregationBuilder.NAME, (p, c) -> ParsedCardinality.fromXContent(p, (String) c)); namedXContents.put(InternalHDRPercentiles.NAME, (p, c) -> ParsedHDRPercentiles.fromXContent(p, (String) c)); @@ -184,7 +196,7 @@ protected ScriptService mockScriptService() { protected abstract void assertReduced(T reduced, List inputs); @Override - protected final T createTestInstance() { + public final T createTestInstance() { return createTestInstance(randomAlphaOfLength(5)); } From d611ab48550180bd735d2f9819f16932dd38b167 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Wed, 10 May 2017 14:59:14 +0100 Subject: [PATCH 280/619] Avoid race when shutting down controller processes (#24579) This commit terminates any controller processes plugins might have after the node has been closed. This gives the plugins a chance to shut down their controllers gracefully. Previously there was a race condition where controller processes could be shut down gracefully and terminated by two threads running in parallel, leading to non-deterministic outcomes. Additionally, controller processes that failed to shut down gracefully were not forcibly terminated when running as a Windows service; there was a reliance on the plugin to shut down its controller gracefully in this situation. This commit also fixes this problem. --- .../org/elasticsearch/bootstrap/Bootstrap.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index 2b47908c35260..1e53faa9efc4e 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -163,16 +163,6 @@ private void setup(boolean addShutdownHook, Environment environment) throws Boot try { spawner.spawnNativePluginControllers(environment); - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - try { - spawner.close(); - } catch (IOException e) { - throw new ElasticsearchException("Failed to destroy spawned controllers", e); - } - } - }); } catch (IOException e) { throw new BootstrapException(e); } @@ -191,7 +181,7 @@ public void run() { @Override public void run() { try { - IOUtils.close(node); + IOUtils.close(node, spawner); LoggerContext context = (LoggerContext) LogManager.getContext(false); Configurator.shutdown(context); } catch (IOException ex) { @@ -269,7 +259,7 @@ private void start() throws NodeValidationException { static void stop() throws IOException { try { - IOUtils.close(INSTANCE.node); + IOUtils.close(INSTANCE.node, INSTANCE.spawner); } finally { INSTANCE.keepAliveLatch.countDown(); } From 64ecf4f4bf023ba2a1ccf2409730cd44b82de9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 10 May 2017 17:12:56 +0200 Subject: [PATCH 281/619] Bumping version to v6.0.0-alpha2 --- buildSrc/version.properties | 2 +- core/src/main/java/org/elasticsearch/Version.java | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/buildSrc/version.properties b/buildSrc/version.properties index 680c2fc89f5bf..69eebdd23275d 100644 --- a/buildSrc/version.properties +++ b/buildSrc/version.properties @@ -1,5 +1,5 @@ # When updating elasticsearch, please update 'rest' version in core/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy -elasticsearch = 6.0.0-alpha1 +elasticsearch = 6.0.0-alpha2 lucene = 7.0.0-snapshot-89f6d17 # optional dependencies diff --git a/core/src/main/java/org/elasticsearch/Version.java b/core/src/main/java/org/elasticsearch/Version.java index ea25a1bad8ae2..d23417df071c5 100644 --- a/core/src/main/java/org/elasticsearch/Version.java +++ b/core/src/main/java/org/elasticsearch/Version.java @@ -87,7 +87,10 @@ public class Version implements Comparable { public static final int V_6_0_0_alpha1_ID_UNRELEASED = 6000001; public static final Version V_6_0_0_alpha1_UNRELEASED = new Version(V_6_0_0_alpha1_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_7_0_0); - public static final Version CURRENT = V_6_0_0_alpha1_UNRELEASED; + public static final int V_6_0_0_alpha2_ID_UNRELEASED = 6000002; + public static final Version V_6_0_0_alpha2_UNRELEASED = + new Version(V_6_0_0_alpha2_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_7_0_0); + public static final Version CURRENT = V_6_0_0_alpha2_UNRELEASED; // unreleased versions must be added to the above list with the suffix _UNRELEASED (with the exception of CURRENT) @@ -102,6 +105,8 @@ public static Version readVersion(StreamInput in) throws IOException { public static Version fromId(int id) { switch (id) { + case V_6_0_0_alpha2_ID_UNRELEASED: + return V_6_0_0_alpha2_UNRELEASED; case V_6_0_0_alpha1_ID_UNRELEASED: return V_6_0_0_alpha1_UNRELEASED; case V_5_5_0_ID_UNRELEASED: From fbc8345db56d99a45009449bba1a1899d45b0cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 10 May 2017 17:36:12 +0200 Subject: [PATCH 282/619] Tests: Fix VersionUtilsTests after version bump --- .../elasticsearch/test/test/VersionUtilsTests.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/framework/src/test/java/org/elasticsearch/test/test/VersionUtilsTests.java b/test/framework/src/test/java/org/elasticsearch/test/test/VersionUtilsTests.java index eee31dd09b471..5641917bc45ad 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/test/VersionUtilsTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/test/VersionUtilsTests.java @@ -47,14 +47,14 @@ public void testRandomVersionBetween() { // sub range got = VersionUtils.randomVersionBetween(random(), Version.V_5_0_0, - Version.V_6_0_0_alpha1_UNRELEASED); + Version.V_6_0_0_alpha2_UNRELEASED); assertTrue(got.onOrAfter(Version.V_5_0_0)); - assertTrue(got.onOrBefore(Version.V_6_0_0_alpha1_UNRELEASED)); + assertTrue(got.onOrBefore(Version.V_6_0_0_alpha2_UNRELEASED)); // unbounded lower - got = VersionUtils.randomVersionBetween(random(), null, Version.V_6_0_0_alpha1_UNRELEASED); + got = VersionUtils.randomVersionBetween(random(), null, Version.V_6_0_0_alpha2_UNRELEASED); assertTrue(got.onOrAfter(VersionUtils.getFirstVersion())); - assertTrue(got.onOrBefore(Version.V_6_0_0_alpha1_UNRELEASED)); + assertTrue(got.onOrBefore(Version.V_6_0_0_alpha2_UNRELEASED)); got = VersionUtils.randomVersionBetween(random(), null, VersionUtils.allReleasedVersions().get(0)); assertTrue(got.onOrAfter(VersionUtils.getFirstVersion())); assertTrue(got.onOrBefore(VersionUtils.allReleasedVersions().get(0))); @@ -72,9 +72,9 @@ public void testRandomVersionBetween() { assertEquals(got, VersionUtils.getFirstVersion()); got = VersionUtils.randomVersionBetween(random(), Version.CURRENT, Version.CURRENT); assertEquals(got, Version.CURRENT); - got = VersionUtils.randomVersionBetween(random(), Version.V_6_0_0_alpha1_UNRELEASED, - Version.V_6_0_0_alpha1_UNRELEASED); - assertEquals(got, Version.V_6_0_0_alpha1_UNRELEASED); + got = VersionUtils.randomVersionBetween(random(), Version.V_6_0_0_alpha2_UNRELEASED, + Version.V_6_0_0_alpha2_UNRELEASED); + assertEquals(got, Version.V_6_0_0_alpha2_UNRELEASED); // implicit range of one got = VersionUtils.randomVersionBetween(random(), null, VersionUtils.getFirstVersion()); From 2e602d6641d009bcd541e9d33e41a599a9cc033b Mon Sep 17 00:00:00 2001 From: javanna Date: Wed, 10 May 2017 17:51:58 +0200 Subject: [PATCH 283/619] [TEST] reset subAggregationsSupplier in AggregationsTests to empty sub-aggs once the maxDepth is reached If we don't do this, although we don't set the subAggsSupplier again, we will reuse the one set in the previous run, hence we can end up in situations where we keep on generating sub aggregations till a StackOverflowError gets thrown. --- .../aggregations/AggregationsTests.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java index 99129529e2c80..78df82c550319 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java @@ -150,21 +150,28 @@ public void testFromXContent() throws IOException { } private static InternalAggregations createTestInstance() { - return createTestInstance(0, 5); + return createTestInstance(1, 0, 5); } - private static InternalAggregations createTestInstance(final int currentDepth, final int maxDepth) { - int numAggs = randomIntBetween(1, 5); + private static InternalAggregations createTestInstance(final int minNumAggs, final int currentDepth, final int maxDepth) { + int numAggs = randomIntBetween(minNumAggs, 4); List aggs = new ArrayList<>(numAggs); for (int i = 0; i < numAggs; i++) { InternalAggregationTestCase testCase = randomFrom(aggsTests); - if (testCase instanceof InternalMultiBucketAggregationTestCase && currentDepth < maxDepth) { + if (testCase instanceof InternalMultiBucketAggregationTestCase) { InternalMultiBucketAggregationTestCase multiBucketAggTestCase = (InternalMultiBucketAggregationTestCase) testCase; - multiBucketAggTestCase.subAggregationsSupplier = () -> createTestInstance(currentDepth + 1, maxDepth); - } - if (testCase instanceof InternalSingleBucketAggregationTestCase && currentDepth < maxDepth) { + if (currentDepth < maxDepth) { + multiBucketAggTestCase.subAggregationsSupplier = () -> createTestInstance(0, currentDepth + 1, maxDepth); + } else { + multiBucketAggTestCase.subAggregationsSupplier = () -> InternalAggregations.EMPTY; + } + } else if (testCase instanceof InternalSingleBucketAggregationTestCase) { InternalSingleBucketAggregationTestCase singleBucketAggTestCase = (InternalSingleBucketAggregationTestCase) testCase; - singleBucketAggTestCase.subAggregationsSupplier = () -> createTestInstance(currentDepth + 1, maxDepth); + if (currentDepth < maxDepth) { + singleBucketAggTestCase.subAggregationsSupplier = () -> createTestInstance(0, currentDepth + 1, maxDepth); + } else { + singleBucketAggTestCase.subAggregationsSupplier = () -> InternalAggregations.EMPTY; + } } aggs.add(testCase.createTestInstance()); } From ae5919e5dfb6fbf079aab2a24ef32771ac47e9c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 10 May 2017 18:18:22 +0200 Subject: [PATCH 284/619] Tests: Fix VersionTests after version bump --- .../java/org/elasticsearch/VersionTests.java | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/VersionTests.java b/core/src/test/java/org/elasticsearch/VersionTests.java index c84e069f80ae7..06a4fe117faf5 100644 --- a/core/src/test/java/org/elasticsearch/VersionTests.java +++ b/core/src/test/java/org/elasticsearch/VersionTests.java @@ -34,7 +34,7 @@ import java.util.Set; import static org.elasticsearch.Version.V_5_3_0_UNRELEASED; -import static org.elasticsearch.Version.V_6_0_0_alpha1_UNRELEASED; +import static org.elasticsearch.Version.V_6_0_0_alpha2_UNRELEASED; import static org.elasticsearch.test.VersionUtils.randomVersion; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.containsString; @@ -46,30 +46,30 @@ public class VersionTests extends ESTestCase { public void testVersionComparison() throws Exception { - assertThat(V_5_3_0_UNRELEASED.before(V_6_0_0_alpha1_UNRELEASED), is(true)); + assertThat(V_5_3_0_UNRELEASED.before(V_6_0_0_alpha2_UNRELEASED), is(true)); assertThat(V_5_3_0_UNRELEASED.before(V_5_3_0_UNRELEASED), is(false)); - assertThat(V_6_0_0_alpha1_UNRELEASED.before(V_5_3_0_UNRELEASED), is(false)); + assertThat(V_6_0_0_alpha2_UNRELEASED.before(V_5_3_0_UNRELEASED), is(false)); - assertThat(V_5_3_0_UNRELEASED.onOrBefore(V_6_0_0_alpha1_UNRELEASED), is(true)); + assertThat(V_5_3_0_UNRELEASED.onOrBefore(V_6_0_0_alpha2_UNRELEASED), is(true)); assertThat(V_5_3_0_UNRELEASED.onOrBefore(V_5_3_0_UNRELEASED), is(true)); - assertThat(V_6_0_0_alpha1_UNRELEASED.onOrBefore(V_5_3_0_UNRELEASED), is(false)); + assertThat(V_6_0_0_alpha2_UNRELEASED.onOrBefore(V_5_3_0_UNRELEASED), is(false)); - assertThat(V_5_3_0_UNRELEASED.after(V_6_0_0_alpha1_UNRELEASED), is(false)); + assertThat(V_5_3_0_UNRELEASED.after(V_6_0_0_alpha2_UNRELEASED), is(false)); assertThat(V_5_3_0_UNRELEASED.after(V_5_3_0_UNRELEASED), is(false)); - assertThat(V_6_0_0_alpha1_UNRELEASED.after(V_5_3_0_UNRELEASED), is(true)); + assertThat(V_6_0_0_alpha2_UNRELEASED.after(V_5_3_0_UNRELEASED), is(true)); - assertThat(V_5_3_0_UNRELEASED.onOrAfter(V_6_0_0_alpha1_UNRELEASED), is(false)); + assertThat(V_5_3_0_UNRELEASED.onOrAfter(V_6_0_0_alpha2_UNRELEASED), is(false)); assertThat(V_5_3_0_UNRELEASED.onOrAfter(V_5_3_0_UNRELEASED), is(true)); - assertThat(V_6_0_0_alpha1_UNRELEASED.onOrAfter(V_5_3_0_UNRELEASED), is(true)); + assertThat(V_6_0_0_alpha2_UNRELEASED.onOrAfter(V_5_3_0_UNRELEASED), is(true)); assertTrue(Version.fromString("5.0.0-alpha2").onOrAfter(Version.fromString("5.0.0-alpha1"))); assertTrue(Version.fromString("5.0.0").onOrAfter(Version.fromString("5.0.0-beta2"))); assertTrue(Version.fromString("5.0.0-rc1").onOrAfter(Version.fromString("5.0.0-beta24"))); assertTrue(Version.fromString("5.0.0-alpha24").before(Version.fromString("5.0.0-beta0"))); - assertThat(V_5_3_0_UNRELEASED, is(lessThan(V_6_0_0_alpha1_UNRELEASED))); + assertThat(V_5_3_0_UNRELEASED, is(lessThan(V_6_0_0_alpha2_UNRELEASED))); assertThat(V_5_3_0_UNRELEASED.compareTo(V_5_3_0_UNRELEASED), is(0)); - assertThat(V_6_0_0_alpha1_UNRELEASED, is(greaterThan(V_5_3_0_UNRELEASED))); + assertThat(V_6_0_0_alpha2_UNRELEASED, is(greaterThan(V_5_3_0_UNRELEASED))); } public void testMin() { @@ -97,7 +97,7 @@ public void testMax() { } public void testMinimumIndexCompatibilityVersion() { - assertEquals(Version.V_5_0_0, Version.V_6_0_0_alpha1_UNRELEASED.minimumIndexCompatibilityVersion()); + assertEquals(Version.V_5_0_0, Version.V_6_0_0_alpha2_UNRELEASED.minimumIndexCompatibilityVersion()); assertEquals(Version.fromId(2000099), Version.V_5_0_0.minimumIndexCompatibilityVersion()); assertEquals(Version.fromId(2000099), Version.V_5_1_1_UNRELEASED.minimumIndexCompatibilityVersion()); @@ -157,7 +157,7 @@ public void testVersionNoPresentInSettings() { public void testIndexCreatedVersion() { // an actual index has a IndexMetaData.SETTING_INDEX_UUID final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_2, - Version.V_5_2_0_UNRELEASED, Version.V_6_0_0_alpha1_UNRELEASED); + Version.V_5_2_0_UNRELEASED, Version.V_6_0_0_alpha2_UNRELEASED); assertEquals(version, Version.indexCreated(Settings.builder().put(IndexMetaData.SETTING_INDEX_UUID, "foo").put(IndexMetaData.SETTING_VERSION_CREATED, version).build())); } @@ -170,11 +170,11 @@ public void testMinCompatVersion() { assertThat(Version.fromString("2.3.0").minimumCompatibilityVersion(), equalTo(major)); // from 6.0 on we are supporting the latest minor of the previous major... this might fail once we add a new version ie. 5.x is // released since we need to bump the supported minor in Version#minimumCompatibilityVersion() - Version lastVersion = VersionUtils.getPreviousVersion(Version.V_6_0_0_alpha1_UNRELEASED); - assertEquals(lastVersion.major, Version.V_6_0_0_alpha1_UNRELEASED.minimumCompatibilityVersion().major); + Version lastVersion = VersionUtils.getPreviousVersion(Version.V_6_0_0_alpha2_UNRELEASED); + assertEquals(lastVersion.major, Version.V_6_0_0_alpha2_UNRELEASED.minimumCompatibilityVersion().major); assertEquals("did you miss to bump the minor in Version#minimumCompatibilityVersion()", - lastVersion.minor, Version.V_6_0_0_alpha1_UNRELEASED.minimumCompatibilityVersion().minor); - assertEquals(0, Version.V_6_0_0_alpha1_UNRELEASED.minimumCompatibilityVersion().revision); + lastVersion.minor, Version.V_6_0_0_alpha2_UNRELEASED.minimumCompatibilityVersion().minor); + assertEquals(0, Version.V_6_0_0_alpha2_UNRELEASED.minimumCompatibilityVersion().revision); } public void testToString() { @@ -326,8 +326,8 @@ public static void assertUnknownVersion(Version version) { public void testIsCompatible() { assertTrue(isCompatible(Version.CURRENT, Version.CURRENT.minimumCompatibilityVersion())); - assertTrue(isCompatible(Version.V_5_0_0, Version.V_6_0_0_alpha1_UNRELEASED)); - assertFalse(isCompatible(Version.fromId(2000099), Version.V_6_0_0_alpha1_UNRELEASED)); + assertTrue(isCompatible(Version.V_5_0_0, Version.V_6_0_0_alpha2_UNRELEASED)); + assertFalse(isCompatible(Version.fromId(2000099), Version.V_6_0_0_alpha2_UNRELEASED)); assertFalse(isCompatible(Version.fromId(2000099), Version.V_5_0_0)); } From d2c69701e6f033b8eff6cf4cf71de4eed6620b04 Mon Sep 17 00:00:00 2001 From: Lee Hinman Date: Wed, 10 May 2017 11:27:57 -0600 Subject: [PATCH 285/619] Fix SocketPermission in test framework for alpha2 bump It was using the wrong version, which can cause errors like ``` 1> java.security.AccessControlException: access denied ("java.net.SocketPermission" "[0:0:0:0:0:0:0:1]:34221" "connect,resolve") 1> at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472) ~[?:1.8.0_111] 1> at java.security.AccessController.checkPermission(AccessController.java:884) ~[?:1.8.0_111] 1> at java.lang.SecurityManager.checkPermission(SecurityManager.java:549) ~[?:1.8.0_111] 1> at java.lang.SecurityManager.checkConnect(SecurityManager.java:1051) ~[?:1.8.0_111] 1> at sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:625) ~[?:?] 1> at org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor.processSessionRequests(DefaultConnectingIOReactor.java:273) ~[httpcore-nio-4.4.5.jar:4.4.5] 1> at org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor.processEvents(DefaultConnectingIOReactor.java:139) ~[httpcore-nio-4.4.5.jar:4.4.5] 1> at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor.execute(AbstractMultiworkerIOReactor.java:348) ~[httpcore-nio-4.4.5.jar:4.4.5] 1> at org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager.execute(PoolingNHttpClientConnectionManager.java:192) ~[httpasyncclient-4.1.2.jar:4.1.2] 1> at org.apache.http.impl.nio.client.CloseableHttpAsyncClientBase$1.run(CloseableHttpAsyncClientBase.java:64) ~[httpasyncclient-4.1.2.jar:4.1.2] ``` When running tests --- .../resources/org/elasticsearch/bootstrap/test-framework.policy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy b/core/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy index 1e26c42a4cc10..c7267715dcd40 100644 --- a/core/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy +++ b/core/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy @@ -64,7 +64,7 @@ grant codeBase "${codebase.mocksocket-1.1.jar}" { }; -grant codeBase "${codebase.rest-6.0.0-alpha1-SNAPSHOT.jar}" { +grant codeBase "${codebase.rest-6.0.0-alpha2-SNAPSHOT.jar}" { // rest makes socket connections for rest tests permission java.net.SocketPermission "*", "connect"; }; From fbf532a626935b70da005fffdd8e5a14f5e97739 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Wed, 10 May 2017 14:27:43 -0400 Subject: [PATCH 286/619] Revert "Add global checkpoint assertion in index shard" This reverts commit 89b305c09e429228178fe8796dc6a5d39e69459c. --- .../java/org/elasticsearch/index/shard/IndexShard.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java index 8e1d47a65e0b5..84c3ee2ededc5 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -1515,8 +1515,6 @@ public long getGlobalCheckpoint() { */ public void updateGlobalCheckpointOnReplica(final long globalCheckpoint) { verifyReplicationTarget(); - // we sample the recovery stage before sampling the local checkpoint or we are subject to a race condition in the below assertion - final RecoveryState.Stage stage = recoveryState().getStage(); final SequenceNumbersService seqNoService = getEngine().seqNoService(); final long localCheckpoint = seqNoService.getLocalCheckpoint(); if (globalCheckpoint > localCheckpoint) { @@ -1526,10 +1524,10 @@ public void updateGlobalCheckpointOnReplica(final long globalCheckpoint) { * case that the global checkpoint update from the primary is ahead of the local checkpoint on this shard. In this case, we * ignore the global checkpoint update. This can happen if we are in the translog stage of recovery. Prior to this, the engine * is not opened and this shard will not receive global checkpoint updates, and after this the shard will be contributing to - * calculations of the the global checkpoint. + * calculations of the the global checkpoint. However, we can not assert that we are in the translog stage of recovery here as + * while the global checkpoint update may have emanated from the primary when we were in that state, we could subsequently move + * to recovery finalization, or even finished recovery before the update arrives here. */ - assert stage == RecoveryState.Stage.TRANSLOG - : "expected recovery stage [" + RecoveryState.Stage.TRANSLOG + "] but was [" + stage + "]"; return; } seqNoService.updateGlobalCheckpointOnReplica(globalCheckpoint); From 743217a4309dbe11a0c6b597cd3eedf5156d78bc Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Wed, 10 May 2017 15:48:40 -0400 Subject: [PATCH 287/619] Enhances get snapshots API to allow retrieving repository index only (#24477) Currently, the get snapshots API (e.g. /_snapshot/{repositoryName}/_all) provides information about snapshots in the repository, including the snapshot state, number of shards snapshotted, failures, etc. In order to provide information about each snapshot in the repository, the call must read the snapshot metadata blob (`snap-{snapshot_uuid}.dat`) for every snapshot. In cloud-based repositories, this can be expensive, both from a cost and performance perspective. Sometimes, all the user wants is to retrieve all the names/uuids of each snapshot, and the indices that went into each snapshot, without any of the other status information about the snapshot. This minimal information can be retrieved from the repository index blob (`index-N`) without needing to read each snapshot metadata blob. This commit enhances the get snapshots API with an optional `verbose` parameter. If `verbose` is set to false on the request, then the get snapshots API will only retrieve the minimal information about each snapshot (the name, uuid, and indices in the snapshot), and only read this information from the repository index blob, thereby giving users the option to retrieve the snapshots in a repository in a more cost-effective and efficient manner. Closes #24288 --- .../snapshots/get/GetSnapshotsRequest.java | 31 ++++ .../get/GetSnapshotsRequestBuilder.java | 14 ++ .../get/TransportGetSnapshotsAction.java | 68 ++++++- .../repositories/RepositoryData.java | 171 ++++++++++++------ .../blobstore/BlobStoreRepository.java | 5 +- .../admin/cluster/RestGetSnapshotsAction.java | 3 +- .../elasticsearch/snapshots/SnapshotId.java | 7 +- .../elasticsearch/snapshots/SnapshotInfo.java | 114 ++++++++---- .../snapshots/SnapshotsService.java | 5 +- .../bwcompat/RestoreBackwardsCompatIT.java | 8 + .../index/shard/IndexShardTests.java | 2 +- .../repositories/RepositoryDataTests.java | 49 +++-- .../blobstore/BlobStoreRepositoryTests.java | 10 +- .../SharedClusterSnapshotRestoreIT.java | 89 +++++++++ docs/reference/modules/snapshots.asciidoc | 9 + .../rest-api-spec/api/snapshot.get.json | 4 + .../test/snapshot.get/10_basic.yaml | 41 +++++ 17 files changed, 496 insertions(+), 134 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsRequest.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsRequest.java index fd2c97ed5d43c..e90f9e578ce37 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsRequest.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsRequest.java @@ -28,6 +28,7 @@ import java.io.IOException; import static org.elasticsearch.action.ValidateActions.addValidationError; +import static org.elasticsearch.snapshots.SnapshotInfo.VERBOSE_INTRODUCED; /** * Get snapshot request @@ -43,6 +44,8 @@ public class GetSnapshotsRequest extends MasterNodeRequest private boolean ignoreUnavailable; + private boolean verbose = true; + public GetSnapshotsRequest() { } @@ -123,6 +126,7 @@ public GetSnapshotsRequest ignoreUnavailable(boolean ignoreUnavailable) { this.ignoreUnavailable = ignoreUnavailable; return this; } + /** * @return Whether snapshots should be ignored when unavailable (corrupt or temporarily not fetchable) */ @@ -130,12 +134,36 @@ public boolean ignoreUnavailable() { return ignoreUnavailable; } + /** + * Set to {@code false} to only show the snapshot names and the indices they contain. + * This is useful when the snapshots belong to a cloud-based repository where each + * blob read is a concern (cost wise and performance wise), as the snapshot names and + * indices they contain can be retrieved from a single index blob in the repository, + * whereas the rest of the information requires reading a snapshot metadata file for + * each snapshot requested. Defaults to {@code true}, which returns all information + * about each requested snapshot. + */ + public GetSnapshotsRequest verbose(boolean verbose) { + this.verbose = verbose; + return this; + } + + /** + * Returns whether the request will return a verbose response. + */ + public boolean verbose() { + return verbose; + } + @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); repository = in.readString(); snapshots = in.readStringArray(); ignoreUnavailable = in.readBoolean(); + if (in.getVersion().onOrAfter(VERBOSE_INTRODUCED)) { + verbose = in.readBoolean(); + } } @Override @@ -144,5 +172,8 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(repository); out.writeStringArray(snapshots); out.writeBoolean(ignoreUnavailable); + if (out.getVersion().onOrAfter(VERBOSE_INTRODUCED)) { + out.writeBoolean(verbose); + } } } diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsRequestBuilder.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsRequestBuilder.java index 3b0ac47c69f9f..2115bd0bc3b81 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsRequestBuilder.java @@ -96,4 +96,18 @@ public GetSnapshotsRequestBuilder setIgnoreUnavailable(boolean ignoreUnavailable return this; } + /** + * Set to {@code false} to only show the snapshot names and the indices they contain. + * This is useful when the snapshots belong to a cloud-based repository where each + * blob read is a concern (cost wise and performance wise), as the snapshot names and + * indices they contain can be retrieved from a single index blob in the repository, + * whereas the rest of the information requires reading a snapshot metadata file for + * each snapshot requested. Defaults to {@code true}, which returns all information + * about each requested snapshot. + */ + public GetSnapshotsRequestBuilder setVerbose(boolean verbose) { + request.verbose(verbose); + return this; + } + } diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java index 88b84f36ff6ca..eec218a4119ba 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java @@ -19,6 +19,7 @@ package org.elasticsearch.action.admin.cluster.snapshots.get; +import org.apache.lucene.util.CollectionUtil; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.master.TransportMasterNodeAction; @@ -30,6 +31,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.repositories.IndexId; import org.elasticsearch.repositories.RepositoryData; import org.elasticsearch.snapshots.SnapshotId; import org.elasticsearch.snapshots.SnapshotInfo; @@ -39,11 +41,13 @@ import org.elasticsearch.transport.TransportService; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; /** * Transport Action for get snapshots operation @@ -76,31 +80,35 @@ protected ClusterBlockException checkBlock(GetSnapshotsRequest request, ClusterS } @Override - protected void masterOperation(final GetSnapshotsRequest request, ClusterState state, + protected void masterOperation(final GetSnapshotsRequest request, final ClusterState state, final ActionListener listener) { try { final String repository = request.repository(); - List snapshotInfoBuilder = new ArrayList<>(); final Map allSnapshotIds = new HashMap<>(); - final List currentSnapshotIds = new ArrayList<>(); - final RepositoryData repositoryData = snapshotsService.getRepositoryData(repository); + final List currentSnapshots = new ArrayList<>(); for (SnapshotInfo snapshotInfo : snapshotsService.currentSnapshots(repository)) { SnapshotId snapshotId = snapshotInfo.snapshotId(); allSnapshotIds.put(snapshotId.getName(), snapshotId); - currentSnapshotIds.add(snapshotId); + currentSnapshots.add(snapshotInfo); } + + final RepositoryData repositoryData; if (isCurrentSnapshotsOnly(request.snapshots()) == false) { + repositoryData = snapshotsService.getRepositoryData(repository); for (SnapshotId snapshotId : repositoryData.getAllSnapshotIds()) { allSnapshotIds.put(snapshotId.getName(), snapshotId); } + } else { + repositoryData = null; } + final Set toResolve = new HashSet<>(); if (isAllSnapshots(request.snapshots())) { toResolve.addAll(allSnapshotIds.values()); } else { for (String snapshotOrPattern : request.snapshots()) { if (GetSnapshotsRequest.CURRENT_SNAPSHOT.equalsIgnoreCase(snapshotOrPattern)) { - toResolve.addAll(currentSnapshotIds); + toResolve.addAll(currentSnapshots.stream().map(SnapshotInfo::snapshotId).collect(Collectors.toList())); } else if (Regex.isSimpleMatchPattern(snapshotOrPattern) == false) { if (allSnapshotIds.containsKey(snapshotOrPattern)) { toResolve.add(allSnapshotIds.get(snapshotOrPattern)); @@ -121,9 +129,23 @@ protected void masterOperation(final GetSnapshotsRequest request, ClusterState s } } - snapshotInfoBuilder.addAll(snapshotsService.snapshots( - repository, new ArrayList<>(toResolve), repositoryData.getIncompatibleSnapshotIds(), request.ignoreUnavailable())); - listener.onResponse(new GetSnapshotsResponse(snapshotInfoBuilder)); + final List snapshotInfos; + if (request.verbose()) { + final Set incompatibleSnapshots = repositoryData != null ? + new HashSet<>(repositoryData.getIncompatibleSnapshotIds()) : Collections.emptySet(); + snapshotInfos = snapshotsService.snapshots(repository, new ArrayList<>(toResolve), + incompatibleSnapshots, request.ignoreUnavailable()); + } else { + if (repositoryData != null) { + // want non-current snapshots as well, which are found in the repository data + snapshotInfos = buildSimpleSnapshotInfos(toResolve, repositoryData, currentSnapshots); + } else { + // only want current snapshots + snapshotInfos = currentSnapshots.stream().map(SnapshotInfo::basic).collect(Collectors.toList()); + CollectionUtil.timSort(snapshotInfos); + } + } + listener.onResponse(new GetSnapshotsResponse(snapshotInfos)); } catch (Exception e) { listener.onFailure(e); } @@ -136,4 +158,32 @@ private boolean isAllSnapshots(String[] snapshots) { private boolean isCurrentSnapshotsOnly(String[] snapshots) { return (snapshots.length == 1 && GetSnapshotsRequest.CURRENT_SNAPSHOT.equalsIgnoreCase(snapshots[0])); } + + private List buildSimpleSnapshotInfos(final Set toResolve, + final RepositoryData repositoryData, + final List currentSnapshots) { + List snapshotInfos = new ArrayList<>(); + for (SnapshotInfo snapshotInfo : currentSnapshots) { + if (toResolve.remove(snapshotInfo.snapshotId())) { + snapshotInfos.add(snapshotInfo.basic()); + } + } + Map> snapshotsToIndices = new HashMap<>(); + for (IndexId indexId : repositoryData.getIndices().values()) { + for (SnapshotId snapshotId : repositoryData.getSnapshots(indexId)) { + if (toResolve.contains(snapshotId)) { + snapshotsToIndices.computeIfAbsent(snapshotId, (k) -> new ArrayList<>()) + .add(indexId.getName()); + } + } + } + for (Map.Entry> entry : snapshotsToIndices.entrySet()) { + final List indices = entry.getValue(); + CollectionUtil.timSort(indices); + final SnapshotId snapshotId = entry.getKey(); + snapshotInfos.add(new SnapshotInfo(snapshotId, indices, repositoryData.getSnapshotState(snapshotId))); + } + CollectionUtil.timSort(snapshotInfos); + return Collections.unmodifiableList(snapshotInfos); + } } diff --git a/core/src/main/java/org/elasticsearch/repositories/RepositoryData.java b/core/src/main/java/org/elasticsearch/repositories/RepositoryData.java index ee4a64cac87dc..ac52ac30d69d9 100644 --- a/core/src/main/java/org/elasticsearch/repositories/RepositoryData.java +++ b/core/src/main/java/org/elasticsearch/repositories/RepositoryData.java @@ -20,14 +20,17 @@ package org.elasticsearch.repositories; import org.elasticsearch.ElasticsearchParseException; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.snapshots.SnapshotId; +import org.elasticsearch.snapshots.SnapshotState; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; @@ -51,8 +54,8 @@ public final class RepositoryData { /** * An instance initialized for an empty repository. */ - public static final RepositoryData EMPTY = - new RepositoryData(EMPTY_REPO_GEN, Collections.emptyList(), Collections.emptyMap(), Collections.emptyList()); + public static final RepositoryData EMPTY = new RepositoryData(EMPTY_REPO_GEN, + Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), Collections.emptyList()); /** * The generational id of the index file from which the repository data was read. @@ -61,7 +64,11 @@ public final class RepositoryData { /** * The ids of the snapshots in the repository. */ - private final List snapshotIds; + private final Map snapshotIds; + /** + * The states of each snapshot in the repository. + */ + private final Map snapshotStates; /** * The indices found in the repository across all snapshots, as a name to {@link IndexId} mapping */ @@ -75,19 +82,22 @@ public final class RepositoryData { */ private final List incompatibleSnapshotIds; - public RepositoryData(long genId, List snapshotIds, Map> indexSnapshots, + public RepositoryData(long genId, + Map snapshotIds, + Map snapshotStates, + Map> indexSnapshots, List incompatibleSnapshotIds) { this.genId = genId; - this.snapshotIds = Collections.unmodifiableList(snapshotIds); - this.indices = Collections.unmodifiableMap(indexSnapshots.keySet() - .stream() - .collect(Collectors.toMap(IndexId::getName, Function.identity()))); + this.snapshotIds = Collections.unmodifiableMap(snapshotIds); + this.snapshotStates = Collections.unmodifiableMap(snapshotStates); + this.indices = Collections.unmodifiableMap(indexSnapshots.keySet().stream() + .collect(Collectors.toMap(IndexId::getName, Function.identity()))); this.indexSnapshots = Collections.unmodifiableMap(indexSnapshots); this.incompatibleSnapshotIds = Collections.unmodifiableList(incompatibleSnapshotIds); } protected RepositoryData copy() { - return new RepositoryData(genId, snapshotIds, indexSnapshots, incompatibleSnapshotIds); + return new RepositoryData(genId, snapshotIds, snapshotStates, indexSnapshots, incompatibleSnapshotIds); } /** @@ -98,17 +108,17 @@ public long getGenId() { } /** - * Returns an unmodifiable list of the snapshot ids. + * Returns an unmodifiable collection of the snapshot ids. */ - public List getSnapshotIds() { - return snapshotIds; + public Collection getSnapshotIds() { + return Collections.unmodifiableCollection(snapshotIds.values()); } /** * Returns an immutable collection of the snapshot ids in the repository that are incompatible with the * current ES version. */ - public List getIncompatibleSnapshotIds() { + public Collection getIncompatibleSnapshotIds() { return incompatibleSnapshotIds; } @@ -116,13 +126,22 @@ public List getIncompatibleSnapshotIds() { * Returns an immutable collection of all the snapshot ids in the repository, both active and * incompatible snapshots. */ - public List getAllSnapshotIds() { + public Collection getAllSnapshotIds() { List allSnapshotIds = new ArrayList<>(snapshotIds.size() + incompatibleSnapshotIds.size()); - allSnapshotIds.addAll(snapshotIds); + allSnapshotIds.addAll(snapshotIds.values()); allSnapshotIds.addAll(incompatibleSnapshotIds); return Collections.unmodifiableList(allSnapshotIds); } + /** + * Returns the {@link SnapshotState} for the given snapshot. Returns {@code null} if + * there is no state for the snapshot. + */ + @Nullable + public SnapshotState getSnapshotState(final SnapshotId snapshotId) { + return snapshotStates.get(snapshotId.getUUID()); + } + /** * Returns an unmodifiable map of the index names to {@link IndexId} in the repository. */ @@ -134,15 +153,19 @@ public Map getIndices() { * Add a snapshot and its indices to the repository; returns a new instance. If the snapshot * already exists in the repository data, this method throws an IllegalArgumentException. */ - public RepositoryData addSnapshot(final SnapshotId snapshotId, final List snapshottedIndices) { - if (snapshotIds.contains(snapshotId)) { + public RepositoryData addSnapshot(final SnapshotId snapshotId, + final SnapshotState snapshotState, + final List snapshottedIndices) { + if (snapshotIds.containsKey(snapshotId.getUUID())) { // if the snapshot id already exists in the repository data, it means an old master // that is blocked from the cluster is trying to finalize a snapshot concurrently with // the new master, so we make the operation idempotent return this; } - List snapshots = new ArrayList<>(snapshotIds); - snapshots.add(snapshotId); + Map snapshots = new HashMap<>(snapshotIds); + snapshots.put(snapshotId.getUUID(), snapshotId); + Map newSnapshotStates = new HashMap<>(snapshotStates); + newSnapshotStates.put(snapshotId.getUUID(), snapshotState); Map> allIndexSnapshots = new HashMap<>(indexSnapshots); for (final IndexId indexId : snapshottedIndices) { if (allIndexSnapshots.containsKey(indexId)) { @@ -158,17 +181,18 @@ public RepositoryData addSnapshot(final SnapshotId snapshotId, final List newSnapshotIds = snapshotIds - .stream() - .filter(id -> snapshotId.equals(id) == false) - .collect(Collectors.toList()); + Map newSnapshotIds = snapshotIds.values().stream() + .filter(id -> snapshotId.equals(id) == false) + .collect(Collectors.toMap(SnapshotId::getUUID, Function.identity())); + Map newSnapshotStates = new HashMap<>(snapshotStates); + newSnapshotStates.remove(snapshotId.getUUID()); Map> indexSnapshots = new HashMap<>(); for (final IndexId indexId : indices.values()) { Set set; @@ -176,7 +200,8 @@ public RepositoryData removeSnapshot(final SnapshotId snapshotId) { assert snapshotIds != null; if (snapshotIds.contains(snapshotId)) { if (snapshotIds.size() == 1) { - // removing the snapshot will mean no more snapshots have this index, so just skip over it + // removing the snapshot will mean no more snapshots + // have this index, so just skip over it continue; } set = new LinkedHashSet<>(snapshotIds); @@ -187,21 +212,7 @@ public RepositoryData removeSnapshot(final SnapshotId snapshotId) { indexSnapshots.put(indexId, set); } - return new RepositoryData(genId, newSnapshotIds, indexSnapshots, incompatibleSnapshotIds); - } - - /** - * Returns a new {@link RepositoryData} instance containing the same snapshot data as the - * invoking instance, with the given incompatible snapshots added to the new instance. - */ - public RepositoryData addIncompatibleSnapshots(final List incompatibleSnapshotIds) { - List newSnapshotIds = new ArrayList<>(this.snapshotIds); - List newIncompatibleSnapshotIds = new ArrayList<>(this.incompatibleSnapshotIds); - for (SnapshotId snapshotId : incompatibleSnapshotIds) { - newSnapshotIds.remove(snapshotId); - newIncompatibleSnapshotIds.add(snapshotId); - } - return new RepositoryData(this.genId, newSnapshotIds, this.indexSnapshots, newIncompatibleSnapshotIds); + return new RepositoryData(genId, newSnapshotIds, newSnapshotStates, indexSnapshots, incompatibleSnapshotIds); } /** @@ -219,7 +230,7 @@ public Set getSnapshots(final IndexId indexId) { * Initializes the indices in the repository metadata; returns a new instance. */ public RepositoryData initIndices(final Map> indexSnapshots) { - return new RepositoryData(genId, snapshotIds, indexSnapshots, incompatibleSnapshotIds); + return new RepositoryData(genId, snapshotIds, snapshotStates, indexSnapshots, incompatibleSnapshotIds); } @Override @@ -232,6 +243,7 @@ public boolean equals(Object obj) { } @SuppressWarnings("unchecked") RepositoryData that = (RepositoryData) obj; return snapshotIds.equals(that.snapshotIds) + && snapshotStates.equals(that.snapshotStates) && indices.equals(that.indices) && indexSnapshots.equals(that.indexSnapshots) && incompatibleSnapshotIds.equals(that.incompatibleSnapshotIds); @@ -239,7 +251,7 @@ public boolean equals(Object obj) { @Override public int hashCode() { - return Objects.hash(snapshotIds, indices, indexSnapshots, incompatibleSnapshotIds); + return Objects.hash(snapshotIds, snapshotStates, indices, indexSnapshots, incompatibleSnapshotIds); } /** @@ -291,6 +303,9 @@ public List resolveNewIndices(final List indicesToResolve) { private static final String INCOMPATIBLE_SNAPSHOTS = "incompatible-snapshots"; private static final String INDICES = "indices"; private static final String INDEX_ID = "id"; + private static final String NAME = "name"; + private static final String UUID = "uuid"; + private static final String STATE = "state"; /** * Writes the snapshots metadata and the related indices metadata to x-content, omitting the @@ -301,7 +316,13 @@ public XContentBuilder snapshotsToXContent(final XContentBuilder builder, final // write the snapshots list builder.startArray(SNAPSHOTS); for (final SnapshotId snapshot : getSnapshotIds()) { - snapshot.toXContent(builder, params); + builder.startObject(); + builder.field(NAME, snapshot.getName()); + builder.field(UUID, snapshot.getUUID()); + if (snapshotStates.containsKey(snapshot.getUUID())) { + builder.field(STATE, snapshotStates.get(snapshot.getUUID()).value()); + } + builder.endObject(); } builder.endArray(); // write the indices map @@ -313,7 +334,7 @@ public XContentBuilder snapshotsToXContent(final XContentBuilder builder, final Set snapshotIds = indexSnapshots.get(indexId); assert snapshotIds != null; for (final SnapshotId snapshotId : snapshotIds) { - snapshotId.toXContent(builder, params); + builder.value(snapshotId.getUUID()); } builder.endArray(); builder.endObject(); @@ -327,20 +348,47 @@ public XContentBuilder snapshotsToXContent(final XContentBuilder builder, final * Reads an instance of {@link RepositoryData} from x-content, loading the snapshots and indices metadata. */ public static RepositoryData snapshotsFromXContent(final XContentParser parser, long genId) throws IOException { - List snapshots = new ArrayList<>(); + Map snapshots = new HashMap<>(); + Map snapshotStates = new HashMap<>(); Map> indexSnapshots = new HashMap<>(); if (parser.nextToken() == XContentParser.Token.START_OBJECT) { while (parser.nextToken() == XContentParser.Token.FIELD_NAME) { - String currentFieldName = parser.currentName(); - if (SNAPSHOTS.equals(currentFieldName)) { + String field = parser.currentName(); + if (SNAPSHOTS.equals(field)) { if (parser.nextToken() == XContentParser.Token.START_ARRAY) { while (parser.nextToken() != XContentParser.Token.END_ARRAY) { - snapshots.add(SnapshotId.fromXContent(parser)); + final SnapshotId snapshotId; + // the new format from 5.0 which contains the snapshot name and uuid + if (parser.currentToken() == XContentParser.Token.START_OBJECT) { + String name = null; + String uuid = null; + SnapshotState state = null; + while (parser.nextToken() != XContentParser.Token.END_OBJECT) { + String currentFieldName = parser.currentName(); + parser.nextToken(); + if (NAME.equals(currentFieldName)) { + name = parser.text(); + } else if (UUID.equals(currentFieldName)) { + uuid = parser.text(); + } else if (STATE.equals(currentFieldName)) { + state = SnapshotState.fromValue(parser.numberValue().byteValue()); + } + } + snapshotId = new SnapshotId(name, uuid); + if (state != null) { + snapshotStates.put(uuid, state); + } + } else { + // the old format pre 5.0 that only contains the snapshot name, use the name as the uuid too + final String name = parser.text(); + snapshotId = new SnapshotId(name, name); + } + snapshots.put(snapshotId.getUUID(), snapshotId); } } else { - throw new ElasticsearchParseException("expected array for [" + currentFieldName + "]"); + throw new ElasticsearchParseException("expected array for [" + field + "]"); } - } else if (INDICES.equals(currentFieldName)) { + } else if (INDICES.equals(field)) { if (parser.nextToken() != XContentParser.Token.START_OBJECT) { throw new ElasticsearchParseException("start object expected [indices]"); } @@ -361,7 +409,22 @@ public static RepositoryData snapshotsFromXContent(final XContentParser parser, throw new ElasticsearchParseException("start array expected [snapshots]"); } while (parser.nextToken() != XContentParser.Token.END_ARRAY) { - snapshotIds.add(SnapshotId.fromXContent(parser)); + String uuid = null; + // the old format pre 5.4.1 which contains the snapshot name and uuid + if (parser.currentToken() == XContentParser.Token.START_OBJECT) { + while (parser.nextToken() != XContentParser.Token.END_OBJECT) { + String currentFieldName = parser.currentName(); + parser.nextToken(); + if (UUID.equals(currentFieldName)) { + uuid = parser.text(); + } + } + } else { + // the new format post 5.4.1 that only contains the snapshot uuid, + // since we already have the name/uuid combo in the snapshots array + uuid = parser.text(); + } + snapshotIds.add(snapshots.get(uuid)); } } } @@ -369,13 +432,13 @@ public static RepositoryData snapshotsFromXContent(final XContentParser parser, indexSnapshots.put(new IndexId(indexName, indexId), snapshotIds); } } else { - throw new ElasticsearchParseException("unknown field name [" + currentFieldName + "]"); + throw new ElasticsearchParseException("unknown field name [" + field + "]"); } } } else { throw new ElasticsearchParseException("start object expected"); } - return new RepositoryData(genId, snapshots, indexSnapshots, Collections.emptyList()); + return new RepositoryData(genId, snapshots, snapshotStates, indexSnapshots, Collections.emptyList()); } /** @@ -419,7 +482,7 @@ public RepositoryData incompatibleSnapshotsFromXContent(final XContentParser par } else { throw new ElasticsearchParseException("start object expected"); } - return new RepositoryData(this.genId, this.snapshotIds, this.indexSnapshots, incompatibleSnapshotIds); + return new RepositoryData(this.genId, this.snapshotIds, this.snapshotStates, this.indexSnapshots, incompatibleSnapshotIds); } } diff --git a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index f9fcb3a470afb..4c9ebd94de61b 100644 --- a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -473,10 +473,7 @@ public SnapshotInfo finalizeSnapshot(final SnapshotId snapshotId, shardFailures); snapshotFormat.write(blobStoreSnapshot, snapshotsBlobContainer, snapshotId.getUUID()); final RepositoryData repositoryData = getRepositoryData(); - List snapshotIds = repositoryData.getSnapshotIds(); - if (!snapshotIds.contains(snapshotId)) { - writeIndexGen(repositoryData.addSnapshot(snapshotId, indices), repositoryStateId); - } + writeIndexGen(repositoryData.addSnapshot(snapshotId, blobStoreSnapshot.state(), indices), repositoryStateId); return blobStoreSnapshot; } catch (IOException ex) { throw new RepositoryException(metadata.name(), "failed to update snapshot in repository", ex); diff --git a/core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetSnapshotsAction.java b/core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetSnapshotsAction.java index 508953cdde8e5..7348cb5896cb4 100644 --- a/core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetSnapshotsAction.java +++ b/core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetSnapshotsAction.java @@ -42,7 +42,6 @@ public RestGetSnapshotsAction(Settings settings, RestController controller) { controller.registerHandler(GET, "/_snapshot/{repository}/{snapshot}", this); } - @Override public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { String repository = request.param("repository"); @@ -50,7 +49,7 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC GetSnapshotsRequest getSnapshotsRequest = getSnapshotsRequest(repository).snapshots(snapshots); getSnapshotsRequest.ignoreUnavailable(request.paramAsBoolean("ignore_unavailable", getSnapshotsRequest.ignoreUnavailable())); - + getSnapshotsRequest.verbose(request.paramAsBoolean("verbose", getSnapshotsRequest.verbose())); getSnapshotsRequest.masterNodeTimeout(request.paramAsTime("master_timeout", getSnapshotsRequest.masterNodeTimeout())); return channel -> client.admin().cluster().getSnapshots(getSnapshotsRequest, new RestToXContentListener<>(channel)); } diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotId.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotId.java index 4866a79afb95a..ffd7547099c66 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotId.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotId.java @@ -32,7 +32,7 @@ /** * SnapshotId - snapshot name + snapshot UUID */ -public final class SnapshotId implements Writeable, ToXContent { +public final class SnapshotId implements Comparable, Writeable, ToXContent { private static final String NAME = "name"; private static final String UUID = "uuid"; @@ -106,6 +106,11 @@ public int hashCode() { return hashCode; } + @Override + public int compareTo(final SnapshotId other) { + return this.name.compareTo(other.name); + } + private int computeHashCode() { return Objects.hash(name, uuid); } diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java index 519393d49e10c..994d5d2a9affb 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Objects; @@ -69,11 +70,17 @@ public final class SnapshotInfo implements Comparable, ToXContent, private static final String SUCCESSFUL_SHARDS = "successful_shards"; private static final Version VERSION_INCOMPATIBLE_INTRODUCED = Version.V_5_2_0_UNRELEASED; + public static final Version VERBOSE_INTRODUCED = Version.V_6_0_0_alpha1_UNRELEASED; + + private static final Comparator COMPARATOR = + Comparator.comparing(SnapshotInfo::startTime).thenComparing(SnapshotInfo::snapshotId); private final SnapshotId snapshotId; + @Nullable private final SnapshotState state; + @Nullable private final String reason; private final List indices; @@ -91,6 +98,10 @@ public final class SnapshotInfo implements Comparable, ToXContent, private final List shardFailures; + public SnapshotInfo(SnapshotId snapshotId, List indices, SnapshotState state) { + this(snapshotId, indices, state, null, null, 0L, 0L, 0, 0, Collections.emptyList()); + } + public SnapshotInfo(SnapshotId snapshotId, List indices, long startTime) { this(snapshotId, indices, SnapshotState.IN_PROGRESS, null, Version.CURRENT, startTime, 0L, 0, 0, Collections.emptyList()); } @@ -104,8 +115,8 @@ public SnapshotInfo(SnapshotId snapshotId, List indices, long startTime, private SnapshotInfo(SnapshotId snapshotId, List indices, SnapshotState state, String reason, Version version, long startTime, long endTime, int totalShards, int successfulShards, List shardFailures) { this.snapshotId = Objects.requireNonNull(snapshotId); - this.indices = Objects.requireNonNull(indices); - this.state = Objects.requireNonNull(state); + this.indices = Collections.unmodifiableList(Objects.requireNonNull(indices)); + this.state = state; this.reason = reason; this.version = version; this.startTime = startTime; @@ -126,7 +137,11 @@ public SnapshotInfo(final StreamInput in) throws IOException { indicesListBuilder.add(in.readString()); } indices = Collections.unmodifiableList(indicesListBuilder); - state = SnapshotState.fromValue(in.readByte()); + if (in.getVersion().onOrAfter(VERBOSE_INTRODUCED)) { + state = in.readBoolean() ? SnapshotState.fromValue(in.readByte()) : null; + } else { + state = SnapshotState.fromValue(in.readByte()); + } reason = in.readOptionalString(); startTime = in.readVLong(); endTime = in.readVLong(); @@ -159,6 +174,14 @@ public static SnapshotInfo incompatible(SnapshotId snapshotId) { null, 0L, 0L, 0, 0, Collections.emptyList()); } + /** + * Gets a new {@link SnapshotInfo} instance from the given {@link SnapshotInfo} with + * all information stripped out except the snapshot id, state, and indices. + */ + public SnapshotInfo basic() { + return new SnapshotInfo(snapshotId, indices, state); + } + /** * Returns snapshot id * @@ -169,25 +192,27 @@ public SnapshotId snapshotId() { } /** - * Returns snapshot state + * Returns snapshot state; {@code null} if the state is unknown. * * @return snapshot state */ + @Nullable public SnapshotState state() { return state; } /** - * Returns snapshot failure reason + * Returns snapshot failure reason; {@code null} if the snapshot succeeded. * * @return snapshot failure reason */ + @Nullable public String reason() { return reason; } /** - * Returns indices that were included into this snapshot + * Returns indices that were included in this snapshot. * * @return list of indices */ @@ -196,7 +221,8 @@ public List indices() { } /** - * Returns time when snapshot started + * Returns time when snapshot started; a value of {@code 0L} will be returned if + * {@link #state()} returns {@code null}. * * @return snapshot start time */ @@ -205,9 +231,8 @@ public long startTime() { } /** - * Returns time when snapshot ended - *

- * Can be 0L if snapshot is still running + * Returns time when snapshot ended; a value of {@code 0L} will be returned if the + * snapshot is still running or if {@link #state()} returns {@code null}. * * @return snapshot end time */ @@ -216,7 +241,8 @@ public long endTime() { } /** - * Returns total number of shards that were snapshotted + * Returns total number of shards that were snapshotted; a value of {@code 0} will + * be returned if {@link #state()} returns {@code null}. * * @return number of shards */ @@ -225,7 +251,8 @@ public int totalShards() { } /** - * Number of failed shards + * Number of failed shards; a value of {@code 0} will be returned if there were no + * failed shards, or if {@link #state()} returns {@code null}. * * @return number of failed shards */ @@ -234,7 +261,8 @@ public int failedShards() { } /** - * Returns total number of shards that were successfully snapshotted + * Returns total number of shards that were successfully snapshotted; a value of + * {@code 0} will be returned if {@link #state()} returns {@code null}. * * @return number of successful shards */ @@ -243,7 +271,8 @@ public int successfulShards() { } /** - * Returns shard failures + * Returns shard failures; an empty list will be returned if there were no shard + * failures, or if {@link #state()} returns {@code null}. * * @return shard failures */ @@ -253,7 +282,7 @@ public List shardFailures() { /** * Returns the version of elasticsearch that the snapshot was created with. Will only - * return {@code null} if {@link #state()} returns {@link SnapshotState#INCOMPATIBLE}. + * return {@code null} if {@link #state()} returns {@code null} or {@link SnapshotState#INCOMPATIBLE}. * * @return version of elasticsearch that the snapshot was created with */ @@ -263,16 +292,12 @@ public Version version() { } /** - * Compares two snapshots by their start time - * - * @param o other snapshot - * @return the value {@code 0} if snapshots were created at the same time; - * a value less than {@code 0} if this snapshot was created before snapshot {@code o}; and - * a value greater than {@code 0} if this snapshot was created after snapshot {@code o}; + * Compares two snapshots by their start time; if the start times are the same, then + * compares the two snapshots by their snapshot ids. */ @Override public int compareTo(final SnapshotInfo o) { - return Long.compare(startTime, o.startTime); + return COMPARATOR.compare(this, o); } @Override @@ -328,15 +353,15 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa if (version != null) { builder.field(VERSION_ID, version.id); builder.field(VERSION, version.toString()); - } else { - builder.field(VERSION, "unknown"); } builder.startArray(INDICES); for (String index : indices) { builder.value(index); } builder.endArray(); - builder.field(STATE, state); + if (state != null) { + builder.field(STATE, state); + } if (reason != null) { builder.field(REASON, reason); } @@ -349,18 +374,22 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa builder.field(END_TIME_IN_MILLIS, endTime); builder.timeValueField(DURATION_IN_MILLIS, DURATION, endTime - startTime); } - builder.startArray(FAILURES); - for (SnapshotShardFailure shardFailure : shardFailures) { - builder.startObject(); - shardFailure.toXContent(builder, params); + if (!shardFailures.isEmpty()) { + builder.startArray(FAILURES); + for (SnapshotShardFailure shardFailure : shardFailures) { + builder.startObject(); + shardFailure.toXContent(builder, params); + builder.endObject(); + } + builder.endArray(); + } + if (totalShards != 0) { + builder.startObject(SHARDS); + builder.field(TOTAL, totalShards); + builder.field(FAILED, failedShards()); + builder.field(SUCCESSFUL, successfulShards); builder.endObject(); } - builder.endArray(); - builder.startObject(SHARDS); - builder.field(TOTAL, totalShards); - builder.field(FAILED, failedShards()); - builder.field(SUCCESSFUL, successfulShards); - builder.endObject(); builder.endObject(); return builder; } @@ -496,10 +525,19 @@ public void writeTo(final StreamOutput out) throws IOException { for (String index : indices) { out.writeString(index); } - if (out.getVersion().before(VERSION_INCOMPATIBLE_INTRODUCED) && state == SnapshotState.INCOMPATIBLE) { - out.writeByte(SnapshotState.FAILED.value()); + if (out.getVersion().onOrAfter(VERBOSE_INTRODUCED)) { + if (state != null) { + out.writeBoolean(true); + out.writeByte(state.value()); + } else { + out.writeBoolean(false); + } } else { - out.writeByte(state.value()); + if (out.getVersion().before(VERSION_INCOMPATIBLE_INTRODUCED) && state == SnapshotState.INCOMPATIBLE) { + out.writeByte(SnapshotState.FAILED.value()); + } else { + out.writeByte(state.value()); + } } out.writeOptionalString(reason); out.writeVLong(startTime); diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java index a30324a13fc91..f8abcf318086f 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java @@ -26,8 +26,6 @@ import org.apache.lucene.util.CollectionUtil; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.OriginalIndices; -import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterState; @@ -67,7 +65,6 @@ import org.elasticsearch.repositories.Repository; import org.elasticsearch.repositories.RepositoryData; import org.elasticsearch.repositories.RepositoryMissingException; -import org.elasticsearch.search.SearchShardTarget; import org.elasticsearch.threadpool.ThreadPool; import java.io.IOException; @@ -171,7 +168,7 @@ public SnapshotInfo snapshot(final String repositoryName, final SnapshotId snaps */ public List snapshots(final String repositoryName, final List snapshotIds, - final List incompatibleSnapshotIds, + final Set incompatibleSnapshotIds, final boolean ignoreUnavailable) { final Set snapshotSet = new HashSet<>(); final Set snapshotIdsToIterate = new HashSet<>(snapshotIds); diff --git a/core/src/test/java/org/elasticsearch/bwcompat/RestoreBackwardsCompatIT.java b/core/src/test/java/org/elasticsearch/bwcompat/RestoreBackwardsCompatIT.java index 228991f787bad..394f09120d344 100644 --- a/core/src/test/java/org/elasticsearch/bwcompat/RestoreBackwardsCompatIT.java +++ b/core/src/test/java/org/elasticsearch/bwcompat/RestoreBackwardsCompatIT.java @@ -161,6 +161,14 @@ private void testOldSnapshot(String version, String repo, String snapshot) throw SnapshotInfo snapshotInfo = getSnapshotsResponse.getSnapshots().get(0); assertThat(snapshotInfo.version().toString(), equalTo(version)); + logger.info("--> get less verbose snapshot info"); + getSnapshotsResponse = client().admin().cluster().prepareGetSnapshots(repo) + .setSnapshots(snapshot).setVerbose(false).get(); + assertEquals(1, getSnapshotsResponse.getSnapshots().size()); + snapshotInfo = getSnapshotsResponse.getSnapshots().get(0); + assertEquals(snapshot, snapshotInfo.snapshotId().getName()); + assertNull(snapshotInfo.version()); // in verbose=false mode, version doesn't exist + logger.info("--> restoring snapshot"); RestoreSnapshotResponse response = client().admin().cluster().prepareRestoreSnapshot(repo, snapshot).setRestoreGlobalState(true).setWaitForCompletion(true).get(); assertThat(response.status(), equalTo(RestStatus.OK)); diff --git a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java index ab898985e43af..c925775fa5bd0 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java +++ b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java @@ -1538,7 +1538,7 @@ public MetaData getSnapshotMetaData(SnapshotInfo snapshot, List indices public RepositoryData getRepositoryData() { Map> map = new HashMap<>(); map.put(new IndexId(indexName, "blah"), emptySet()); - return new RepositoryData(EMPTY_REPO_GEN, Collections.emptyList(), map, Collections.emptyList()); + return new RepositoryData(EMPTY_REPO_GEN, Collections.emptyMap(), Collections.emptyMap(), map, Collections.emptyList()); } @Override diff --git a/core/src/test/java/org/elasticsearch/repositories/RepositoryDataTests.java b/core/src/test/java/org/elasticsearch/repositories/RepositoryDataTests.java index 6c548a38cb356..40ff1bad9767f 100644 --- a/core/src/test/java/org/elasticsearch/repositories/RepositoryDataTests.java +++ b/core/src/test/java/org/elasticsearch/repositories/RepositoryDataTests.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.snapshots.SnapshotId; +import org.elasticsearch.snapshots.SnapshotState; import org.elasticsearch.test.ESTestCase; import java.io.IOException; @@ -82,7 +83,8 @@ public void testAddSnapshots() { for (int i = 0; i < numOld; i++) { indices.add(indexIdMap.get(indexNames.get(i))); } - RepositoryData newRepoData = repositoryData.addSnapshot(newSnapshot, indices); + RepositoryData newRepoData = repositoryData.addSnapshot(newSnapshot, + randomFrom(SnapshotState.SUCCESS, SnapshotState.PARTIAL, SnapshotState.FAILED), indices); // verify that the new repository data has the new snapshot and its indices assertTrue(newRepoData.getSnapshotIds().contains(newSnapshot)); for (IndexId indexId : indices) { @@ -97,15 +99,21 @@ public void testAddSnapshots() { public void testInitIndices() { final int numSnapshots = randomIntBetween(1, 30); - final List snapshotIds = new ArrayList<>(numSnapshots); + final Map snapshotIds = new HashMap<>(numSnapshots); for (int i = 0; i < numSnapshots; i++) { - snapshotIds.add(new SnapshotId(randomAlphaOfLength(8), UUIDs.randomBase64UUID())); + final SnapshotId snapshotId = new SnapshotId(randomAlphaOfLength(8), UUIDs.randomBase64UUID()); + snapshotIds.put(snapshotId.getUUID(), snapshotId); } - RepositoryData repositoryData = new RepositoryData(EMPTY_REPO_GEN, snapshotIds, Collections.emptyMap(), Collections.emptyList()); + RepositoryData repositoryData = new RepositoryData(EMPTY_REPO_GEN, snapshotIds, + Collections.emptyMap(), Collections.emptyMap(), Collections.emptyList()); // test that initializing indices works Map> indices = randomIndices(snapshotIds); RepositoryData newRepoData = repositoryData.initIndices(indices); - assertEquals(repositoryData.getSnapshotIds(), newRepoData.getSnapshotIds()); + List expected = new ArrayList<>(repositoryData.getSnapshotIds()); + Collections.sort(expected); + List actual = new ArrayList<>(newRepoData.getSnapshotIds()); + Collections.sort(actual); + assertEquals(expected, actual); for (IndexId indexId : indices.keySet()) { assertEquals(indices.get(indexId), newRepoData.getSnapshots(indexId)); } @@ -136,25 +144,32 @@ public void testResolveIndexId() { assertEquals(new IndexId(notInRepoData, notInRepoData), repositoryData.resolveIndexId(notInRepoData)); } - public static RepositoryData generateRandomRepoData() { - return generateRandomRepoData(new ArrayList<>()); - } - - public static RepositoryData generateRandomRepoData(final List origSnapshotIds) { - List snapshotIds = randomSnapshots(origSnapshotIds); - return new RepositoryData(EMPTY_REPO_GEN, snapshotIds, randomIndices(snapshotIds), Collections.emptyList()); + public void testGetSnapshotState() { + final SnapshotId snapshotId = new SnapshotId(randomAlphaOfLength(8), UUIDs.randomBase64UUID()); + final SnapshotState state = randomFrom(SnapshotState.values()); + final RepositoryData repositoryData = RepositoryData.EMPTY.addSnapshot(snapshotId, state, Collections.emptyList()); + assertEquals(state, repositoryData.getSnapshotState(snapshotId)); + assertNull(repositoryData.getSnapshotState(new SnapshotId(randomAlphaOfLength(8), UUIDs.randomBase64UUID()))); } - private static List randomSnapshots(final List origSnapshotIds) { + public static RepositoryData generateRandomRepoData() { + final int numIndices = randomIntBetween(1, 30); + final List indices = new ArrayList<>(numIndices); + for (int i = 0; i < numIndices; i++) { + indices.add(new IndexId(randomAlphaOfLength(8), UUIDs.randomBase64UUID())); + } final int numSnapshots = randomIntBetween(1, 30); - final List snapshotIds = new ArrayList<>(origSnapshotIds); + RepositoryData repositoryData = RepositoryData.EMPTY; for (int i = 0; i < numSnapshots; i++) { - snapshotIds.add(new SnapshotId(randomAlphaOfLength(8), UUIDs.randomBase64UUID())); + final SnapshotId snapshotId = new SnapshotId(randomAlphaOfLength(8), UUIDs.randomBase64UUID()); + final List someIndices = indices.subList(0, randomIntBetween(1, numIndices)); + repositoryData = repositoryData.addSnapshot(snapshotId, randomFrom(SnapshotState.values()), someIndices); } - return snapshotIds; + return repositoryData; } - private static Map> randomIndices(final List snapshotIds) { + private static Map> randomIndices(final Map snapshotIdsMap) { + final List snapshotIds = new ArrayList<>(snapshotIdsMap.values()); final int totalSnapshots = snapshotIds.size(); final int numIndices = randomIntBetween(1, 30); final Map> indices = new HashMap<>(numIndices); diff --git a/core/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryTests.java b/core/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryTests.java index 87e8d595f4b99..7e4d5cc54a900 100644 --- a/core/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryTests.java +++ b/core/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryTests.java @@ -29,6 +29,7 @@ import org.elasticsearch.repositories.RepositoryData; import org.elasticsearch.repositories.RepositoryException; import org.elasticsearch.snapshots.SnapshotId; +import org.elasticsearch.snapshots.SnapshotState; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESSingleNodeTestCase; @@ -143,7 +144,7 @@ public void testIndexGenerationalFiles() throws Exception { assertThat(repository.readSnapshotIndexLatestBlob(), equalTo(1L)); // removing a snapshot and writing to a new index generational file - repositoryData = repository.getRepositoryData().removeSnapshot(repositoryData.getSnapshotIds().get(0)); + repositoryData = repository.getRepositoryData().removeSnapshot(repositoryData.getSnapshotIds().iterator().next()); repository.writeIndexGen(repositoryData, repositoryData.getGenId()); assertEquals(repository.getRepositoryData(), repositoryData); assertThat(repository.latestIndexBlobId(), equalTo(2L)); @@ -181,8 +182,8 @@ public void testReadAndWriteIncompatibleSnapshots() throws Exception { for (int i = 0; i < numSnapshots; i++) { snapshotIds.add(new SnapshotId(randomAlphaOfLength(8), UUIDs.randomBase64UUID())); } - RepositoryData repositoryData = new RepositoryData(readData.getGenId(), Collections.emptyList(), Collections.emptyMap(), - snapshotIds); + RepositoryData repositoryData = new RepositoryData(readData.getGenId(), + Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), snapshotIds); repository.blobContainer().deleteBlob("incompatible-snapshots"); repository.writeIncompatibleSnapshots(repositoryData); readData = repository.getRepositoryData(); @@ -228,7 +229,8 @@ private RepositoryData addRandomSnapshotsToRepoData(RepositoryData repoData, boo for (int j = 0; j < numIndices; j++) { indexIds.add(new IndexId(randomAlphaOfLength(8), UUIDs.randomBase64UUID())); } - repoData = repoData.addSnapshot(snapshotId, indexIds); + repoData = repoData.addSnapshot(snapshotId, + randomFrom(SnapshotState.SUCCESS, SnapshotState.PARTIAL, SnapshotState.FAILED), indexIds); } return repoData; } diff --git a/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java b/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java index 67911f3e5b03d..381755f87388c 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java +++ b/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java @@ -88,8 +88,10 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -2754,4 +2756,91 @@ public void testSnapshotStatusOnFailedIndex() throws Exception { } } } + + public void testGetSnapshotsFromIndexBlobOnly() throws Exception { + logger.info("--> creating repository"); + final Path repoPath = randomRepoPath(); + final Client client = client(); + assertAcked(client.admin().cluster() + .preparePutRepository("test-repo") + .setType("fs") + .setVerify(false) + .setSettings(Settings.builder().put("location", repoPath))); + + logger.info("--> creating random number of indices"); + final int numIndices = randomIntBetween(1, 10); + for (int i = 0; i < numIndices; i++) { + assertAcked(prepareCreate("test-idx-" + i).setSettings(Settings.builder() + .put(SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 0))); + } + + logger.info("--> creating random number of snapshots"); + final int numSnapshots = randomIntBetween(1, 10); + final Map> indicesPerSnapshot = new HashMap<>(); + for (int i = 0; i < numSnapshots; i++) { + // index some additional docs (maybe) for each index + for (int j = 0; j < numIndices; j++) { + if (randomBoolean()) { + final int numDocs = randomIntBetween(1, 5); + for (int k = 0; k < numDocs; k++) { + index("test-idx-" + j, "doc", Integer.toString(k), "foo", "bar" + k); + } + refresh(); + } + } + final boolean all = randomBoolean(); + boolean atLeastOne = false; + List indices = new ArrayList<>(); + for (int j = 0; j < numIndices; j++) { + if (all || randomBoolean() || !atLeastOne) { + indices.add("test-idx-" + j); + atLeastOne = true; + } + } + final String snapshotName = "test-snap-" + i; + indicesPerSnapshot.put(snapshotName, indices); + client.admin().cluster() + .prepareCreateSnapshot("test-repo", snapshotName) + .setWaitForCompletion(true) + .setIndices(indices.toArray(new String[indices.size()])) + .get(); + } + + logger.info("--> verify _all returns snapshot info"); + GetSnapshotsResponse response = client().admin().cluster() + .prepareGetSnapshots("test-repo") + .setSnapshots("_all") + .setVerbose(false) + .get(); + assertEquals(indicesPerSnapshot.size(), response.getSnapshots().size()); + verifySnapshotInfo(response, indicesPerSnapshot); + + logger.info("--> verify wildcard returns snapshot info"); + response = client().admin().cluster() + .prepareGetSnapshots("test-repo") + .setSnapshots("test-snap-*") + .setVerbose(false) + .get(); + assertEquals(indicesPerSnapshot.size(), response.getSnapshots().size()); + verifySnapshotInfo(response, indicesPerSnapshot); + + logger.info("--> verify individual requests return snapshot info"); + for (int i = 0; i < numSnapshots; i++) { + response = client().admin().cluster() + .prepareGetSnapshots("test-repo") + .setSnapshots("test-snap-" + i) + .setVerbose(false) + .get(); + assertEquals(1, response.getSnapshots().size()); + verifySnapshotInfo(response, indicesPerSnapshot); + } + } + + private void verifySnapshotInfo(final GetSnapshotsResponse response, final Map> indicesPerSnapshot) { + for (SnapshotInfo snapshotInfo : response.getSnapshots()) { + final List expected = snapshotInfo.indices(); + assertEquals(expected, indicesPerSnapshot.get(snapshotInfo.snapshotId().getName())); + assertEquals(SnapshotState.SUCCESS, snapshotInfo.state()); + } + } } diff --git a/docs/reference/modules/snapshots.asciidoc b/docs/reference/modules/snapshots.asciidoc index 94138dbdb0f34..64e9e2e1663aa 100644 --- a/docs/reference/modules/snapshots.asciidoc +++ b/docs/reference/modules/snapshots.asciidoc @@ -350,6 +350,15 @@ GET /_snapshot/my_backup/_all The command fails if some of the snapshots are unavailable. The boolean parameter `ignore_unavailable` can be used to return all snapshots that are currently available. +Getting all snapshots in the repository can be costly on cloud-based repositories, +both from a cost and performance perspective. If the only information required is +the snapshot names/uuids in the repository and the indices in each snapshot, then +the optional boolean parameter `verbose` can be set to `false` to execute a more +performant and cost-effective retrieval of the snapshots in the repository. Note +that setting `verbose` to `false` will omit all other information about the snapshot +such as status information, the number of snapshotted shards, etc. The default +value of the `verbose` parameter is `true`. + A currently running snapshot can be retrieved using the following command: [source,sh] diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.get.json b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.get.json index 760809cdf9baa..02f5259bc2ec8 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.get.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.get.json @@ -25,6 +25,10 @@ "ignore_unavailable": { "type": "boolean", "description": "Whether to ignore unavailable snapshots, defaults to false which means a SnapshotMissingException is thrown" + }, + "verbose": { + "type": "boolean", + "description": "Whether to show verbose snapshot info or only show the basic info found in the repository index blob" } } }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get/10_basic.yaml index 24a7ac6adc616..515662355da3e 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get/10_basic.yaml @@ -33,6 +33,11 @@ setup: - is_true: snapshots + - do: + snapshot.delete: + repository: test_repo_get_1 + snapshot: test_snapshot + --- "Get missing snapshot info throws an exception": @@ -52,3 +57,39 @@ setup: ignore_unavailable: true - is_true: snapshots + +--- +"Get snapshot info when verbose is false": + - skip: + version: " - 5.99.99" + reason: verbose mode was introduced in 6.0 + + - do: + indices.create: + index: test_index + body: + settings: + number_of_shards: 1 + number_of_replicas: 0 + + - do: + snapshot.create: + repository: test_repo_get_1 + snapshot: test_snapshot + wait_for_completion: true + + - do: + snapshot.get: + repository: test_repo_get_1 + snapshot: test_snapshot + verbose: false + + - is_true: snapshots + - match: { snapshots.0.snapshot: test_snapshot } + - match: { snapshots.0.state: SUCCESS } + - is_false: snapshots.0.version + + - do: + snapshot.delete: + repository: test_repo_get_1 + snapshot: test_snapshot From 6ac8a1eb85636822bdd7d60718a6fb1a55fd4384 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 10 May 2017 13:09:31 -0700 Subject: [PATCH 288/619] Deprecate Fine Grain Settings for Scripts (#24573) --- .../elasticsearch/script/ScriptSettings.java | 14 +++++---- .../elasticsearch/script/FileScriptTests.java | 15 ++++++++-- .../script/ScriptContextTests.java | 29 ++++++++++++++----- .../script/ScriptModesTests.java | 13 +++++++++ .../script/ScriptServiceTests.java | 16 +++++++++- .../script/ScriptSettingsTests.java | 24 +++++++++++++++ .../test/ESSingleNodeTestCase.java | 2 -- 7 files changed, 93 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/script/ScriptSettings.java b/core/src/main/java/org/elasticsearch/script/ScriptSettings.java index cc928a8b5977c..9d9b12558697d 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptSettings.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptSettings.java @@ -41,7 +41,8 @@ public class ScriptSettings { scriptTypeSettingMap.put(scriptType, Setting.boolSetting( ScriptModes.sourceKey(scriptType), scriptType.isDefaultEnabled(), - Property.NodeScope)); + Property.NodeScope, + Property.Deprecated)); } SCRIPT_TYPE_SETTING_MAP = Collections.unmodifiableMap(scriptTypeSettingMap); } @@ -61,7 +62,7 @@ private static Map> contextSettings(ScriptContex Map> scriptContextSettingMap = new HashMap<>(); for (ScriptContext scriptContext : scriptContextRegistry.scriptContexts()) { scriptContextSettingMap.put(scriptContext, - Setting.boolSetting(ScriptModes.operationKey(scriptContext), false, Property.NodeScope)); + Setting.boolSetting(ScriptModes.operationKey(scriptContext), false, Property.NodeScope, Property.Deprecated)); } return scriptContextSettingMap; } @@ -91,7 +92,7 @@ private static List> languageSettings(Map defaultLangAndTypeFn = settings -> { final Setting globalTypeSetting = scriptTypeSettingMap.get(scriptType); final Setting langAndTypeSetting = Setting.boolSetting(ScriptModes.getGlobalKey(language, scriptType), - defaultIfNothingSet, Property.NodeScope); + defaultIfNothingSet, Property.NodeScope, Property.Deprecated); if (langAndTypeSetting.exists(settings)) { // fine-grained e.g. script.engine.groovy.inline @@ -106,7 +107,7 @@ private static List> languageSettings(Map langAndTypeSetting = Setting.boolSetting(ScriptModes.getGlobalKey(language, scriptType), - defaultLangAndTypeFn, Property.NodeScope); + defaultLangAndTypeFn, Property.NodeScope, Property.Deprecated); scriptModeSettings.add(langAndTypeSetting); for (ScriptContext scriptContext : scriptContextRegistry.scriptContexts()) { @@ -117,7 +118,7 @@ private static List> languageSettings(Map globalOpSetting = scriptContextSettingMap.get(scriptContext); final Setting globalTypeSetting = scriptTypeSettingMap.get(scriptType); final Setting langAndTypeAndContextSetting = Setting.boolSetting(langAndTypeAndContextName, - defaultIfNothingSet, Property.NodeScope); + defaultIfNothingSet, Property.NodeScope, Property.Deprecated); // fallback logic for script mode settings if (langAndTypeAndContextSetting.exists(settings)) { @@ -138,7 +139,8 @@ private static List> languageSettings(Map setting = Setting.boolSetting(langAndTypeAndContextName, defaultSettingFn, Property.NodeScope); + Setting setting = + Setting.boolSetting(langAndTypeAndContextName, defaultSettingFn, Property.NodeScope, Property.Deprecated); scriptModeSettings.add(setting); } } diff --git a/core/src/test/java/org/elasticsearch/script/FileScriptTests.java b/core/src/test/java/org/elasticsearch/script/FileScriptTests.java index af32e8abec797..0ad3bb08ab21a 100644 --- a/core/src/test/java/org/elasticsearch/script/FileScriptTests.java +++ b/core/src/test/java/org/elasticsearch/script/FileScriptTests.java @@ -31,6 +31,8 @@ // TODO: these really should just be part of ScriptService tests, there is nothing special about them public class FileScriptTests extends ESTestCase { + private ScriptSettings scriptSettings; + ScriptService makeScriptService(Settings settings) throws Exception { Path homeDir = createTempDir(); Path scriptsDir = homeDir.resolve("config").resolve("scripts"); @@ -47,7 +49,7 @@ ScriptService makeScriptService(Settings settings) throws Exception { MockScriptEngine scriptEngine = new MockScriptEngine(MockScriptEngine.NAME, Collections.singletonMap(scriptSource, script -> "1")); ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(Collections.singleton(scriptEngine)); ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Collections.emptyList()); - ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry); + scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry); return new ScriptService(settings, new Environment(settings), null, scriptEngineRegistry, scriptContextRegistry, scriptSettings); } @@ -60,7 +62,9 @@ public void testFileScriptFound() throws Exception { assertNotNull(compiledScript); MockCompiledScript executable = (MockCompiledScript) compiledScript.compiled(); assertEquals("script1.mockscript", executable.getName()); - assertSettingDeprecationsAndWarnings(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}, + assertSettingDeprecationsAndWarnings(ScriptSettingsTests.buildDeprecatedSettingsArray( + new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}, + scriptSettings, "script.engine." + MockScriptEngine.NAME + ".file.aggs"), "File scripts are deprecated. Use stored or inline scripts instead."); } @@ -81,7 +85,12 @@ public void testAllOpsDisabled() throws Exception { assertTrue(e.getMessage(), e.getMessage().contains("scripts of type [file], operation [" + context.getKey() + "] and lang [" + MockScriptEngine.NAME + "] are disabled")); } } - assertSettingDeprecationsAndWarnings(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}, + assertSettingDeprecationsAndWarnings(ScriptSettingsTests.buildDeprecatedSettingsArray( + new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}, scriptSettings, + "script.engine." + MockScriptEngine.NAME + ".file.aggs", + "script.engine." + MockScriptEngine.NAME + ".file.search", + "script.engine." + MockScriptEngine.NAME + ".file.update", + "script.engine." + MockScriptEngine.NAME + ".file.ingest"), "File scripts are deprecated. Use stored or inline scripts instead."); } } diff --git a/core/src/test/java/org/elasticsearch/script/ScriptContextTests.java b/core/src/test/java/org/elasticsearch/script/ScriptContextTests.java index 83608a010b1ff..1d627c0d6a34b 100644 --- a/core/src/test/java/org/elasticsearch/script/ScriptContextTests.java +++ b/core/src/test/java/org/elasticsearch/script/ScriptContextTests.java @@ -37,14 +37,18 @@ public class ScriptContextTests extends ESTestCase { private static final String PLUGIN_NAME = "testplugin"; + private static final String SCRIPT_PLUGIN_CUSTOM_SETTING = "script." + PLUGIN_NAME + "_custom_globally_disabled_op"; + private static final String SCRIPT_ENGINE_CUSTOM_SETTING = "script.engine." + MockScriptEngine.NAME + ".inline." + PLUGIN_NAME + "_custom_exp_disabled_op"; + + private ScriptSettings scriptSettings; ScriptService makeScriptService() throws Exception { Settings settings = Settings.builder() .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) // no file watching, so we don't need a ResourceWatcherService .put(ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING.getKey(), "false") - .put("script." + PLUGIN_NAME + "_custom_globally_disabled_op", "false") - .put("script.engine." + MockScriptEngine.NAME + ".inline." + PLUGIN_NAME + "_custom_exp_disabled_op", "false") + .put(SCRIPT_PLUGIN_CUSTOM_SETTING, "false") + .put(SCRIPT_ENGINE_CUSTOM_SETTING, "false") .build(); MockScriptEngine scriptEngine = new MockScriptEngine(MockScriptEngine.NAME, Collections.singletonMap("1", script -> "1")); @@ -54,7 +58,7 @@ ScriptService makeScriptService() throws Exception { new ScriptContext.Plugin(PLUGIN_NAME, "custom_exp_disabled_op"), new ScriptContext.Plugin(PLUGIN_NAME, "custom_globally_disabled_op")); ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(customContexts); - ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry); + scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry); ScriptService scriptService = new ScriptService(settings, new Environment(settings), null, scriptEngineRegistry, scriptContextRegistry, scriptSettings); ClusterState empty = ClusterState.builder(new ClusterName("_name")).build(); @@ -67,6 +71,8 @@ ScriptService makeScriptService() throws Exception { return scriptService; } + + public void testCustomGlobalScriptContextSettings() throws Exception { ScriptService scriptService = makeScriptService(); for (ScriptType scriptType : ScriptType.values()) { @@ -78,7 +84,9 @@ public void testCustomGlobalScriptContextSettings() throws Exception { assertThat(e.getMessage(), containsString("scripts of type [" + scriptType + "], operation [" + PLUGIN_NAME + "_custom_globally_disabled_op] and lang [" + MockScriptEngine.NAME + "] are disabled")); } } - assertSettingDeprecationsAndWarnings(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}); + assertSettingDeprecationsAndWarnings( + ScriptSettingsTests.buildDeprecatedSettingsArray(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}, + scriptSettings, SCRIPT_PLUGIN_CUSTOM_SETTING, SCRIPT_ENGINE_CUSTOM_SETTING)); } public void testCustomScriptContextSettings() throws Exception { @@ -95,7 +103,9 @@ public void testCustomScriptContextSettings() throws Exception { assertNotNull(scriptService.compile(script, ScriptContext.Standard.AGGS)); assertNotNull(scriptService.compile(script, ScriptContext.Standard.SEARCH)); assertNotNull(scriptService.compile(script, new ScriptContext.Plugin(PLUGIN_NAME, "custom_op"))); - assertSettingDeprecationsAndWarnings(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}); + assertSettingDeprecationsAndWarnings( + ScriptSettingsTests.buildDeprecatedSettingsArray(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}, + scriptSettings, SCRIPT_PLUGIN_CUSTOM_SETTING, SCRIPT_ENGINE_CUSTOM_SETTING)); } public void testUnknownPluginScriptContext() throws Exception { @@ -109,7 +119,9 @@ public void testUnknownPluginScriptContext() throws Exception { assertTrue(e.getMessage(), e.getMessage().contains("script context [" + PLUGIN_NAME + "_unknown] not supported")); } } - assertSettingDeprecationsAndWarnings(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}); + assertSettingDeprecationsAndWarnings( + ScriptSettingsTests.buildDeprecatedSettingsArray(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}, + scriptSettings, SCRIPT_PLUGIN_CUSTOM_SETTING, SCRIPT_ENGINE_CUSTOM_SETTING)); } public void testUnknownCustomScriptContext() throws Exception { @@ -129,7 +141,8 @@ public String getKey() { assertTrue(e.getMessage(), e.getMessage().contains("script context [test] not supported")); } } - assertSettingDeprecationsAndWarnings(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}); + assertSettingDeprecationsAndWarnings( + ScriptSettingsTests.buildDeprecatedSettingsArray(new Setting[] {ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING}, + scriptSettings, SCRIPT_PLUGIN_CUSTOM_SETTING, SCRIPT_ENGINE_CUSTOM_SETTING)); } - } diff --git a/core/src/test/java/org/elasticsearch/script/ScriptModesTests.java b/core/src/test/java/org/elasticsearch/script/ScriptModesTests.java index a2db206858c3d..63bd3fd377773 100644 --- a/core/src/test/java/org/elasticsearch/script/ScriptModesTests.java +++ b/core/src/test/java/org/elasticsearch/script/ScriptModesTests.java @@ -26,9 +26,11 @@ import org.junit.After; import org.junit.Before; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -123,9 +125,11 @@ public void testScriptTypeGenericSettings() { randomScriptModes[i] = randomBoolean(); } ScriptType[] randomScriptTypes = randomScriptTypesSet.toArray(new ScriptType[randomScriptTypesSet.size()]); + List deprecated = new ArrayList<>(); Settings.Builder builder = Settings.builder(); for (int i = 0; i < randomInt; i++) { builder.put("script" + "." + randomScriptTypes[i].getName(), randomScriptModes[i]); + deprecated.add("script" + "." + randomScriptTypes[i].getName()); } this.scriptModes = new ScriptModes(scriptSettings, builder.build()); @@ -141,6 +145,8 @@ public void testScriptTypeGenericSettings() { if (randomScriptTypesSet.contains(ScriptType.INLINE) == false) { assertScriptModesAllOps(false, ScriptType.INLINE); } + assertSettingDeprecationsAndWarnings( + ScriptSettingsTests.buildDeprecatedSettingsArray(scriptSettings, deprecated.toArray(new String[] {}))); } public void testScriptContextGenericSettings() { @@ -155,9 +161,11 @@ public void testScriptContextGenericSettings() { randomScriptModes[i] = randomBoolean(); } ScriptContext[] randomScriptContexts = randomScriptContextsSet.toArray(new ScriptContext[randomScriptContextsSet.size()]); + List deprecated = new ArrayList<>(); Settings.Builder builder = Settings.builder(); for (int i = 0; i < randomInt; i++) { builder.put("script" + "." + randomScriptContexts[i].getKey(), randomScriptModes[i]); + deprecated.add("script" + "." + randomScriptContexts[i].getKey()); } this.scriptModes = new ScriptModes(scriptSettings, builder.build()); @@ -168,6 +176,8 @@ public void testScriptContextGenericSettings() { ScriptContext[] complementOf = complementOf(randomScriptContexts); assertScriptModes(true, new ScriptType[]{ScriptType.FILE}, complementOf); assertScriptModes(false, new ScriptType[]{ScriptType.STORED, ScriptType.INLINE}, complementOf); + assertSettingDeprecationsAndWarnings( + ScriptSettingsTests.buildDeprecatedSettingsArray(scriptSettings, deprecated.toArray(new String[] {}))); } public void testConflictingScriptTypeAndOpGenericSettings() { @@ -182,6 +192,9 @@ public void testConflictingScriptTypeAndOpGenericSettings() { ScriptContext[] complementOf = complementOf(scriptContext); assertScriptModes(true, new ScriptType[]{ScriptType.FILE, ScriptType.STORED}, complementOf); assertScriptModes(true, new ScriptType[]{ScriptType.INLINE}, complementOf); + assertSettingDeprecationsAndWarnings( + ScriptSettingsTests.buildDeprecatedSettingsArray( + scriptSettings, "script." + scriptContext.getKey(), "script.stored", "script.inline")); } private void assertScriptModesAllOps(boolean expectedScriptEnabled, ScriptType... scriptTypes) { diff --git a/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java b/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java index 6928d565487cd..9fdbcd6f90d19 100644 --- a/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java +++ b/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java @@ -40,10 +40,12 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import static org.hamcrest.CoreMatchers.containsString; @@ -263,6 +265,7 @@ public void testFineGrainedSettings() throws IOException { } while (engineSettings.containsKey(settingKey)); engineSettings.put(settingKey, randomBoolean()); } + List deprecated = new ArrayList<>(); //set the selected fine-grained settings Settings.Builder builder = Settings.builder(); for (Map.Entry entry : scriptSourceSettings.entrySet()) { @@ -271,6 +274,7 @@ public void testFineGrainedSettings() throws IOException { } else { builder.put("script" + "." + entry.getKey().getName(), "false"); } + deprecated.add("script" + "." + entry.getKey().getName()); } for (Map.Entry entry : scriptContextSettings.entrySet()) { if (entry.getValue()) { @@ -278,6 +282,7 @@ public void testFineGrainedSettings() throws IOException { } else { builder.put("script" + "." + entry.getKey().getKey(), "false"); } + deprecated.add("script" + "." + entry.getKey().getKey()); } for (Map.Entry entry : engineSettings.entrySet()) { int delimiter = entry.getKey().indexOf('.'); @@ -290,6 +295,7 @@ public void testFineGrainedSettings() throws IOException { } else { builder.put("script.engine" + "." + lang + "." + part2, "false"); } + deprecated.add("script.engine" + "." + lang + "." + part2); } buildScriptService(builder.build()); @@ -320,7 +326,9 @@ public void testFineGrainedSettings() throws IOException { } } } - assertWarnings("File scripts are deprecated. Use stored or inline scripts instead."); + assertSettingDeprecationsAndWarnings( + ScriptSettingsTests.buildDeprecatedSettingsArray(scriptSettings, deprecated.toArray(new String[] {})), + "File scripts are deprecated. Use stored or inline scripts instead."); } public void testCompileNonRegisteredContext() throws IOException { @@ -381,6 +389,8 @@ public void testCompilationStatsOnCacheHit() throws IOException { scriptService.compile(script, randomFrom(scriptContexts)); scriptService.compile(script, randomFrom(scriptContexts)); assertEquals(1L, scriptService.stats().getCompilations()); + assertSettingDeprecationsAndWarnings( + ScriptSettingsTests.buildDeprecatedSettingsArray(scriptSettings, "script.inline")); } public void testFileScriptCountedInCompilationStats() throws IOException { @@ -406,6 +416,8 @@ public void testCacheEvictionCountedInCacheEvictionsStats() throws IOException { scriptService.compile(new Script(ScriptType.INLINE, "test", "2+2", Collections.emptyMap()), randomFrom(scriptContexts)); assertEquals(2L, scriptService.stats().getCompilations()); assertEquals(1L, scriptService.stats().getCacheEvictions()); + assertSettingDeprecationsAndWarnings( + ScriptSettingsTests.buildDeprecatedSettingsArray(scriptSettings, "script.inline")); } public void testDefaultLanguage() throws IOException { @@ -415,6 +427,8 @@ public void testDefaultLanguage() throws IOException { CompiledScript script = scriptService.compile( new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, "1 + 1", Collections.emptyMap()), randomFrom(scriptContexts)); assertEquals(script.lang(), Script.DEFAULT_SCRIPT_LANG); + assertSettingDeprecationsAndWarnings( + ScriptSettingsTests.buildDeprecatedSettingsArray(scriptSettings, "script.inline")); } public void testStoreScript() throws Exception { diff --git a/core/src/test/java/org/elasticsearch/script/ScriptSettingsTests.java b/core/src/test/java/org/elasticsearch/script/ScriptSettingsTests.java index 6eea1fa8010b7..1315ce4970bdd 100644 --- a/core/src/test/java/org/elasticsearch/script/ScriptSettingsTests.java +++ b/core/src/test/java/org/elasticsearch/script/ScriptSettingsTests.java @@ -33,6 +33,29 @@ public class ScriptSettingsTests extends ESTestCase { + public static Setting[] buildDeprecatedSettingsArray(ScriptSettings scriptSettings, String... keys) { + return buildDeprecatedSettingsArray(null, scriptSettings, keys); + } + + public static Setting[] buildDeprecatedSettingsArray(Setting[] deprecated, ScriptSettings scriptSettings, String... keys) { + Setting[] settings = new Setting[keys.length + (deprecated == null ? 0 : deprecated.length)]; + int count = 0; + + for (Setting setting : scriptSettings.getSettings()) { + for (String key : keys) { + if (setting.getKey().equals(key)) { + settings[count++] = setting; + } + } + } + + if (deprecated != null) { + System.arraycopy(deprecated, 0, settings, keys.length, deprecated.length); + } + + return settings; + } + public void testSettingsAreProperlyPropogated() { ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(Collections.singletonList(new CustomScriptEngine())); @@ -47,6 +70,7 @@ public void testSettingsAreProperlyPropogated() { assertThat(setting.getDefaultRaw(s), equalTo(Boolean.toString(enabled))); } } + assertSettingDeprecationsAndWarnings(buildDeprecatedSettingsArray(scriptSettings, "script.inline")); } private static class CustomScriptEngine implements ScriptEngine { diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java index 99cb7690c8268..bc36a7999ba2d 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java @@ -172,8 +172,6 @@ private Node newNode() { // This needs to tie into the ESIntegTestCase#indexSettings() method .put(Environment.PATH_SHARED_DATA_SETTING.getKey(), createTempDir().getParent()) .put("node.name", "node_s_0") - .put("script.inline", "true") - .put("script.stored", "true") .put(ScriptService.SCRIPT_MAX_COMPILATIONS_PER_MINUTE.getKey(), 1000) .put(EsExecutors.PROCESSORS_SETTING.getKey(), 1) // limit the number of threads created .put(NetworkModule.HTTP_ENABLED.getKey(), false) From 63a408a128ae5133cbfcc1151f935893a332a750 Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Wed, 10 May 2017 17:38:23 -0400 Subject: [PATCH 289/619] Updates version for get snapshot API with verbose option to 5.5 --- .../src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java index 994d5d2a9affb..433a631821319 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java @@ -70,7 +70,7 @@ public final class SnapshotInfo implements Comparable, ToXContent, private static final String SUCCESSFUL_SHARDS = "successful_shards"; private static final Version VERSION_INCOMPATIBLE_INTRODUCED = Version.V_5_2_0_UNRELEASED; - public static final Version VERBOSE_INTRODUCED = Version.V_6_0_0_alpha1_UNRELEASED; + public static final Version VERBOSE_INTRODUCED = Version.V_5_5_0_UNRELEASED; private static final Comparator COMPARATOR = Comparator.comparing(SnapshotInfo::startTime).thenComparing(SnapshotInfo::snapshotId); From 6a113ae499ee1b848e83cc3a337bb29f0ce9d178 Mon Sep 17 00:00:00 2001 From: James Baiera Date: Wed, 10 May 2017 17:42:20 -0400 Subject: [PATCH 290/619] Introduce Kerberos Test Fixture for Repository HDFS Security Tests (#24493) This PR introduces a subproject in test/fixtures that contains a Vagrantfile used for standing up a KRB5 KDC (Kerberos). The PR also includes helper scripts for provisioning principals, a few changes to the HDFS Fixture to allow it to interface with the KDC, as well as a new suite of integration tests for the HDFS Repository plugin. The HDFS Repository plugin senses if the local environment can support the HDFS Fixture (Windows is generally a restricted environment). If it can use the regular fixture, it then tests if Vagrant is installed with a compatible version to determine if the secure test fixtures should be enabled. If the secure tests are enabled, then we create a Kerberos KDC fixture, tasks for adding the required principals, and an HDFS fixture configured for security. A new integration test task is also configured to use the KDC and secure HDFS fixture and to run a testing suite that uses authentication. At the end of the secure integration test the fixtures are torn down. --- plugins/repository-hdfs/build.gradle | 114 +++++++++++++++++ .../test/secure_hdfs_repository/10_basic.yaml | 30 +++++ .../20_repository_create.yaml | 29 +++++ .../20_repository_delete.yaml | 54 ++++++++ .../20_repository_verify.yaml | 25 ++++ .../secure_hdfs_repository/30_snapshot.yaml | 50 ++++++++ .../30_snapshot_get.yaml | 72 +++++++++++ .../secure_hdfs_repository/40_restore.yaml | 81 ++++++++++++ settings.gradle | 1 + .../src/main/java/hdfs/MiniHDFS.java | 65 ++++++++-- test/fixtures/krb5kdc-fixture/Vagrantfile | 53 ++++++++ test/fixtures/krb5kdc-fixture/build.gradle | 84 ++++++++++++ .../src/main/resources/env.properties | 20 +++ .../src/main/resources/provision/addprinc.sh | 58 +++++++++ .../main/resources/provision/installkdc.sh | 120 ++++++++++++++++++ .../resources/provision/kdc.conf.template | 35 +++++ .../resources/provision/krb5.conf.template | 50 ++++++++ 17 files changed, 930 insertions(+), 11 deletions(-) create mode 100644 plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/10_basic.yaml create mode 100644 plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_create.yaml create mode 100644 plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_delete.yaml create mode 100644 plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_verify.yaml create mode 100644 plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot.yaml create mode 100644 plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot_get.yaml create mode 100644 plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/40_restore.yaml create mode 100644 test/fixtures/krb5kdc-fixture/Vagrantfile create mode 100644 test/fixtures/krb5kdc-fixture/build.gradle create mode 100644 test/fixtures/krb5kdc-fixture/src/main/resources/env.properties create mode 100755 test/fixtures/krb5kdc-fixture/src/main/resources/provision/addprinc.sh create mode 100755 test/fixtures/krb5kdc-fixture/src/main/resources/provision/installkdc.sh create mode 100644 test/fixtures/krb5kdc-fixture/src/main/resources/provision/kdc.conf.template create mode 100644 test/fixtures/krb5kdc-fixture/src/main/resources/provision/krb5.conf.template diff --git a/plugins/repository-hdfs/build.gradle b/plugins/repository-hdfs/build.gradle index f17819dba40af..2a94b569278f7 100644 --- a/plugins/repository-hdfs/build.gradle +++ b/plugins/repository-hdfs/build.gradle @@ -18,6 +18,8 @@ */ import org.apache.tools.ant.taskdefs.condition.Os +import org.elasticsearch.gradle.test.RestIntegTestTask + import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths @@ -68,6 +70,61 @@ task hdfsFixture(type: org.elasticsearch.gradle.test.AntFixture) { baseDir } +// MIT Kerberos Vagrant Testing Fixture +String box = "krb5kdc" +Map vagrantEnvVars = [ + 'VAGRANT_CWD' : "${project(':test:fixtures:krb5kdc-fixture').projectDir}", + 'VAGRANT_VAGRANTFILE' : 'Vagrantfile', + 'VAGRANT_PROJECT_DIR' : "${project(':test:fixtures:krb5kdc-fixture').projectDir}" +] + +task krb5kdcUpdate(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { + command 'box' + subcommand 'update' + boxName box + environmentVars vagrantEnvVars +} + +task krb5kdcFixture(type: org.elasticsearch.gradle.test.VagrantFixture) { + command 'up' + args '--provision', '--provider', 'virtualbox' + boxName box + environmentVars vagrantEnvVars + dependsOn krb5kdcUpdate +} + +task krb5AddPrincipals { + dependsOn krb5kdcFixture +} + +List principals = [ "elasticsearch", "hdfs/hdfs.build.elastic.co" ] +String realm = "BUILD.ELASTIC.CO" + +for (String principal : principals) { + Task create = project.tasks.create("addPrincipal#${principal}", org.elasticsearch.gradle.vagrant.VagrantCommandTask) { + command 'ssh' + args '--command', "sudo bash /vagrant/src/main/resources/provision/addprinc.sh $principal" + boxName box + environmentVars vagrantEnvVars + dependsOn krb5kdcFixture + } + krb5AddPrincipals.dependsOn(create) +} + +task secureHdfsFixture(type: org.elasticsearch.gradle.test.AntFixture) { + dependsOn project.configurations.hdfsFixture, krb5kdcFixture, krb5AddPrincipals + executable = new File(project.javaHome, 'bin/java') + env 'CLASSPATH', "${ -> project.configurations.hdfsFixture.asPath }" + + Path keytabPath = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("keytabs").resolve("hdfs_hdfs.build.elastic.co.keytab") + Path krb5Config = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("conf").resolve("krb5.conf") + + args "-Djava.security.krb5.conf=${krb5Config}", 'hdfs.MiniHDFS', + baseDir, + "hdfs/hdfs.build.elastic.co@${realm}", + "${keytabPath}" +} + boolean fixtureSupported = false; if (Os.isFamily(Os.FAMILY_WINDOWS)) { // hdfs fixture will not start without hadoop native libraries on windows @@ -89,12 +146,69 @@ if (Os.isFamily(Os.FAMILY_WINDOWS)) { if (fixtureSupported) { integTestCluster.dependsOn hdfsFixture + integTestRunner.systemProperty 'tests.rest.suite', 'hdfs_repository' } else { logger.warn("hdfsFixture unsupported, please set HADOOP_HOME and put HADOOP_HOME\\bin in PATH") // just tests that the plugin loads integTestRunner.systemProperty 'tests.rest.suite', 'hdfs_repository/10_basic' } +boolean secureFixtureSupported = false; +if (fixtureSupported) { + // Only do secure fixture support if the regular fixture is supported, + // and if vagrant is installed. The ignoreExitValue on exec only matters + // in cases where the command can be found and successfully started. In + // situations where the vagrant command isn't able to be started at all + // (it's not installed) then Gradle still throws ExecException. + ByteArrayOutputStream pipe = new ByteArrayOutputStream() + try { + ExecResult runResult = exec { + commandLine 'vagrant', '--version' + standardOutput pipe + ignoreExitValue true + } + String output = pipe.toString().trim() + if (runResult.exitValue == 0) { + secureFixtureSupported = (output ==~ /Vagrant 1\.(8\.[6-9]|9\.[0-9])+/) + } else { + logger.warn("Could not read installed vagrant version:\n" + output) + } + } catch (org.gradle.process.internal.ExecException e) { + logger.warn("Could not find vagrant: " + e.message) + // Swallow error. Vagrant isn't installed. Leave secure fixture support off. + } +} + +// Create a Integration Test suite just for security based tests +if (secureFixtureSupported) { + // This must execute before the afterEvaluate block from integTestSecure + project.afterEvaluate { + Path elasticsearchKT = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("keytabs").resolve("elasticsearch.keytab").toAbsolutePath() + Path krb5conf = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("conf").resolve("krb5.conf").toAbsolutePath() + + project.integTestSecureCluster.dependsOn(project.bundlePlugin) + project.integTestSecure.clusterConfig.plugin(project.path) + project.integTestSecure.clusterConfig.extraConfigFile("repository-hdfs/krb5.keytab", "${elasticsearchKT}") + project.integTestSecure.clusterConfig.jvmArgs = "-Xms" + System.getProperty('tests.heap.size', '512m') + + " " + "-Xmx" + System.getProperty('tests.heap.size', '512m') + + " " + "-Djava.security.krb5.conf=${krb5conf}" + + " " + System.getProperty('tests.jvm.argline', '') + } + + RestIntegTestTask integTestSecure = project.tasks.create('integTestSecure', RestIntegTestTask.class) { + description = "Runs rest tests against an elasticsearch cluster with HDFS secured by MIT Kerberos." + } + + integTestSecure.mustRunAfter(project.integTest) + project.check.dependsOn(integTestSecure) + + // Fixture dependencies + integTestSecureCluster.dependsOn secureHdfsFixture, krb5kdcFixture + integTestSecureRunner.systemProperty 'tests.rest.suite', 'secure_hdfs_repository' +} else { + logger.warn("secured hdfsFixture is unsupported, please install Vagrant 1.8.6+ to enable") +} + thirdPartyAudit.excludes = [ // classes are missing, because we added hadoop jars one by one until tests pass. 'com.google.gson.stream.JsonReader', diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/10_basic.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/10_basic.yaml new file mode 100644 index 0000000000000..6fbbfc82e872d --- /dev/null +++ b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/10_basic.yaml @@ -0,0 +1,30 @@ +# Integration tests for HDFS Repository plugin +# +# Check plugin is installed +# +"Plugin loaded": + - do: + cluster.state: {} + + # Get master node id + - set: { master_node: master } + + - do: + nodes.info: {} + + - match: { nodes.$master.plugins.0.name: repository-hdfs } +--- +# +# Check that we can't use file:// repositories or anything like that +# We only test this plugin against hdfs:// +# +"HDFS only": + - do: + catch: /Invalid scheme/ + snapshot.create_repository: + repository: misconfigured_repository + body: + type: hdfs + settings: + uri: "file://bogus" + path: "foo/bar" diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_create.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_create.yaml new file mode 100644 index 0000000000000..46cc9e92479c5 --- /dev/null +++ b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_create.yaml @@ -0,0 +1,29 @@ +# Integration tests for HDFS Repository plugin +# +# Tests creating a repository +# +"HDFS Repository Creation": + # Create repository + - do: + snapshot.create_repository: + repository: test_repository_create + body: + type: hdfs + settings: + uri: "hdfs://localhost:9998" + path: "/user/elasticsearch/test/repository_create" + security: + principal: "elasticsearch@BUILD.ELASTIC.CO" + + # Get repository + - do: + snapshot.get_repository: + repository: test_repository_create + + - is_true: test_repository_create + - match: {test_repository_create.settings.path : "/user/elasticsearch/test/repository_create"} + + # Remove our repository + - do: + snapshot.delete_repository: + repository: test_repository_create diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_delete.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_delete.yaml new file mode 100644 index 0000000000000..7bff67d3d01b0 --- /dev/null +++ b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_delete.yaml @@ -0,0 +1,54 @@ +# Integration tests for HDFS Repository plugin +# +# Tests creating a repository, then deleting it and creating it again. +# +"HDFS Delete Repository": + # Create repository + - do: + snapshot.create_repository: + repository: test_repo_hdfs_1 + body: + type: hdfs + settings: + uri: "hdfs://localhost:9998" + path: "/user/elasticsearch/foo/bar" + security: + principal: "elasticsearch@BUILD.ELASTIC.CO" + + # Get repository + - do: + snapshot.get_repository: + repository: test_repo_hdfs_1 + + - is_true: test_repo_hdfs_1 + - match: {test_repo_hdfs_1.settings.path : "/user/elasticsearch/foo/bar"} + + # Delete repository + - do: + snapshot.delete_repository: + repository: test_repo_hdfs_1 + + # Get repository: It should be gone + - do: + catch: /repository_missing_exception/ + snapshot.get_repository: + repository: test_repo_hdfs_1 + + # Create it again + - do: + snapshot.create_repository: + repository: test_repo_hdfs_1 + body: + type: hdfs + settings: + uri: "hdfs://localhost:9998" + path: "/user/elasticsearch/foo/bar" + security: + principal: "elasticsearch@BUILD.ELASTIC.CO" + + # Get repository again + - do: + snapshot.get_repository: + repository: test_repo_hdfs_1 + + - is_true: test_repo_hdfs_1 diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_verify.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_verify.yaml new file mode 100644 index 0000000000000..4593c13caee23 --- /dev/null +++ b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_verify.yaml @@ -0,0 +1,25 @@ +# Integration tests for HDFS Repository plugin +# +# Tests explicit verify +# +"HDFS Repository Verify": + - do: + snapshot.create_repository: + repository: test_repository_verify + body: + type: hdfs + settings: + uri: "hdfs://localhost:9998" + path: "/user/elasticsearch/test/repository_verify" + security: + principal: "elasticsearch@BUILD.ELASTIC.CO" + + # Verify repository + - do: + snapshot.verify_repository: + repository: test_repository_verify + + # Remove our repository + - do: + snapshot.delete_repository: + repository: test_repository_verify diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot.yaml new file mode 100644 index 0000000000000..e232782084e16 --- /dev/null +++ b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot.yaml @@ -0,0 +1,50 @@ +# Integration tests for HDFS Repository plugin +# +# Actually perform a snapshot to hdfs +# +--- +"take snapshot": + # Create repository + - do: + snapshot.create_repository: + repository: test_snapshot_repository + body: + type: hdfs + settings: + uri: "hdfs://localhost:9998" + path: "/user/elasticsearch/test/snapshot" + security: + principal: "elasticsearch@BUILD.ELASTIC.CO" + + # Create index + - do: + indices.create: + index: test_index + body: + settings: + number_of_shards: 1 + number_of_replicas: 1 + + # Create snapshot + - do: + snapshot.create: + repository: test_snapshot_repository + snapshot: test_snapshot + wait_for_completion: true + + - match: { snapshot.snapshot: test_snapshot } + - match: { snapshot.state : SUCCESS } + - match: { snapshot.shards.successful: 1 } + - match: { snapshot.shards.failed : 0 } + + # Remove our snapshot + - do: + snapshot.delete: + repository: test_snapshot_repository + snapshot: test_snapshot + + # Remove our repository + - do: + snapshot.delete_repository: + repository: test_snapshot_repository + diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot_get.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot_get.yaml new file mode 100644 index 0000000000000..20d988884113f --- /dev/null +++ b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot_get.yaml @@ -0,0 +1,72 @@ +# Integration tests for HDFS Repository plugin +# +# Tests retrieving information about snapshot +# +--- +"Get a snapshot": + # Create repository + - do: + snapshot.create_repository: + repository: test_snapshot_get_repository + body: + type: hdfs + settings: + uri: "hdfs://localhost:9998" + path: "/user/elasticsearch/test/snapshot_get" + security: + principal: "elasticsearch@BUILD.ELASTIC.CO" + + # Create index + - do: + indices.create: + index: test_index + body: + settings: + number_of_shards: 1 + number_of_replicas: 0 + + # Wait for green + - do: + cluster.health: + wait_for_status: green + + # Create snapshot + - do: + snapshot.create: + repository: test_snapshot_get_repository + snapshot: test_snapshot_get + wait_for_completion: true + + - match: { snapshot.snapshot: test_snapshot_get } + - match: { snapshot.state : SUCCESS } + - match: { snapshot.shards.successful: 1 } + - match: { snapshot.shards.failed : 0 } + + # Get snapshot info + - do: + snapshot.get: + repository: test_snapshot_get_repository + snapshot: test_snapshot_get + + - length: { snapshots: 1 } + - match: { snapshots.0.snapshot : test_snapshot_get } + + # List snapshot info + - do: + snapshot.get: + repository: test_snapshot_get_repository + snapshot: "*" + + - length: { snapshots: 1 } + - match: { snapshots.0.snapshot : test_snapshot_get } + + # Remove our snapshot + - do: + snapshot.delete: + repository: test_snapshot_get_repository + snapshot: test_snapshot_get + + # Remove our repository + - do: + snapshot.delete_repository: + repository: test_snapshot_get_repository diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/40_restore.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/40_restore.yaml new file mode 100644 index 0000000000000..9807a0b46b10e --- /dev/null +++ b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/40_restore.yaml @@ -0,0 +1,81 @@ +# Integration tests for HDFS Repository plugin +# +# Actually perform a snapshot to hdfs, then restore it +# +--- +"Create a snapshot and then restore it": + + # Create repository + - do: + snapshot.create_repository: + repository: test_restore_repository + body: + type: hdfs + settings: + uri: "hdfs://localhost:9998" + path: "/user/elasticsearch/test/restore" + security: + principal: "elasticsearch@BUILD.ELASTIC.CO" + + # Create index + - do: + indices.create: + index: test_index + body: + settings: + number_of_shards: 1 + number_of_replicas: 0 + + # Wait for green + - do: + cluster.health: + wait_for_status: green + + # Take snapshot + - do: + snapshot.create: + repository: test_restore_repository + snapshot: test_restore + wait_for_completion: true + + - match: { snapshot.snapshot: test_restore } + - match: { snapshot.state : SUCCESS } + - match: { snapshot.shards.successful: 1 } + - match: { snapshot.shards.failed : 0 } + - is_true: snapshot.version + - gt: { snapshot.version_id: 0} + + # Close index + - do: + indices.close: + index : test_index + + # Restore index + - do: + snapshot.restore: + repository: test_restore_repository + snapshot: test_restore + wait_for_completion: true + + # Check recovery stats + - do: + indices.recovery: + index: test_index + + - match: { test_index.shards.0.type: SNAPSHOT } + - match: { test_index.shards.0.stage: DONE } + - match: { test_index.shards.0.index.files.recovered: 0} + - match: { test_index.shards.0.index.size.recovered_in_bytes: 0} + - match: { test_index.shards.0.index.files.reused: 1} + - gt: { test_index.shards.0.index.size.reused_in_bytes: 0} + + # Remove our snapshot + - do: + snapshot.delete: + repository: test_restore_repository + snapshot: test_restore + + # Remove our repository + - do: + snapshot.delete_repository: + repository: test_restore_repository diff --git a/settings.gradle b/settings.gradle index c67c44dd2fff4..6c18f1c1efb72 100644 --- a/settings.gradle +++ b/settings.gradle @@ -25,6 +25,7 @@ List projects = [ 'test:framework', 'test:fixtures:example-fixture', 'test:fixtures:hdfs-fixture', + 'test:fixtures:krb5kdc-fixture', 'test:logger-usage', 'modules:aggs-matrix-stats', 'modules:analysis-common', diff --git a/test/fixtures/hdfs-fixture/src/main/java/hdfs/MiniHDFS.java b/test/fixtures/hdfs-fixture/src/main/java/hdfs/MiniHDFS.java index a4bf47f8eae87..f868f4da97a2a 100644 --- a/test/fixtures/hdfs-fixture/src/main/java/hdfs/MiniHDFS.java +++ b/test/fixtures/hdfs-fixture/src/main/java/hdfs/MiniHDFS.java @@ -19,19 +19,24 @@ package hdfs; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hdfs.DFSConfigKeys; -import org.apache.hadoop.hdfs.MiniDFSCluster; - -import java.io.IOException; +import java.lang.management.ManagementFactory; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.util.Locale; +import java.util.ArrayList; +import java.util.List; -import java.lang.management.ManagementFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.CommonConfigurationKeysPublic; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.permission.AclEntry; +import org.apache.hadoop.fs.permission.AclEntryType; +import org.apache.hadoop.fs.permission.FsAction; +import org.apache.hadoop.hdfs.DFSConfigKeys; +import org.apache.hadoop.hdfs.MiniDFSCluster; +import org.apache.hadoop.security.UserGroupInformation; /** * MiniHDFS test fixture. There is a CLI tool, but here we can @@ -43,9 +48,11 @@ public class MiniHDFS { private static String PID_FILE_NAME = "pid"; public static void main(String[] args) throws Exception { - if (args.length != 1) { - throw new IllegalArgumentException("MiniHDFS "); + if (args.length != 1 && args.length != 3) { + throw new IllegalArgumentException("MiniHDFS [ ]"); } + boolean secure = args.length == 3; + // configure Paths Path baseDir = Paths.get(args[0]); // hadoop-home/, so logs will not complain @@ -57,13 +64,49 @@ public static void main(String[] args) throws Exception { // hdfs-data/, where any data is going Path hdfsHome = baseDir.resolve("hdfs-data"); - // start cluster + // configure cluster Configuration cfg = new Configuration(); cfg.set(MiniDFSCluster.HDFS_MINIDFS_BASEDIR, hdfsHome.toAbsolutePath().toString()); // lower default permission: TODO: needed? cfg.set(DFSConfigKeys.DFS_DATANODE_DATA_DIR_PERMISSION_KEY, "766"); + + // optionally configure security + if (secure) { + String kerberosPrincipal = args[1]; + String keytabFile = args[2]; + + cfg.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, "kerberos"); + cfg.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHORIZATION, "true"); + cfg.set(DFSConfigKeys.DFS_NAMENODE_KERBEROS_PRINCIPAL_KEY, kerberosPrincipal); + cfg.set(DFSConfigKeys.DFS_DATANODE_KERBEROS_PRINCIPAL_KEY, kerberosPrincipal); + cfg.set(DFSConfigKeys.DFS_WEB_AUTHENTICATION_KERBEROS_PRINCIPAL_KEY, kerberosPrincipal); + cfg.set(DFSConfigKeys.DFS_NAMENODE_KEYTAB_FILE_KEY, keytabFile); + cfg.set(DFSConfigKeys.DFS_DATANODE_KEYTAB_FILE_KEY, keytabFile); + cfg.set(DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_KEY, "true"); + cfg.set(DFSConfigKeys.IGNORE_SECURE_PORTS_FOR_TESTING_KEY, "true"); + } + + UserGroupInformation.setConfiguration(cfg); + // TODO: remove hardcoded port! - MiniDFSCluster dfs = new MiniDFSCluster.Builder(cfg).nameNodePort(9999).build(); + MiniDFSCluster.Builder builder = new MiniDFSCluster.Builder(cfg); + if (secure) { + builder.nameNodePort(9998); + } else { + builder.nameNodePort(9999); + } + MiniDFSCluster dfs = builder.build(); + + // Set the elasticsearch user directory up + if (UserGroupInformation.isSecurityEnabled()) { + FileSystem fs = dfs.getFileSystem(); + org.apache.hadoop.fs.Path esUserPath = new org.apache.hadoop.fs.Path("/user/elasticsearch"); + fs.mkdirs(esUserPath); + List acls = new ArrayList<>(); + acls.add(new AclEntry.Builder().setType(AclEntryType.USER).setName("elasticsearch").setPermission(FsAction.ALL).build()); + fs.modifyAclEntries(esUserPath, acls); + fs.close(); + } // write our PID file Path tmp = Files.createTempFile(baseDir, null, null); diff --git a/test/fixtures/krb5kdc-fixture/Vagrantfile b/test/fixtures/krb5kdc-fixture/Vagrantfile new file mode 100644 index 0000000000000..72be4dad9cbe5 --- /dev/null +++ b/test/fixtures/krb5kdc-fixture/Vagrantfile @@ -0,0 +1,53 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Licensed to Elasticsearch under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch 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. + +# This Vagrantfile exists to define a virtual machine running MIT's Kerberos 5 +# for usage as a testing fixture for the build process. +# +# In order to connect to the KDC process on this virtual machine, find and use +# the rendered krb5.conf file in the build output directory (build/conf). +# +# In order to provision principals in the KDC, use the provided addprinc.sh +# script with vagrant's ssh facility: +# +# vagrant ssh -c /vagrant/src/main/resources/provision/addprinc.sh principal +# +# You will find the newly created principal's keytab file in the build output +# directory (build/keytabs). Principal creation is idempotent, and will recopy +# existing user keytabs from the KDC if they already exist. + +Vagrant.configure("2") do |config| + + config.vm.define "krb5kdc" do |config| + config.vm.box = "elastic/ubuntu-14.04-x86_64" + end + + config.vm.hostname = "kerberos.build.elastic.co" + + if Vagrant.has_plugin?("vagrant-cachier") + config.cache.scope = :box + end + + config.vm.network "forwarded_port", guest: 88, host: 60088, protocol: "tcp" + config.vm.network "forwarded_port", guest: 88, host: 60088, protocol: "udp" + + config.vm.provision "shell", path: "src/main/resources/provision/installkdc.sh" + +end diff --git a/test/fixtures/krb5kdc-fixture/build.gradle b/test/fixtures/krb5kdc-fixture/build.gradle new file mode 100644 index 0000000000000..046ecc5fc0416 --- /dev/null +++ b/test/fixtures/krb5kdc-fixture/build.gradle @@ -0,0 +1,84 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +apply plugin: 'elasticsearch.build' + +Map vagrantEnvVars = [ + 'VAGRANT_CWD' : "${project.projectDir.absolutePath}", + 'VAGRANT_VAGRANTFILE' : 'Vagrantfile', + 'VAGRANT_PROJECT_DIR' : "${project.projectDir.absolutePath}" +] + +String box = "krb5kdc" + +List defaultPrincipals = [ "elasticsearch" ] + +task update(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { + command 'box' + subcommand 'update' + boxName box + environmentVars vagrantEnvVars +} + +task up(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { + command 'up' + args '--provision', '--provider', 'virtualbox' + boxName box + environmentVars vagrantEnvVars + dependsOn update +} + +task addDefaultPrincipals { + dependsOn up +} + +for (String principal : defaultPrincipals) { + Task addTask = project.tasks.create("addPrincipal#${principal}", org.elasticsearch.gradle.vagrant.VagrantCommandTask) { + command 'ssh' + args '--command', "sudo bash /vagrant/src/main/resources/provision/addprinc.sh $principal" + boxName box + environmentVars vagrantEnvVars + dependsOn up + } + addDefaultPrincipals.dependsOn(addTask) +} + +task halt(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { + command 'halt' + boxName box + environmentVars vagrantEnvVars +} + +task destroy(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { + command 'destroy' + args '-f' + boxName box + environmentVars vagrantEnvVars + dependsOn halt +} + +thirdPartyAudit.enabled = false +licenseHeaders.enabled = false +test.enabled = false + +// installKDC uses tabs in it for the Kerberos ACL file. +// Ignore it for pattern checking. +forbiddenPatterns { + exclude "**/installkdc.sh" +} diff --git a/test/fixtures/krb5kdc-fixture/src/main/resources/env.properties b/test/fixtures/krb5kdc-fixture/src/main/resources/env.properties new file mode 100644 index 0000000000000..21ef7e1225af9 --- /dev/null +++ b/test/fixtures/krb5kdc-fixture/src/main/resources/env.properties @@ -0,0 +1,20 @@ +# Licensed to Elasticsearch under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch 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. + +realm=BUILD.ELASTIC.CO +kdc=kerberos.build.elastic.co +zone=build.elastic.co \ No newline at end of file diff --git a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/addprinc.sh b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/addprinc.sh new file mode 100755 index 0000000000000..137135dc2aa4d --- /dev/null +++ b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/addprinc.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# Licensed to Elasticsearch under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch 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. + +set -e + +if [[ $# -lt 1 ]]; then + echo 'Usage: addprinc.sh ' + exit 1 +fi + +PRINC="$1" +USER=$(echo $PRINC | tr "/" "_") + +VDIR=/vagrant +RESOURCES=$VDIR/src/main/resources +PROV_DIR=$RESOURCES/provision +ENVPROP_FILE=$RESOURCES/env.properties +BUILD_DIR=$VDIR/build +CONF_DIR=$BUILD_DIR/conf +KEYTAB_DIR=$BUILD_DIR/keytabs +LOCALSTATEDIR=/etc +LOGDIR=/var/log/krb5 + +mkdir -p $KEYTAB_DIR + +REALM=$(cat $ENVPROP_FILE | grep realm= | head -n 1 | cut -d '=' -f 2) + +ADMIN_PRIN=admin/admin@$REALM +ADMIN_KTAB=$LOCALSTATEDIR/admin.keytab + +USER_PRIN=$PRINC@$REALM +USER_KTAB=$LOCALSTATEDIR/$USER.keytab + +if [ -f $USER_KTAB ]; then + echo "Principal '${PRINC}@${REALM}' already exists. Re-copying keytab..." +else + echo "Provisioning '${PRINC}@${REALM}' principal and keytab..." + sudo kadmin -p $ADMIN_PRIN -kt $ADMIN_KTAB -q "addprinc -randkey $USER_PRIN" + sudo kadmin -p $ADMIN_PRIN -kt $ADMIN_KTAB -q "ktadd -k $USER_KTAB $USER_PRIN" +fi + +sudo cp $USER_KTAB $KEYTAB_DIR/$USER.keytab diff --git a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/installkdc.sh b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/installkdc.sh new file mode 100755 index 0000000000000..2dc8ed92c9462 --- /dev/null +++ b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/installkdc.sh @@ -0,0 +1,120 @@ +#!/bin/bash + +# Licensed to Elasticsearch under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch 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. + +set -e + +# KDC installation steps and considerations based on https://web.mit.edu/kerberos/krb5-latest/doc/admin/install_kdc.html +# and helpful input from https://help.ubuntu.com/community/Kerberos + +VDIR=/vagrant +RESOURCES=$VDIR/src/main/resources +PROV_DIR=$RESOURCES/provision +ENVPROP_FILE=$RESOURCES/env.properties +BUILD_DIR=$VDIR/build +CONF_DIR=$BUILD_DIR/conf +KEYTAB_DIR=$BUILD_DIR/keytabs +LOCALSTATEDIR=/etc +LOGDIR=/var/log/krb5 + +MARKER_FILE=/etc/marker + +# Output location for our rendered configuration files and keytabs +mkdir -p $BUILD_DIR +rm -rf $BUILD_DIR/* +mkdir -p $CONF_DIR +mkdir -p $KEYTAB_DIR + +if [ -f $MARKER_FILE ]; then + echo "Already provisioned..." + echo "Recopying configuration files..." + cp $LOCALSTATEDIR/krb5.conf $CONF_DIR/krb5.conf + cp $LOCALSTATEDIR/krb5kdc/kdc.conf $CONF_DIR/kdc.conf + exit 0; +fi + +# Pull environment information +REALM_NAME=$(cat $ENVPROP_FILE | grep realm= | cut -d '=' -f 2) +KDC_NAME=$(cat $ENVPROP_FILE | grep kdc= | cut -d '=' -f 2) +BUILD_ZONE=$(cat $ENVPROP_FILE | grep zone= | cut -d '=' -f 2) +ELASTIC_ZONE=$(echo $BUILD_ZONE | cut -d '.' -f 1,2) + +# Transfer and interpolate krb5.conf +cp $PROV_DIR/krb5.conf.template $LOCALSTATEDIR/krb5.conf +sed -i 's/${REALM_NAME}/'$REALM_NAME'/g' $LOCALSTATEDIR/krb5.conf +sed -i 's/${KDC_NAME}/'$KDC_NAME'/g' $LOCALSTATEDIR/krb5.conf +sed -i 's/${BUILD_ZONE}/'$BUILD_ZONE'/g' $LOCALSTATEDIR/krb5.conf +sed -i 's/${ELASTIC_ZONE}/'$ELASTIC_ZONE'/g' $LOCALSTATEDIR/krb5.conf +cp $LOCALSTATEDIR/krb5.conf $CONF_DIR/krb5.conf + +# Transfer and interpolate the kdc.conf +mkdir -p $LOCALSTATEDIR/krb5kdc +cp $PROV_DIR/kdc.conf.template $LOCALSTATEDIR/krb5kdc/kdc.conf +sed -i 's/${REALM_NAME}/'$REALM_NAME'/g' $LOCALSTATEDIR/krb5kdc/kdc.conf +sed -i 's/${KDC_NAME}/'$KDC_NAME'/g' $LOCALSTATEDIR/krb5kdc/kdc.conf +sed -i 's/${BUILD_ZONE}/'$BUILD_ZONE'/g' $LOCALSTATEDIR/krb5kdc/kdc.conf +sed -i 's/${ELASTIC_ZONE}/'$ELASTIC_ZONE'/g' $LOCALSTATEDIR/krb5.conf +cp $LOCALSTATEDIR/krb5kdc/kdc.conf $CONF_DIR/kdc.conf + +# Touch logging locations +mkdir -p $LOGDIR +touch $LOGDIR/kadmin.log +touch $LOGDIR/krb5kdc.log +touch $LOGDIR/krb5lib.log + +# Update package manager +apt-get update -qqy + +# Installation asks a bunch of questions via debconf. Set the answers ahead of time +debconf-set-selections <<< "krb5-config krb5-config/read_conf boolean true" +debconf-set-selections <<< "krb5-config krb5-config/kerberos_servers string $KDC_NAME" +debconf-set-selections <<< "krb5-config krb5-config/add_servers boolean true" +debconf-set-selections <<< "krb5-config krb5-config/admin_server string $KDC_NAME" +debconf-set-selections <<< "krb5-config krb5-config/add_servers_realm string $REALM_NAME" +debconf-set-selections <<< "krb5-config krb5-config/default_realm string $REALM_NAME" +debconf-set-selections <<< "krb5-admin-server krb5-admin-server/kadmind boolean true" +debconf-set-selections <<< "krb5-admin-server krb5-admin-server/newrealm note" +debconf-set-selections <<< "krb5-kdc krb5-kdc/debconf boolean true" +debconf-set-selections <<< "krb5-kdc krb5-kdc/purge_data_too boolean false" + +# Install krb5 packages +apt-get install -qqy krb5-{admin-server,kdc} + +# /dev/random produces output very slowly on Ubuntu VM's. Install haveged to increase entropy. +apt-get install -qqy haveged +haveged + +# Create kerberos database with stash file and garbage password +kdb5_util create -s -r $REALM_NAME -P zyxwvutsrpqonmlk9876 + +# Set up admin acls +cat << EOF > /etc/krb5kdc/kadm5.acl +*/admin@$REALM_NAME * +*/*@$REALM_NAME i +EOF + +# Create admin principal +kadmin.local -q "addprinc -pw elastic admin/admin@$REALM_NAME" +kadmin.local -q "ktadd -k /etc/admin.keytab admin/admin@$REALM_NAME" + +# Start Kerberos Services +krb5kdc +kadmind + +# Mark that the vm is already provisioned +touch $MARKER_FILE \ No newline at end of file diff --git a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/kdc.conf.template b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/kdc.conf.template new file mode 100644 index 0000000000000..22909ddf60013 --- /dev/null +++ b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/kdc.conf.template @@ -0,0 +1,35 @@ +# Licensed to Elasticsearch under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch 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. + +[kdcdefaults] + kdc_listen = 88 + kdc_tcp_listen = 88 + +[realms] + ${REALM_NAME} = { + kadmind_port = 749 + max_life = 12h 0m 0s + max_renewable_life = 7d 0h 0m 0s + master_key_type = aes256-cts + # remove aes256-cts:normal since unlimited strength policy needs installed for java to use it. + supported_enctypes = aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal + } + +[logging] + kdc = FILE:/var/log/krb5kdc.log + admin_server = FILE:/var/log/kadmin.log + default = FILE:/var/log/krb5lib.log diff --git a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/krb5.conf.template b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/krb5.conf.template new file mode 100644 index 0000000000000..e572c12e70957 --- /dev/null +++ b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/krb5.conf.template @@ -0,0 +1,50 @@ +# Licensed to Elasticsearch under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch 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. + +[libdefaults] + default_realm = ${REALM_NAME} + dns_canonicalize_hostname = false + dns_lookup_kdc = false + dns_lookup_realm = false + dns_uri_lookup = false + forwardable = true + ignore_acceptor_hostname = true + rdns = false + default_tgs_enctypes = rc4-hmac + default_tkt_enctypes = rc4-hmac + permitted_enctypes = rc4-hmac + # udp_preference_limit = 1 + kdc_timeout = 3000 + +[realms] + ${REALM_NAME} = { + kdc = ${KDC_NAME}:88 + kdc = ${KDC_NAME}:60088 + kdc = localhost:60088 + kdc = localhost:88 + kdc = 127.0.0.1:60088 + kdc = 127.0.0.1:88 + admin_server = ${KDC_NAME}:749 + default_domain = ${BUILD_ZONE} + } + +[domain_realm] + ${BUILD_ZONE} = ${REALM_NAME} + .${BUILD_ZONE} = ${REALM_NAME} + ${ELASTIC_ZONE} = ${REALM_NAME} + .${ELASTIC_ZONE} = ${REALM_NAME} + From 349d1bd23e378bb7d45ba7c165a85cfbe05cc592 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 10 May 2017 15:00:29 -0700 Subject: [PATCH 291/619] Fixes ScriptServiceTests.testDefaultBehaviourFineGrainedSettings due to an error with the fine grained settings deprecation. --- .../org/elasticsearch/script/ScriptServiceTests.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java b/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java index 9fdbcd6f90d19..c4fd39550b507 100644 --- a/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java +++ b/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java @@ -219,11 +219,13 @@ public void testInlineScriptCompiledOnceCache() throws IOException { public void testDefaultBehaviourFineGrainedSettings() throws IOException { Settings.Builder builder = Settings.builder(); //rarely inject the default settings, which have no effect + boolean deprecate = false; if (rarely()) { builder.put("script.file", "true"); + deprecate = true; } buildScriptService(builder.build()); - createFileScripts("mustache", "dtest"); + createFileScripts("dtest"); for (ScriptContext scriptContext : scriptContexts) { // only file scripts are accepted by default @@ -231,7 +233,12 @@ public void testDefaultBehaviourFineGrainedSettings() throws IOException { assertCompileRejected("dtest", "script", ScriptType.STORED, scriptContext); assertCompileAccepted("dtest", "file_script", ScriptType.FILE, scriptContext); } - assertWarnings("File scripts are deprecated. Use stored or inline scripts instead."); + if (deprecate) { + assertSettingDeprecationsAndWarnings(ScriptSettingsTests.buildDeprecatedSettingsArray(scriptSettings, "script.file"), + "File scripts are deprecated. Use stored or inline scripts instead."); + } else { + assertWarnings("File scripts are deprecated. Use stored or inline scripts instead."); + } } public void testFineGrainedSettings() throws IOException { From 0b089ac0845db09d8ad51c766d0831257fd97571 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Wed, 10 May 2017 18:54:22 -0400 Subject: [PATCH 292/619] Refresh at end of concurrent relocate test We have to do something to force the global checkpoint to be synchronized to the replicas or the assertions at the end of the test that they are in sync will trip. Since the last write operation to hit a replica shard will only carry the penultimate global checkpoint (it will advance when the replicas respond with their local checkpoint), and a background sync will not happen until the primary shard falls idle, we force a sync through a refresh action. --- .../java/org/elasticsearch/recovery/RelocationIT.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/recovery/RelocationIT.java b/core/src/test/java/org/elasticsearch/recovery/RelocationIT.java index dca450c8a1df7..7010034bc4f26 100644 --- a/core/src/test/java/org/elasticsearch/recovery/RelocationIT.java +++ b/core/src/test/java/org/elasticsearch/recovery/RelocationIT.java @@ -367,10 +367,11 @@ public void indexShardStateChanged(IndexShard indexShard, @Nullable IndexShardSt } } - // refresh is a replication action so this forces a global checkpoint sync which is needed as these are asserted on in tear down - client().admin().indices().prepareRefresh("test").get(); - } + + // refresh is a replication action so this forces a global checkpoint sync which is needed as these are asserted on in tear down + client().admin().indices().prepareRefresh("test").get(); + } public void testCancellationCleansTempFiles() throws Exception { @@ -506,6 +507,10 @@ public void testIndexAndRelocateConcurrently() throws ExecutionException, Interr assertNoFailures(afterRelocation); assertSearchHits(afterRelocation, ids.toArray(new String[ids.size()])); } + + // refresh is a replication action so this forces a global checkpoint sync which is needed as these are asserted on in tear down + client().admin().indices().prepareRefresh("test").get(); + } class RecoveryCorruption extends MockTransportService.DelegateTransport { From d447b79e1614994ee8087232c10e8ee85a981e7c Mon Sep 17 00:00:00 2001 From: Chris Ivens Date: Thu, 11 May 2017 03:05:19 +0100 Subject: [PATCH 293/619] Docs: Tiny typo to Painless dispatch justification (#24588) --- docs/reference/modules/scripting/painless.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/modules/scripting/painless.asciidoc b/docs/reference/modules/scripting/painless.asciidoc index f12f56dc50c5f..638391ee908d5 100644 --- a/docs/reference/modules/scripting/painless.asciidoc +++ b/docs/reference/modules/scripting/painless.asciidoc @@ -374,7 +374,7 @@ has <> and We have a few justifications for this different way of dispatching methods: 1. It makes operating on `def` types simpler and, presumably, faster. Using -receiver, name, and arity means when Painless sees a call on a `def` objects it +receiver, name, and arity means when Painless sees a call on a `def` object it can dispatch the appropriate method without having to do expensive comparisons of the types of the parameters. The same is true for invocations with `def` typed parameters. From 65f2717ab7a07b8c7881db63cfa44ec429b95f30 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 10 May 2017 22:39:43 -0400 Subject: [PATCH 294/619] Make PreConfiguredTokenFilter harder to misuse (#24572) There are now three public static method to build instances of PreConfiguredTokenFilter and the ctor is private. I chose static methods instead of constructors because those allow us to change out the implementation returned if we so desire. Relates to #23658 --- .../analysis/PreConfiguredTokenFilter.java | 46 ++++++++---- .../indices/analysis/AnalysisModule.java | 13 ++-- .../analysis/PreBuiltTokenFilters.java | 15 ---- .../index/analysis/AnalysisRegistryTests.java | 6 +- .../index/analysis/CustomNormalizerTests.java | 3 +- .../index/mapper/KeywordFieldMapperTests.java | 3 +- .../indices/analysis/AnalysisModuleTests.java | 74 +++++++++++++++++++ .../analysis/common/CommonAnalysisPlugin.java | 45 ++++++----- .../common/CommonAnalysisFactoryTests.java | 4 + .../analysis/AnalysisFactoryTestCase.java | 4 - 10 files changed, 142 insertions(+), 71 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/analysis/PreConfiguredTokenFilter.java b/core/src/main/java/org/elasticsearch/index/analysis/PreConfiguredTokenFilter.java index b410e8fb70e85..1d9e4459c7e50 100644 --- a/core/src/main/java/org/elasticsearch/index/analysis/PreConfiguredTokenFilter.java +++ b/core/src/main/java/org/elasticsearch/index/analysis/PreConfiguredTokenFilter.java @@ -27,6 +27,7 @@ import org.elasticsearch.index.IndexSettings; import org.elasticsearch.indices.analysis.AnalysisModule; import org.elasticsearch.indices.analysis.PreBuiltCacheFactory; +import org.elasticsearch.indices.analysis.PreBuiltCacheFactory.CachingStrategy; import java.io.IOException; import java.util.function.BiFunction; @@ -36,31 +37,46 @@ * Provides pre-configured, shared {@link TokenFilter}s. */ public final class PreConfiguredTokenFilter implements AnalysisModule.AnalysisProvider { + /** + * Create a pre-configured token filter that may not vary at all. + */ + public static PreConfiguredTokenFilter singleton(String name, boolean useFilterForMultitermQueries, + Function create) { + return new PreConfiguredTokenFilter(name, useFilterForMultitermQueries, CachingStrategy.ONE, + (tokenStream, version) -> create.apply(tokenStream)); + } + + /** + * Create a pre-configured token filter that may vary based on the Lucene version. + */ + public static PreConfiguredTokenFilter luceneVersion(String name, boolean useFilterForMultitermQueries, + BiFunction create) { + return new PreConfiguredTokenFilter(name, useFilterForMultitermQueries, CachingStrategy.LUCENE, + (tokenStream, version) -> create.apply(tokenStream, version.luceneVersion)); + } + + /** + * Create a pre-configured token filter that may vary based on the Elasticsearch version. + */ + public static PreConfiguredTokenFilter elasticsearchVersion(String name, boolean useFilterForMultitermQueries, + BiFunction create) { + return new PreConfiguredTokenFilter(name, useFilterForMultitermQueries, CachingStrategy.ELASTICSEARCH, + (tokenStream, version) -> create.apply(tokenStream, version)); + } + private final String name; private final boolean useFilterForMultitermQueries; private final PreBuiltCacheFactory.PreBuiltCache cache; private final BiFunction create; - /** - * Standard ctor with all the power. - */ - public PreConfiguredTokenFilter(String name, boolean useFilterForMultitermQueries, - PreBuiltCacheFactory.CachingStrategy cachingStrategy, BiFunction create) { + private PreConfiguredTokenFilter(String name, boolean useFilterForMultitermQueries, + PreBuiltCacheFactory.CachingStrategy cache, BiFunction create) { this.name = name; this.useFilterForMultitermQueries = useFilterForMultitermQueries; - cache = PreBuiltCacheFactory.getCache(cachingStrategy); + this.cache = PreBuiltCacheFactory.getCache(cache); this.create = create; } - /** - * Convenience ctor for token streams that don't vary based on version. - */ - public PreConfiguredTokenFilter(String name, boolean useFilterForMultitermQueries, - PreBuiltCacheFactory.CachingStrategy cachingStrategy, Function create) { - this(name, useFilterForMultitermQueries, cachingStrategy, (input, version) -> create.apply(input)); - // TODO why oh why aren't these all CachingStrategy.ONE? They *can't* vary based on version because they don't get it, right?! - } - @Override public TokenFilterFactory get(IndexSettings indexSettings, Environment environment, String name, Settings settings) throws IOException { return getTokenFilterFactory(Version.indexCreated(settings)); diff --git a/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java b/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java index 06ef3e315c6ab..ffe80a0f5f446 100644 --- a/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java +++ b/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java @@ -272,10 +272,8 @@ static Map setupPreConfiguredTokenFilters(List NamedRegistry preConfiguredTokenFilters = new NamedRegistry<>("pre-configured token_filter"); // Add filters available in lucene-core - preConfiguredTokenFilters.register("lowercase", - new PreConfiguredTokenFilter("lowercase", true, CachingStrategy.LUCENE, LowerCaseFilter::new)); - preConfiguredTokenFilters.register("standard", - new PreConfiguredTokenFilter("standard", false, CachingStrategy.LUCENE, StandardFilter::new)); + preConfiguredTokenFilters.register("lowercase", PreConfiguredTokenFilter.singleton("lowercase", true, LowerCaseFilter::new)); + preConfiguredTokenFilters.register("standard", PreConfiguredTokenFilter.singleton("standard", false, StandardFilter::new)); /* Note that "stop" is available in lucene-core but it's pre-built * version uses a set of English stop words that are in * lucene-analyzers-common so "stop" is defined in the analysis-common @@ -288,9 +286,12 @@ static Map setupPreConfiguredTokenFilters(List // This has been migrated but has to stick around until PreBuiltTokenizers is removed. continue; default: + if (CachingStrategy.ONE != preBuilt.getCachingStrategy()) { + throw new UnsupportedOperationException("shim not available for " + preBuilt.getCachingStrategy()); + } String name = preBuilt.name().toLowerCase(Locale.ROOT); - preConfiguredTokenFilters.register(name, - new PreConfiguredTokenFilter(name, preBuilt.isMultiTermAware(), preBuilt.getCachingStrategy(), preBuilt::create)); + preConfiguredTokenFilters.register(name, PreConfiguredTokenFilter.singleton(name, preBuilt.isMultiTermAware(), + tokenStream -> preBuilt.create(tokenStream, Version.CURRENT))); } } diff --git a/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltTokenFilters.java b/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltTokenFilters.java index 02f6d8aadc5e0..427c0431fb550 100644 --- a/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltTokenFilters.java +++ b/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltTokenFilters.java @@ -30,7 +30,6 @@ import org.apache.lucene.analysis.cz.CzechStemFilter; import org.apache.lucene.analysis.de.GermanNormalizationFilter; import org.apache.lucene.analysis.de.GermanStemFilter; -import org.apache.lucene.analysis.en.PorterStemFilter; import org.apache.lucene.analysis.fa.PersianNormalizationFilter; import org.apache.lucene.analysis.fr.FrenchAnalyzer; import org.apache.lucene.analysis.hi.HindiNormalizationFilter; @@ -70,20 +69,6 @@ protected boolean isMultiTermAware() { }, // Extended Token Filters - SNOWBALL(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new SnowballFilter(tokenStream, "English"); - } - }, - - STEMMER(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new PorterStemFilter(tokenStream); - } - }, - ELISION(CachingStrategy.ONE) { @Override public TokenStream create(TokenStream tokenStream, Version version) { diff --git a/core/src/test/java/org/elasticsearch/index/analysis/AnalysisRegistryTests.java b/core/src/test/java/org/elasticsearch/index/analysis/AnalysisRegistryTests.java index 471d6f9cccc29..033296676275d 100644 --- a/core/src/test/java/org/elasticsearch/index/analysis/AnalysisRegistryTests.java +++ b/core/src/test/java/org/elasticsearch/index/analysis/AnalysisRegistryTests.java @@ -34,7 +34,6 @@ import org.elasticsearch.indices.analysis.AnalysisModule; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; import org.elasticsearch.indices.analysis.PreBuiltAnalyzers; -import org.elasticsearch.indices.analysis.PreBuiltCacheFactory; import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.IndexSettingsModule; @@ -207,12 +206,11 @@ public void testBuiltInAnalyzersAreCached() throws IOException { public void testPreConfiguredTokenFiltersAreCached() throws IOException { AtomicBoolean built = new AtomicBoolean(false); - PreConfiguredTokenFilter assertsBuiltOnce = new PreConfiguredTokenFilter("asserts_built_once", false, - PreBuiltCacheFactory.CachingStrategy.ONE, (tokens, version) -> { + PreConfiguredTokenFilter assertsBuiltOnce = PreConfiguredTokenFilter.singleton("asserts_built_once", false, tokenStream -> { if (false == built.compareAndSet(false, true)) { fail("Attempted to build the token filter twice when it should have been cached"); } - return new MockTokenFilter(tokens, MockTokenFilter.EMPTY_STOPSET); + return new MockTokenFilter(tokenStream, MockTokenFilter.EMPTY_STOPSET); }); try (AnalysisRegistry registryWithPreBuiltTokenFilter = new AnalysisRegistry(emptyEnvironment, emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap(), singletonMap("asserts_built_once", assertsBuiltOnce))) { diff --git a/core/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java b/core/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java index 5cdc589405714..a818d9c7178f0 100644 --- a/core/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java +++ b/core/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java @@ -24,7 +24,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; -import org.elasticsearch.indices.analysis.PreBuiltCacheFactory.CachingStrategy; import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTokenStreamTestCase; @@ -113,7 +112,7 @@ public void testIllegalCharFilters() throws IOException { private static class MockAnalysisPlugin implements AnalysisPlugin { @Override public List getPreConfiguredTokenFilters() { - return singletonList(new PreConfiguredTokenFilter("mock_forbidden", false, CachingStrategy.ONE, MockLowerCaseFilter::new)); + return singletonList(PreConfiguredTokenFilter.singleton("mock_forbidden", false, MockLowerCaseFilter::new)); } @Override diff --git a/core/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java index 518f669f81f3f..3ecef3aa0f514 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java @@ -32,7 +32,6 @@ import org.elasticsearch.index.IndexService; import org.elasticsearch.index.analysis.PreConfiguredTokenFilter; import org.elasticsearch.index.mapper.MapperService.MergeReason; -import org.elasticsearch.indices.analysis.PreBuiltCacheFactory.CachingStrategy; import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESSingleNodeTestCase; @@ -55,7 +54,7 @@ public class KeywordFieldMapperTests extends ESSingleNodeTestCase { public static class MockAnalysisPlugin extends Plugin implements AnalysisPlugin { @Override public List getPreConfiguredTokenFilters() { - return singletonList(new PreConfiguredTokenFilter("mock_other_lowercase", true, CachingStrategy.ONE, MockLowerCaseFilter::new)); + return singletonList(PreConfiguredTokenFilter.singleton("mock_other_lowercase", true, MockLowerCaseFilter::new)); } }; diff --git a/core/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java b/core/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java index 3d479ca2da283..2572b7aeb0f9d 100644 --- a/core/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java +++ b/core/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.indices.analysis; import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.ar.ArabicNormalizationFilter; @@ -28,6 +29,7 @@ import org.apache.lucene.analysis.hunspell.Dictionary; import org.apache.lucene.analysis.miscellaneous.KeywordRepeatFilter; import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.store.Directory; import org.apache.lucene.store.SimpleFSDirectory; import org.elasticsearch.Version; @@ -43,6 +45,7 @@ import org.elasticsearch.index.analysis.CustomAnalyzer; import org.elasticsearch.index.analysis.IndexAnalyzers; import org.elasticsearch.index.analysis.NamedAnalyzer; +import org.elasticsearch.index.analysis.PreConfiguredTokenFilter; import org.elasticsearch.index.analysis.StandardTokenizerFactory; import org.elasticsearch.index.analysis.StopTokenFilterFactory; import org.elasticsearch.index.analysis.TokenFilterFactory; @@ -61,17 +64,23 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.Set; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; +import static org.apache.lucene.analysis.BaseTokenStreamTestCase.assertTokenStreamContents; import static org.hamcrest.Matchers.either; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; public class AnalysisModuleTests extends ESTestCase { + private final Settings emptyNodeSettings = Settings.builder() + .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) + .build(); public IndexAnalyzers getIndexAnalyzers(Settings settings) throws IOException { return getIndexAnalyzers(getNewRegistry(settings), settings); @@ -264,6 +273,71 @@ public void testUnderscoreInAnalyzerName() throws IOException { } } + /** + * Tests that plugins can register pre-configured token filters that vary in behavior based on Elasticsearch version, Lucene version, + * and that do not vary based on version at all. + */ + public void testPluginPreConfiguredTokenFilters() throws IOException { + // Simple token filter that appends text to the term + final class AppendTokenFilter extends TokenFilter { + private final CharTermAttribute term = addAttribute(CharTermAttribute.class); + private final char[] appendMe; + + protected AppendTokenFilter(TokenStream input, String appendMe) { + super(input); + this.appendMe = appendMe.toCharArray(); + } + + @Override + public boolean incrementToken() throws IOException { + if (false == input.incrementToken()) { + return false; + } + term.resizeBuffer(term.length() + appendMe.length); + System.arraycopy(appendMe, 0, term.buffer(), term.length(), appendMe.length); + term.setLength(term.length() + appendMe.length); + return true; + } + } + boolean noVersionSupportsMultiTerm = randomBoolean(); + boolean luceneVersionSupportsMultiTerm = randomBoolean(); + boolean elasticsearchVersionSupportsMultiTerm = randomBoolean(); + AnalysisRegistry registry = new AnalysisModule(new Environment(emptyNodeSettings), singletonList(new AnalysisPlugin() { + @Override + public List getPreConfiguredTokenFilters() { + return Arrays.asList( + PreConfiguredTokenFilter.singleton("no_version", noVersionSupportsMultiTerm, + tokenStream -> new AppendTokenFilter(tokenStream, "no_version")), + PreConfiguredTokenFilter.luceneVersion("lucene_version", luceneVersionSupportsMultiTerm, + (tokenStream, luceneVersion) -> new AppendTokenFilter(tokenStream, luceneVersion.toString())), + PreConfiguredTokenFilter.elasticsearchVersion("elasticsearch_version", elasticsearchVersionSupportsMultiTerm, + (tokenStream, esVersion) -> new AppendTokenFilter(tokenStream, esVersion.toString())) + ); + } + })).getAnalysisRegistry(); + + Version version = VersionUtils.randomVersion(random()); + IndexAnalyzers analyzers = getIndexAnalyzers(registry, Settings.builder() + .put("index.analysis.analyzer.no_version.tokenizer", "keyword") + .put("index.analysis.analyzer.no_version.filter", "no_version") + .put("index.analysis.analyzer.lucene_version.tokenizer", "keyword") + .put("index.analysis.analyzer.lucene_version.filter", "lucene_version") + .put("index.analysis.analyzer.elasticsearch_version.tokenizer", "keyword") + .put("index.analysis.analyzer.elasticsearch_version.filter", "elasticsearch_version") + .put(IndexMetaData.SETTING_VERSION_CREATED, version) + .build()); + assertTokenStreamContents(analyzers.get("no_version").tokenStream("", "test"), new String[] {"testno_version"}); + assertTokenStreamContents(analyzers.get("lucene_version").tokenStream("", "test"), new String[] {"test" + version.luceneVersion}); + assertTokenStreamContents(analyzers.get("elasticsearch_version").tokenStream("", "test"), new String[] {"test" + version}); + + assertEquals("test" + (noVersionSupportsMultiTerm ? "no_version" : ""), + analyzers.get("no_version").normalize("", "test").utf8ToString()); + assertEquals("test" + (luceneVersionSupportsMultiTerm ? version.luceneVersion.toString() : ""), + analyzers.get("lucene_version").normalize("", "test").utf8ToString()); + assertEquals("test" + (elasticsearchVersionSupportsMultiTerm ? version.toString() : ""), + analyzers.get("elasticsearch_version").normalize("", "test").utf8ToString()); + } + public void testRegisterHunspellDictionary() throws Exception { Settings settings = Settings.builder() .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) diff --git a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java index afe235ac8a5f9..1261d15ed6544 100644 --- a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java +++ b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java @@ -36,13 +36,13 @@ import org.apache.lucene.analysis.ngram.EdgeNGramTokenFilter; import org.apache.lucene.analysis.ngram.NGramTokenFilter; import org.apache.lucene.analysis.reverse.ReverseStringFilter; +import org.apache.lucene.analysis.snowball.SnowballFilter; import org.apache.lucene.analysis.standard.ClassicFilter; import org.elasticsearch.index.analysis.CharFilterFactory; import org.elasticsearch.index.analysis.HtmlStripCharFilterFactory; import org.elasticsearch.index.analysis.PreConfiguredTokenFilter; import org.elasticsearch.index.analysis.TokenFilterFactory; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; -import org.elasticsearch.indices.analysis.PreBuiltCacheFactory.CachingStrategy; import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.plugins.Plugin; @@ -73,41 +73,40 @@ public Map> getCharFilters() { @Override public List getPreConfiguredTokenFilters() { - // TODO we should revisit the caching strategies. List filters = new ArrayList<>(); - filters.add(new PreConfiguredTokenFilter("asciifolding", true, CachingStrategy.ONE, input -> new ASCIIFoldingFilter(input))); - filters.add(new PreConfiguredTokenFilter("classic", false, CachingStrategy.ONE, ClassicFilter::new)); - filters.add(new PreConfiguredTokenFilter("common_grams", false, CachingStrategy.LUCENE, input -> - new CommonGramsFilter(input, CharArraySet.EMPTY_SET))); - filters.add(new PreConfiguredTokenFilter("edge_ngram", false, CachingStrategy.LUCENE, input -> + filters.add(PreConfiguredTokenFilter.singleton("asciifolding", true, input -> new ASCIIFoldingFilter(input))); + filters.add(PreConfiguredTokenFilter.singleton("classic", false, ClassicFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("common_grams", false, + input -> new CommonGramsFilter(input, CharArraySet.EMPTY_SET))); + filters.add(PreConfiguredTokenFilter.singleton("edge_ngram", false, input -> new EdgeNGramTokenFilter(input, EdgeNGramTokenFilter.DEFAULT_MIN_GRAM_SIZE, EdgeNGramTokenFilter.DEFAULT_MAX_GRAM_SIZE))); // TODO deprecate edgeNGram - filters.add(new PreConfiguredTokenFilter("edgeNGram", false, CachingStrategy.LUCENE, input -> + filters.add(PreConfiguredTokenFilter.singleton("edgeNGram", false, input -> new EdgeNGramTokenFilter(input, EdgeNGramTokenFilter.DEFAULT_MIN_GRAM_SIZE, EdgeNGramTokenFilter.DEFAULT_MAX_GRAM_SIZE))); - filters.add(new PreConfiguredTokenFilter("kstem", false, CachingStrategy.ONE, KStemFilter::new)); - filters.add(new PreConfiguredTokenFilter("length", false, CachingStrategy.LUCENE, input -> + filters.add(PreConfiguredTokenFilter.singleton("kstem", false, KStemFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("length", false, input -> new LengthFilter(input, 0, Integer.MAX_VALUE))); // TODO this one seems useless - filters.add(new PreConfiguredTokenFilter("ngram", false, CachingStrategy.LUCENE, NGramTokenFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("ngram", false, NGramTokenFilter::new)); // TODO deprecate nGram - filters.add(new PreConfiguredTokenFilter("nGram", false, CachingStrategy.LUCENE, NGramTokenFilter::new)); - filters.add(new PreConfiguredTokenFilter("porter_stem", false, CachingStrategy.ONE, PorterStemFilter::new)); - filters.add(new PreConfiguredTokenFilter("reverse", false, CachingStrategy.LUCENE, input -> new ReverseStringFilter(input))); + filters.add(PreConfiguredTokenFilter.singleton("nGram", false, NGramTokenFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("porter_stem", false, PorterStemFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("reverse", false, input -> new ReverseStringFilter(input))); + filters.add(PreConfiguredTokenFilter.singleton("snowball", false, input -> new SnowballFilter(input, "English"))); + filters.add(PreConfiguredTokenFilter.singleton("stemmer", false, PorterStemFilter::new)); // The stop filter is in lucene-core but the English stop words set is in lucene-analyzers-common - filters.add(new PreConfiguredTokenFilter("stop", false, CachingStrategy.LUCENE, input -> - new StopFilter(input, StopAnalyzer.ENGLISH_STOP_WORDS_SET))); - filters.add(new PreConfiguredTokenFilter("trim", false, CachingStrategy.LUCENE, TrimFilter::new)); - filters.add(new PreConfiguredTokenFilter("truncate", false, CachingStrategy.ONE, input -> - new TruncateTokenFilter(input, 10))); - filters.add(new PreConfiguredTokenFilter("unique", false, CachingStrategy.ONE, input -> new UniqueTokenFilter(input))); - filters.add(new PreConfiguredTokenFilter("uppercase", true, CachingStrategy.LUCENE, UpperCaseFilter::new)); - filters.add(new PreConfiguredTokenFilter("word_delimiter", false, CachingStrategy.ONE, input -> + filters.add(PreConfiguredTokenFilter.singleton("stop", false, input -> new StopFilter(input, StopAnalyzer.ENGLISH_STOP_WORDS_SET))); + filters.add(PreConfiguredTokenFilter.singleton("trim", false, TrimFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("truncate", false, input -> new TruncateTokenFilter(input, 10))); + filters.add(PreConfiguredTokenFilter.singleton("unique", false, input -> new UniqueTokenFilter(input))); + filters.add(PreConfiguredTokenFilter.singleton("uppercase", true, UpperCaseFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("word_delimiter", false, input -> new WordDelimiterFilter(input, WordDelimiterFilter.GENERATE_WORD_PARTS | WordDelimiterFilter.GENERATE_NUMBER_PARTS | WordDelimiterFilter.SPLIT_ON_CASE_CHANGE | WordDelimiterFilter.SPLIT_ON_NUMERICS | WordDelimiterFilter.STEM_ENGLISH_POSSESSIVE, null))); - filters.add(new PreConfiguredTokenFilter("word_delimiter_graph", false, CachingStrategy.ONE, input -> + filters.add(PreConfiguredTokenFilter.singleton("word_delimiter_graph", false, input -> new WordDelimiterGraphFilter(input, WordDelimiterGraphFilter.GENERATE_WORD_PARTS | WordDelimiterGraphFilter.GENERATE_NUMBER_PARTS diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java index 73a6c3d273291..cf78f6646a2e8 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java @@ -19,7 +19,9 @@ package org.elasticsearch.analysis.common; +import org.apache.lucene.analysis.en.PorterStemFilterFactory; import org.apache.lucene.analysis.reverse.ReverseStringFilterFactory; +import org.apache.lucene.analysis.snowball.SnowballPorterFilterFactory; import org.elasticsearch.index.analysis.HtmlStripCharFilterFactory; import org.elasticsearch.indices.analysis.AnalysisFactoryTestCase; @@ -77,6 +79,8 @@ protected Map> getPreConfiguredTokenFilters() { filters.put("nGram", null); filters.put("porter_stem", null); filters.put("reverse", ReverseStringFilterFactory.class); + filters.put("snowball", SnowballPorterFilterFactory.class); + filters.put("stemmer", PorterStemFilterFactory.class); filters.put("stop", null); filters.put("trim", null); filters.put("truncate", null); diff --git a/test/framework/src/main/java/org/elasticsearch/indices/analysis/AnalysisFactoryTestCase.java b/test/framework/src/main/java/org/elasticsearch/indices/analysis/AnalysisFactoryTestCase.java index 534db0be39fb7..fe22734d97408 100644 --- a/test/framework/src/main/java/org/elasticsearch/indices/analysis/AnalysisFactoryTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/indices/analysis/AnalysisFactoryTestCase.java @@ -350,15 +350,11 @@ protected Map> getPreConfiguredTokenFilters() { case LOWERCASE: // This has been migrated but has to stick around until PreBuiltTokenizers is removed. continue; - case SNOWBALL: case DUTCH_STEM: case FRENCH_STEM: case RUSSIAN_STEM: luceneFactoryClass = SnowballPorterFilterFactory.class; break; - case STEMMER: - luceneFactoryClass = PorterStemFilterFactory.class; - break; case DELIMITED_PAYLOAD_FILTER: luceneFactoryClass = org.apache.lucene.analysis.payloads.DelimitedPayloadTokenFilterFactory.class; break; From bc223b6aef84ba4ce782b3820324c6ca95b08db3 Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Wed, 10 May 2017 23:08:59 -0400 Subject: [PATCH 295/619] Removes completed snapshot from cluster state on master change (#24605) Previously, if a master node updated the cluster state to reflect that a snapshot is completed, but subsequently failed before processing a cluster state to remove the snapshot from the cluster state, then the newly elected master would not know that it needed to clean up the leftover cluster state. This commit ensures that the newly elected master sees if there is a snapshot in the cluster state that is in the completed state but has not yet been removed from the cluster state. Closes #24452 --- .../snapshots/SnapshotsService.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java index f8abcf318086f..5f930ad4859f1 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java @@ -634,6 +634,7 @@ public void applyClusterState(ClusterChangedEvent event) { if (event.routingTableChanged()) { processStartedShards(event); } + removeFinishedSnapshotFromClusterState(event); finalizeSnapshotDeletionFromPreviousMaster(event); } } catch (Exception e) { @@ -663,6 +664,26 @@ private void finalizeSnapshotDeletionFromPreviousMaster(ClusterChangedEvent even } } + /** + * Removes a finished snapshot from the cluster state. This can happen if the previous + * master node processed a cluster state update that marked the snapshot as finished, + * but the previous master node died before removing the snapshot in progress from the + * cluster state. It is then the responsibility of the new master node to end the + * snapshot and remove it from the cluster state. + */ + private void removeFinishedSnapshotFromClusterState(ClusterChangedEvent event) { + if (event.localNodeMaster() && !event.previousState().nodes().isLocalNodeElectedMaster()) { + SnapshotsInProgress snapshotsInProgress = event.state().custom(SnapshotsInProgress.TYPE); + if (snapshotsInProgress != null && !snapshotsInProgress.entries().isEmpty()) { + for (SnapshotsInProgress.Entry entry : snapshotsInProgress.entries()) { + if (entry.state().completed()) { + endSnapshot(entry); + } + } + } + } + } + /** * Cleans up shard snapshots that were running on removed nodes * From 0789a7405574dab8cd3937b54390395ccdb2f65f Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 10 May 2017 20:12:17 -0700 Subject: [PATCH 296/619] S3 Repository: Remove deprecated settings (#24445) These settings are deprecated in 5.5. This change removes them for 6.0. --- .../migration/migrate_6_0/plugins.asciidoc | 21 +- .../repositories/s3/InternalAwsS3Service.java | 45 +--- .../repositories/s3/S3ClientSettings.java | 4 +- .../repositories/s3/S3Repository.java | 224 +++++------------- .../repositories/s3/S3RepositoryPlugin.java | 16 +- .../s3/AbstractS3SnapshotRestoreTest.java | 54 ++--- .../s3/AwsS3ServiceImplTests.java | 50 ---- .../repositories/s3/S3RepositoryTests.java | 35 +-- 8 files changed, 112 insertions(+), 337 deletions(-) diff --git a/docs/reference/migration/migrate_6_0/plugins.asciidoc b/docs/reference/migration/migrate_6_0/plugins.asciidoc index ccb04400be6a3..ef829c15e28c2 100644 --- a/docs/reference/migration/migrate_6_0/plugins.asciidoc +++ b/docs/reference/migration/migrate_6_0/plugins.asciidoc @@ -23,9 +23,24 @@ the region of the configured bucket. * Specifying s3 signer type has been removed, including `cloud.aws.signer` and `cloud.aws.s3.signer`. -* All `cloud.aws` and `repositories.s3` settings have been removed. Use `s3.client.*` settings instead. - -* All repository level client settings have been removed. Use `s3.client.*` settings instead. +* Global repositories settings have been removed. This includes `repositories.s3.bucket`, +`repositories.s3.server_side_encryption`, `repositories.s3.buffer_size`, +`repositories.s3.max_retries`, `repositories.s3.use_throttle_retries`, +`repositories.s3.chunk_size`, `repositories.s3.compress`, `repositories.s3.storage_class`, +`repositories.s3.canned_acl`, `repositories.s3.base_path`, and +`repositories.s3.path_style_access`. Instead, these settings should be set directly in the + settings per repository. + See {plugins}/repository-s3-repository.html[S3 Repository settings]. + +* Shared client settings have been removed. This includes `cloud.aws.access_key`, + `cloud.aws.secret_key`, `cloud.aws.protocol`, `cloud.aws.proxy.host`, + `cloud.aws.proxy.port`, `cloud.aws.proxy.username`, `cloud.aws.proxy.password`, + `cloud.aws.signer`, `cloud.aws.read_timeout`, `cloud.aws.s3.access_key`, + `cloud.aws.s3.secret_key`, `cloud.aws.s3.protocol`, `cloud.aws.s3.proxy.host`, + `cloud.aws.s3.proxy.port`, `cloud.aws.s3.proxy.username`, `cloud.aws.s3.proxy.password`, + `cloud.aws.s3.signer`, `cloud.aws.s3.read_timeout`, `repositories.s3.access_key`, + `repositories.s3.secret_key`, `repositories.s3.endpoint` and `repositories.s3.protocol`. +Instead, use the new named client settings under `s3.client.CLIENT_NAME.*`. ==== Azure Repository plugin diff --git a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/InternalAwsS3Service.java b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/InternalAwsS3Service.java index 95b746a0a2ba6..c76280f116ca8 100644 --- a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/InternalAwsS3Service.java +++ b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/InternalAwsS3Service.java @@ -24,27 +24,20 @@ import java.util.function.Function; import com.amazonaws.ClientConfiguration; -import com.amazonaws.Protocol; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.auth.InstanceProfileCredentialsProvider; import com.amazonaws.http.IdleConnectionReaper; import com.amazonaws.internal.StaticCredentialsProvider; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.S3ClientOptions; import org.apache.logging.log4j.Logger; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.cluster.metadata.RepositoryMetaData; import org.elasticsearch.common.Strings; import org.elasticsearch.common.component.AbstractLifecycleComponent; -import org.elasticsearch.common.logging.DeprecationLogger; -import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; -import static org.elasticsearch.repositories.s3.S3Repository.getValue; class InternalAwsS3Service extends AbstractLifecycleComponent implements AwsS3Service { @@ -70,33 +63,17 @@ public synchronized AmazonS3 client(Settings repositorySettings) { S3ClientSettings clientSettings = clientsSettings.get(clientName); if (clientSettings == null) { - throw new IllegalArgumentException("Unknown s3 client name [" + clientName + "]. " + - "Existing client configs: " + + throw new IllegalArgumentException("Unknown s3 client name [" + clientName + "]. Existing client configs: " + Strings.collectionToDelimitedString(clientsSettings.keySet(), ",")); } - // If the user defined a path style access setting, we rely on it, - // otherwise we use the default value set by the SDK - Boolean pathStyleAccess = null; - if (S3Repository.Repository.PATH_STYLE_ACCESS_SETTING.exists(repositorySettings) || - S3Repository.Repositories.PATH_STYLE_ACCESS_SETTING.exists(settings)) { - pathStyleAccess = getValue(repositorySettings, settings, - S3Repository.Repository.PATH_STYLE_ACCESS_SETTING, - S3Repository.Repositories.PATH_STYLE_ACCESS_SETTING); - } - - logger.debug("creating S3 client with client_name [{}], endpoint [{}], path_style_access [{}]", - clientName, clientSettings.endpoint, pathStyleAccess); + logger.debug("creating S3 client with client_name [{}], endpoint [{}]", clientName, clientSettings.endpoint); AWSCredentialsProvider credentials = buildCredentials(logger, clientSettings); ClientConfiguration configuration = buildConfiguration(clientSettings, repositorySettings); client = new AmazonS3Client(credentials, configuration); - if (pathStyleAccess != null) { - client.setS3ClientOptions(new S3ClientOptions().withPathStyleAccess(pathStyleAccess)); - } - if (Strings.hasText(clientSettings.endpoint)) { client.setEndpoint(clientSettings.endpoint); } @@ -121,14 +98,8 @@ static ClientConfiguration buildConfiguration(S3ClientSettings clientSettings, S clientConfiguration.setProxyPassword(clientSettings.proxyPassword); } - Integer maxRetries = getRepoValue(repositorySettings, S3Repository.Repository.MAX_RETRIES_SETTING, clientSettings.maxRetries); - if (maxRetries != null) { - // If not explicitly set, default to 3 with exponential backoff policy - clientConfiguration.setMaxErrorRetry(maxRetries); - } - boolean useThrottleRetries = getRepoValue(repositorySettings, - S3Repository.Repository.USE_THROTTLE_RETRIES_SETTING, clientSettings.throttleRetries); - clientConfiguration.setUseThrottleRetries(useThrottleRetries); + clientConfiguration.setMaxErrorRetry(clientSettings.maxRetries); + clientConfiguration.setUseThrottleRetries(clientSettings.throttleRetries); clientConfiguration.setSocketTimeout(clientSettings.readTimeoutMillis); return clientConfiguration; @@ -145,14 +116,6 @@ static AWSCredentialsProvider buildCredentials(Logger logger, S3ClientSettings c } } - /** Returns the value for a given setting from the repository, or returns the fallback value. */ - private static T getRepoValue(Settings repositorySettings, Setting repositorySetting, T fallback) { - if (repositorySetting.exists(repositorySettings)) { - return repositorySetting.get(repositorySettings); - } - return fallback; - } - @Override protected void doStart() throws ElasticsearchException { } diff --git a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3ClientSettings.java b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3ClientSettings.java index 8be6aaff74fb8..4d32d2518fff1 100644 --- a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3ClientSettings.java +++ b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3ClientSettings.java @@ -81,11 +81,11 @@ class S3ClientSettings { /** The number of retries to use when an s3 request fails. */ static final Setting.AffixSetting MAX_RETRIES_SETTING = Setting.affixKeySetting(PREFIX, "max_retries", - key -> Setting.intSetting(key, S3Repository.Repositories.MAX_RETRIES_SETTING, 0, Property.NodeScope)); + key -> Setting.intSetting(key, ClientConfiguration.DEFAULT_RETRY_POLICY.getMaxErrorRetry(), 0, Property.NodeScope)); /** Whether retries should be throttled (ie use backoff). */ static final Setting.AffixSetting USE_THROTTLE_RETRIES_SETTING = Setting.affixKeySetting(PREFIX, "use_throttle_retries", - key -> Setting.boolSetting(key, S3Repository.Repositories.USE_THROTTLE_RETRIES_SETTING, Property.NodeScope)); + key -> Setting.boolSetting(key, ClientConfiguration.DEFAULT_THROTTLE_RETRIES, Property.NodeScope)); /** Credentials to authenticate with s3. */ final BasicAWSCredentials credentials; diff --git a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java index b183fd3b81471..b73848eed600f 100644 --- a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java +++ b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java @@ -51,156 +51,66 @@ */ class S3Repository extends BlobStoreRepository { - public static final String TYPE = "s3"; + static final String TYPE = "s3"; /** - * Global S3 repositories settings. Starting with: repositories.s3 - * NOTE: These are legacy settings. Use the named client config settings above. + * Default is to use 100MB (S3 defaults) for heaps above 2GB and 5% of + * the available memory for smaller heaps. */ - public interface Repositories { - /** - * repositories.s3.bucket: The name of the bucket to be used for snapshots. - */ - Setting BUCKET_SETTING = Setting.simpleString("repositories.s3.bucket", Property.NodeScope, Property.Deprecated); - /** - * repositories.s3.server_side_encryption: When set to true files are encrypted on server side using AES256 algorithm. - * Defaults to false. - */ - Setting SERVER_SIDE_ENCRYPTION_SETTING = - Setting.boolSetting("repositories.s3.server_side_encryption", false, Property.NodeScope, Property.Deprecated); + private static final ByteSizeValue DEFAULT_BUFFER_SIZE = new ByteSizeValue( + Math.max( + ByteSizeUnit.MB.toBytes(5), // minimum value + Math.min( + ByteSizeUnit.MB.toBytes(100), + JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() / 20)), + ByteSizeUnit.BYTES); - /** - * Default is to use 100MB (S3 defaults) for heaps above 2GB and 5% of - * the available memory for smaller heaps. - */ - ByteSizeValue DEFAULT_BUFFER_SIZE = new ByteSizeValue( - Math.max( - ByteSizeUnit.MB.toBytes(5), // minimum value - Math.min( - ByteSizeUnit.MB.toBytes(100), - JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() / 20)), - ByteSizeUnit.BYTES); - /** - * repositories.s3.buffer_size: Minimum threshold below which the chunk is uploaded using a single request. Beyond this threshold, - * the S3 repository will use the AWS Multipart Upload API to split the chunk into several parts, each of buffer_size length, and - * to upload each part in its own request. Note that setting a buffer size lower than 5mb is not allowed since it will prevents the - * use of the Multipart API and may result in upload errors. Defaults to the minimum between 100MB and 5% of the heap size. - */ - Setting BUFFER_SIZE_SETTING = - Setting.byteSizeSetting("repositories.s3.buffer_size", DEFAULT_BUFFER_SIZE, - new ByteSizeValue(5, ByteSizeUnit.MB), new ByteSizeValue(5, ByteSizeUnit.TB), Property.NodeScope, Property.Deprecated); - /** - * repositories.s3.max_retries: Number of retries in case of S3 errors. Defaults to 3. - */ - Setting MAX_RETRIES_SETTING = Setting.intSetting("repositories.s3.max_retries", 3, Property.NodeScope, Property.Deprecated); - /** - * repositories.s3.use_throttle_retries: Set to `true` if you want to throttle retries. Defaults to AWS SDK default value (`false`). - */ - Setting USE_THROTTLE_RETRIES_SETTING = Setting.boolSetting("repositories.s3.use_throttle_retries", - ClientConfiguration.DEFAULT_THROTTLE_RETRIES, Property.NodeScope, Property.Deprecated); - /** - * repositories.s3.chunk_size: Big files can be broken down into chunks during snapshotting if needed. Defaults to 1g. - */ - Setting CHUNK_SIZE_SETTING = - Setting.byteSizeSetting("repositories.s3.chunk_size", new ByteSizeValue(1, ByteSizeUnit.GB), - new ByteSizeValue(5, ByteSizeUnit.MB), new ByteSizeValue(5, ByteSizeUnit.TB), Property.NodeScope, Property.Deprecated); - /** - * repositories.s3.compress: When set to true metadata files are stored in compressed format. This setting doesn’t affect index - * files that are already compressed by default. Defaults to false. - */ - Setting COMPRESS_SETTING = Setting.boolSetting("repositories.s3.compress", false, Property.NodeScope, Property.Deprecated); - /** - * repositories.s3.storage_class: Sets the S3 storage class type for the backup files. Values may be standard, reduced_redundancy, - * standard_ia. Defaults to standard. - */ - Setting STORAGE_CLASS_SETTING = Setting.simpleString("repositories.s3.storage_class", Property.NodeScope, Property.Deprecated); - /** - * repositories.s3.canned_acl: The S3 repository supports all S3 canned ACLs : private, public-read, public-read-write, - * authenticated-read, log-delivery-write, bucket-owner-read, bucket-owner-full-control. Defaults to private. - */ - Setting CANNED_ACL_SETTING = Setting.simpleString("repositories.s3.canned_acl", Property.NodeScope, Property.Deprecated); - /** - * repositories.s3.base_path: Specifies the path within bucket to repository data. Defaults to root directory. - */ - Setting BASE_PATH_SETTING = Setting.simpleString("repositories.s3.base_path", Property.NodeScope, Property.Deprecated); - /** - * repositories.s3.path_style_access: When set to true configures the client to use path-style access for all requests. - Amazon S3 supports virtual-hosted-style and path-style access in all Regions. The path-style syntax, however, - requires that you use the region-specific endpoint when attempting to access a bucket. - The default behaviour is to detect which access style to use based on the configured endpoint (an IP will result - in path-style access) and the bucket being accessed (some buckets are not valid DNS names). Setting this flag - will result in path-style access being used for all requests. - */ - Setting PATH_STYLE_ACCESS_SETTING = Setting.boolSetting("repositories.s3.path_style_access", false, - Property.NodeScope, Property.Deprecated); - } + static final Setting BUCKET_SETTING = Setting.simpleString("bucket"); /** - * Per S3 repository specific settings. Same settings as Repositories settings but without the repositories.s3 prefix. - * If undefined, they use the repositories.s3.xxx equivalent setting. + * When set to true files are encrypted on server side using AES256 algorithm. + * Defaults to false. */ - public interface Repository { + static final Setting SERVER_SIDE_ENCRYPTION_SETTING = Setting.boolSetting("server_side_encryption", false); - Setting BUCKET_SETTING = Setting.simpleString("bucket"); + /** + * Minimum threshold below which the chunk is uploaded using a single request. Beyond this threshold, + * the S3 repository will use the AWS Multipart Upload API to split the chunk into several parts, each of buffer_size length, and + * to upload each part in its own request. Note that setting a buffer size lower than 5mb is not allowed since it will prevents the + * use of the Multipart API and may result in upload errors. Defaults to the minimum between 100MB and 5% of the heap size. + */ + static final Setting BUFFER_SIZE_SETTING = Setting.byteSizeSetting("buffer_size", DEFAULT_BUFFER_SIZE, + new ByteSizeValue(5, ByteSizeUnit.MB), new ByteSizeValue(5, ByteSizeUnit.TB)); - /** - * server_side_encryption - * @see Repositories#SERVER_SIDE_ENCRYPTION_SETTING - */ - Setting SERVER_SIDE_ENCRYPTION_SETTING = Setting.boolSetting("server_side_encryption", false); + /** + * Big files can be broken down into chunks during snapshotting if needed. Defaults to 1g. + */ + static final Setting CHUNK_SIZE_SETTING = Setting.byteSizeSetting("chunk_size", new ByteSizeValue(1, ByteSizeUnit.GB), + new ByteSizeValue(5, ByteSizeUnit.MB), new ByteSizeValue(5, ByteSizeUnit.TB)); - /** - * buffer_size - * @see Repositories#BUFFER_SIZE_SETTING - */ - Setting BUFFER_SIZE_SETTING = - Setting.byteSizeSetting("buffer_size", Repositories.DEFAULT_BUFFER_SIZE, - new ByteSizeValue(5, ByteSizeUnit.MB), new ByteSizeValue(5, ByteSizeUnit.TB)); - /** - * max_retries - * @see Repositories#MAX_RETRIES_SETTING - */ - Setting MAX_RETRIES_SETTING = Setting.intSetting("max_retries", 3, Property.Deprecated); - /** - * use_throttle_retries - * @see Repositories#USE_THROTTLE_RETRIES_SETTING - */ - Setting USE_THROTTLE_RETRIES_SETTING = Setting.boolSetting("use_throttle_retries", - ClientConfiguration.DEFAULT_THROTTLE_RETRIES, Property.Deprecated); - /** - * chunk_size - * @see Repositories#CHUNK_SIZE_SETTING - */ - Setting CHUNK_SIZE_SETTING = - Setting.byteSizeSetting("chunk_size", new ByteSizeValue(1, ByteSizeUnit.GB), - new ByteSizeValue(5, ByteSizeUnit.MB), new ByteSizeValue(5, ByteSizeUnit.TB)); - /** - * compress - * @see Repositories#COMPRESS_SETTING - */ - Setting COMPRESS_SETTING = Setting.boolSetting("compress", false); - /** - * storage_class - * @see Repositories#STORAGE_CLASS_SETTING - */ - Setting STORAGE_CLASS_SETTING = Setting.simpleString("storage_class"); - /** - * canned_acl - * @see Repositories#CANNED_ACL_SETTING - */ - Setting CANNED_ACL_SETTING = Setting.simpleString("canned_acl"); - /** - * base_path - * @see Repositories#BASE_PATH_SETTING - */ - Setting BASE_PATH_SETTING = Setting.simpleString("base_path"); - /** - * path_style_access - * @see Repositories#PATH_STYLE_ACCESS_SETTING - */ - Setting PATH_STYLE_ACCESS_SETTING = Setting.boolSetting("path_style_access", false, Property.Deprecated); - } + /** + * When set to true metadata files are stored in compressed format. This setting doesn’t affect index + * files that are already compressed by default. Defaults to false. + */ + static final Setting COMPRESS_SETTING = Setting.boolSetting("compress", false); + + /** + * Sets the S3 storage class type for the backup files. Values may be standard, reduced_redundancy, + * standard_ia. Defaults to standard. + */ + static final Setting STORAGE_CLASS_SETTING = Setting.simpleString("storage_class"); + + /** + * The S3 repository supports all S3 canned ACLs : private, public-read, public-read-write, + * authenticated-read, log-delivery-write, bucket-owner-read, bucket-owner-full-control. Defaults to private. + */ + static final Setting CANNED_ACL_SETTING = Setting.simpleString("canned_acl"); + + /** + * Specifies the path within bucket to repository data. Defaults to root directory. + */ + static final Setting BASE_PATH_SETTING = Setting.simpleString("base_path"); private final S3BlobStore blobStore; @@ -217,25 +127,25 @@ public interface Repository { NamedXContentRegistry namedXContentRegistry, AwsS3Service s3Service) throws IOException { super(metadata, settings, namedXContentRegistry); - String bucket = getValue(metadata.settings(), settings, Repository.BUCKET_SETTING, Repositories.BUCKET_SETTING); + String bucket = BUCKET_SETTING.get(metadata.settings()); if (bucket == null) { throw new RepositoryException(metadata.name(), "No bucket defined for s3 gateway"); } - boolean serverSideEncryption = getValue(metadata.settings(), settings, Repository.SERVER_SIDE_ENCRYPTION_SETTING, Repositories.SERVER_SIDE_ENCRYPTION_SETTING); - ByteSizeValue bufferSize = getValue(metadata.settings(), settings, Repository.BUFFER_SIZE_SETTING, Repositories.BUFFER_SIZE_SETTING); - this.chunkSize = getValue(metadata.settings(), settings, Repository.CHUNK_SIZE_SETTING, Repositories.CHUNK_SIZE_SETTING); - this.compress = getValue(metadata.settings(), settings, Repository.COMPRESS_SETTING, Repositories.COMPRESS_SETTING); + boolean serverSideEncryption = SERVER_SIDE_ENCRYPTION_SETTING.get(metadata.settings()); + ByteSizeValue bufferSize = BUFFER_SIZE_SETTING.get(metadata.settings()); + this.chunkSize = CHUNK_SIZE_SETTING.get(metadata.settings()); + this.compress = COMPRESS_SETTING.get(metadata.settings()); // We make sure that chunkSize is bigger or equal than/to bufferSize if (this.chunkSize.getBytes() < bufferSize.getBytes()) { - throw new RepositoryException(metadata.name(), Repository.CHUNK_SIZE_SETTING.getKey() + " (" + this.chunkSize + - ") can't be lower than " + Repository.BUFFER_SIZE_SETTING.getKey() + " (" + bufferSize + ")."); + throw new RepositoryException(metadata.name(), CHUNK_SIZE_SETTING.getKey() + " (" + this.chunkSize + + ") can't be lower than " + BUFFER_SIZE_SETTING.getKey() + " (" + bufferSize + ")."); } // Parse and validate the user's S3 Storage Class setting - String storageClass = getValue(metadata.settings(), settings, Repository.STORAGE_CLASS_SETTING, Repositories.STORAGE_CLASS_SETTING); - String cannedACL = getValue(metadata.settings(), settings, Repository.CANNED_ACL_SETTING, Repositories.CANNED_ACL_SETTING); + String storageClass = STORAGE_CLASS_SETTING.get(metadata.settings()); + String cannedACL = CANNED_ACL_SETTING.get(metadata.settings()); logger.debug("using bucket [{}], chunk_size [{}], server_side_encryption [{}], " + "buffer_size [{}], cannedACL [{}], storageClass [{}]", @@ -244,13 +154,8 @@ public interface Repository { AmazonS3 client = s3Service.client(metadata.settings()); blobStore = new S3BlobStore(settings, client, bucket, serverSideEncryption, bufferSize, cannedACL, storageClass); - String basePath = getValue(metadata.settings(), settings, Repository.BASE_PATH_SETTING, Repositories.BASE_PATH_SETTING); + String basePath = BASE_PATH_SETTING.get(metadata.settings()); if (Strings.hasLength(basePath)) { - if (basePath.startsWith("/")) { - basePath = basePath.substring(1); - deprecationLogger.deprecated("S3 repository base_path trimming the leading `/`, and " + - "leading `/` will not be supported for the S3 repository in future releases"); - } this.basePath = new BlobPath().add(basePath); } else { this.basePath = BlobPath.cleanPath(); @@ -276,15 +181,4 @@ protected boolean isCompress() { protected ByteSizeValue chunkSize() { return chunkSize; } - - public static T getValue(Settings repositorySettings, - Settings globalSettings, - Setting repositorySetting, - Setting repositoriesSetting) { - if (repositorySetting.exists(repositorySettings)) { - return repositorySetting.get(repositorySettings); - } else { - return repositoriesSetting.get(globalSettings); - } - } } diff --git a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java index a4512ab815d06..010c4b92c21a0 100644 --- a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java +++ b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java @@ -79,7 +79,6 @@ public Map getRepositories(Environment env, NamedXCo @Override public List> getSettings() { return Arrays.asList( - // named s3 client configuration settings S3ClientSettings.ACCESS_KEY_SETTING, S3ClientSettings.SECRET_KEY_SETTING, @@ -91,19 +90,6 @@ public List> getSettings() { S3ClientSettings.PROXY_PASSWORD_SETTING, S3ClientSettings.READ_TIMEOUT_SETTING, S3ClientSettings.MAX_RETRIES_SETTING, - S3ClientSettings.USE_THROTTLE_RETRIES_SETTING, - - // Register S3 repositories settings: repositories.s3 - S3Repository.Repositories.BUCKET_SETTING, - S3Repository.Repositories.SERVER_SIDE_ENCRYPTION_SETTING, - S3Repository.Repositories.BUFFER_SIZE_SETTING, - S3Repository.Repositories.MAX_RETRIES_SETTING, - S3Repository.Repositories.CHUNK_SIZE_SETTING, - S3Repository.Repositories.COMPRESS_SETTING, - S3Repository.Repositories.STORAGE_CLASS_SETTING, - S3Repository.Repositories.CANNED_ACL_SETTING, - S3Repository.Repositories.BASE_PATH_SETTING, - S3Repository.Repositories.USE_THROTTLE_RETRIES_SETTING, - S3Repository.Repositories.PATH_STYLE_ACCESS_SETTING); + S3ClientSettings.USE_THROTTLE_RETRIES_SETTING); } } diff --git a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AbstractS3SnapshotRestoreTest.java b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AbstractS3SnapshotRestoreTest.java index e3a524c640eff..4a6d1c123838d 100644 --- a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AbstractS3SnapshotRestoreTest.java +++ b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AbstractS3SnapshotRestoreTest.java @@ -52,43 +52,29 @@ @ClusterScope(scope = Scope.SUITE, numDataNodes = 2, numClientNodes = 0, transportClientRatio = 0.0) public abstract class AbstractS3SnapshotRestoreTest extends AbstractAwsTestCase { - @Override - public Settings nodeSettings(int nodeOrdinal) { - // nodeSettings is called before `wipeBefore()` so we need to define basePath here - globalBasePath = "repo-" + randomInt(); - return Settings.builder().put(super.nodeSettings(nodeOrdinal)) - .put(S3Repository.Repositories.BASE_PATH_SETTING.getKey(), globalBasePath) - .build(); - } - private String basePath; - private String globalBasePath; @Before public final void wipeBefore() { wipeRepositories(); basePath = "repo-" + randomInt(); cleanRepositoryFiles(basePath); - cleanRepositoryFiles(globalBasePath); } @After public final void wipeAfter() { wipeRepositories(); cleanRepositoryFiles(basePath); - cleanRepositoryFiles(globalBasePath); } @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch-cloud-aws/issues/211") public void testSimpleWorkflow() { Client client = client(); Settings.Builder settings = Settings.builder() - .put(S3Repository.Repository.CHUNK_SIZE_SETTING.getKey(), randomIntBetween(1000, 10000)); + .put(S3Repository.CHUNK_SIZE_SETTING.getKey(), randomIntBetween(1000, 10000)); // We sometime test getting the base_path from node settings using repositories.s3.base_path - if (usually()) { - settings.put(S3Repository.Repository.BASE_PATH_SETTING.getKey(), basePath); - } + settings.put(S3Repository.BASE_PATH_SETTING.getKey(), basePath); logger.info("--> creating s3 repository with bucket[{}] and path [{}]", internalCluster().getInstance(Settings.class).get("repositories.s3.bucket"), basePath); PutRepositoryResponse putRepositoryResponse = client.admin().cluster().preparePutRepository("test-repo") @@ -163,10 +149,9 @@ public void testEncryption() { logger.info("--> creating s3 repository with bucket[{}] and path [{}]", internalCluster().getInstance(Settings.class).get("repositories.s3.bucket"), basePath); Settings repositorySettings = Settings.builder() - .put(S3Repository.Repository.BASE_PATH_SETTING.getKey(), basePath) - .put(S3Repository.Repository.CHUNK_SIZE_SETTING.getKey(), randomIntBetween(1000, 10000)) - .put(S3Repository.Repository.SERVER_SIDE_ENCRYPTION_SETTING.getKey(), true) - .put(S3Repository.Repository.USE_THROTTLE_RETRIES_SETTING.getKey(), randomBoolean()) + .put(S3Repository.BASE_PATH_SETTING.getKey(), basePath) + .put(S3Repository.CHUNK_SIZE_SETTING.getKey(), randomIntBetween(1000, 10000)) + .put(S3Repository.SERVER_SIDE_ENCRYPTION_SETTING.getKey(), true) .build(); PutRepositoryResponse putRepositoryResponse = client.admin().cluster().preparePutRepository("test-repo") @@ -257,8 +242,8 @@ public void testRepositoryWithCustomCredentialsIsNotAccessibleByDefaultCredentia try { client.admin().cluster().preparePutRepository("test-repo") .setType("s3").setSettings(Settings.builder() - .put(S3Repository.Repository.BASE_PATH_SETTING.getKey(), basePath) - .put(S3Repository.Repository.BUCKET_SETTING.getKey(), bucketSettings.get("bucket")) + .put(S3Repository.BASE_PATH_SETTING.getKey(), basePath) + .put(S3Repository.BUCKET_SETTING.getKey(), bucketSettings.get("bucket")) ).get(); fail("repository verification should have raise an exception!"); } catch (RepositoryVerificationException e) { @@ -269,7 +254,7 @@ public void testRepositoryWithBasePath() { Client client = client(); PutRepositoryResponse putRepositoryResponse = client.admin().cluster().preparePutRepository("test-repo") .setType("s3").setSettings(Settings.builder() - .put(S3Repository.Repository.BASE_PATH_SETTING.getKey(), basePath) + .put(S3Repository.BASE_PATH_SETTING.getKey(), basePath) ).get(); assertThat(putRepositoryResponse.isAcknowledged(), equalTo(true)); @@ -282,8 +267,8 @@ public void testRepositoryWithCustomCredentials() { logger.info("--> creating s3 repository with bucket[{}] and path [{}]", bucketSettings.get("bucket"), basePath); PutRepositoryResponse putRepositoryResponse = client.admin().cluster().preparePutRepository("test-repo") .setType("s3").setSettings(Settings.builder() - .put(S3Repository.Repository.BASE_PATH_SETTING.getKey(), basePath) - .put(S3Repository.Repository.BUCKET_SETTING.getKey(), bucketSettings.get("bucket")) + .put(S3Repository.BASE_PATH_SETTING.getKey(), basePath) + .put(S3Repository.BUCKET_SETTING.getKey(), bucketSettings.get("bucket")) ).get(); assertThat(putRepositoryResponse.isAcknowledged(), equalTo(true)); @@ -297,8 +282,8 @@ public void testRepositoryWithCustomEndpointProtocol() { logger.info("--> creating s3 repostoriy with endpoint [{}], bucket[{}] and path [{}]", bucketSettings.get("endpoint"), bucketSettings.get("bucket"), basePath); PutRepositoryResponse putRepositoryResponse = client.admin().cluster().preparePutRepository("test-repo") .setType("s3").setSettings(Settings.builder() - .put(S3Repository.Repository.BUCKET_SETTING.getKey(), bucketSettings.get("bucket")) - .put(S3Repository.Repository.BASE_PATH_SETTING.getKey(), basePath) + .put(S3Repository.BUCKET_SETTING.getKey(), bucketSettings.get("bucket")) + .put(S3Repository.BASE_PATH_SETTING.getKey(), basePath) ).get(); assertThat(putRepositoryResponse.isAcknowledged(), equalTo(true)); assertRepositoryIsOperational(client, "test-repo"); @@ -315,8 +300,8 @@ public void testRepositoryInRemoteRegionIsRemote() { try { client.admin().cluster().preparePutRepository("test-repo") .setType("s3").setSettings(Settings.builder() - .put(S3Repository.Repository.BASE_PATH_SETTING.getKey(), basePath) - .put(S3Repository.Repository.BUCKET_SETTING.getKey(), bucketSettings.get("bucket")) + .put(S3Repository.BASE_PATH_SETTING.getKey(), basePath) + .put(S3Repository.BUCKET_SETTING.getKey(), bucketSettings.get("bucket")) // Below setting intentionally omitted to assert bucket is not available in default region. // .put("region", privateBucketSettings.get("region")) ).get(); @@ -333,8 +318,8 @@ public void testRepositoryInRemoteRegion() { logger.info("--> creating s3 repository with bucket[{}] and path [{}]", bucketSettings.get("bucket"), basePath); PutRepositoryResponse putRepositoryResponse = client.admin().cluster().preparePutRepository("test-repo") .setType("s3").setSettings(Settings.builder() - .put(S3Repository.Repository.BASE_PATH_SETTING.getKey(), basePath) - .put(S3Repository.Repository.BUCKET_SETTING.getKey(), bucketSettings.get("bucket")) + .put(S3Repository.BASE_PATH_SETTING.getKey(), basePath) + .put(S3Repository.BUCKET_SETTING.getKey(), bucketSettings.get("bucket")) ).get(); assertThat(putRepositoryResponse.isAcknowledged(), equalTo(true)); @@ -349,7 +334,7 @@ public void testNonExistingRepo86() { logger.info("--> creating s3 repository with bucket[{}] and path [{}]", internalCluster().getInstance(Settings.class).get("repositories.s3.bucket"), basePath); PutRepositoryResponse putRepositoryResponse = client.admin().cluster().preparePutRepository("test-repo") .setType("s3").setSettings(Settings.builder() - .put(S3Repository.Repository.BASE_PATH_SETTING.getKey(), basePath) + .put(S3Repository.BASE_PATH_SETTING.getKey(), basePath) ).get(); assertThat(putRepositoryResponse.isAcknowledged(), equalTo(true)); @@ -370,7 +355,7 @@ public void testGetDeleteNonExistingSnapshot86() { logger.info("--> creating s3 repository without any path"); PutRepositoryResponse putRepositoryResponse = client.preparePutRepository("test-repo") .setType("s3").setSettings(Settings.builder() - .put(S3Repository.Repository.BASE_PATH_SETTING.getKey(), basePath) + .put(S3Repository.BASE_PATH_SETTING.getKey(), basePath) ).get(); assertThat(putRepositoryResponse.isAcknowledged(), equalTo(true)); @@ -460,8 +445,7 @@ public void cleanRepositoryFiles(String basePath) { // We check that settings has been set in elasticsearch.yml integration test file // as described in README assertThat("Your settings in elasticsearch.yml are incorrect. Check README file.", bucketName, notNullValue()); - AmazonS3 client = internalCluster().getInstance(AwsS3Service.class).client( - Settings.builder().put(S3Repository.Repository.USE_THROTTLE_RETRIES_SETTING.getKey(), randomBoolean()).build()); + AmazonS3 client = internalCluster().getInstance(AwsS3Service.class).client(Settings.EMPTY); try { ObjectListing prevListing = null; //From http://docs.amazonwebservices.com/AmazonS3/latest/dev/DeletingMultipleObjectsUsingJava.html diff --git a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java index 017e7fbeb3f16..85398571dc1d1 100644 --- a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java +++ b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java @@ -87,17 +87,6 @@ public void testAWSConfigurationWithAwsSettings() { "aws_proxy_password", 3, false, 10000); } - public void testGlobalMaxRetriesBackcompat() { - Settings settings = Settings.builder() - .put(S3Repository.Repositories.MAX_RETRIES_SETTING.getKey(), 10) - .build(); - launchAWSConfigurationTest(settings, Settings.EMPTY, Protocol.HTTPS, null, -1, null, - null, 10, false, 50000); - assertSettingDeprecationsAndWarnings(new Setting[]{ - S3Repository.Repositories.MAX_RETRIES_SETTING - }); - } - public void testRepositoryMaxRetries() { Settings settings = Settings.builder() .put("s3.client.default.max_retries", 5) @@ -106,31 +95,6 @@ public void testRepositoryMaxRetries() { null, 5, false, 50000); } - public void testRepositoryMaxRetriesBackcompat() { - Settings repositorySettings = Settings.builder() - .put(S3Repository.Repository.MAX_RETRIES_SETTING.getKey(), 20).build(); - Settings settings = Settings.builder() - .put(S3Repository.Repositories.MAX_RETRIES_SETTING.getKey(), 10) - .build(); - launchAWSConfigurationTest(settings, repositorySettings, Protocol.HTTPS, null, -1, null, - null, 20, false, 50000); - assertSettingDeprecationsAndWarnings(new Setting[]{ - S3Repository.Repositories.MAX_RETRIES_SETTING, - S3Repository.Repository.MAX_RETRIES_SETTING - }); - } - - public void testGlobalThrottleRetriesBackcompat() { - Settings settings = Settings.builder() - .put(S3Repository.Repositories.USE_THROTTLE_RETRIES_SETTING.getKey(), true) - .build(); - launchAWSConfigurationTest(settings, Settings.EMPTY, Protocol.HTTPS, null, -1, null, - null, 3, true, 50000); - assertSettingDeprecationsAndWarnings(new Setting[]{ - S3Repository.Repositories.USE_THROTTLE_RETRIES_SETTING - }); - } - public void testRepositoryThrottleRetries() { Settings settings = Settings.builder() .put("s3.client.default.use_throttle_retries", true) @@ -139,20 +103,6 @@ public void testRepositoryThrottleRetries() { null, 3, true, 50000); } - public void testRepositoryThrottleRetriesBackcompat() { - Settings repositorySettings = Settings.builder() - .put(S3Repository.Repository.USE_THROTTLE_RETRIES_SETTING.getKey(), true).build(); - Settings settings = Settings.builder() - .put(S3Repository.Repositories.USE_THROTTLE_RETRIES_SETTING.getKey(), false) - .build(); - launchAWSConfigurationTest(settings, repositorySettings, Protocol.HTTPS, null, -1, null, - null, 3, true, 50000); - assertSettingDeprecationsAndWarnings(new Setting[]{ - S3Repository.Repositories.USE_THROTTLE_RETRIES_SETTING, - S3Repository.Repository.USE_THROTTLE_RETRIES_SETTING - }); - } - private void launchAWSConfigurationTest(Settings settings, Settings singleRepositorySettings, Protocol expectedProtocol, diff --git a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java index ce20d05a37ee2..c319d08ebf6bd 100644 --- a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java +++ b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java @@ -19,12 +19,12 @@ package org.elasticsearch.repositories.s3; +import java.io.IOException; + import com.amazonaws.services.s3.AbstractAmazonS3; import com.amazonaws.services.s3.AmazonS3; import org.elasticsearch.cluster.metadata.RepositoryMetaData; import org.elasticsearch.common.component.AbstractLifecycleComponent; -import org.elasticsearch.common.settings.SecureString; -import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; @@ -33,11 +33,6 @@ import org.elasticsearch.test.ESTestCase; import org.hamcrest.Matchers; -import java.io.IOException; - -import static org.elasticsearch.repositories.s3.S3Repository.Repositories; -import static org.elasticsearch.repositories.s3.S3Repository.Repository; -import static org.elasticsearch.repositories.s3.S3Repository.getValue; import static org.hamcrest.Matchers.containsString; public class S3RepositoryTests extends ESTestCase { @@ -82,15 +77,15 @@ public void testInvalidChunkBufferSizeSettings() throws IOException { private void assertValidBuffer(long bufferMB, long chunkMB) throws IOException { RepositoryMetaData metadata = new RepositoryMetaData("dummy-repo", "mock", Settings.builder() - .put(Repository.BUFFER_SIZE_SETTING.getKey(), new ByteSizeValue(bufferMB, ByteSizeUnit.MB)) - .put(Repository.CHUNK_SIZE_SETTING.getKey(), new ByteSizeValue(chunkMB, ByteSizeUnit.MB)).build()); + .put(S3Repository.BUFFER_SIZE_SETTING.getKey(), new ByteSizeValue(bufferMB, ByteSizeUnit.MB)) + .put(S3Repository.CHUNK_SIZE_SETTING.getKey(), new ByteSizeValue(chunkMB, ByteSizeUnit.MB)).build()); new S3Repository(metadata, Settings.EMPTY, NamedXContentRegistry.EMPTY, new DummyS3Service()); } private void assertInvalidBuffer(int bufferMB, int chunkMB, Class clazz, String msg) throws IOException { RepositoryMetaData metadata = new RepositoryMetaData("dummy-repo", "mock", Settings.builder() - .put(Repository.BUFFER_SIZE_SETTING.getKey(), new ByteSizeValue(bufferMB, ByteSizeUnit.MB)) - .put(Repository.CHUNK_SIZE_SETTING.getKey(), new ByteSizeValue(chunkMB, ByteSizeUnit.MB)).build()); + .put(S3Repository.BUFFER_SIZE_SETTING.getKey(), new ByteSizeValue(bufferMB, ByteSizeUnit.MB)) + .put(S3Repository.CHUNK_SIZE_SETTING.getKey(), new ByteSizeValue(chunkMB, ByteSizeUnit.MB)).build()); Exception e = expectThrows(clazz, () -> new S3Repository(metadata, Settings.EMPTY, NamedXContentRegistry.EMPTY, new DummyS3Service())); @@ -99,26 +94,14 @@ private void assertInvalidBuffer(int bufferMB, int chunkMB, Class[] { Repositories.BASE_PATH_SETTING }, - "S3 repository base_path" + - " trimming the leading `/`, and leading `/` will not be supported for the S3 repository in future releases"); + assertEquals("foo/bar/", s3repo.basePath().buildAsString()); } public void testDefaultBufferSize() { - ByteSizeValue defaultBufferSize = S3Repository.Repository.BUFFER_SIZE_SETTING.get(Settings.EMPTY); + ByteSizeValue defaultBufferSize = S3Repository.BUFFER_SIZE_SETTING.get(Settings.EMPTY); assertThat(defaultBufferSize, Matchers.lessThanOrEqualTo(new ByteSizeValue(100, ByteSizeUnit.MB))); assertThat(defaultBufferSize, Matchers.greaterThanOrEqualTo(new ByteSizeValue(5, ByteSizeUnit.MB))); - - ByteSizeValue defaultNodeBufferSize = S3Repository.Repositories.BUFFER_SIZE_SETTING.get(Settings.EMPTY); - assertEquals(defaultBufferSize, defaultNodeBufferSize); } } From 77feabb3d5f2d522444b2dcc88fede7ecfa347d4 Mon Sep 17 00:00:00 2001 From: propulkit Date: Thu, 11 May 2017 07:34:42 +0200 Subject: [PATCH 297/619] Machine Permission update on Google Compute Machine permission can be updated without deleting instances. Backport of #24607 in master branch --- docs/plugins/discovery-gce.asciidoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/plugins/discovery-gce.asciidoc b/docs/plugins/discovery-gce.asciidoc index 16c84f9033d39..4b31158b69486 100644 --- a/docs/plugins/discovery-gce.asciidoc +++ b/docs/plugins/discovery-gce.asciidoc @@ -433,8 +433,7 @@ gcloud config set project es-cloud [[discovery-gce-usage-tips-permissions]] ===== Machine Permissions -If you have created a machine without the correct permissions, you will see `403 unauthorized` error messages. The only -way to alter these permissions is to delete the instance (NOT THE DISK). Then create another with the correct permissions. +If you have created a machine without the correct permissions, you will see `403 unauthorized` error messages. To change machine permission on an existing instance, first stop the instance then Edit. Scroll down to `Access Scopes` to change permission. The other way to alter these permissions is to delete the instance (NOT THE DISK). Then create another with the correct permissions. Creating machines with gcloud:: + From e13db1b269a070f739cff28094ebe949464ca501 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Thu, 11 May 2017 11:06:55 +0200 Subject: [PATCH 298/619] Validates updated settings on closed indices (#24487) We allow non-dynamic settings to be updated on closed indices but we don't check if the updated settings can be used to open/create the index. This can lead to unrecoverable state where the settings are updated but the index cannot be reopened since the settings are not valid. Trying to update the invalid settings is also not possible since the update will fail to validate the current settings. This change adds the validation of the updated settings for closed indices and make sure that the new settings do not prevent the reopen of the index. Fixes #23787 --- .../metadata/MetaDataUpdateSettingsService.java | 4 ++++ .../indices/settings/UpdateSettingsIT.java | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java index 279dfa57d1616..b4b1fc8051961 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java @@ -276,7 +276,11 @@ public ClusterState execute(ClusterState currentState) { for (Index index : closeIndices) { final IndexMetaData currentMetaData = currentState.getMetaData().getIndexSafe(index); final IndexMetaData updatedMetaData = updatedState.metaData().getIndexSafe(index); + // Verifies that the current index settings can be updated with the updated dynamic settings. indicesService.verifyIndexMetadata(currentMetaData, updatedMetaData); + // Now check that we can create the index with the updated settings (dynamic and non-dynamic). + // This step is mandatory since we allow to update non-dynamic settings on closed indices. + indicesService.verifyIndexMetadata(updatedMetaData, updatedMetaData); } } catch (IOException ex) { throw ExceptionsHelper.convertToElastic(ex); diff --git a/core/src/test/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java b/core/src/test/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java index 188498b56d026..e8c1604c79915 100644 --- a/core/src/test/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java +++ b/core/src/test/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java @@ -49,6 +49,18 @@ import static org.hamcrest.Matchers.nullValue; public class UpdateSettingsIT extends ESIntegTestCase { + public void testInvalidUpdateOnClosedIndex() { + createIndex("test"); + assertAcked(client().admin().indices().prepareClose("test").get()); + IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> + client() + .admin() + .indices() + .prepareUpdateSettings("test") + .setSettings(Settings.builder().put("index.analysis.char_filter.invalid_char.type", "invalid")) + .get()); + assertEquals(exception.getMessage(), "Unknown char_filter type [invalid] for [invalid_char]"); + } public void testInvalidDynamicUpdate() { createIndex("test"); From e5b42bed5078a5c09529340d9a07341f3f11197a Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Tue, 9 May 2017 10:46:49 +0200 Subject: [PATCH 299/619] inner_hits: Reuse inner hit query weight Previously query weight was created for each search hit that needed to compute inner hits, with this change the weight of the inner hit query is computed once for all search hits. Closes #23917 --- .../fetch/subphase/InnerHitsContext.java | 223 ++++++++++++------ .../subphase/InnerHitsFetchSubPhase.java | 60 ++--- 2 files changed, 184 insertions(+), 99 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsContext.java b/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsContext.java index c1155bdfbd6bf..c8eee3e91eff3 100644 --- a/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsContext.java +++ b/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsContext.java @@ -19,19 +19,31 @@ package org.elasticsearch.search.fetch.subphase; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.ReaderUtil; import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.CollectionTerminatedException; +import org.apache.lucene.search.Collector; +import org.apache.lucene.search.ConjunctionDISI; +import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.DocValuesTermsQuery; +import org.apache.lucene.search.LeafCollector; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.ScorerSupplier; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TopDocsCollector; import org.apache.lucene.search.TopFieldCollector; import org.apache.lucene.search.TopScoreDocCollector; +import org.apache.lucene.search.TotalHitCountCollector; +import org.apache.lucene.search.Weight; import org.apache.lucene.search.join.BitSetProducer; import org.apache.lucene.search.join.ParentChildrenBlockJoinQuery; +import org.apache.lucene.util.Bits; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.index.mapper.DocumentMapper; @@ -40,11 +52,11 @@ import org.elasticsearch.index.mapper.ParentFieldMapper; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHitField; -import org.elasticsearch.search.fetch.FetchSubPhase; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SubSearchContext; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -57,7 +69,7 @@ public InnerHitsContext() { this.innerHits = new HashMap<>(); } - public InnerHitsContext(Map innerHits) { + InnerHitsContext(Map innerHits) { this.innerHits = Objects.requireNonNull(innerHits); } @@ -77,14 +89,16 @@ public void addInnerHitDefinition(BaseInnerHits innerHit) { public abstract static class BaseInnerHits extends SubSearchContext { private final String name; + final SearchContext context; private InnerHitsContext childInnerHits; - protected BaseInnerHits(String name, SearchContext context) { + BaseInnerHits(String name, SearchContext context) { super(context); this.name = name; + this.context = context; } - public abstract TopDocs topDocs(SearchContext context, FetchSubPhase.HitContext hitContext) throws IOException; + public abstract TopDocs[] topDocs(SearchHit[] hits) throws IOException; public String getName() { return name; @@ -98,6 +112,12 @@ public InnerHitsContext innerHits() { public void setChildInnerHits(Map childInnerHits) { this.childInnerHits = new InnerHitsContext(childInnerHits); } + + Weight createInnerHitQueryWeight() throws IOException { + final boolean needsScores = size() != 0 && (sort() == null || sort().sort.needsScores()); + return context.searcher().createNormalizedWeight(query(), needsScores); + } + } public static final class NestedInnerHits extends BaseInnerHits { @@ -112,35 +132,48 @@ public NestedInnerHits(String name, SearchContext context, ObjectMapper parentOb } @Override - public TopDocs topDocs(SearchContext context, FetchSubPhase.HitContext hitContext) throws IOException { - Query rawParentFilter; - if (parentObjectMapper == null) { - rawParentFilter = Queries.newNonNestedFilter(); - } else { - rawParentFilter = parentObjectMapper.nestedTypeFilter(); - } - BitSetProducer parentFilter = context.bitsetFilterCache().getBitSetProducer(rawParentFilter); - Query childFilter = childObjectMapper.nestedTypeFilter(); - int parentDocId = hitContext.readerContext().docBase + hitContext.docId(); - Query q = Queries.filtered(query(), new ParentChildrenBlockJoinQuery(parentFilter, childFilter, parentDocId)); - - if (size() == 0) { - return new TopDocs(context.searcher().count(q), Lucene.EMPTY_SCORE_DOCS, 0); - } else { - int topN = Math.min(from() + size(), context.searcher().getIndexReader().maxDoc()); - TopDocsCollector topDocsCollector; - if (sort() != null) { - topDocsCollector = TopFieldCollector.create(sort().sort, topN, true, trackScores(), trackScores()); + public TopDocs[] topDocs(SearchHit[] hits) throws IOException { + Weight innerHitQueryWeight = createInnerHitQueryWeight(); + TopDocs[] result = new TopDocs[hits.length]; + for (int i = 0; i < hits.length; i++) { + SearchHit hit = hits[i]; + Query rawParentFilter; + if (parentObjectMapper == null) { + rawParentFilter = Queries.newNonNestedFilter(); } else { - topDocsCollector = TopScoreDocCollector.create(topN); + rawParentFilter = parentObjectMapper.nestedTypeFilter(); } - try { - context.searcher().search(q, topDocsCollector); - } finally { - clearReleasables(Lifetime.COLLECTION); + + int parentDocId = hit.docId(); + final int readerIndex = ReaderUtil.subIndex(parentDocId, searcher().getIndexReader().leaves()); + // With nested inner hits the nested docs are always in the same segement, so need to use the other segments + LeafReaderContext ctx = searcher().getIndexReader().leaves().get(readerIndex); + + Query childFilter = childObjectMapper.nestedTypeFilter(); + BitSetProducer parentFilter = context.bitsetFilterCache().getBitSetProducer(rawParentFilter); + Query q = new ParentChildrenBlockJoinQuery(parentFilter, childFilter, parentDocId); + Weight weight = context.searcher().createNormalizedWeight(q, false); + if (size() == 0) { + TotalHitCountCollector totalHitCountCollector = new TotalHitCountCollector(); + intersect(weight, innerHitQueryWeight, totalHitCountCollector, ctx); + result[i] = new TopDocs(totalHitCountCollector.getTotalHits(), Lucene.EMPTY_SCORE_DOCS, 0); + } else { + int topN = Math.min(from() + size(), context.searcher().getIndexReader().maxDoc()); + TopDocsCollector topDocsCollector; + if (sort() != null) { + topDocsCollector = TopFieldCollector.create(sort().sort, topN, true, trackScores(), trackScores()); + } else { + topDocsCollector = TopScoreDocCollector.create(topN); + } + try { + intersect(weight, innerHitQueryWeight, topDocsCollector, ctx); + } finally { + clearReleasables(Lifetime.COLLECTION); + } + result[i] = topDocsCollector.topDocs(from(), size()); } - return topDocsCollector.topDocs(from(), size()); } + return result; } } @@ -156,53 +189,65 @@ public ParentChildInnerHits(String name, SearchContext context, MapperService ma } @Override - public TopDocs topDocs(SearchContext context, FetchSubPhase.HitContext hitContext) throws IOException { - final Query hitQuery; - if (isParentHit(hitContext.hit())) { - String field = ParentFieldMapper.joinField(hitContext.hit().getType()); - hitQuery = new DocValuesTermsQuery(field, hitContext.hit().getId()); - } else if (isChildHit(hitContext.hit())) { - DocumentMapper hitDocumentMapper = mapperService.documentMapper(hitContext.hit().getType()); - final String parentType = hitDocumentMapper.parentFieldMapper().type(); - SearchHitField parentField = hitContext.hit().field(ParentFieldMapper.NAME); - if (parentField == null) { - throw new IllegalStateException("All children must have a _parent"); - } - Term uidTerm = context.mapperService().createUidTerm(parentType, parentField.getValue()); - if (uidTerm == null) { - hitQuery = new MatchNoDocsQuery("Missing type: " + parentType); + public TopDocs[] topDocs(SearchHit[] hits) throws IOException { + Weight innerHitQueryWeight = createInnerHitQueryWeight(); + TopDocs[] result = new TopDocs[hits.length]; + for (int i = 0; i < hits.length; i++) { + SearchHit hit = hits[i]; + final Query hitQuery; + if (isParentHit(hit)) { + String field = ParentFieldMapper.joinField(hit.getType()); + hitQuery = new DocValuesTermsQuery(field, hit.getId()); + } else if (isChildHit(hit)) { + DocumentMapper hitDocumentMapper = mapperService.documentMapper(hit.getType()); + final String parentType = hitDocumentMapper.parentFieldMapper().type(); + SearchHitField parentField = hit.field(ParentFieldMapper.NAME); + if (parentField == null) { + throw new IllegalStateException("All children must have a _parent"); + } + Term uidTerm = context.mapperService().createUidTerm(parentType, parentField.getValue()); + if (uidTerm == null) { + hitQuery = new MatchNoDocsQuery("Missing type: " + parentType); + } else { + hitQuery = new TermQuery(uidTerm); + } } else { - hitQuery = new TermQuery(uidTerm); + result[i] = Lucene.EMPTY_TOP_DOCS; + continue; } - } else { - return Lucene.EMPTY_TOP_DOCS; - } - BooleanQuery q = new BooleanQuery.Builder() - .add(query(), Occur.MUST) - // Only include docs that have the current hit as parent - .add(hitQuery, Occur.FILTER) - // Only include docs that have this inner hits type - .add(documentMapper.typeFilter(context.getQueryShardContext()), Occur.FILTER) - .build(); - if (size() == 0) { - final int count = context.searcher().count(q); - return new TopDocs(count, Lucene.EMPTY_SCORE_DOCS, 0); - } else { - int topN = Math.min(from() + size(), context.searcher().getIndexReader().maxDoc()); - TopDocsCollector topDocsCollector; - if (sort() != null) { - topDocsCollector = TopFieldCollector.create(sort().sort, topN, true, trackScores(), trackScores()); + BooleanQuery q = new BooleanQuery.Builder() + // Only include docs that have the current hit as parent + .add(hitQuery, Occur.FILTER) + // Only include docs that have this inner hits type + .add(documentMapper.typeFilter(context.getQueryShardContext()), Occur.FILTER) + .build(); + Weight weight = context.searcher().createNormalizedWeight(q, false); + if (size() == 0) { + TotalHitCountCollector totalHitCountCollector = new TotalHitCountCollector(); + for (LeafReaderContext ctx : context.searcher().getIndexReader().leaves()) { + intersect(weight, innerHitQueryWeight, totalHitCountCollector, ctx); + } + result[i] = new TopDocs(totalHitCountCollector.getTotalHits(), Lucene.EMPTY_SCORE_DOCS, 0); } else { - topDocsCollector = TopScoreDocCollector.create(topN); + int topN = Math.min(from() + size(), context.searcher().getIndexReader().maxDoc()); + TopDocsCollector topDocsCollector; + if (sort() != null) { + topDocsCollector = TopFieldCollector.create(sort().sort, topN, true, trackScores(), trackScores()); + } else { + topDocsCollector = TopScoreDocCollector.create(topN); + } + try { + for (LeafReaderContext ctx : context.searcher().getIndexReader().leaves()) { + intersect(weight, innerHitQueryWeight, topDocsCollector, ctx); + } + } finally { + clearReleasables(Lifetime.COLLECTION); + } + result[i] = topDocsCollector.topDocs(from(), size()); } - try { - context.searcher().search(q, topDocsCollector); - } finally { - clearReleasables(Lifetime.COLLECTION); - } - return topDocsCollector.topDocs(from(), size()); } + return result; } private boolean isParentHit(SearchHit hit) { @@ -214,4 +259,42 @@ private boolean isChildHit(SearchHit hit) { return documentMapper.type().equals(hitDocumentMapper.parentFieldMapper().type()); } } + + static void intersect(Weight weight, Weight innerHitQueryWeight, Collector collector, LeafReaderContext ctx) throws IOException { + ScorerSupplier scorerSupplier = weight.scorerSupplier(ctx); + if (scorerSupplier == null) { + return; + } + // use random access since this scorer will be consumed on a minority of documents + Scorer scorer = scorerSupplier.get(true); + + ScorerSupplier innerHitQueryScorerSupplier = innerHitQueryWeight.scorerSupplier(ctx); + if (innerHitQueryScorerSupplier == null) { + return; + } + // use random access since this scorer will be consumed on a minority of documents + Scorer innerHitQueryScorer = innerHitQueryScorerSupplier.get(true); + + final LeafCollector leafCollector; + try { + leafCollector = collector.getLeafCollector(ctx); + // Just setting the innerHitQueryScorer is ok, because that is the actual scoring part of the query + leafCollector.setScorer(innerHitQueryScorer); + } catch (CollectionTerminatedException e) { + return; + } + + try { + Bits acceptDocs = ctx.reader().getLiveDocs(); + DocIdSetIterator iterator = ConjunctionDISI.intersectIterators(Arrays.asList(innerHitQueryScorer.iterator(), + scorer.iterator())); + for (int docId = iterator.nextDoc(); docId < DocIdSetIterator.NO_MORE_DOCS; docId = iterator.nextDoc()) { + if (acceptDocs == null || acceptDocs.get(docId)) { + leafCollector.collect(docId); + } + } + } catch (CollectionTerminatedException e) { + // ignore and continue + } + } } diff --git a/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsFetchSubPhase.java b/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsFetchSubPhase.java index 48294bd82c549..90e1b5cf8283f 100644 --- a/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsFetchSubPhase.java +++ b/core/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsFetchSubPhase.java @@ -22,12 +22,11 @@ import org.apache.lucene.search.FieldDoc; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; -import org.elasticsearch.ExceptionsHelper; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.fetch.FetchPhase; import org.elasticsearch.search.fetch.FetchSearchResult; import org.elasticsearch.search.fetch.FetchSubPhase; -import org.elasticsearch.search.SearchHit; -import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; @@ -43,39 +42,42 @@ public InnerHitsFetchSubPhase(FetchPhase fetchPhase) { } @Override - public void hitExecute(SearchContext context, HitContext hitContext) { + public void hitsExecute(SearchContext context, SearchHit[] hits) throws IOException { if ((context.innerHits() != null && context.innerHits().getInnerHits().size() > 0) == false) { return; } - Map results = new HashMap<>(); + for (Map.Entry entry : context.innerHits().getInnerHits().entrySet()) { InnerHitsContext.BaseInnerHits innerHits = entry.getValue(); - TopDocs topDocs; - try { - topDocs = innerHits.topDocs(context, hitContext); - } catch (IOException e) { - throw ExceptionsHelper.convertToElastic(e); - } - innerHits.queryResult().topDocs(topDocs, innerHits.sort() == null ? null : innerHits.sort().formats); - int[] docIdsToLoad = new int[topDocs.scoreDocs.length]; - for (int i = 0; i < topDocs.scoreDocs.length; i++) { - docIdsToLoad[i] = topDocs.scoreDocs[i].doc; - } - innerHits.docIdsToLoad(docIdsToLoad, 0, docIdsToLoad.length); - fetchPhase.execute(innerHits); - FetchSearchResult fetchResult = innerHits.fetchResult(); - SearchHit[] internalHits = fetchResult.fetchResult().hits().internalHits(); - for (int i = 0; i < internalHits.length; i++) { - ScoreDoc scoreDoc = topDocs.scoreDocs[i]; - SearchHit searchHitFields = internalHits[i]; - searchHitFields.score(scoreDoc.score); - if (scoreDoc instanceof FieldDoc) { - FieldDoc fieldDoc = (FieldDoc) scoreDoc; - searchHitFields.sortValues(fieldDoc.fields, innerHits.sort().formats); + TopDocs[] topDocs = innerHits.topDocs(hits); + for (int i = 0; i < hits.length; i++) { + SearchHit hit = hits[i]; + TopDocs topDoc = topDocs[i]; + + Map results = hit.getInnerHits(); + if (results == null) { + hit.setInnerHits(results = new HashMap<>()); + } + innerHits.queryResult().topDocs(topDoc, innerHits.sort() == null ? null : innerHits.sort().formats); + int[] docIdsToLoad = new int[topDoc.scoreDocs.length]; + for (int j = 0; j < topDoc.scoreDocs.length; j++) { + docIdsToLoad[j] = topDoc.scoreDocs[j].doc; + } + innerHits.docIdsToLoad(docIdsToLoad, 0, docIdsToLoad.length); + fetchPhase.execute(innerHits); + FetchSearchResult fetchResult = innerHits.fetchResult(); + SearchHit[] internalHits = fetchResult.fetchResult().hits().internalHits(); + for (int j = 0; j < internalHits.length; j++) { + ScoreDoc scoreDoc = topDoc.scoreDocs[j]; + SearchHit searchHitFields = internalHits[j]; + searchHitFields.score(scoreDoc.score); + if (scoreDoc instanceof FieldDoc) { + FieldDoc fieldDoc = (FieldDoc) scoreDoc; + searchHitFields.sortValues(fieldDoc.fields, innerHits.sort().formats); + } } + results.put(entry.getKey(), fetchResult.hits()); } - results.put(entry.getKey(), fetchResult.hits()); } - hitContext.hit().setInnerHits(results); } } From c4fc8edc032b564b3f8379b475888ed3d38b6881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Thu, 11 May 2017 11:50:35 +0200 Subject: [PATCH 300/619] Add parsing for single bucket aggregations (#24564) This adds parsing to all implementations of SingleBucketAggregations. They are mostly similar, so they share the common base class `ParsedSingleBucketAggregation` and the shared base test `InternalSingleBucketAggregationTestCase`. --- .../aggregations/ParsedAggregation.java | 2 +- .../bucket/ParsedSingleBucketAggregation.java | 93 +++++++++++++++++++ .../bucket/children/ParsedChildren.java | 36 +++++++ .../bucket/filter/ParsedFilter.java | 36 +++++++ .../bucket/global/ParsedGlobal.java | 36 +++++++ .../bucket/missing/ParsedMissing.java | 36 +++++++ .../bucket/nested/ParsedNested.java | 36 +++++++ .../bucket/nested/ParsedReverseNested.java | 36 +++++++ .../bucket/sampler/InternalSampler.java | 2 +- .../bucket/sampler/ParsedSampler.java | 36 +++++++ .../aggregations/AggregationsTests.java | 14 +++ ...ternalSingleBucketAggregationTestCase.java | 51 +++++++++- .../children/InternalChildrenTests.java | 5 + .../bucket/filter/InternalFilterTests.java | 6 ++ .../bucket/global/InternalGlobalTests.java | 5 + .../bucket/missing/InternalMissingTests.java | 5 + .../bucket/nested/InternalNestedTests.java | 6 ++ .../nested/InternalReverseNestedTests.java | 6 ++ .../bucket/sampler/InternalSamplerTests.java | 6 ++ .../test/InternalAggregationTestCase.java | 23 ++++- 20 files changed, 471 insertions(+), 5 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/ParsedSingleBucketAggregation.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ParsedChildren.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/ParsedFilter.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/global/ParsedGlobal.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/missing/ParsedMissing.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ParsedNested.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ParsedReverseNested.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/ParsedSampler.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java index 3ff91d8d36d70..97f27043124ae 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java @@ -41,7 +41,7 @@ protected static void declareAggregationFields(ObjectParser metadata; + protected Map metadata; @Override public final String getName() { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/ParsedSingleBucketAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/ParsedSingleBucketAggregation.java new file mode 100644 index 0000000000000..99d9bfa1955ad --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/ParsedSingleBucketAggregation.java @@ -0,0 +1,93 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations.bucket; + +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParserUtils; +import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.aggregations.Aggregations; +import org.elasticsearch.search.aggregations.ParsedAggregation; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; + +/** + * A base class for all the single bucket aggregations. + */ +public abstract class ParsedSingleBucketAggregation extends ParsedAggregation implements SingleBucketAggregation { + + private long docCount; + protected Aggregations aggregations = new Aggregations(Collections.emptyList()); + + @Override + public long getDocCount() { + return docCount; + } + + protected void setDocCount(long docCount) { + this.docCount = docCount; + } + + @Override + public Aggregations getAggregations() { + return aggregations; + } + + @Override + public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + builder.field(CommonFields.DOC_COUNT.getPreferredName(), docCount); + aggregations.toXContentInternal(builder, params); + return builder; + } + + protected static T parseXContent(final XContentParser parser, T aggregation, String name) + throws IOException { + aggregation.setName(name); + XContentParser.Token token = parser.currentToken(); + String currentFieldName = parser.currentName(); + if (token == XContentParser.Token.FIELD_NAME) { + token = parser.nextToken(); + } + ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation); + + List aggregations = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (CommonFields.DOC_COUNT.getPreferredName().equals(currentFieldName)) { + aggregation.setDocCount(parser.longValue()); + } + } else if (token == XContentParser.Token.START_OBJECT) { + if (CommonFields.META.getPreferredName().equals(currentFieldName)) { + aggregation.metadata = parser.map(); + } else { + aggregations.add(XContentParserUtils.parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Aggregation.class)); + } + } + } + aggregation.aggregations = new Aggregations(aggregations); + return aggregation; + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ParsedChildren.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ParsedChildren.java new file mode 100644 index 0000000000000..9ce6661923e4b --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ParsedChildren.java @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations.bucket.children; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; + +import java.io.IOException; + +public class ParsedChildren extends ParsedSingleBucketAggregation implements Children { + + @Override + public String getType() { + return ChildrenAggregationBuilder.NAME; + } + + public static ParsedChildren fromXContent(XContentParser parser, final String name) throws IOException { + return parseXContent(parser, new ParsedChildren(), name); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/ParsedFilter.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/ParsedFilter.java new file mode 100644 index 0000000000000..5f5cf104498e8 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/ParsedFilter.java @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations.bucket.filter; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; + +import java.io.IOException; + +public class ParsedFilter extends ParsedSingleBucketAggregation implements Filter { + + @Override + public String getType() { + return FilterAggregationBuilder.NAME; + } + + public static ParsedFilter fromXContent(XContentParser parser, final String name) throws IOException { + return parseXContent(parser, new ParsedFilter(), name); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/global/ParsedGlobal.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/global/ParsedGlobal.java new file mode 100644 index 0000000000000..062752805b1c8 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/global/ParsedGlobal.java @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations.bucket.global; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; + +import java.io.IOException; + +public class ParsedGlobal extends ParsedSingleBucketAggregation implements Global { + + @Override + public String getType() { + return GlobalAggregationBuilder.NAME; + } + + public static ParsedGlobal fromXContent(XContentParser parser, final String name) throws IOException { + return parseXContent(parser, new ParsedGlobal(), name); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/missing/ParsedMissing.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/missing/ParsedMissing.java new file mode 100644 index 0000000000000..2897372df8954 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/missing/ParsedMissing.java @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations.bucket.missing; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; + +import java.io.IOException; + +public class ParsedMissing extends ParsedSingleBucketAggregation implements Missing { + + @Override + public String getType() { + return MissingAggregationBuilder.NAME; + } + + public static ParsedMissing fromXContent(XContentParser parser, final String name) throws IOException { + return parseXContent(parser, new ParsedMissing(), name); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ParsedNested.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ParsedNested.java new file mode 100644 index 0000000000000..f241675678cfe --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ParsedNested.java @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations.bucket.nested; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; + +import java.io.IOException; + +public class ParsedNested extends ParsedSingleBucketAggregation implements Nested { + + @Override + public String getType() { + return NestedAggregationBuilder.NAME; + } + + public static ParsedNested fromXContent(XContentParser parser, final String name) throws IOException { + return parseXContent(parser, new ParsedNested(), name); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ParsedReverseNested.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ParsedReverseNested.java new file mode 100644 index 0000000000000..dec15c3eded10 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ParsedReverseNested.java @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations.bucket.nested; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; + +import java.io.IOException; + +public class ParsedReverseNested extends ParsedSingleBucketAggregation implements Nested { + + @Override + public String getType() { + return ReverseNestedAggregationBuilder.NAME; + } + + public static ParsedReverseNested fromXContent(XContentParser parser, final String name) throws IOException { + return parseXContent(parser, new ParsedReverseNested(), name); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSampler.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSampler.java index 3b5b42d59fdd5..5d7e19ccad511 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSampler.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSampler.java @@ -49,7 +49,7 @@ public String getWriteableName() { @Override public String getType() { - return "sampler"; + return NAME; } @Override diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/ParsedSampler.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/ParsedSampler.java new file mode 100644 index 0000000000000..5e1c4d77b79cc --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/ParsedSampler.java @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations.bucket.sampler; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; + +import java.io.IOException; + +public class ParsedSampler extends ParsedSingleBucketAggregation implements Sampler { + + @Override + public String getType() { + return InternalSampler.NAME; + } + + public static ParsedSampler fromXContent(XContentParser parser, final String name) throws IOException { + return parseXContent(parser, new ParsedSampler(), name); + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java index 78df82c550319..a338c8ec9ac54 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java @@ -27,8 +27,15 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.bucket.children.InternalChildrenTests; +import org.elasticsearch.search.aggregations.bucket.filter.InternalFilterTests; +import org.elasticsearch.search.aggregations.bucket.global.InternalGlobalTests; import org.elasticsearch.search.aggregations.bucket.histogram.InternalDateHistogramTests; import org.elasticsearch.search.aggregations.bucket.histogram.InternalHistogramTests; +import org.elasticsearch.search.aggregations.bucket.missing.InternalMissingTests; +import org.elasticsearch.search.aggregations.bucket.nested.InternalNestedTests; +import org.elasticsearch.search.aggregations.bucket.nested.InternalReverseNestedTests; +import org.elasticsearch.search.aggregations.bucket.sampler.InternalSamplerTests; import org.elasticsearch.search.aggregations.bucket.terms.DoubleTermsTests; import org.elasticsearch.search.aggregations.bucket.terms.LongTermsTests; import org.elasticsearch.search.aggregations.bucket.terms.StringTermsTests; @@ -103,6 +110,13 @@ private static List getAggsTests() { aggsTests.add(new LongTermsTests()); aggsTests.add(new DoubleTermsTests()); aggsTests.add(new StringTermsTests()); + aggsTests.add(new InternalMissingTests()); + aggsTests.add(new InternalNestedTests()); + aggsTests.add(new InternalReverseNestedTests()); + aggsTests.add(new InternalChildrenTests()); + aggsTests.add(new InternalGlobalTests()); + aggsTests.add(new InternalFilterTests()); + aggsTests.add(new InternalSamplerTests()); return Collections.unmodifiableList(aggsTests); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java index f84a364ab1da4..cae34768ecd2d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java @@ -19,32 +19,45 @@ package org.elasticsearch.search.aggregations.bucket; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.rest.action.search.RestSearchAction; +import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.metrics.max.InternalMax; import org.elasticsearch.search.aggregations.metrics.min.InternalMin; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.test.InternalAggregationTestCase; +import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Supplier; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.elasticsearch.common.xcontent.XContentHelper.toXContent; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent; public abstract class InternalSingleBucketAggregationTestCase extends InternalAggregationTestCase { - private final boolean hasInternalMax = randomBoolean(); - private final boolean hasInternalMin = randomBoolean(); + private boolean hasInternalMax; + private boolean hasInternalMin; public Supplier subAggregationsSupplier; @Override public void setUp() throws Exception { super.setUp(); + hasInternalMax = randomBoolean(); + hasInternalMin = randomBoolean(); subAggregationsSupplier = () -> { List aggs = new ArrayList<>(); if (hasInternalMax) { @@ -89,4 +102,38 @@ protected final void assertReduced(T reduced, List inputs) { } extraAssertReduced(reduced, inputs); } + + @Override + protected void assertFromXContent(T aggregation, ParsedAggregation parsedAggregation) throws IOException { + assertTrue(parsedAggregation instanceof ParsedSingleBucketAggregation); + ParsedSingleBucketAggregation parsed = (ParsedSingleBucketAggregation) parsedAggregation; + + assertEquals(aggregation.getDocCount(), parsed.getDocCount()); + InternalAggregations aggregations = aggregation.getAggregations(); + Map expectedAggregations = new HashMap<>(); + int expectedNumberOfAggregations = 0; + for (Aggregation expectedAggregation : aggregations) { + // since we shuffle xContent, we cannot rely on the order of the original inner aggregations for comparison + assertTrue(expectedAggregation instanceof InternalAggregation); + expectedAggregations.put(expectedAggregation.getName(), expectedAggregation); + expectedNumberOfAggregations++; + } + int parsedNumberOfAggregations = 0; + for (Aggregation parsedAgg : parsed.getAggregations()) { + assertTrue(parsedAgg instanceof ParsedAggregation); + assertTrue(expectedAggregations.keySet().contains(parsedAgg.getName())); + Aggregation expectedInternalAggregation = expectedAggregations.get(parsedAgg.getName()); + final XContentType xContentType = randomFrom(XContentType.values()); + final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); + BytesReference expectedBytes = toXContent(expectedInternalAggregation, xContentType, params, false); + BytesReference actualBytes = toXContent(parsedAgg, xContentType, params, false); + assertToXContentEquivalent(expectedBytes, actualBytes, xContentType); + parsedNumberOfAggregations++; + } + assertEquals(expectedNumberOfAggregations, parsedNumberOfAggregations); + Class parsedClass = implementationClass(); + assertTrue(parsedClass != null && parsedClass.isInstance(parsedAggregation)); + } + + protected abstract Class implementationClass(); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/InternalChildrenTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/InternalChildrenTests.java index b248d5ed98117..285837c6e47ae 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/InternalChildrenTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/InternalChildrenTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -44,4 +45,8 @@ protected Reader instanceReader() { return InternalChildren::new; } + @Override + protected Class implementationClass() { + return ParsedChildren.class; + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filter/InternalFilterTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filter/InternalFilterTests.java index 3e74b9c21877e..8f888e13afe48 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filter/InternalFilterTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filter/InternalFilterTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -43,4 +44,9 @@ protected void extraAssertReduced(InternalFilter reduced, List i protected Reader instanceReader() { return InternalFilter::new; } + + @Override + protected Class implementationClass() { + return ParsedFilter.class; + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/global/InternalGlobalTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/global/InternalGlobalTests.java index 9092c3e028079..392f88b4d4e62 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/global/InternalGlobalTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/global/InternalGlobalTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -44,4 +45,8 @@ protected Reader instanceReader() { return InternalGlobal::new; } + @Override + protected Class implementationClass() { + return ParsedGlobal.class; + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/missing/InternalMissingTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/missing/InternalMissingTests.java index f3e151721bf30..75a28e87cefe7 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/missing/InternalMissingTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/missing/InternalMissingTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -44,4 +45,8 @@ protected Reader instanceReader() { return InternalMissing::new; } + @Override + protected Class implementationClass() { + return ParsedMissing.class; + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalNestedTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalNestedTests.java index 7b410723666d4..f6299ebf7bb25 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalNestedTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalNestedTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -43,4 +44,9 @@ protected void extraAssertReduced(InternalNested reduced, List i protected Reader instanceReader() { return InternalNested::new; } + + @Override + protected Class implementationClass() { + return ParsedNested.class; + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalReverseNestedTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalReverseNestedTests.java index f918024733e3a..08940fcd3aefe 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalReverseNestedTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalReverseNestedTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -43,4 +44,9 @@ protected void extraAssertReduced(InternalReverseNested reduced, List instanceReader() { return InternalReverseNested::new; } + + @Override + protected Class implementationClass() { + return ParsedReverseNested.class; + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSamplerTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSamplerTests.java index 1c4fb6d2a65f7..06319080923c2 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSamplerTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSamplerTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -42,4 +43,9 @@ protected void extraAssertReduced(InternalSampler reduced, List protected Writeable.Reader instanceReader() { return InternalSampler::new; } + + @Override + protected Class implementationClass() { + return ParsedSampler.class; + } } \ No newline at end of file diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java index 64b09a0245ff5..99cf71f65e4e3 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java @@ -38,10 +38,24 @@ import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.ParsedAggregation; +import org.elasticsearch.search.aggregations.bucket.children.ChildrenAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.children.ParsedChildren; +import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter; +import org.elasticsearch.search.aggregations.bucket.global.GlobalAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.global.ParsedGlobal; import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram; import org.elasticsearch.search.aggregations.bucket.histogram.ParsedHistogram; +import org.elasticsearch.search.aggregations.bucket.missing.MissingAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.missing.ParsedMissing; +import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested; +import org.elasticsearch.search.aggregations.bucket.nested.ParsedReverseNested; +import org.elasticsearch.search.aggregations.bucket.nested.ReverseNestedAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.sampler.InternalSampler; +import org.elasticsearch.search.aggregations.bucket.sampler.ParsedSampler; import org.elasticsearch.search.aggregations.bucket.terms.DoubleTerms; import org.elasticsearch.search.aggregations.bucket.terms.LongTerms; import org.elasticsearch.search.aggregations.bucket.terms.ParsedDoubleTerms; @@ -139,6 +153,13 @@ public static List getNamedXContents() { namedXContents.put(StringTerms.NAME, (p, c) -> ParsedStringTerms.fromXContent(p, (String) c)); namedXContents.put(LongTerms.NAME, (p, c) -> ParsedLongTerms.fromXContent(p, (String) c)); namedXContents.put(DoubleTerms.NAME, (p, c) -> ParsedDoubleTerms.fromXContent(p, (String) c)); + namedXContents.put(MissingAggregationBuilder.NAME, (p, c) -> ParsedMissing.fromXContent(p, (String) c)); + namedXContents.put(NestedAggregationBuilder.NAME, (p, c) -> ParsedNested.fromXContent(p, (String) c)); + namedXContents.put(ReverseNestedAggregationBuilder.NAME, (p, c) -> ParsedReverseNested.fromXContent(p, (String) c)); + namedXContents.put(ChildrenAggregationBuilder.NAME, (p, c) -> ParsedChildren.fromXContent(p, (String) c)); + namedXContents.put(GlobalAggregationBuilder.NAME, (p, c) -> ParsedGlobal.fromXContent(p, (String) c)); + namedXContents.put(FilterAggregationBuilder.NAME, (p, c) -> ParsedFilter.fromXContent(p, (String) c)); + namedXContents.put(InternalSampler.NAME, (p, c) -> ParsedSampler.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) @@ -248,7 +269,7 @@ public final void testFromXContent() throws IOException { } //norelease TODO make abstract - protected void assertFromXContent(T aggregation, ParsedAggregation parsedAggregation) { + protected void assertFromXContent(T aggregation, ParsedAggregation parsedAggregation) throws IOException { } @SuppressWarnings("unchecked") From b6cb9a4e8a1f644127b5b30acb0c064932a28d8e Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Thu, 11 May 2017 07:23:16 -0400 Subject: [PATCH 301/619] Fix checkpoints advance test This commit fixes an issue in the checkpoints advance test. Namely, when there zero documents indexed, after the global checkpoint is synced, the global checkpoint will have advanced to the no ops performed. There is a larger conceptual problem here, namely that the primary does not update its knowledge of its own local checkpoint upon recovery which causes the global checkpoint to initially be unassigned and then advance to no ops performed, but this will be addressed in a follow-up. --- .../index/replication/IndexLevelReplicationTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java b/core/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java index c08e3976bde1a..c13177a62505d 100644 --- a/core/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java +++ b/core/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java @@ -158,7 +158,7 @@ public void testCheckpointsAdvance() throws Exception { final ShardRouting shardRouting = shard.routingEntry(); assertThat(shardRouting + " local checkpoint mismatch", shardStats.getLocalCheckpoint(), equalTo(numDocs - 1L)); /* - * After the last indexing operation completes, the primary will advance its global checkpoint. Without an other indexing + * After the last indexing operation completes, the primary will advance its global checkpoint. Without another indexing * operation, or a background sync, the primary will not have broadcast this global checkpoint to its replicas. However, a * shard could have recovered from the primary in which case its global checkpoint will be in-sync with the primary. * Therefore, we can only assert that the global checkpoint is number of docs minus one (matching the primary, in case of a @@ -178,6 +178,7 @@ public void testCheckpointsAdvance() throws Exception { // simulate a background global checkpoint sync at which point we expect the global checkpoint to advance on the replicas shards.syncGlobalCheckpoint(); + final long noOpsPerformed = SequenceNumbersService.NO_OPS_PERFORMED; for (IndexShard shard : shards) { final SeqNoStats shardStats = shard.seqNoStats(); final ShardRouting shardRouting = shard.routingEntry(); @@ -185,7 +186,7 @@ public void testCheckpointsAdvance() throws Exception { assertThat( shardRouting + " global checkpoint mismatch", shardStats.getGlobalCheckpoint(), - numDocs == 0 ? equalTo(unassignedSeqNo) : equalTo(numDocs - 1L)); + numDocs == 0 ? equalTo(noOpsPerformed) : equalTo(numDocs - 1L)); assertThat(shardRouting + " max seq no mismatch", shardStats.getMaxSeqNo(), equalTo(numDocs - 1L)); } } From e49ebd04fd2522b4695a023fe2adc8de8b05c10a Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 10 May 2017 23:30:01 -0400 Subject: [PATCH 302/619] Get more information when reindex test fails It looks like it leaks contexts but it isn't clear why so this adds a little more logging. This is the failure: https://elasticsearch-ci.elastic.co/job/elastic+elasticsearch+5.4+multijob-intake/94/console --- .../org/elasticsearch/index/reindex/ReindexFailureTests.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java index 47b79a499b132..96ed13f5b29e6 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.action.bulk.BulkItemResponse.Failure; import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.test.junit.annotations.TestLogging; import java.util.ArrayList; import java.util.List; @@ -38,6 +39,7 @@ /** * Tests failure capturing and abort-on-failure behavior of reindex. */ +@TestLogging("_root:DEBUG") public class ReindexFailureTests extends ReindexTestCase { public void testFailuresCauseAbortDefault() throws Exception { /* @@ -107,7 +109,7 @@ public void testResponseOnSearchFailure() throws Exception { logger.info("Didn't trigger a reindex failure on the {} attempt", attempt); attempt++; } catch (ExecutionException e) { - logger.info("Triggered a reindex failure on the {} attempt", attempt); + logger.info("Triggered a reindex failure on the {} attempt: {}", attempt, e.getMessage()); assertThat(e.getMessage(), either(containsString("all shards failed")) .or(containsString("No search context found")) From 840da4aebf3eba2df856aea75ae2a93ff371796c Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Wed, 10 May 2017 12:18:19 +0200 Subject: [PATCH 303/619] Removed deprecated template query. Relates to #19390 --- .../migration/migrate_6_0/search.asciidoc | 2 + .../query-dsl/special-queries.asciidoc | 8 - .../query-dsl/template-query.asciidoc | 127 ---------- .../search/suggesters/phrase-suggest.asciidoc | 4 +- .../script/mustache/MustachePlugin.java | 7 - .../script/mustache/TemplateQueryBuilder.java | 151 ------------ .../script/mustache/SearchTemplateIT.java | 10 - .../mustache/TemplateQueryBuilderTests.java | 229 ------------------ .../test/lang_mustache/40_template_query.yaml | 96 -------- 9 files changed, 4 insertions(+), 630 deletions(-) delete mode 100644 docs/reference/query-dsl/template-query.asciidoc delete mode 100644 modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TemplateQueryBuilder.java delete mode 100644 modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/TemplateQueryBuilderTests.java delete mode 100644 modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/40_template_query.yaml diff --git a/docs/reference/migration/migrate_6_0/search.asciidoc b/docs/reference/migration/migrate_6_0/search.asciidoc index 80d67eae72d47..82c2ba8f71793 100644 --- a/docs/reference/migration/migrate_6_0/search.asciidoc +++ b/docs/reference/migration/migrate_6_0/search.asciidoc @@ -51,6 +51,8 @@ * The `disable_coord` parameter of the `bool` and `common_terms` queries has been removed. If provided, it will be ignored and issue a deprecation warning. +* The `template` query has been removed. This query was deprecated since 5.0 + ==== Search shards API The search shards API no longer accepts the `type` url parameter, which didn't diff --git a/docs/reference/query-dsl/special-queries.asciidoc b/docs/reference/query-dsl/special-queries.asciidoc index b705f01c6fce1..3e3c140d6f582 100644 --- a/docs/reference/query-dsl/special-queries.asciidoc +++ b/docs/reference/query-dsl/special-queries.asciidoc @@ -9,12 +9,6 @@ This group contains queries which do not fit into the other groups: This query finds documents which are similar to the specified text, document, or collection of documents. -<>:: - -The `template` query accepts a Mustache template (either inline, indexed, or -from a file), and a map of parameters, and combines the two to generate the -final query to execute. - <>:: This query allows a script to act as a filter. Also see the @@ -27,8 +21,6 @@ the specified document. include::mlt-query.asciidoc[] -include::template-query.asciidoc[] - include::script-query.asciidoc[] include::percolate-query.asciidoc[] diff --git a/docs/reference/query-dsl/template-query.asciidoc b/docs/reference/query-dsl/template-query.asciidoc deleted file mode 100644 index 2d3b5724d49ef..0000000000000 --- a/docs/reference/query-dsl/template-query.asciidoc +++ /dev/null @@ -1,127 +0,0 @@ -[[query-dsl-template-query]] -=== Template Query - -deprecated[5.0.0, Use the <> API] - -A query that accepts a query template and a map of key/value pairs to fill in -template parameters. Templating is based on Mustache. For simple token substitution all you provide -is a query containing some variable that you want to substitute and the actual -values: - -[source,js] ------------------------------------------- -GET /_search -{ - "query": { - "template": { - "inline": { "match": { "text": "{{query_string}}" }}, - "params" : { - "query_string" : "all about search" - } - } - } -} ------------------------------------------- -// CONSOLE -// TEST[warning:[template] query is deprecated, use search template api instead] - -The above request is translated into: - -[source,js] ------------------------------------------- -GET /_search -{ - "query": { - "match": { - "text": "all about search" - } - } -} ------------------------------------------- -// CONSOLE - -Alternatively passing the template as an escaped string works as well: - -[source,js] ------------------------------------------- -GET /_search -{ - "query": { - "template": { - "inline": "{ \"match\": { \"text\": \"{{query_string}}\" }}", <1> - "params" : { - "query_string" : "all about search" - } - } - } -} ------------------------------------------- -// CONSOLE -// TEST[warning:[template] query is deprecated, use search template api instead] - -<1> New line characters (`\n`) should be escaped as `\\n` or removed, - and quotes (`"`) should be escaped as `\\"`. - -==== Stored templates - -You can register a template by storing it in the `config/scripts` directory, in a file using the `.mustache` extension. -In order to execute the stored template, reference it by name in the `file` -parameter: - - -[source,js] ------------------------------------------- -GET /_search -{ - "query": { - "template": { - "file": "my_template", <1> - "params" : { - "query_string" : "all about search" - } - } - } -} ------------------------------------------- -// CONSOLE -// TEST[warning:[template] query is deprecated, use search template api instead] - -<1> Name of the query template in `config/scripts/`, i.e., `my_template.mustache`. - -Alternatively, you can register a query template in the cluster state with: - -[source,js] ------------------------------------------- -PUT /_search/template/my_template -{ - "template": { "match": { "text": "{{query_string}}" }} -} ------------------------------------------- -// CONSOLE - -and refer to it in the `template` query with the `id` parameter: - - -[source,js] ------------------------------------------- -GET /_search -{ - "query": { - "template": { - "stored": "my_template", <1> - "params" : { - "query_string" : "all about search" - } - } - } -} ------------------------------------------- -// CONSOLE -// TEST[continued] -// TEST[warning:[template] query is deprecated, use search template api instead] - -<1> Name of the query template in `config/scripts/`, i.e., `my_template.mustache`. - - -There is also a dedicated `template` endpoint, allows you to template an entire search request. -Please see <> for more details. diff --git a/docs/reference/search/suggesters/phrase-suggest.asciidoc b/docs/reference/search/suggesters/phrase-suggest.asciidoc index f4d4f46e3a80e..53c0c9be4e467 100644 --- a/docs/reference/search/suggesters/phrase-suggest.asciidoc +++ b/docs/reference/search/suggesters/phrase-suggest.asciidoc @@ -213,8 +213,8 @@ The response contains suggestions scored by the most likely spell correction fir Checks each suggestion against the specified `query` to prune suggestions for which no matching docs exist in the index. The collate query for a suggestion is run only on the local shard from which the suggestion has - been generated from. The `query` must be specified, and it is run as - a <>. + been generated from. The `query` must be specified and it can be templated, + see <> for more information. The current suggestion is automatically made available as the `{{suggestion}}` variable, which should be used in your query. You can still specify your own template `params` -- the `suggestion` value will be added to the diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustachePlugin.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustachePlugin.java index 7b5c97aa9a793..105fa3f6f803b 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustachePlugin.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustachePlugin.java @@ -39,8 +39,6 @@ import java.util.List; import java.util.function.Supplier; -import static java.util.Collections.singletonList; - public class MustachePlugin extends Plugin implements ScriptPlugin, ActionPlugin, SearchPlugin { @Override @@ -54,11 +52,6 @@ public ScriptEngine getScriptEngine(Settings settings) { new ActionHandler<>(MultiSearchTemplateAction.INSTANCE, TransportMultiSearchTemplateAction.class)); } - @Override - public List> getQueries() { - return singletonList(new QuerySpec<>(TemplateQueryBuilder.NAME, TemplateQueryBuilder::new, TemplateQueryBuilder::fromXContent)); - } - @Override public List getRestHandlers(Settings settings, RestController restController, ClusterSettings clusterSettings, IndexScopedSettings indexScopedSettings, SettingsFilter settingsFilter, IndexNameExpressionResolver indexNameExpressionResolver, diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TemplateQueryBuilder.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TemplateQueryBuilder.java deleted file mode 100644 index 66a5c09977bd7..0000000000000 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TemplateQueryBuilder.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -package org.elasticsearch.script.mustache; - -import org.apache.lucene.search.Query; -import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.logging.DeprecationLogger; -import org.elasticsearch.common.logging.Loggers; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentType; -import org.elasticsearch.index.query.AbstractQueryBuilder; -import org.elasticsearch.index.query.BoolQueryBuilder; -import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.index.query.QueryParseContext; -import org.elasticsearch.index.query.QueryRewriteContext; -import org.elasticsearch.index.query.QueryShardContext; -import org.elasticsearch.script.Script; -import org.elasticsearch.script.ScriptType; - -import java.io.IOException; -import java.util.Collections; -import java.util.Map; -import java.util.Objects; - -/** - * Facilitates creating template query requests. - * */ -@Deprecated -// TODO remove this class in 6.0 -public class TemplateQueryBuilder extends AbstractQueryBuilder { - - public static final String NAME = "template"; - private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(Loggers.getLogger(TemplateQueryBuilder.class)); - - /** Template to fill. */ - private final Script template; - - public TemplateQueryBuilder(String template, ScriptType scriptType, Map params) { - this(new Script(scriptType, "mustache", template, params)); - } - - public TemplateQueryBuilder(String template, ScriptType scriptType, Map params, XContentType ct) { - this(new Script(scriptType, "mustache", template, scriptType == ScriptType.INLINE ? - (ct == null ? Collections.emptyMap() : Collections.singletonMap(Script.CONTENT_TYPE_OPTION, ct.mediaType())) - : null, params)); - } - - TemplateQueryBuilder(Script template) { - DEPRECATION_LOGGER.deprecated("[{}] query is deprecated, use search template api instead", NAME); - if (template == null) { - throw new IllegalArgumentException("query template cannot be null"); - } - this.template = template; - } - - public Script template() { - return template; - } - - /** - * Read from a stream. - */ - public TemplateQueryBuilder(StreamInput in) throws IOException { - super(in); - template = new Script(in); - } - - @Override - protected void doWriteTo(StreamOutput out) throws IOException { - template.writeTo(out); - } - - @Override - protected void doXContent(XContentBuilder builder, Params builderParams) throws IOException { - builder.field(TemplateQueryBuilder.NAME); - template.toXContent(builder, builderParams); - } - - @Override - public String getWriteableName() { - return NAME; - } - - @Override - protected Query doToQuery(QueryShardContext context) throws IOException { - throw new UnsupportedOperationException("this query must be rewritten first"); - } - - @Override - protected int doHashCode() { - return Objects.hash(template); - } - - @Override - protected boolean doEquals(TemplateQueryBuilder other) { - return Objects.equals(template, other.template); - } - - @Override - protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException { - BytesReference querySource = queryRewriteContext.getTemplateBytes(template); - try (XContentParser qSourceParser = XContentFactory.xContent(querySource).createParser(queryRewriteContext.getXContentRegistry(), - querySource)) { - final QueryParseContext queryParseContext = queryRewriteContext.newParseContext(qSourceParser); - final QueryBuilder queryBuilder = queryParseContext.parseInnerQueryBuilder(); - if (boost() != DEFAULT_BOOST || queryName() != null) { - final BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder(); - boolQueryBuilder.must(queryBuilder); - return boolQueryBuilder; - } - return queryBuilder; - } - } - - /** - * In the simplest case, parse template string and variables from the request, - * compile the template and execute the template against the given variables. - */ - public static TemplateQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException { - XContentParser parser = parseContext.parser(); - Script template = Script.parse(parser, Script.DEFAULT_TEMPLATE_LANG); - - // for deprecation of stored script namespaces the default lang is ignored, - // so the template lang must be set for a stored script - if (template.getType() == ScriptType.STORED) { - template = new Script(ScriptType.STORED, Script.DEFAULT_TEMPLATE_LANG, template.getIdOrCode(), template.getParams()); - } - - return new TemplateQueryBuilder(template); - } -} diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java index b79257ebe2f99..4ac9263706e27 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java @@ -21,7 +21,6 @@ import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse; import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.xcontent.XContentType; @@ -289,15 +288,6 @@ public void testIndexedTemplate() throws Exception { assertHitCount(searchResponse.getResponse(), 1); assertWarnings("use of [/mustache/2] for looking up" + " stored scripts/templates has been deprecated, use only [2] instead"); - - Map vars = new HashMap<>(); - vars.put("fieldParam", "bar"); - - TemplateQueryBuilder builder = new TemplateQueryBuilder("3", ScriptType.STORED, vars); - SearchResponse sr = client().prepareSearch().setQuery(builder) - .execute().actionGet(); - assertHitCount(sr, 1); - assertWarnings("[template] query is deprecated, use search template api instead"); } // Relates to #10397 diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/TemplateQueryBuilderTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/TemplateQueryBuilderTests.java deleted file mode 100644 index 3b70c5df626a5..0000000000000 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/TemplateQueryBuilderTests.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.script.mustache; - -import org.apache.lucene.index.memory.MemoryIndex; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.Query; -import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentType; -import org.elasticsearch.index.query.BoolQueryBuilder; -import org.elasticsearch.index.query.MatchAllQueryBuilder; -import org.elasticsearch.index.query.MatchQueryBuilder; -import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.index.query.QueryShardContext; -import org.elasticsearch.index.query.TermQueryBuilder; -import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.script.MockScriptPlugin; -import org.elasticsearch.script.Script; -import org.elasticsearch.script.ScriptType; -import org.elasticsearch.search.internal.SearchContext; -import org.elasticsearch.test.AbstractQueryTestCase; -import org.junit.After; -import org.junit.Before; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - -import static org.hamcrest.Matchers.containsString; - -public class TemplateQueryBuilderTests extends AbstractQueryTestCase { - - /** - * The query type all template tests will be based on. - */ - private QueryBuilder templateBase; - - /** - * All tests in this class cause deprecation warnings when a new {@link TemplateQueryBuilder} is created. - * Instead of having to check them in every single test, we do it after each test is run - */ - @After - public void checkWarning() { - assertWarnings("[template] query is deprecated, use search template api instead"); - } - - @Override - protected Collection> getPlugins() { - return Arrays.asList(MustachePlugin.class, CustomScriptPlugin.class); - } - - public static class CustomScriptPlugin extends MockScriptPlugin { - - @Override - @SuppressWarnings("unchecked") - protected Map, Object>> pluginScripts() { - Map, Object>> scripts = new HashMap<>(); - - scripts.put("{ \"match_all\" : {}}", - s -> new BytesArray("{ \"match_all\" : {}}")); - - scripts.put("{ \"match_all\" : {\"_name\" : \"foobar\"}}", - s -> new BytesArray("{ \"match_all\" : {\"_name\" : \"foobar\"}}")); - - scripts.put("{\n" + - " \"term\" : {\n" + - " \"foo\" : {\n" + - " \"value\" : \"bar\",\n" + - " \"boost\" : 2.0\n" + - " }\n" + - " }\n" + - "}", s -> new BytesArray("{\n" + - " \"term\" : {\n" + - " \"foo\" : {\n" + - " \"value\" : \"bar\",\n" + - " \"boost\" : 2.0\n" + - " }\n" + - " }\n" + - "}")); - return scripts; - } - } - - @Before - public void setup() { - templateBase = new MatchQueryBuilder("field", "some values"); - } - - @Override - protected boolean supportsBoostAndQueryName() { - return false; - } - - @Override - protected TemplateQueryBuilder doCreateTestQueryBuilder() { - return new TemplateQueryBuilder(new Script(ScriptType.INLINE, "mustache", templateBase.toString(), Collections.emptyMap())); - } - - @Override - protected void doAssertLuceneQuery(TemplateQueryBuilder queryBuilder, Query query, SearchContext context) throws IOException { - QueryShardContext queryShardContext = context.getQueryShardContext(); - assertEquals(rewrite(QueryBuilder.rewriteQuery(templateBase, queryShardContext).toQuery(queryShardContext)), rewrite(query)); - } - - public void testIllegalArgument() { - expectThrows(IllegalArgumentException.class, () -> new TemplateQueryBuilder((Script) null)); - } - - /** - * Override superclass test since template query doesn't support boost and queryName, so - * we need to mutate other existing field in the test query. - */ - @Override - public void testUnknownField() throws IOException { - TemplateQueryBuilder testQuery = createTestQueryBuilder(); - XContentType xContentType = randomFrom(XContentType.JSON, XContentType.YAML); - String testQueryAsString = toXContent(testQuery, xContentType).string(); - String queryAsString = testQueryAsString.replace("inline", "bogusField"); - try { - parseQuery(createParser(xContentType.xContent(), queryAsString)); - fail("IllegalArgumentException expected"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage(), containsString("[script] unknown field [bogusField], parser not found")); - } - } - - public void testJSONGeneration() throws IOException { - Map vars = new HashMap<>(); - vars.put("template", "filled"); - TemplateQueryBuilder builder = new TemplateQueryBuilder("I am a $template string", ScriptType.INLINE, vars); - XContentBuilder content = XContentFactory.jsonBuilder(); - content.startObject(); - builder.doXContent(content, null); - content.endObject(); - content.close(); - assertEquals("{\"template\":{\"inline\":\"I am a $template string\",\"lang\":\"mustache\",\"params\":{\"template\":\"filled\"}}}", - content.string()); - } - - public void testRawEscapedTemplate() throws IOException { - String expectedTemplateString = "{\"match_{{template}}\": {}}\""; - String query = "{\"template\": {\"inline\": \"{\\\"match_{{template}}\\\": {}}\\\"\",\"params\" : {\"template\" : \"all\"}}}"; - Map params = new HashMap<>(); - params.put("template", "all"); - QueryBuilder expectedBuilder = new TemplateQueryBuilder(expectedTemplateString, ScriptType.INLINE, params); - assertParsedQuery(query, expectedBuilder); - } - - public void testRawTemplate() throws IOException { - String expectedTemplateString = "{\"match_{{template}}\":{}}"; - String query = "{\"template\": {\"inline\": {\"match_{{template}}\": {}},\"params\" : {\"template\" : \"all\"}}}"; - Map params = new HashMap<>(); - params.put("template", "all"); - QueryBuilder expectedBuilder = new TemplateQueryBuilder(expectedTemplateString, ScriptType.INLINE, params, XContentType.JSON); - assertParsedQuery(query, expectedBuilder); - } - - @Override - public void testMustRewrite() throws IOException { - String query = "{ \"match_all\" : {}}"; - QueryBuilder builder = new TemplateQueryBuilder(new Script(ScriptType.INLINE, "mockscript", query, - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()), Collections.emptyMap())); - try { - builder.toQuery(createShardContext()); - fail(); - } catch (UnsupportedOperationException ex) { - assertEquals("this query must be rewritten first", ex.getMessage()); - } - assertEquals(new MatchAllQueryBuilder(), builder.rewrite(createShardContext())); - } - - public void testRewriteWithInnerName() throws IOException { - final String query = "{ \"match_all\" : {\"_name\" : \"foobar\"}}"; - QueryBuilder builder = new TemplateQueryBuilder(new Script(ScriptType.INLINE, "mockscript", query, - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()), Collections.emptyMap())); - assertEquals(new MatchAllQueryBuilder().queryName("foobar"), builder.rewrite(createShardContext())); - - builder = new TemplateQueryBuilder(new Script(ScriptType.INLINE, "mockscript", query, Collections.singletonMap( - Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()), Collections.emptyMap())).queryName("outer"); - assertEquals(new BoolQueryBuilder().must(new MatchAllQueryBuilder().queryName("foobar")).queryName("outer"), - builder.rewrite(createShardContext())); - } - - public void testRewriteWithInnerBoost() throws IOException { - final TermQueryBuilder query = new TermQueryBuilder("foo", "bar").boost(2); - QueryBuilder builder = new TemplateQueryBuilder(new Script(ScriptType.INLINE, "mockscript", query.toString(), - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()), Collections.emptyMap())); - assertEquals(query, builder.rewrite(createShardContext())); - - builder = new TemplateQueryBuilder(new Script(ScriptType.INLINE, "mockscript", query.toString(), - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()), Collections.emptyMap())).boost(3); - assertEquals(new BoolQueryBuilder().must(query).boost(3), builder.rewrite(createShardContext())); - } - - @Override - protected Query rewrite(Query query) throws IOException { - // TemplateQueryBuilder adds some optimization if the template and query builder have boosts / query names that wraps - // the actual QueryBuilder that comes from the template into a BooleanQueryBuilder to give it an outer boost / name - // this causes some queries to be not exactly equal but equivalent such that we need to rewrite them before comparing. - if (query != null) { - MemoryIndex idx = new MemoryIndex(); - return idx.createSearcher().rewrite(query); - } - return new MatchAllDocsQuery(); // null == *:* - } -} diff --git a/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/40_template_query.yaml b/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/40_template_query.yaml deleted file mode 100644 index a21184650f55d..0000000000000 --- a/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/40_template_query.yaml +++ /dev/null @@ -1,96 +0,0 @@ ---- -"Template query": - - skip: - features: warnings - - - do: - index: - index: test - type: testtype - id: 1 - body: { "text": "value1" } - - do: - index: - index: test - type: testtype - id: 2 - body: { "text": "value2 value3" } - - do: - indices.refresh: {} - - - do: - put_template: - id: "1" - body: { "template": { "match": { "text": "{{my_value}}" } } } - - match: { acknowledged: true } - - - do: - warnings: - - '[template] query is deprecated, use search template api instead' - search: - body: { "query": { "template": { "inline": { "term": { "text": { "value": "{{template}}" } } }, "params": { "template": "value1" } } } } - - - match: { hits.total: 1 } - - - do: - warnings: - - '[template] query is deprecated, use search template api instead' - search: - body: { "query": { "template": { "file": "file_query_template", "params": { "my_value": "value1" } } } } - - - match: { hits.total: 1 } - - - do: - warnings: - - '[template] query is deprecated, use search template api instead' - search: - body: { "query": { "template": { "stored": "1", "params": { "my_value": "value1" } } } } - - - match: { hits.total: 1 } - - - do: - warnings: - - '[template] query is deprecated, use search template api instead' - search: - body: { "query": { "template": { "stored": "1", "params": { "my_value": "value1" } } } } - - - match: { hits.total: 1 } - - - do: - warnings: - - '[template] query is deprecated, use search template api instead' - search: - body: { "query": { "template": { "inline": {"match_{{template}}": {}}, "params" : { "template" : "all" } } } } - - - match: { hits.total: 2 } - - - do: - warnings: - - '[template] query is deprecated, use search template api instead' - search: - body: { "query": { "template": { "inline": "{ \"term\": { \"text\": { \"value\": \"{{template}}\" } } }", "params": { "template": "value1" } } } } - - - match: { hits.total: 1 } - - - do: - warnings: - - '[template] query is deprecated, use search template api instead' - search: - body: { "query": { "template": { "inline": "{\"match_{{template}}\": {}}", "params" : { "template" : "all" } } } } - - - match: { hits.total: 2 } - - - do: - warnings: - - '[template] query is deprecated, use search template api instead' - search: - body: { "query": { "template": { "inline": "{\"match_all\": {}}", "params" : {} } } } - - - match: { hits.total: 2 } - - - do: - warnings: - - '[template] query is deprecated, use search template api instead' - search: - body: { "query": { "template": { "inline": "{\"query_string\": { \"query\" : \"{{query}}\" }}", "params" : { "query" : "text:\"value2 value3\"" } } } } - - match: { hits.total: 1 } From 64d0d9184d4a9861458bee9cf3550213c71153f5 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Thu, 11 May 2017 15:09:26 +0200 Subject: [PATCH 304/619] Fix IndexShardIT#testDurableFlagHasEffect to only test if operations have been synced With global checkpoints we also take into account if a global checkpoint must be fsynced. Yet, with recent addition of inlining global checkpoints into indexing operations from a test perspective unnecessary fsyncs might be reported if `Translog#syncNeeded` is checked. Now the test only check if the last write location triggers an fsync instead. Closes #24600 --- .../index/shard/IndexShardIT.java | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java b/core/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java index 338634d977e97..a5e5ecd8aa6e3 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java +++ b/core/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java @@ -69,6 +69,7 @@ import org.elasticsearch.test.InternalSettingsPlugin; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; @@ -81,6 +82,8 @@ import java.util.concurrent.CyclicBarrier; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; +import java.util.function.Supplier; import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; @@ -168,33 +171,43 @@ public void testDurableFlagHasEffect() throws Exception { IndicesService indicesService = getInstanceFromNode(IndicesService.class); IndexService test = indicesService.indexService(resolveIndex("test")); IndexShard shard = test.getShardOrNull(0); - shard.checkIdle(Long.MIN_VALUE); + Translog translog = ShardUtilsTests.getShardEngine(shard).getTranslog(); + Predicate needsSync = (tlog) -> { + // we can't use tlog.needsSync() here since it also takes the global checkpoint into account + // we explicitly want to check here if our durability checks are taken into account so we only + // check if we are synced upto the current write location + Translog.Location lastWriteLocation = tlog.getLastWriteLocation(); + try { + // the lastWriteLocaltion has a Integer.MAX_VALUE size so we have to create a new one + return tlog.ensureSynced(new Translog.Location(lastWriteLocation.generation, lastWriteLocation.translogLocation, 0)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }; setDurability(shard, Translog.Durability.REQUEST); - assertBusy(() -> assertFalse(ShardUtilsTests.getShardEngine(shard).getTranslog().syncNeeded())); + assertFalse(needsSync.test(translog)); setDurability(shard, Translog.Durability.ASYNC); client().prepareIndex("test", "bar", "2").setSource("{}", XContentType.JSON).get(); - assertTrue(ShardUtilsTests.getShardEngine(shard).getTranslog().syncNeeded()); + assertTrue(needsSync.test(translog)); setDurability(shard, Translog.Durability.REQUEST); client().prepareDelete("test", "bar", "1").get(); - shard.checkIdle(Long.MIN_VALUE); - assertBusy(() -> assertFalse(ShardUtilsTests.getShardEngine(shard).getTranslog().syncNeeded())); + assertFalse(needsSync.test(translog)); setDurability(shard, Translog.Durability.ASYNC); client().prepareDelete("test", "bar", "2").get(); - assertTrue(ShardUtilsTests.getShardEngine(shard).getTranslog().syncNeeded()); + assertTrue(translog.syncNeeded()); setDurability(shard, Translog.Durability.REQUEST); assertNoFailures(client().prepareBulk() .add(client().prepareIndex("test", "bar", "3").setSource("{}", XContentType.JSON)) .add(client().prepareDelete("test", "bar", "1")).get()); - shard.checkIdle(Long.MIN_VALUE); - assertBusy(() -> assertFalse(ShardUtilsTests.getShardEngine(shard).getTranslog().syncNeeded())); + assertFalse(needsSync.test(translog)); setDurability(shard, Translog.Durability.ASYNC); assertNoFailures(client().prepareBulk() .add(client().prepareIndex("test", "bar", "4").setSource("{}", XContentType.JSON)) .add(client().prepareDelete("test", "bar", "3")).get()); setDurability(shard, Translog.Durability.REQUEST); - assertTrue(ShardUtilsTests.getShardEngine(shard).getTranslog().syncNeeded()); + assertTrue(needsSync.test(translog)); } private void setDurability(IndexShard shard, Translog.Durability durability) { From 8f798f1231cf2ee128c1701a4e435ec1d23d2fa9 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Thu, 11 May 2017 15:28:39 +0200 Subject: [PATCH 305/619] Add up-to-date javadocs on Translog#syncNeeded --- .../main/java/org/elasticsearch/index/translog/Translog.java | 3 +++ .../java/org/elasticsearch/index/translog/TranslogWriter.java | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/index/translog/Translog.java b/core/src/main/java/org/elasticsearch/index/translog/Translog.java index d6457e1d32af7..8d2be4104135d 100644 --- a/core/src/main/java/org/elasticsearch/index/translog/Translog.java +++ b/core/src/main/java/org/elasticsearch/index/translog/Translog.java @@ -541,6 +541,9 @@ public void sync() throws IOException { } } + /** + * Returns true if an fsync is required to ensure durability of the translogs operations or it's metadata. + */ public boolean syncNeeded() { try (ReleasableLock lock = readLock.acquire()) { return current.syncNeeded(); diff --git a/core/src/main/java/org/elasticsearch/index/translog/TranslogWriter.java b/core/src/main/java/org/elasticsearch/index/translog/TranslogWriter.java index b4400f60b812c..daf9a44b6665e 100644 --- a/core/src/main/java/org/elasticsearch/index/translog/TranslogWriter.java +++ b/core/src/main/java/org/elasticsearch/index/translog/TranslogWriter.java @@ -209,7 +209,8 @@ public void sync() throws IOException { } /** - * returns true if there are buffered ops + * Returns true if there are buffered operations that have not been flushed and fsynced to disk or if the latest global + * checkpoint has not yet been fsynced */ public boolean syncNeeded() { return totalOffset != lastSyncedCheckpoint.offset || globalCheckpointSupplier.getAsLong() != lastSyncedCheckpoint.globalCheckpoint; From 8188569fd11988560098a9b25fd9300a9b4c0416 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 11 May 2017 10:06:20 -0400 Subject: [PATCH 306/619] Add qa module that tests reindex-from-remote against pre-5.0 versions of Elasticsearch (#24561) Adds tests for reindex-from-remote for the latest 2.4, 1.7, and 0.90 releases. 2.4 and 1.7 are fairly popular versions but 0.90 is a point of pride. This fixes any issues those tests revealed. Closes #23828 Closes #24520 --- .../reindex/remote/RemoteRequestBuilders.java | 29 +++- .../remote/RemoteScrollableHitSource.java | 8 +- .../remote/RemoteRequestBuildersTests.java | 39 ++++-- qa/reindex-from-old/build.gradle | 82 +++++++++++ .../smoketest/ReindexFromOldRemoteIT.java | 108 +++++++++++++++ settings.gradle | 2 + test/fixtures/old-elasticsearch/build.gradle | 32 +++++ .../src/main/java/oldes/OldElasticsearch.java | 131 ++++++++++++++++++ .../org/elasticsearch/test/ESTestCase.java | 2 +- 9 files changed, 417 insertions(+), 16 deletions(-) create mode 100644 qa/reindex-from-old/build.gradle create mode 100644 qa/reindex-from-old/src/test/java/org/elasticsearch/smoketest/ReindexFromOldRemoteIT.java create mode 100644 test/fixtures/old-elasticsearch/build.gradle create mode 100644 test/fixtures/old-elasticsearch/src/main/java/oldes/OldElasticsearch.java diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuilders.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuilders.java index 1cd1df230a4e2..b81d504b9f362 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuilders.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuilders.java @@ -43,6 +43,7 @@ import java.util.Map; import static java.util.Collections.singletonMap; +import static org.elasticsearch.common.unit.TimeValue.timeValueMillis; final class RemoteRequestBuilders { private RemoteRequestBuilders() {} @@ -59,7 +60,14 @@ static String initialSearchPath(SearchRequest searchRequest) { static Map initialSearchParams(SearchRequest searchRequest, Version remoteVersion) { Map params = new HashMap<>(); if (searchRequest.scroll() != null) { - params.put("scroll", searchRequest.scroll().keepAlive().getStringRep()); + TimeValue keepAlive = searchRequest.scroll().keepAlive(); + if (remoteVersion.before(Version.V_5_0_0)) { + /* Versions of Elasticsearch before 5.0 couldn't parse nanos or micros + * so we toss out that resolution, rounding up because more scroll + * timeout seems safer than less. */ + keepAlive = timeValueMillis((long) Math.ceil(keepAlive.millisFrac())); + } + params.put("scroll", keepAlive.getStringRep()); } params.put("size", Integer.toString(searchRequest.source().size())); if (searchRequest.source().version() == null || searchRequest.source().version() == true) { @@ -93,6 +101,10 @@ static Map initialSearchParams(SearchRequest searchRequest, Vers if (remoteVersion.before(Version.fromId(2000099))) { // Versions before 2.0.0 need prompting to return interesting fields. Note that timestamp isn't available at all.... searchRequest.source().storedField("_parent").storedField("_routing").storedField("_ttl"); + if (remoteVersion.before(Version.fromId(1000099))) { + // Versions before 1.0.0 don't support `"_source": true` so we have to ask for the _source in a funny way. + searchRequest.source().storedField("_source"); + } } if (searchRequest.source().storedFields() != null && false == searchRequest.source().storedFields().fieldNames().isEmpty()) { StringBuilder fields = new StringBuilder(searchRequest.source().storedFields().fieldNames().get(0)); @@ -105,7 +117,7 @@ static Map initialSearchParams(SearchRequest searchRequest, Vers return params; } - static HttpEntity initialSearchEntity(SearchRequest searchRequest, BytesReference query) { + static HttpEntity initialSearchEntity(SearchRequest searchRequest, BytesReference query, Version remoteVersion) { // EMPTY is safe here because we're not calling namedObject try (XContentBuilder entity = JsonXContent.contentBuilder(); XContentParser queryParser = XContentHelper.createParser(NamedXContentRegistry.EMPTY, query)) { @@ -125,7 +137,10 @@ static HttpEntity initialSearchEntity(SearchRequest searchRequest, BytesReferenc if (searchRequest.source().fetchSource() != null) { entity.field("_source", searchRequest.source().fetchSource()); } else { - entity.field("_source", true); + if (remoteVersion.onOrAfter(Version.fromId(1000099))) { + // Versions before 1.0 don't support `"_source": true` so we have to ask for the source as a stored field. + entity.field("_source", true); + } } entity.endObject(); @@ -167,7 +182,13 @@ static String scrollPath() { return "/_search/scroll"; } - static Map scrollParams(TimeValue keepAlive) { + static Map scrollParams(TimeValue keepAlive, Version remoteVersion) { + if (remoteVersion.before(Version.V_5_0_0)) { + /* Versions of Elasticsearch before 5.0 couldn't parse nanos or micros + * so we toss out that resolution, rounding up so we shouldn't end up + * with 0s. */ + keepAlive = timeValueMillis((long) Math.ceil(keepAlive.millisFrac())); + } return singletonMap("scroll", keepAlive.getStringRep()); } diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java index 6b7b6ca3aa070..f3caeb004c474 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java @@ -87,7 +87,7 @@ protected void doStart(Consumer onResponse) { lookupRemoteVersion(version -> { remoteVersion = version; execute("POST", initialSearchPath(searchRequest), initialSearchParams(searchRequest, version), - initialSearchEntity(searchRequest, query), RESPONSE_PARSER, r -> onStartResponse(onResponse, r)); + initialSearchEntity(searchRequest, query, remoteVersion), RESPONSE_PARSER, r -> onStartResponse(onResponse, r)); }); } @@ -106,8 +106,10 @@ private void onStartResponse(Consumer onResponse, Response res @Override protected void doStartNextScroll(String scrollId, TimeValue extraKeepAlive, Consumer onResponse) { - execute("POST", scrollPath(), scrollParams(timeValueNanos(searchRequest.scroll().keepAlive().nanos() + extraKeepAlive.nanos())), - scrollEntity(scrollId, remoteVersion), RESPONSE_PARSER, onResponse); + Map scrollParams = scrollParams( + timeValueNanos(searchRequest.scroll().keepAlive().nanos() + extraKeepAlive.nanos()), + remoteVersion); + execute("POST", scrollPath(), scrollParams, scrollEntity(scrollId, remoteVersion), RESPONSE_PARSER, onResponse); } @Override diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuildersTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuildersTests.java index 8c082227f8686..5f30318351f13 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuildersTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuildersTests.java @@ -35,6 +35,7 @@ import java.nio.charset.StandardCharsets; import java.util.Map; +import static org.elasticsearch.common.unit.TimeValue.timeValueMillis; import static org.elasticsearch.index.reindex.remote.RemoteRequestBuilders.clearScrollEntity; import static org.elasticsearch.index.reindex.remote.RemoteRequestBuilders.initialSearchEntity; import static org.elasticsearch.index.reindex.remote.RemoteRequestBuilders.initialSearchParams; @@ -43,6 +44,7 @@ import static org.elasticsearch.index.reindex.remote.RemoteRequestBuilders.scrollParams; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.either; +import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.not; @@ -153,39 +155,60 @@ public void testInitialSearchParamsMisc() { if (scroll == null) { assertThat(params, not(hasKey("scroll"))); } else { - assertEquals(scroll, TimeValue.parseTimeValue(params.get("scroll"), "scroll")); + assertScroll(remoteVersion, params, scroll); } assertThat(params, hasEntry("size", Integer.toString(size))); assertThat(params, fetchVersion == null || fetchVersion == true ? hasEntry("version", null) : not(hasEntry("version", null))); } + private void assertScroll(Version remoteVersion, Map params, TimeValue requested) { + if (remoteVersion.before(Version.V_5_0_0)) { + // Versions of Elasticsearch prior to 5.0 can't parse nanos or micros in TimeValue. + assertThat(params.get("scroll"), not(either(endsWith("nanos")).or(endsWith("micros")))); + if (requested.getStringRep().endsWith("nanos") || requested.getStringRep().endsWith("micros")) { + long millis = (long) Math.ceil(requested.millisFrac()); + assertEquals(TimeValue.parseTimeValue(params.get("scroll"), "scroll"), timeValueMillis(millis)); + return; + } + } + assertEquals(requested, TimeValue.parseTimeValue(params.get("scroll"), "scroll")); + } + public void testInitialSearchEntity() throws IOException { + Version remoteVersion = Version.fromId(between(0, Version.CURRENT.id)); + SearchRequest searchRequest = new SearchRequest(); searchRequest.source(new SearchSourceBuilder()); String query = "{\"match_all\":{}}"; - HttpEntity entity = initialSearchEntity(searchRequest, new BytesArray(query)); + HttpEntity entity = initialSearchEntity(searchRequest, new BytesArray(query), remoteVersion); assertEquals(ContentType.APPLICATION_JSON.toString(), entity.getContentType().getValue()); - assertEquals("{\"query\":" + query + ",\"_source\":true}", - Streams.copyToString(new InputStreamReader(entity.getContent(), StandardCharsets.UTF_8))); + if (remoteVersion.onOrAfter(Version.fromId(1000099))) { + assertEquals("{\"query\":" + query + ",\"_source\":true}", + Streams.copyToString(new InputStreamReader(entity.getContent(), StandardCharsets.UTF_8))); + } else { + assertEquals("{\"query\":" + query + "}", + Streams.copyToString(new InputStreamReader(entity.getContent(), StandardCharsets.UTF_8))); + } // Source filtering is included if set up searchRequest.source().fetchSource(new String[] {"in1", "in2"}, new String[] {"out"}); - entity = initialSearchEntity(searchRequest, new BytesArray(query)); + entity = initialSearchEntity(searchRequest, new BytesArray(query), remoteVersion); assertEquals(ContentType.APPLICATION_JSON.toString(), entity.getContentType().getValue()); assertEquals("{\"query\":" + query + ",\"_source\":{\"includes\":[\"in1\",\"in2\"],\"excludes\":[\"out\"]}}", Streams.copyToString(new InputStreamReader(entity.getContent(), StandardCharsets.UTF_8))); // Invalid XContent fails RuntimeException e = expectThrows(RuntimeException.class, - () -> initialSearchEntity(searchRequest, new BytesArray("{}, \"trailing\": {}"))); + () -> initialSearchEntity(searchRequest, new BytesArray("{}, \"trailing\": {}"), remoteVersion)); assertThat(e.getCause().getMessage(), containsString("Unexpected character (',' (code 44))")); - e = expectThrows(RuntimeException.class, () -> initialSearchEntity(searchRequest, new BytesArray("{"))); + e = expectThrows(RuntimeException.class, () -> initialSearchEntity(searchRequest, new BytesArray("{"), remoteVersion)); assertThat(e.getCause().getMessage(), containsString("Unexpected end-of-input")); } public void testScrollParams() { + Version remoteVersion = Version.fromId(between(0, Version.CURRENT.id)); TimeValue scroll = TimeValue.parseTimeValue(randomPositiveTimeValue(), "test"); - assertEquals(scroll, TimeValue.parseTimeValue(scrollParams(scroll).get("scroll"), "scroll")); + assertScroll(remoteVersion, scrollParams(scroll, remoteVersion), scroll); } public void testScrollEntity() throws IOException { diff --git a/qa/reindex-from-old/build.gradle b/qa/reindex-from-old/build.gradle new file mode 100644 index 0000000000000..7dd119013b8fb --- /dev/null +++ b/qa/reindex-from-old/build.gradle @@ -0,0 +1,82 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +description = """\ +Tests reindex-from-remote against some specific versions of +Elasticsearch prior to 5.0. Versions of Elasticsearch >= 5.0 +should be able to use the standard launching mechanism which +is more flexible and reliable. +""" + +apply plugin: 'elasticsearch.standalone-rest-test' +apply plugin: 'elasticsearch.rest-test' + +integTestCluster { + // Whitelist reindexing from the local node so we can test it. + setting 'reindex.remote.whitelist', '127.0.0.1:*' +} + +configurations { + oldesFixture + es2 + es1 + es090 +} + +dependencies { + oldesFixture project(':test:fixtures:old-elasticsearch') + /* Right now we just test against the latest version of each major we expect + * reindex-from-remote to work against. We could randomize the versions but + * that doesn't seem worth it at this point. */ + es2 'org.elasticsearch.distribution.zip:elasticsearch:2.4.5@zip' + es1 'org.elasticsearch:elasticsearch:1.7.6@zip' + es090 'org.elasticsearch:elasticsearch:0.90.13@zip' +} + +/* Set up tasks to unzip and run the old versions of ES before running the + * integration tests. */ +for (String version : ['2', '1', '090']) { + Task unzip = task("unzipEs${version}", type: Sync) { + Configuration oldEsDependency = configurations['es' + version] + dependsOn oldEsDependency + // Use a closure here to delay resolution of the dependency until we need it + from { + oldEsDependency.collect { zipTree(it) } + } + into temporaryDir + } + Task fixture = task("oldEs${version}Fixture", + type: org.elasticsearch.gradle.test.AntFixture) { + dependsOn project.configurations.oldesFixture + dependsOn unzip + executable = new File(project.javaHome, 'bin/java') + env 'CLASSPATH', "${ -> project.configurations.oldesFixture.asPath }" + args 'oldes.OldElasticsearch', + baseDir, + unzip.temporaryDir, + version == '090' + } + integTestCluster.dependsOn fixture + integTestRunner { + /* Use a closure on the string to delay evaluation until right before we + * run the integration tests so that we can be sure that the file is ready. + */ + systemProperty "es${version}.port", "${ -> fixture.addressAndPort }" + } +} diff --git a/qa/reindex-from-old/src/test/java/org/elasticsearch/smoketest/ReindexFromOldRemoteIT.java b/qa/reindex-from-old/src/test/java/org/elasticsearch/smoketest/ReindexFromOldRemoteIT.java new file mode 100644 index 0000000000000..162e68e402730 --- /dev/null +++ b/qa/reindex-from-old/src/test/java/org/elasticsearch/smoketest/ReindexFromOldRemoteIT.java @@ -0,0 +1,108 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.smoketest; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.util.EntityUtils; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.test.rest.ESRestTestCase; + +import java.io.IOException; +import java.util.Map; +import java.util.TreeMap; + +import static java.util.Collections.singletonMap; +import static org.hamcrest.Matchers.containsString; + +public class ReindexFromOldRemoteIT extends ESRestTestCase { + private void oldEsTestCase(String portPropertyName, String requestsPerSecond) throws IOException { + int oldEsPort = Integer.parseInt(System.getProperty(portPropertyName)); + try (RestClient oldEs = RestClient.builder(new HttpHost("127.0.0.1", oldEsPort)).build()) { + try { + HttpEntity entity = new StringEntity("{\"settings\":{\"number_of_shards\": 1}}", ContentType.APPLICATION_JSON); + oldEs.performRequest("PUT", "/test", singletonMap("refresh", "true"), entity); + + entity = new StringEntity("{\"test\":\"test\"}", ContentType.APPLICATION_JSON); + oldEs.performRequest("PUT", "/test/doc/testdoc1", singletonMap("refresh", "true"), entity); + oldEs.performRequest("PUT", "/test/doc/testdoc2", singletonMap("refresh", "true"), entity); + oldEs.performRequest("PUT", "/test/doc/testdoc3", singletonMap("refresh", "true"), entity); + oldEs.performRequest("PUT", "/test/doc/testdoc4", singletonMap("refresh", "true"), entity); + oldEs.performRequest("PUT", "/test/doc/testdoc5", singletonMap("refresh", "true"), entity); + + entity = new StringEntity( + "{\n" + + " \"source\":{\n" + + " \"index\": \"test\",\n" + + " \"size\": 1,\n" + + " \"remote\": {\n" + + " \"host\": \"http://127.0.0.1:" + oldEsPort + "\"\n" + + " }\n" + + " },\n" + + " \"dest\": {\n" + + " \"index\": \"test\"\n" + + " }\n" + + "}", + ContentType.APPLICATION_JSON); + Map params = new TreeMap<>(); + params.put("refresh", "true"); + params.put("pretty", "true"); + if (requestsPerSecond != null) { + params.put("requests_per_second", requestsPerSecond); + } + client().performRequest("POST", "/_reindex", params, entity); + + Response response = client().performRequest("POST", "test/_search", singletonMap("pretty", "true")); + String result = EntityUtils.toString(response.getEntity()); + assertThat(result, containsString("\"_id\" : \"testdoc1\"")); + } finally { + oldEs.performRequest("DELETE", "/test"); + } + } + } + + public void testEs2() throws IOException { + oldEsTestCase("es2.port", null); + } + + public void testEs1() throws IOException { + oldEsTestCase("es1.port", null); + } + + public void testEs090() throws IOException { + oldEsTestCase("es090.port", null); + } + + public void testEs2WithFunnyThrottle() throws IOException { + oldEsTestCase("es2.port", "11"); // 11 requests per second should give us a nice "funny" number on the scroll timeout + } + + public void testEs1WithFunnyThrottle() throws IOException { + oldEsTestCase("es1.port", "11"); // 11 requests per second should give us a nice "funny" number on the scroll timeout + } + + public void testEs090WithFunnyThrottle() throws IOException { + oldEsTestCase("es090.port", "11"); // 11 requests per second should give us a nice "funny" number on the scroll timeout + } + +} diff --git a/settings.gradle b/settings.gradle index 6c18f1c1efb72..6e16b5a8b38da 100644 --- a/settings.gradle +++ b/settings.gradle @@ -26,6 +26,7 @@ List projects = [ 'test:fixtures:example-fixture', 'test:fixtures:hdfs-fixture', 'test:fixtures:krb5kdc-fixture', + 'test:fixtures:old-elasticsearch', 'test:logger-usage', 'modules:aggs-matrix-stats', 'modules:analysis-common', @@ -63,6 +64,7 @@ List projects = [ 'qa:evil-tests', 'qa:multi-cluster-search', 'qa:no-bootstrap-tests', + 'qa:reindex-from-old', 'qa:rolling-upgrade', 'qa:smoke-test-client', 'qa:smoke-test-http', diff --git a/test/fixtures/old-elasticsearch/build.gradle b/test/fixtures/old-elasticsearch/build.gradle new file mode 100644 index 0000000000000..5cfc02bbba3c6 --- /dev/null +++ b/test/fixtures/old-elasticsearch/build.gradle @@ -0,0 +1,32 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +description = """\ +Launches versions of Elasticsearch prior to 5.0 for testing. +These need special handling because they do not support writing +a "ports" file with the port on which Elasticsearch is running. +""" + +apply plugin: 'elasticsearch.build' +test.enabled = false + +dependencies { + // Just for the constants.... + compile "org.apache.lucene:lucene-core:${versions.lucene}" +} diff --git a/test/fixtures/old-elasticsearch/src/main/java/oldes/OldElasticsearch.java b/test/fixtures/old-elasticsearch/src/main/java/oldes/OldElasticsearch.java new file mode 100644 index 0000000000000..bd4c3f8ccd105 --- /dev/null +++ b/test/fixtures/old-elasticsearch/src/main/java/oldes/OldElasticsearch.java @@ -0,0 +1,131 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package oldes; + +import org.apache.lucene.util.Constants; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Starts a version of Elasticsearch that has been unzipped into an empty directory, + * instructing it to ask the OS for an unused port, grepping the logs for the port + * it actually got, and writing a {@code ports} file with the port. This is only + * required for versions of Elasticsearch before 5.0 because they do not support + * writing a "ports" file. + */ +public class OldElasticsearch { + public static void main(String[] args) throws IOException { + Path baseDir = Paths.get(args[0]); + Path unzipDir = Paths.get(args[1]); + + // 0.90 must be explicitly foregrounded + boolean explicitlyForeground; + switch (args[2]) { + case "true": + explicitlyForeground = true; + break; + case "false": + explicitlyForeground = false; + break; + default: + System.err.println("the third argument must be true or false"); + System.exit(1); + return; + } + + Iterator children = Files.list(unzipDir).iterator(); + if (false == children.hasNext()) { + System.err.println("expected the es directory to contain a single child directory but contained none."); + System.exit(1); + } + Path esDir = children.next(); + if (children.hasNext()) { + System.err.println("expected the es directory to contains a single child directory but contained [" + esDir + "] and [" + + children.next() + "]."); + System.exit(1); + } + if (false == Files.isDirectory(esDir)) { + System.err.println("expected the es directory to contains a single child directory but contained a single child file."); + System.exit(1); + } + + Path bin = esDir.resolve("bin").resolve("elasticsearch" + (Constants.WINDOWS ? ".bat" : "")); + Path config = esDir.resolve("config").resolve("elasticsearch.yml"); + + Files.write(config, Arrays.asList("http.port: 0", "transport.tcp.port: 0", "network.host: 127.0.0.1"), StandardCharsets.UTF_8); + + List command = new ArrayList<>(); + command.add(bin.toString()); + if (explicitlyForeground) { + command.add("-f"); + } + command.add("-p"); + command.add("../pid"); + ProcessBuilder subprocess = new ProcessBuilder(command); + Process process = subprocess.start(); + System.out.println("Running " + command); + + int pid = 0; + int port = 0; + + Pattern pidPattern = Pattern.compile("pid\\[(\\d+)\\]"); + Pattern httpPortPattern = Pattern.compile("\\[http\\s+\\].+bound_address.+127\\.0\\.0\\.1:(\\d+)"); + try (BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = stdout.readLine()) != null && (pid == 0 || port == 0)) { + System.out.println(line); + Matcher m = pidPattern.matcher(line); + if (m.find()) { + pid = Integer.parseInt(m.group(1)); + System.out.println("Found pid: " + pid); + continue; + } + m = httpPortPattern.matcher(line); + if (m.find()) { + port = Integer.parseInt(m.group(1)); + System.out.println("Found port: " + port); + continue; + } + } + } + + if (port == 0) { + System.err.println("port not found"); + System.exit(1); + } + + Path tmp = Files.createTempFile(baseDir, null, null); + Files.write(tmp, Integer.toString(port).getBytes(StandardCharsets.UTF_8)); + Files.move(tmp, baseDir.resolve("ports"), StandardCopyOption.ATOMIC_MOVE); + } +} diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index ed8f271bab14e..7e07df35e8b50 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -629,7 +629,7 @@ public static String[] generateRandomStringArray(int maxArraySize, int maxString return generateRandomStringArray(maxArraySize, maxStringSize, allowNull, true); } - private static String[] TIME_SUFFIXES = new String[]{"d", "h", "ms", "s", "m"}; + private static final String[] TIME_SUFFIXES = new String[]{"d", "h", "ms", "s", "m", "micros", "nanos"}; private static String randomTimeValue(int lower, int upper) { return randomIntBetween(lower, upper) + randomFrom(TIME_SUFFIXES); From 6ea2ae32b8b372379fe9e2fbc4365dac03747617 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Thu, 11 May 2017 16:20:40 +0200 Subject: [PATCH 307/619] Move DeleteByQuery and Reindex requests into core (#24578) This allows other plugins to use a client to call the functionality that is in the core modules without duplicating the logic. Plugins can now safely send the request and response classes via the client even if the requests are executed locally. All relevant classes are loaded by the core classloader such that plugins can share them. --- .../resources/checkstyle_suppressions.xml | 1 - .../reindex}/AbstractBulkByScrollRequest.java | 4 ++-- .../AbstractBulkByScrollRequestBuilder.java | 2 +- .../AbstractBulkIndexByScrollRequest.java | 1 - ...stractBulkIndexByScrollRequestBuilder.java | 2 -- .../reindex}/BulkByScrollResponse.java | 15 ++++++------ .../reindex}/BulkByScrollTask.java | 2 +- .../reindex}/ClientScrollableHitSource.java | 2 +- .../index/reindex/DeleteByQueryAction.java | 2 -- .../reindex}/DeleteByQueryRequest.java | 4 ++-- .../reindex/DeleteByQueryRequestBuilder.java | 3 --- .../reindex}/ParentBulkByScrollTask.java | 4 ++-- .../index/reindex/ReindexRequest.java | 3 +-- .../index/reindex/ReindexRequestBuilder.java | 2 -- .../index/reindex}/RemoteInfo.java | 2 +- .../reindex}/ScrollableHitSource.java | 4 ++-- .../reindex}/SuccessfullyProcessed.java | 2 +- .../index/reindex/UpdateByQueryAction.java | 1 - .../index/reindex/UpdateByQueryRequest.java | 2 +- .../reindex/UpdateByQueryRequestBuilder.java | 1 - .../reindex}/WorkingBulkByScrollTask.java | 24 +++++++++---------- .../reindex}/package-info.java | 5 ++-- .../AbstractBulkByScrollRequestTestCase.java | 2 +- .../reindex}/BulkByScrollResponseTests.java | 11 ++++----- .../reindex}/BulkByScrollTaskStatusTests.java | 5 ++-- .../reindex}/BulkByScrollTaskTests.java | 2 +- .../reindex}/DeleteByQueryRequestTests.java | 2 +- .../reindex}/ParentBulkByScrollTaskTests.java | 2 +- .../index/reindex/ReindexRequestTests.java | 2 -- .../reindex/UpdateByQueryRequestTests.java | 1 - .../WorkingBulkByScrollTaskTests.java | 4 +--- .../AbstractAsyncBulkByScrollAction.java | 12 +++++----- .../AbstractBaseReindexRestHandler.java | 3 --- .../AbstractBulkByQueryRestHandler.java | 4 +--- .../reindex}/AsyncDeleteByQueryAction.java | 6 ++--- .../BulkByScrollParallelizationHelper.java | 12 +++++----- ...kIndexByScrollResponseContentListener.java | 3 +-- .../index/reindex/ReindexAction.java | 1 - .../index/reindex/ReindexPlugin.java | 1 - .../reindex/RestDeleteByQueryAction.java | 1 - .../index/reindex/RestReindexAction.java | 1 - .../reindex/TransportDeleteByQueryAction.java | 6 ----- .../index/reindex/TransportReindexAction.java | 9 +------ .../reindex/TransportRethrottleAction.java | 1 - .../reindex/TransportUpdateByQueryAction.java | 6 ----- .../reindex/remote/RemoteResponseParsers.java | 8 +++---- .../remote/RemoteScrollableHitSource.java | 2 +- ...yncBulkByScrollActionMetadataTestCase.java | 2 +- ...AsyncBulkByScrollActionScriptTestCase.java | 8 ++----- .../AsyncBulkByScrollActionTests.java | 8 +++---- ...ulkByScrollParallelizationHelperTests.java | 4 ++-- .../BulkIndexByScrollResponseMatcher.java | 5 ++-- .../BulkIndexByScrollResponseTests.java | 4 +--- .../index/reindex/CancelTests.java | 3 --- .../reindex/DeleteByQueryConcurrentTests.java | 1 - .../index/reindex/ReindexBasicTests.java | 1 - .../index/reindex/ReindexFailureTests.java | 1 - ...ReindexFromRemoteBuildRestClientTests.java | 1 - .../ReindexFromRemoteWhitelistTests.java | 1 - .../ReindexFromRemoteWithAuthTests.java | 1 - .../index/reindex/ReindexMetadataTests.java | 5 +--- .../reindex/ReindexParentChildTests.java | 1 - .../index/reindex/ReindexScriptTests.java | 1 - .../ReindexSourceTargetValidationTests.java | 1 - .../index/reindex/ReindexTestCase.java | 1 - .../index/reindex/ReindexVersioningTests.java | 1 - .../index/reindex/RestReindexActionTests.java | 1 - .../index/reindex/RethrottleTests.java | 3 --- .../index/reindex/RetryTests.java | 5 ---- .../index/reindex/RoundTripTests.java | 3 --- .../TransportRethrottleActionTests.java | 3 --- .../reindex/UpdateByQueryMetadataTests.java | 5 +--- .../UpdateByQueryWhileModifyingTests.java | 1 - .../reindex/UpdateByQueryWithScriptTests.java | 1 - .../index/reindex/remote/RemoteInfoTests.java | 1 + .../RemoteScrollableHitSourceTests.java | 2 +- ...stractAsyncBulkByScrollActionTestCase.java | 2 +- 77 files changed, 90 insertions(+), 182 deletions(-) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/AbstractBulkByScrollRequest.java (99%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/AbstractBulkByScrollRequestBuilder.java (99%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java (97%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java (92%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/BulkByScrollResponse.java (93%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/BulkByScrollTask.java (99%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/ClientScrollableHitSource.java (99%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java (91%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/DeleteByQueryRequest.java (97%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java (90%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/ParentBulkByScrollTask.java (97%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java (97%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java (95%) rename {modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote => core/src/main/java/org/elasticsearch/index/reindex}/RemoteInfo.java (99%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/ScrollableHitSource.java (99%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/SuccessfullyProcessed.java (96%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java (95%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java (97%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java (96%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/WorkingBulkByScrollTask.java (95%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/package-info.java (87%) rename {test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll => core/src/test/java/org/elasticsearch/index/reindex}/AbstractBulkByScrollRequestTestCase.java (98%) rename core/src/test/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/BulkByScrollResponseTests.java (90%) rename core/src/test/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/BulkByScrollTaskStatusTests.java (97%) rename core/src/test/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/BulkByScrollTaskTests.java (99%) rename core/src/test/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/DeleteByQueryRequestTests.java (99%) rename core/src/test/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/ParentBulkByScrollTaskTests.java (99%) rename {modules/reindex => core}/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java (97%) rename {modules/reindex => core}/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java (97%) rename core/src/test/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/WorkingBulkByScrollTaskTests.java (98%) rename {core/src/main/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/main/java/org/elasticsearch/index/reindex}/AbstractAsyncBulkByScrollAction.java (98%) rename {core/src/main/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/main/java/org/elasticsearch/index/reindex}/AsyncDeleteByQueryAction.java (91%) rename {core/src/main/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/main/java/org/elasticsearch/index/reindex}/BulkByScrollParallelizationHelper.java (88%) rename {test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/test/java/org/elasticsearch/index/reindex}/AbstractAsyncBulkByScrollActionMetadataTestCase.java (96%) rename {core/src/test/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/test/java/org/elasticsearch/index/reindex}/AsyncBulkByScrollActionTests.java (99%) rename {core/src/test/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/test/java/org/elasticsearch/index/reindex}/BulkByScrollParallelizationHelperTests.java (94%) rename {test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/test/java/org/elasticsearch/index/reindex}/BulkIndexByScrollResponseMatcher.java (98%) rename test/framework/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/AbstractAsyncBulkByScrollActionTestCase.java (97%) diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 3e0028032134a..9a550740fde36 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -127,7 +127,6 @@ - diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequest.java b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java similarity index 99% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequest.java rename to core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java index 445808750113a..a582248af11ae 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequest.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.Version; import org.elasticsearch.action.ActionRequest; @@ -355,7 +355,7 @@ public int getSlices() { /** * Build a new request for a slice of the parent request. */ - protected abstract Self forSlice(TaskId slicingTask, SearchRequest slice); + public abstract Self forSlice(TaskId slicingTask, SearchRequest slice); /** * Setup a clone of this request with the information needed to process a slice of it. diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestBuilder.java b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestBuilder.java similarity index 99% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestBuilder.java rename to core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestBuilder.java index 4131175f9df09..de3f22f0943a8 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestBuilder.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; import org.elasticsearch.action.ActionRequestBuilder; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java similarity index 97% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java rename to core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java index 6aad6e08a49f0..62c2635b301c5 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java similarity index 92% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java rename to core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java index 369510083d1da..ca1c980995bba 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java @@ -20,8 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.script.Script; diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponse.java b/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollResponse.java similarity index 93% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponse.java rename to core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollResponse.java index 20f2cf2ed06b4..400baf7b9e2c6 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponse.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollResponse.java @@ -17,11 +17,10 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -45,14 +44,14 @@ public class BulkByScrollResponse extends ActionResponse implements ToXContent { private TimeValue took; private BulkByScrollTask.Status status; private List bulkFailures; - private List searchFailures; + private List searchFailures; private boolean timedOut; public BulkByScrollResponse() { } public BulkByScrollResponse(TimeValue took, BulkByScrollTask.Status status, List bulkFailures, - List searchFailures, boolean timedOut) { + List searchFailures, boolean timedOut) { this.took = took; this.status = requireNonNull(status, "Null status not supported"); this.bulkFailures = bulkFailures; @@ -139,7 +138,7 @@ public List getBulkFailures() { /** * All search failures. */ - public List getSearchFailures() { + public List getSearchFailures() { return searchFailures; } @@ -166,7 +165,7 @@ public void readFrom(StreamInput in) throws IOException { took = new TimeValue(in); status = new BulkByScrollTask.Status(in); bulkFailures = in.readList(Failure::new); - searchFailures = in.readList(SearchFailure::new); + searchFailures = in.readList(ScrollableHitSource.SearchFailure::new); timedOut = in.readBoolean(); } @@ -181,7 +180,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws failure.toXContent(builder, params); builder.endObject(); } - for (SearchFailure failure: searchFailures) { + for (ScrollableHitSource.SearchFailure failure: searchFailures) { failure.toXContent(builder, params); } builder.endArray(); @@ -199,4 +198,4 @@ public String toString() { builder.append(",search_failures=").append(getSearchFailures().subList(0, min(3, getSearchFailures().size()))); return builder.append(']').toString(); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTask.java b/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollTask.java similarity index 99% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTask.java rename to core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollTask.java index 7c9124057b3c0..18c6dac92064a 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTask.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollTask.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ClientScrollableHitSource.java b/core/src/main/java/org/elasticsearch/index/reindex/ClientScrollableHitSource.java similarity index 99% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/ClientScrollableHitSource.java rename to core/src/main/java/org/elasticsearch/index/reindex/ClientScrollableHitSource.java index 3bacc187ebb6d..2f6775a1eae08 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ClientScrollableHitSource.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/ClientScrollableHitSource.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java b/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java similarity index 91% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java rename to core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java index b55fe33340e50..c1abb16ca3977 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java @@ -20,8 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; import org.elasticsearch.client.ElasticsearchClient; public class DeleteByQueryAction extends Action { diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequest.java b/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequest.java similarity index 97% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequest.java rename to core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequest.java index 2644d0d94967f..ad70748f3e431 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequest.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.IndicesRequest; @@ -81,7 +81,7 @@ public ActionRequestValidationException validate() { } @Override - protected DeleteByQueryRequest forSlice(TaskId slicingTask, SearchRequest slice) { + public DeleteByQueryRequest forSlice(TaskId slicingTask, SearchRequest slice) { return doForSlice(new DeleteByQueryRequest(slice, false), slicingTask); } diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java b/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java similarity index 90% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java rename to core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java index 3efbc1a562cca..e94d1308a74be 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java @@ -20,9 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTask.java b/core/src/main/java/org/elasticsearch/index/reindex/ParentBulkByScrollTask.java similarity index 97% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTask.java rename to core/src/main/java/org/elasticsearch/index/reindex/ParentBulkByScrollTask.java index a37dc61897c0c..bea9bc203653d 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTask.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/ParentBulkByScrollTask.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.collect.Tuple; @@ -104,7 +104,7 @@ public void onSliceResponse(ActionListener listener, int s /** * Record a failure from a slice and respond to the listener if the request is finished. */ - void onSliceFailure(ActionListener listener, int sliceId, Exception e) { + public void onSliceFailure(ActionListener listener, int sliceId, Exception e) { results.setOnce(sliceId, new Result(sliceId, e)); recordSliceCompletionAndRespondIfAllDone(listener); // TODO cancel when a slice fails? diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java b/core/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java similarity index 97% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java rename to core/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java index 2fa513f9c57a4..76944c7b80438 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.lucene.uid.Versions; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.tasks.TaskId; import java.io.IOException; @@ -128,7 +127,7 @@ public RemoteInfo getRemoteInfo() { } @Override - protected ReindexRequest forSlice(TaskId slicingTask, SearchRequest slice) { + public ReindexRequest forSlice(TaskId slicingTask, SearchRequest slice) { ReindexRequest sliced = doForSlice(new ReindexRequest(slice, destination, false), slicingTask); sliced.setRemoteInfo(remoteInfo); return sliced; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java b/core/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java similarity index 95% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java rename to core/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java index d7de51c588ec3..68bd3f4661828 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java @@ -20,13 +20,11 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexAction; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; -import org.elasticsearch.index.reindex.remote.RemoteInfo; public class ReindexRequestBuilder extends AbstractBulkIndexByScrollRequestBuilder { diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteInfo.java b/core/src/main/java/org/elasticsearch/index/reindex/RemoteInfo.java similarity index 99% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteInfo.java rename to core/src/main/java/org/elasticsearch/index/reindex/RemoteInfo.java index 5fad275cde47a..878a9c61e4c7c 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteInfo.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/RemoteInfo.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex.remote; +package org.elasticsearch.index.reindex; import org.elasticsearch.Version; import org.elasticsearch.common.Nullable; diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ScrollableHitSource.java b/core/src/main/java/org/elasticsearch/index/reindex/ScrollableHitSource.java similarity index 99% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/ScrollableHitSource.java rename to core/src/main/java/org/elasticsearch/index/reindex/ScrollableHitSource.java index 6426bad592f3e..3d1eb582db879 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ScrollableHitSource.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/ScrollableHitSource.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.apache.logging.log4j.Logger; import org.elasticsearch.ElasticsearchException; @@ -111,7 +111,7 @@ public final void close(Runnable onCompletion) { /** * Set the id of the last scroll. Used for debugging. */ - final void setScroll(String scrollId) { + public final void setScroll(String scrollId) { this.scrollId.set(scrollId); } diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/SuccessfullyProcessed.java b/core/src/main/java/org/elasticsearch/index/reindex/SuccessfullyProcessed.java similarity index 96% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/SuccessfullyProcessed.java rename to core/src/main/java/org/elasticsearch/index/reindex/SuccessfullyProcessed.java index a0176e3520254..6547984900e4b 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/SuccessfullyProcessed.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/SuccessfullyProcessed.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; /** * Implemented by {@link BulkByScrollTask} and {@link BulkByScrollTask.Status} to consistently implement diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java b/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java similarity index 95% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java rename to core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java index cb716e8224871..1058f7f13078a 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.client.ElasticsearchClient; public class UpdateByQueryAction extends diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java b/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java similarity index 97% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java rename to core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java index 3e7fac9d4546c..ad0123d76cedf 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java @@ -71,7 +71,7 @@ protected UpdateByQueryRequest self() { } @Override - protected UpdateByQueryRequest forSlice(TaskId slicingTask, SearchRequest slice) { + public UpdateByQueryRequest forSlice(TaskId slicingTask, SearchRequest slice) { UpdateByQueryRequest request = doForSlice(new UpdateByQueryRequest(slice, false), slicingTask); request.setPipeline(pipeline); return request; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java b/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java similarity index 96% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java rename to core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java index 143d66ae6fbcf..06e0426864193 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTask.java b/core/src/main/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTask.java similarity index 95% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTask.java rename to core/src/main/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTask.java index 28f08417abbd3..acb1c0e5547ca 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTask.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTask.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.apache.logging.log4j.Logger; import org.elasticsearch.common.logging.ESLoggerFactory; @@ -115,15 +115,15 @@ TimeValue throttledUntil() { return timeValueNanos(max(0, delayed.future.getDelay(TimeUnit.NANOSECONDS))); } - void setTotal(long totalHits) { + public void setTotal(long totalHits) { total.set(totalHits); } - void countBatch() { + public void countBatch() { batch.incrementAndGet(); } - void countNoop() { + public void countNoop() { noops.incrementAndGet(); } @@ -132,7 +132,7 @@ public long getCreated() { return created.get(); } - void countCreated() { + public void countCreated() { created.incrementAndGet(); } @@ -141,7 +141,7 @@ public long getUpdated() { return updated.get(); } - void countUpdated() { + public void countUpdated() { updated.incrementAndGet(); } @@ -150,15 +150,15 @@ public long getDeleted() { return deleted.get(); } - void countDeleted() { + public void countDeleted() { deleted.incrementAndGet(); } - void countVersionConflict() { + public void countVersionConflict() { versionConflicts.incrementAndGet(); } - void countBulkRetry() { + public void countBulkRetry() { bulkRetries.incrementAndGet(); } @@ -174,8 +174,8 @@ float getRequestsPerSecond() { * Schedule prepareBulkRequestRunnable to run after some delay. This is where throttling plugs into reindexing so the request can be * rescheduled over and over again. */ - void delayPrepareBulkRequest(ThreadPool threadPool, TimeValue lastBatchStartTime, int lastBatchSize, - AbstractRunnable prepareBulkRequestRunnable) { + public void delayPrepareBulkRequest(ThreadPool threadPool, TimeValue lastBatchStartTime, int lastBatchSize, + AbstractRunnable prepareBulkRequestRunnable) { // Synchronize so we are less likely to schedule the same request twice. synchronized (delayedPrepareBulkRequestReference) { TimeValue delay = throttleWaitTime(lastBatchStartTime, timeValueNanos(System.nanoTime()), lastBatchSize); @@ -184,7 +184,7 @@ void delayPrepareBulkRequest(ThreadPool threadPool, TimeValue lastBatchStartTime } } - TimeValue throttleWaitTime(TimeValue lastBatchStartTime, TimeValue now, int lastBatchSize) { + public TimeValue throttleWaitTime(TimeValue lastBatchStartTime, TimeValue now, int lastBatchSize) { long earliestNextBatchStartTime = now.nanos() + (long) perfectlyThrottledBatchTime(lastBatchSize); return timeValueNanos(max(0, earliestNextBatchStartTime - System.nanoTime())); } diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/package-info.java b/core/src/main/java/org/elasticsearch/index/reindex/package-info.java similarity index 87% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/package-info.java rename to core/src/main/java/org/elasticsearch/index/reindex/package-info.java index 3a31ea2f6541c..00cb5106770d1 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/package-info.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/package-info.java @@ -18,6 +18,7 @@ */ /** - * Infrastructure for actions that modify documents based on the results of a scrolling query. + * Infrastructure for actions that modify documents based on the results of a scrolling query + * like reindex, update by query or delete by query. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; diff --git a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestTestCase.java b/core/src/test/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestTestCase.java similarity index 98% rename from test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestTestCase.java rename to core/src/test/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestTestCase.java index c4ac9587e82f7..debeab52d979d 100644 --- a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestTestCase.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestTestCase.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.ActiveShardCount; diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponseTests.java b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollResponseTests.java similarity index 90% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponseTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollResponseTests.java index 1d2146c1515d1..a288328391a9d 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponseTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollResponseTests.java @@ -17,12 +17,11 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.test.ESTestCase; @@ -56,7 +55,7 @@ private List randomIndexingFailures() { randomSimpleString(random()), new IllegalArgumentException("test"))); } - private List randomSearchFailures() { + private List randomSearchFailures() { if (randomBoolean()) { return emptyList(); } @@ -68,7 +67,7 @@ private List randomSearchFailures() { shardId = randomInt(); nodeId = usually() ? randomAlphaOfLength(5) : null; } - return singletonList(new SearchFailure(new ElasticsearchException("foo"), index, shardId, nodeId)); + return singletonList(new ScrollableHitSource.SearchFailure(new ElasticsearchException("foo"), index, shardId, nodeId)); } private void assertResponseEquals(BulkByScrollResponse expected, BulkByScrollResponse actual) { @@ -86,8 +85,8 @@ private void assertResponseEquals(BulkByScrollResponse expected, BulkByScrollRes } assertEquals(expected.getSearchFailures().size(), actual.getSearchFailures().size()); for (int i = 0; i < expected.getSearchFailures().size(); i++) { - SearchFailure expectedFailure = expected.getSearchFailures().get(i); - SearchFailure actualFailure = actual.getSearchFailures().get(i); + ScrollableHitSource.SearchFailure expectedFailure = expected.getSearchFailures().get(i); + ScrollableHitSource.SearchFailure actualFailure = actual.getSearchFailures().get(i); assertEquals(expectedFailure.getIndex(), actualFailure.getIndex()); assertEquals(expectedFailure.getShardId(), actualFailure.getShardId()); assertEquals(expectedFailure.getNodeId(), actualFailure.getNodeId()); diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskStatusTests.java b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java similarity index 97% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskStatusTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java index 503fe1db7cd32..982198c8fee47 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskStatusTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.ElasticsearchException; @@ -26,6 +26,7 @@ import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.test.ESTestCase; +import org.hamcrest.Matchers; import java.io.IOException; import java.util.List; @@ -75,7 +76,7 @@ public static void assertTaskStatusEquals(Version version, BulkByScrollTask.Stat assertEquals(expected.getReasonCancelled(), actual.getReasonCancelled()); assertEquals(expected.getThrottledUntil(), actual.getThrottledUntil()); if (version.onOrAfter(Version.V_5_1_1_UNRELEASED)) { - assertThat(actual.getSliceStatuses(), hasSize(expected.getSliceStatuses().size())); + assertThat(actual.getSliceStatuses(), Matchers.hasSize(expected.getSliceStatuses().size())); for (int i = 0; i < expected.getSliceStatuses().size(); i++) { BulkByScrollTask.StatusOrException sliceStatus = expected.getSliceStatuses().get(i); if (sliceStatus == null) { diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskTests.java b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskTests.java index ff0eae5552042..f4d4ea790bc50 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.ToXContent; diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequestTests.java b/core/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryRequestTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequestTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryRequestTests.java index f5c00f63de9c1..8c84c8f3f5680 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequestTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryRequestTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.search.SearchRequest; diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTaskTests.java b/core/src/test/java/org/elasticsearch/index/reindex/ParentBulkByScrollTaskTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTaskTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/ParentBulkByScrollTaskTests.java index 715fcaaad54c8..6e2d44abed53a 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTaskTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/ParentBulkByScrollTaskTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionListener; import org.elasticsearch.test.ESTestCase; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java b/core/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java similarity index 97% rename from modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java index d1bb6f6096c39..32b01237375b2 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java @@ -20,11 +20,9 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionRequestValidationException; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestTestCase; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.search.slice.SliceBuilder; import static java.util.Collections.emptyMap; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java b/core/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java similarity index 97% rename from modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java index 700f45b42c515..b30968cf056b5 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestTestCase; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.IndicesOptions; diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTaskTests.java b/core/src/test/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTaskTests.java similarity index 98% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTaskTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTaskTests.java index 7356d626c1023..5d594d080b8b0 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTaskTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTaskTests.java @@ -17,10 +17,8 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.WorkingBulkByScrollTask; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.tasks.TaskId; diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollAction.java similarity index 98% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollAction.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollAction.java index 3ba07ea5538f5..2a23823c85843 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollAction.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; @@ -31,7 +31,7 @@ import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.bulk.Retry; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; +import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.client.ParentTaskAssigningClient; @@ -77,7 +77,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; import static org.elasticsearch.action.bulk.BackoffPolicy.exponentialBackoff; -import static org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest.SIZE_ALL_MATCHES; +import static org.elasticsearch.index.reindex.AbstractBulkByScrollRequest.SIZE_ALL_MATCHES; import static org.elasticsearch.common.unit.TimeValue.timeValueNanos; import static org.elasticsearch.rest.RestStatus.CONFLICT; import static org.elasticsearch.search.sort.SortBuilders.fieldSort; @@ -116,8 +116,8 @@ public abstract class AbstractAsyncBulkByScrollAction, ScrollableHitSource.Hit, RequestWrapper> scriptApplier; public AbstractAsyncBulkByScrollAction(WorkingBulkByScrollTask task, Logger logger, ParentTaskAssigningClient client, - ThreadPool threadPool, Request mainRequest, ScriptService scriptService, ClusterState clusterState, - ActionListener listener) { + ThreadPool threadPool, Request mainRequest, ScriptService scriptService, + ClusterState clusterState, ActionListener listener) { this(task, logger, client, threadPool, mainRequest, scriptService, clusterState, listener, client.settings()); } @@ -741,7 +741,7 @@ public DeleteRequest self() { /** * Wraps a {@link DeleteRequest} in a {@link RequestWrapper} */ - static RequestWrapper wrap(DeleteRequest request) { + public static RequestWrapper wrap(DeleteRequest request) { return new DeleteRequestWrapper(request); } diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java index d70b3c9c4ceb0..64b02c4be8150 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java @@ -21,9 +21,6 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.GenericAction; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.action.support.ActiveShardCount; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.settings.Settings; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByQueryRestHandler.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByQueryRestHandler.java index 480ca80e2ee11..32a252ccc4b19 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByQueryRestHandler.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByQueryRestHandler.java @@ -21,8 +21,6 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.action.GenericAction; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -35,7 +33,7 @@ import java.util.Map; import java.util.function.Consumer; -import static org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest.SIZE_ALL_MATCHES; +import static org.elasticsearch.index.reindex.AbstractBulkByScrollRequest.SIZE_ALL_MATCHES; /** * Rest handler for reindex actions that accepts a search request like Update-By-Query or Delete-By-Query diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AsyncDeleteByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AsyncDeleteByQueryAction.java similarity index 91% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/AsyncDeleteByQueryAction.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/AsyncDeleteByQueryAction.java index cdcfb754fb687..2608f5715ba3e 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AsyncDeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AsyncDeleteByQueryAction.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; @@ -32,8 +32,8 @@ */ public class AsyncDeleteByQueryAction extends AbstractAsyncBulkByScrollAction { public AsyncDeleteByQueryAction(WorkingBulkByScrollTask task, Logger logger, ParentTaskAssigningClient client, - ThreadPool threadPool, DeleteByQueryRequest request, ScriptService scriptService, ClusterState clusterState, - ActionListener listener) { + ThreadPool threadPool, DeleteByQueryRequest request, ScriptService scriptService, + ClusterState clusterState, ActionListener listener) { super(task, logger, client, threadPool, request, scriptService, clusterState, listener); } diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelper.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelper.java similarity index 88% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelper.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelper.java index f2bd62c233501..48f1030645450 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelper.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelper.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; import org.elasticsearch.action.ActionListener; @@ -32,13 +32,13 @@ /** * Helps parallelize reindex requests using sliced scrolls. */ -public class BulkByScrollParallelizationHelper { +class BulkByScrollParallelizationHelper { private BulkByScrollParallelizationHelper() {} - public static < - Request extends AbstractBulkByScrollRequest - > void startSlices(Client client, TaskManager taskManager, Action action, - String localNodeId, ParentBulkByScrollTask task, Request request, ActionListener listener) { + public static > void startSlices(Client client, TaskManager taskManager, + Action action, + String localNodeId, ParentBulkByScrollTask task, Request request, + ActionListener listener) { TaskId parentTaskId = new TaskId(localNodeId, task.getId()); for (final SearchRequest slice : sliceIntoSubRequests(request.getSearchRequest(), UidFieldMapper.NAME, request.getSlices())) { // TODO move the request to the correct node. maybe here or somehow do it as part of startup for reindex in general.... diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseContentListener.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseContentListener.java index a8d321a9fab8d..8e5dff170d481 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseContentListener.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseContentListener.java @@ -21,8 +21,7 @@ import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; +import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.rest.BytesRestResponse; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java index 2c84cfc86be6e..1c53a925f0d71 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.client.ElasticsearchClient; public class ReindexAction extends Action { diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java index fb203ee5c6dd8..d601f5c06e728 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java @@ -21,7 +21,6 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestDeleteByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestDeleteByQueryAction.java index 0e11d64a405ca..f906ef7660da8 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestDeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestDeleteByQueryAction.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.settings.Settings; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java index 57d3213da9220..6c16c31efb153 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java @@ -36,7 +36,6 @@ import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.index.VersionType; import org.elasticsearch.index.query.QueryParseContext; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.script.Script; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java index a17b2b81f910f..99e1a9f166dd8 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java @@ -20,12 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.bulk.byscroll.AsyncDeleteByQueryAction; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollParallelizationHelper; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; -import org.elasticsearch.action.bulk.byscroll.ParentBulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.WorkingBulkByScrollTask; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.client.Client; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportReindexAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportReindexAction.java index b232c50c2b2a8..737d885443a1a 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportReindexAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportReindexAction.java @@ -37,13 +37,7 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.bulk.BackoffPolicy; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollParallelizationHelper; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.ParentBulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; -import org.elasticsearch.action.bulk.byscroll.WorkingBulkByScrollTask; +import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.ActionFilters; @@ -68,7 +62,6 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.VersionType; import org.elasticsearch.index.mapper.VersionFieldMapper; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.index.reindex.remote.RemoteScrollableHitSource; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptService; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportRethrottleAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportRethrottleAction.java index 88329f5cb17b4..0901e5ade31a5 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportRethrottleAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportRethrottleAction.java @@ -24,7 +24,6 @@ import org.elasticsearch.action.FailedNodeException; import org.elasticsearch.action.TaskOperationFailure; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.tasks.TransportTasksAction; import org.elasticsearch.client.Client; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java index 12d8696319fb1..8924c7038c99e 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java @@ -21,12 +21,6 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.ParentBulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollParallelizationHelper; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource; -import org.elasticsearch.action.bulk.byscroll.WorkingBulkByScrollTask; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteResponseParsers.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteResponseParsers.java index e9807bdfa5b81..d9a897026d293 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteResponseParsers.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteResponseParsers.java @@ -20,10 +20,10 @@ package org.elasticsearch.index.reindex.remote; import org.elasticsearch.Version; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.BasicHit; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Hit; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Response; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; +import org.elasticsearch.index.reindex.ScrollableHitSource.BasicHit; +import org.elasticsearch.index.reindex.ScrollableHitSource.Hit; +import org.elasticsearch.index.reindex.ScrollableHitSource.Response; +import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.collect.Tuple; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java index f3caeb004c474..85173b7d89962 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java @@ -30,7 +30,7 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.Version; import org.elasticsearch.action.bulk.BackoffPolicy; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource; +import org.elasticsearch.index.reindex.ScrollableHitSource; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.client.ResponseException; import org.elasticsearch.client.ResponseListener; diff --git a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionMetadataTestCase.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionMetadataTestCase.java similarity index 96% rename from test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionMetadataTestCase.java rename to modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionMetadataTestCase.java index b68797381d176..34da9f56b482c 100644 --- a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionMetadataTestCase.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionMetadataTestCase.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; public abstract class AbstractAsyncBulkByScrollActionMetadataTestCase< Request extends AbstractBulkByScrollRequest, diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java index fd41a6d25f384..6ddf6daa8806c 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java @@ -20,12 +20,8 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionRequest; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction.OpType; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction.RequestWrapper; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollActionTestCase; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource; +import org.elasticsearch.index.reindex.AbstractAsyncBulkByScrollAction.OpType; +import org.elasticsearch.index.reindex.AbstractAsyncBulkByScrollAction.RequestWrapper; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.script.CompiledScript; diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/AsyncBulkByScrollActionTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/AsyncBulkByScrollActionTests.java rename to modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java index fa42573e439ee..5c437da3464ee 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/AsyncBulkByScrollActionTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ExceptionsHelper; @@ -36,8 +36,8 @@ import org.elasticsearch.action.bulk.BulkItemResponse.Failure; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Hit; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; +import org.elasticsearch.index.reindex.ScrollableHitSource.Hit; +import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.index.IndexRequest; @@ -696,7 +696,7 @@ private static class DummyAbstractBulkByScrollRequest extends AbstractBulkByScro } @Override - protected DummyAbstractBulkByScrollRequest forSlice(TaskId slicingTask, SearchRequest slice) { + public DummyAbstractBulkByScrollRequest forSlice(TaskId slicingTask, SearchRequest slice) { throw new UnsupportedOperationException(); } diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelperTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelperTests.java similarity index 94% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelperTests.java rename to modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelperTests.java index 498c6bf5286f9..a64415d08b1b1 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelperTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelperTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.index.mapper.IdFieldMapper; @@ -27,7 +27,7 @@ import java.io.IOException; import static java.util.Collections.emptyList; -import static org.elasticsearch.action.bulk.byscroll.BulkByScrollParallelizationHelper.sliceIntoSubRequests; +import static org.elasticsearch.index.reindex.BulkByScrollParallelizationHelper.sliceIntoSubRequests; import static org.elasticsearch.search.RandomSearchRequestGenerator.randomSearchRequest; import static org.elasticsearch.search.RandomSearchRequestGenerator.randomSearchSourceBuilder; diff --git a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkIndexByScrollResponseMatcher.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseMatcher.java similarity index 98% rename from test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkIndexByScrollResponseMatcher.java rename to modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseMatcher.java index 2902e02a30c8a..cb2ff1a7ae24a 100644 --- a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkIndexByScrollResponseMatcher.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseMatcher.java @@ -17,9 +17,8 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; @@ -155,4 +154,4 @@ public void describeTo(Description description) { description.appendText(" and reason cancelled matches ").appendDescriptionOf(reasonCancelledMatcher); description.appendText(" and slices matches ").appendDescriptionOf(slicesMatcher); } -} \ No newline at end of file +} diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseTests.java index 791277ad27097..6809a02585e1b 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseTests.java @@ -20,9 +20,7 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.bulk.BulkItemResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; +import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.test.ESTestCase; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java index 3aa35fb6e19de..a92ceedb0f365 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java @@ -22,9 +22,6 @@ import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.action.ingest.DeletePipelineRequest; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryConcurrentTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryConcurrentTests.java index b80360f12608f..8954f1c35c179 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryConcurrentTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryConcurrentTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.index.query.MatchQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java index c26984e09b649..4f7753fca9abd 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import java.util.ArrayList; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java index 96ed13f5b29e6..746b6adc70e73 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.test.junit.annotations.TestLogging; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java index 7f8a87c2dad9e..d7d9cfe051b8c 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java @@ -21,7 +21,6 @@ import org.elasticsearch.client.RestClient; import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.test.ESTestCase; import java.util.ArrayList; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWhitelistTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWhitelistTests.java index 1a25e4e5e9c45..128cd4043e283 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWhitelistTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWhitelistTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.test.ESTestCase; import java.net.UnknownHostException; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java index 6b9aaee8665d4..c4b5c26e5c4ef 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java @@ -35,7 +35,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.util.concurrent.ThreadContext; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.rest.RestStatus; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java index 490480e1ac584..4611f9dcbcddb 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java @@ -19,10 +19,7 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollActionMetadataTestCase; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Hit; +import org.elasticsearch.index.reindex.ScrollableHitSource.Hit; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.settings.Settings; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java index 17769eeb4ea8b..d0eb1dcd75ef2 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilder; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java index 8ef99bd18d6ce..4e2834a771a94 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.lucene.uid.Versions; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexSourceTargetValidationTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexSourceTargetValidationTests.java index f6d3217575d7d..28b9febe1c289 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexSourceTargetValidationTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexSourceTargetValidationTests.java @@ -33,7 +33,6 @@ import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.test.ESTestCase; import static java.util.Collections.emptyMap; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexTestCase.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexTestCase.java index e7dc7bf3a7732..fcf80ea283cf4 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexTestCase.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexTestCase.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkIndexByScrollResponseMatcher; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexVersioningTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexVersioningTests.java index b9b99ce163841..472dec4675f99 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexVersioningTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexVersioningTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.get.GetResponse; import static org.elasticsearch.action.DocWriteRequest.OpType.CREATE; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RestReindexActionTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RestReindexActionTests.java index b190aee3619a8..ffb609843f3b4 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RestReindexActionTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RestReindexActionTests.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.rest.RestController; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.rest.FakeRestRequest; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java index f40afce9a4e39..228bae4ed4a73 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java @@ -22,9 +22,6 @@ import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.elasticsearch.action.admin.cluster.node.tasks.list.TaskGroup; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.tasks.TaskId; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java index 86fbd47fd9665..9b19c572c0b3e 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java @@ -26,17 +26,12 @@ import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.bulk.Retry; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.BulkIndexByScrollResponseMatcher; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.threadpool.ThreadPool; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java index 5793db023f086..40377fe96462d 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java @@ -20,8 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.Version; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest; -import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.bytes.BytesArray; @@ -31,7 +29,6 @@ import org.elasticsearch.common.io.stream.Streamable; import org.elasticsearch.common.lucene.uid.Versions; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; import org.elasticsearch.tasks.TaskId; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java index b5572c1f34f30..222aedd2e9eeb 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java @@ -23,9 +23,6 @@ import org.elasticsearch.action.FailedNodeException; import org.elasticsearch.action.TaskOperationFailure; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.ParentBulkByScrollTask; import org.elasticsearch.client.Client; import org.elasticsearch.tasks.TaskId; import org.elasticsearch.tasks.TaskInfo; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java index 174fd67153717..b688ce019e3df 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java @@ -19,10 +19,7 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollActionMetadataTestCase; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Hit; +import org.elasticsearch.index.reindex.ScrollableHitSource.Hit; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.settings.Settings; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWhileModifyingTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWhileModifyingTests.java index cb027d156e661..987830ddd3bc9 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWhileModifyingTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWhileModifyingTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.index.engine.VersionConflictEngineException; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java index 3d478094e8bfe..9f43ea2d17067 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.script.ScriptService; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteInfoTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteInfoTests.java index fa1f46aa383a3..d6ab599b43c2d 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteInfoTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteInfoTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.reindex.remote; +import org.elasticsearch.index.reindex.RemoteInfo; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.test.ESTestCase; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSourceTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSourceTests.java index f63b05e96beb7..f67a5b627fb4c 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSourceTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSourceTests.java @@ -40,7 +40,7 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.Version; import org.elasticsearch.action.bulk.BackoffPolicy; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Response; +import org.elasticsearch.index.reindex.ScrollableHitSource.Response; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.client.HeapBufferedAsyncResponseConsumer; import org.elasticsearch.client.RestClient; diff --git a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionTestCase.java similarity index 97% rename from test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionTestCase.java rename to test/framework/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionTestCase.java index 907f8965632dd..b4e01b18a5647 100644 --- a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionTestCase.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.tasks.TaskId; From 9953a961439122d3837728d0de5aa08d967e9749 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 11 May 2017 10:17:36 -0400 Subject: [PATCH 308/619] Build: move dependency so it works in 5.x Relates to #24561 --- qa/reindex-from-old/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/reindex-from-old/build.gradle b/qa/reindex-from-old/build.gradle index 7dd119013b8fb..ca05e6919856a 100644 --- a/qa/reindex-from-old/build.gradle +++ b/qa/reindex-from-old/build.gradle @@ -72,7 +72,7 @@ for (String version : ['2', '1', '090']) { unzip.temporaryDir, version == '090' } - integTestCluster.dependsOn fixture + integTest.dependsOn fixture integTestRunner { /* Use a closure on the string to delay evaluation until right before we * run the integration tests so that we can be sure that the file is ready. From d9cac191a2452fe5d5a823ae925a73f70c0b736d Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Thu, 11 May 2017 11:39:33 -0400 Subject: [PATCH 309/619] Fix missing active IDs prevent advance test This commit addresses an issue in the missing active IDs prevent advance test from the global checkpoint tracker. The assumptions this test was making about reality were violated when global checkpoints were inlined (specifically, the component of that change where the tracker's knowledge of the global checkpoint was updated inline with updates to the tracker's knowledge of local checkpoints for an allocatio ID). The point of the test was to ensure that a lagging shard prevents the global checkpoint from advancing, so this commit rewrites the test with that in mind. --- .../index/seqno/GlobalCheckpointTrackerTests.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java b/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java index b99ac13a0d44a..61eb45813288b 100644 --- a/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java +++ b/core/src/test/java/org/elasticsearch/index/seqno/GlobalCheckpointTrackerTests.java @@ -145,17 +145,20 @@ public void testMissingActiveIdsPreventAdvance() { final Map assigned = new HashMap<>(); assigned.putAll(active); assigned.putAll(initializing); - final String maxActiveID = active.entrySet().stream().max(Comparator.comparing(Map.Entry::getValue)).get().getKey(); tracker.updateAllocationIdsFromMaster( - active.entrySet().stream().filter(e -> !e.getKey().equals(maxActiveID)).map(Map.Entry::getKey).collect(Collectors.toSet()), + active.keySet(), initializing.keySet()); randomSubsetOf(initializing.keySet()).forEach(k -> markAllocationIdAsInSyncQuietly(tracker, k, tracker.getGlobalCheckpoint())); - assigned.forEach(tracker::updateLocalCheckpoint); + final String missingActiveID = randomFrom(active.keySet()); + assigned + .entrySet() + .stream() + .filter(e -> !e.getKey().equals(missingActiveID)) + .forEach(e -> tracker.updateLocalCheckpoint(e.getKey(), e.getValue())); - // now mark all active shards - tracker.updateAllocationIdsFromMaster(active.keySet(), initializing.keySet()); + assertThat(tracker.getGlobalCheckpoint(), equalTo(UNASSIGNED_SEQ_NO)); - // update again + // now update all knowledge of all shards assigned.forEach(tracker::updateLocalCheckpoint); assertThat(tracker.getGlobalCheckpoint(), not(equalTo(UNASSIGNED_SEQ_NO))); } From f7c50f5f71e667388f222dd008a9d1e9ede47c5f Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Thu, 11 May 2017 18:10:27 +0200 Subject: [PATCH 310/619] Painless: Optimize instance creation in LambdaBootstrap (#24618) Optimize instance creation in LambdaBootstrap to allow Hotspot's escape analysis, preventing us from creating many instances stressing GC --- .../painless/LambdaBootstrap.java | 69 +++++++++++++------ .../painless/WriterConstants.java | 6 +- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/LambdaBootstrap.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/LambdaBootstrap.java index 2ffe9afadf38a..3a0b322d5eaf7 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/LambdaBootstrap.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/LambdaBootstrap.java @@ -38,6 +38,7 @@ import static java.lang.invoke.MethodHandles.Lookup; import static org.elasticsearch.painless.Compiler.Loader; import static org.elasticsearch.painless.WriterConstants.CLASS_VERSION; +import static org.elasticsearch.painless.WriterConstants.CTOR_METHOD_NAME; import static org.elasticsearch.painless.WriterConstants.DELEGATE_BOOTSTRAP_HANDLE; import static org.objectweb.asm.Opcodes.ACC_FINAL; import static org.objectweb.asm.Opcodes.ACC_PRIVATE; @@ -89,9 +90,13 @@ * public static final class $$Lambda0 implements Consumer { * private List arg$0; * - * public $$Lambda0(List arg$0) { + * private $$Lambda0(List arg$0) { * this.arg$0 = arg$0; * } + * + * public static Consumer create$lambda(List arg$0) { + * return new $$Lambda0(arg$0); + * } * * public void accept(Object val$0) { * Painless$Script.lambda$0(this.arg$0, val$0); @@ -120,10 +125,17 @@ * on whether or not there are captures. If there are no captures * the same instance of the generated lambda class will be * returned each time by the factory method as there are no - * changing values other than the arguments. If there are - * captures a new instance of the generated lambda class will - * be returned each time with the captures passed into the + * changing values other than the arguments, the lambda is a singleton. + * If there are captures, a new instance of the generated lambda class + * will be returned each time with the captures passed into the * factory method to be stored in the member fields. + * Instead of calling the ctor, a static factory method is created + * in the lambda class, because a method handle to the ctor directly + * is (currently) preventing Hotspot optimizer from correctly doing + * escape analysis. Escape analysis is important to optimize the + * code in a way, that a new instance is not created on each lambda + * invocation with captures, stressing garbage collector (thanks + * to Rémi Forax for the explanation about this on Jaxcon 2017!). */ public final class LambdaBootstrap { @@ -153,6 +165,11 @@ private Capture(int count, Class type) { */ private static final String DELEGATED_CTOR_WRAPPER_NAME = "delegate$ctor"; + /** + * This method name is used to generate the static factory for capturing lambdas. + */ + private static final String LAMBDA_FACTORY_METHOD_NAME = "create$lambda"; + /** * Generates a lambda class for a lambda function/method reference * within a Painless script. Variables with the prefix interface are considered @@ -198,7 +215,8 @@ public static CallSite lambdaBootstrap( // Handles the special case where a method reference refers to a ctor (we need a static wrapper method): if (delegateInvokeType == H_NEWINVOKESPECIAL) { - generateStaticCtorDelegator(cw, delegateClassType, delegateMethodName, delegateMethodType); + assert CTOR_METHOD_NAME.equals(delegateMethodName); + generateStaticCtorDelegator(cw, ACC_PRIVATE, DELEGATED_CTOR_WRAPPER_NAME, delegateClassType, delegateMethodType); // replace the delegate with our static wrapper: delegateMethodName = DELEGATED_CTOR_WRAPPER_NAME; delegateClassType = lambdaClassType; @@ -281,16 +299,15 @@ private static void generateLambdaConstructor( MethodType factoryMethodType, Capture[] captures) { - String conName = ""; String conDesc = factoryMethodType.changeReturnType(void.class).toMethodDescriptorString(); - Method conMeth = new Method(conName, conDesc); + Method conMeth = new Method(CTOR_METHOD_NAME, conDesc); Type baseConType = Type.getType(Object.class); - Method baseConMeth = new Method(conName, + Method baseConMeth = new Method(CTOR_METHOD_NAME, MethodType.methodType(void.class).toMethodDescriptorString()); - int modifiers = ACC_PUBLIC; + int modifiers = (captures.length > 0) ? ACC_PRIVATE : ACC_PUBLIC; GeneratorAdapter constructor = new GeneratorAdapter(modifiers, conMeth, - cw.visitMethod(modifiers, conName, conDesc, null, null)); + cw.visitMethod(modifiers, CTOR_METHOD_NAME, conDesc, null, null)); constructor.visitCode(); constructor.loadThis(); constructor.invokeConstructor(baseConType, baseConMeth); @@ -304,21 +321,31 @@ private static void generateLambdaConstructor( constructor.returnValue(); constructor.endMethod(); + + // Add a factory method, if lambda takes captures. + // @uschindler says: I talked with Rémi Forax about this. Technically, a plain ctor + // and a MethodHandle to the ctor would be enough - BUT: Hotspot is unable to + // do escape analysis through a MethodHandles.findConstructor generated handle. + // Because of this we create a factory method. With this factory method, the + // escape analysis can figure out that everything is final and we don't need + // an instance, so it can omit object creation on heap! + if (captures.length > 0) { + generateStaticCtorDelegator(cw, ACC_PUBLIC, LAMBDA_FACTORY_METHOD_NAME, lambdaClassType, factoryMethodType); + } } /** - * Generates a factory method to delegate to constructors using - * {@code INVOKEDYNAMIC} using the {@link #delegateBootstrap} type converter. + * Generates a factory method to delegate to constructors. */ - private static void generateStaticCtorDelegator(ClassWriter cw, Type delegateClassType, String delegateMethodName, - MethodType delegateMethodType) { - Method wrapperMethod = new Method(DELEGATED_CTOR_WRAPPER_NAME, delegateMethodType.toMethodDescriptorString()); + private static void generateStaticCtorDelegator(ClassWriter cw, int access, String delegatorMethodName, + Type delegateClassType, MethodType delegateMethodType) { + Method wrapperMethod = new Method(delegatorMethodName, delegateMethodType.toMethodDescriptorString()); Method constructorMethod = - new Method(delegateMethodName, delegateMethodType.changeReturnType(void.class).toMethodDescriptorString()); - int modifiers = ACC_PRIVATE | ACC_STATIC; + new Method(CTOR_METHOD_NAME, delegateMethodType.changeReturnType(void.class).toMethodDescriptorString()); + int modifiers = access | ACC_STATIC; GeneratorAdapter factory = new GeneratorAdapter(modifiers, wrapperMethod, - cw.visitMethod(modifiers, DELEGATED_CTOR_WRAPPER_NAME, delegateMethodType.toMethodDescriptorString(), null, null)); + cw.visitMethod(modifiers, delegatorMethodName, delegateMethodType.toMethodDescriptorString(), null, null)); factory.visitCode(); factory.newInstance(delegateClassType); factory.dup(); @@ -329,7 +356,8 @@ private static void generateStaticCtorDelegator(ClassWriter cw, Type delegateCla } /** - * Generates the interface method that will delegate (call) to the delegate method. + * Generates the interface method that will delegate (call) to the delegate method + * with {@code INVOKEDYNAMIC} using the {@link #delegateBootstrap} type converter. */ private static void generateInterfaceMethod( ClassWriter cw, @@ -464,8 +492,7 @@ private static CallSite createCaptureCallSite( try { return new ConstantCallSite( - lookup.findConstructor(lambdaClass, factoryMethodType.changeReturnType(void.class)) - .asType(factoryMethodType)); + lookup.findStatic(lambdaClass, LAMBDA_FACTORY_METHOD_NAME, factoryMethodType)); } catch (ReflectiveOperationException exception) { throw new IllegalStateException("unable to create lambda class", exception); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java index 6b9a71a752b3b..af9f84424b4af 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java @@ -53,8 +53,10 @@ public final class WriterConstants { public static final String CLASS_NAME = BASE_CLASS_NAME + "$Script"; public static final Type CLASS_TYPE = Type.getObjectType(CLASS_NAME.replace('.', '/')); + + public static final String CTOR_METHOD_NAME = ""; - public static final Method CONSTRUCTOR = getAsmMethod(void.class, "", String.class, String.class, BitSet.class); + public static final Method CONSTRUCTOR = getAsmMethod(void.class, CTOR_METHOD_NAME, String.class, String.class, BitSet.class); public static final Method CLINIT = getAsmMethod(void.class, ""); // All of these types are caught by the main method and rethrown as ScriptException @@ -162,7 +164,7 @@ public final class WriterConstants { public static final Type STRING_TYPE = Type.getType(String.class); public static final Type STRINGBUILDER_TYPE = Type.getType(StringBuilder.class); - public static final Method STRINGBUILDER_CONSTRUCTOR = getAsmMethod(void.class, ""); + public static final Method STRINGBUILDER_CONSTRUCTOR = getAsmMethod(void.class, CTOR_METHOD_NAME); public static final Method STRINGBUILDER_APPEND_BOOLEAN = getAsmMethod(StringBuilder.class, "append", boolean.class); public static final Method STRINGBUILDER_APPEND_CHAR = getAsmMethod(StringBuilder.class, "append", char.class); public static final Method STRINGBUILDER_APPEND_INT = getAsmMethod(StringBuilder.class, "append", int.class); From 57fddce8c4e8bb9a8e092907e1cd5a2024fe47dd Mon Sep 17 00:00:00 2001 From: Lee Hinman Date: Thu, 11 May 2017 10:09:14 -0600 Subject: [PATCH 311/619] [TEST] Use at least 1ms for FunctionScoreQueryBuilderTests Previously micros or nanoseconds could be used, which was reduced to 0 milliseconds and `scale` must be higher than 0. --- .../functionscore/FunctionScoreQueryBuilderTests.java | 2 +- .../src/main/java/org/elasticsearch/test/ESTestCase.java | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java index 702e8b6091797..67ddcbd6b0e5a 100644 --- a/core/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java @@ -218,7 +218,7 @@ private static DecayFunctionBuilder createRandomDecayFunction() { break; case DATE_FIELD_NAME: origin = new DateTime(System.currentTimeMillis() - randomIntBetween(0, 1000000), DateTimeZone.UTC).toString(); - scale = randomPositiveTimeValue(); + scale = randomTimeValue(1, 1000, new String[]{"d", "h", "ms", "s", "m"}); offset = randomPositiveTimeValue(); break; default: diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index 7e07df35e8b50..9e6bb080b1604 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -631,8 +631,12 @@ public static String[] generateRandomStringArray(int maxArraySize, int maxString private static final String[] TIME_SUFFIXES = new String[]{"d", "h", "ms", "s", "m", "micros", "nanos"}; - private static String randomTimeValue(int lower, int upper) { - return randomIntBetween(lower, upper) + randomFrom(TIME_SUFFIXES); + public static String randomTimeValue(int lower, int upper, String[] suffixes) { + return randomIntBetween(lower, upper) + randomFrom(suffixes); + } + + public static String randomTimeValue(int lower, int upper) { + return randomTimeValue(lower, upper, TIME_SUFFIXES); } public static String randomTimeValue() { From 952feb58e41bd0220784c44136b840ea4a740e26 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Thu, 11 May 2017 18:26:40 +0200 Subject: [PATCH 312/619] Revert "Move DeleteByQuery and Reindex requests into core (#24578)" This reverts commit 6ea2ae32b8b372379fe9e2fbc4365dac03747617. --- .../resources/checkstyle_suppressions.xml | 1 + .../AbstractAsyncBulkByScrollAction.java | 12 +++++----- .../AbstractBulkByScrollRequest.java | 4 ++-- .../AbstractBulkByScrollRequestBuilder.java | 2 +- .../byscroll}/AsyncDeleteByQueryAction.java | 6 ++--- .../BulkByScrollParallelizationHelper.java | 12 +++++----- .../bulk/byscroll}/BulkByScrollResponse.java | 15 ++++++------ .../bulk/byscroll}/BulkByScrollTask.java | 2 +- .../byscroll}/ClientScrollableHitSource.java | 2 +- .../bulk/byscroll}/DeleteByQueryRequest.java | 4 ++-- .../byscroll}/ParentBulkByScrollTask.java | 4 ++-- .../bulk/byscroll}/ScrollableHitSource.java | 4 ++-- .../bulk/byscroll}/SuccessfullyProcessed.java | 2 +- .../byscroll}/WorkingBulkByScrollTask.java | 24 +++++++++---------- .../bulk/byscroll}/package-info.java | 5 ++-- .../AsyncBulkByScrollActionTests.java | 8 +++---- ...ulkByScrollParallelizationHelperTests.java | 4 ++-- .../byscroll}/BulkByScrollResponseTests.java | 11 +++++---- .../BulkByScrollTaskStatusTests.java | 5 ++-- .../bulk/byscroll}/BulkByScrollTaskTests.java | 2 +- .../byscroll}/DeleteByQueryRequestTests.java | 2 +- .../ParentBulkByScrollTaskTests.java | 2 +- .../WorkingBulkByScrollTaskTests.java | 4 +++- .../AbstractBaseReindexRestHandler.java | 3 +++ .../AbstractBulkByQueryRestHandler.java | 4 +++- .../AbstractBulkIndexByScrollRequest.java | 1 + ...stractBulkIndexByScrollRequestBuilder.java | 2 ++ ...kIndexByScrollResponseContentListener.java | 3 ++- .../index/reindex/DeleteByQueryAction.java | 2 ++ .../reindex/DeleteByQueryRequestBuilder.java | 3 +++ .../index/reindex/ReindexAction.java | 1 + .../index/reindex/ReindexPlugin.java | 1 + .../index/reindex/ReindexRequest.java | 3 ++- .../index/reindex/ReindexRequestBuilder.java | 2 ++ .../reindex/RestDeleteByQueryAction.java | 1 + .../index/reindex/RestReindexAction.java | 1 + .../reindex/TransportDeleteByQueryAction.java | 6 +++++ .../index/reindex/TransportReindexAction.java | 9 ++++++- .../reindex/TransportRethrottleAction.java | 1 + .../reindex/TransportUpdateByQueryAction.java | 6 +++++ .../index/reindex/UpdateByQueryAction.java | 1 + .../index/reindex/UpdateByQueryRequest.java | 2 +- .../reindex/UpdateByQueryRequestBuilder.java | 1 + .../index/reindex/remote}/RemoteInfo.java | 2 +- .../reindex/remote/RemoteResponseParsers.java | 8 +++---- .../remote/RemoteScrollableHitSource.java | 2 +- ...AsyncBulkByScrollActionScriptTestCase.java | 8 +++++-- .../BulkIndexByScrollResponseTests.java | 4 +++- .../index/reindex/CancelTests.java | 3 +++ .../reindex/DeleteByQueryConcurrentTests.java | 1 + .../index/reindex/ReindexBasicTests.java | 1 + .../index/reindex/ReindexFailureTests.java | 1 + ...ReindexFromRemoteBuildRestClientTests.java | 1 + .../ReindexFromRemoteWhitelistTests.java | 1 + .../ReindexFromRemoteWithAuthTests.java | 1 + .../index/reindex/ReindexMetadataTests.java | 5 +++- .../reindex/ReindexParentChildTests.java | 1 + .../index/reindex/ReindexRequestTests.java | 2 ++ .../index/reindex/ReindexScriptTests.java | 1 + .../ReindexSourceTargetValidationTests.java | 1 + .../index/reindex/ReindexTestCase.java | 1 + .../index/reindex/ReindexVersioningTests.java | 1 + .../index/reindex/RestReindexActionTests.java | 1 + .../index/reindex/RethrottleTests.java | 3 +++ .../index/reindex/RetryTests.java | 5 ++++ .../index/reindex/RoundTripTests.java | 3 +++ .../TransportRethrottleActionTests.java | 3 +++ .../reindex/UpdateByQueryMetadataTests.java | 5 +++- .../reindex/UpdateByQueryRequestTests.java | 1 + .../UpdateByQueryWhileModifyingTests.java | 1 + .../reindex/UpdateByQueryWithScriptTests.java | 1 + .../index/reindex/remote/RemoteInfoTests.java | 1 - .../RemoteScrollableHitSourceTests.java | 2 +- ...yncBulkByScrollActionMetadataTestCase.java | 2 +- ...stractAsyncBulkByScrollActionTestCase.java | 2 +- .../AbstractBulkByScrollRequestTestCase.java | 2 +- .../BulkIndexByScrollResponseMatcher.java | 5 ++-- 77 files changed, 182 insertions(+), 90 deletions(-) rename {modules/reindex/src/main/java/org/elasticsearch/index/reindex => core/src/main/java/org/elasticsearch/action/bulk/byscroll}/AbstractAsyncBulkByScrollAction.java (98%) rename core/src/main/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/AbstractBulkByScrollRequest.java (99%) rename core/src/main/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/AbstractBulkByScrollRequestBuilder.java (99%) rename {modules/reindex/src/main/java/org/elasticsearch/index/reindex => core/src/main/java/org/elasticsearch/action/bulk/byscroll}/AsyncDeleteByQueryAction.java (91%) rename {modules/reindex/src/main/java/org/elasticsearch/index/reindex => core/src/main/java/org/elasticsearch/action/bulk/byscroll}/BulkByScrollParallelizationHelper.java (88%) rename core/src/main/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/BulkByScrollResponse.java (93%) rename core/src/main/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/BulkByScrollTask.java (99%) rename core/src/main/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/ClientScrollableHitSource.java (99%) rename core/src/main/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/DeleteByQueryRequest.java (97%) rename core/src/main/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/ParentBulkByScrollTask.java (97%) rename core/src/main/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/ScrollableHitSource.java (99%) rename core/src/main/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/SuccessfullyProcessed.java (96%) rename core/src/main/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/WorkingBulkByScrollTask.java (95%) rename core/src/main/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/package-info.java (87%) rename {modules/reindex/src/test/java/org/elasticsearch/index/reindex => core/src/test/java/org/elasticsearch/action/bulk/byscroll}/AsyncBulkByScrollActionTests.java (99%) rename {modules/reindex/src/test/java/org/elasticsearch/index/reindex => core/src/test/java/org/elasticsearch/action/bulk/byscroll}/BulkByScrollParallelizationHelperTests.java (94%) rename core/src/test/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/BulkByScrollResponseTests.java (90%) rename core/src/test/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/BulkByScrollTaskStatusTests.java (97%) rename core/src/test/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/BulkByScrollTaskTests.java (99%) rename core/src/test/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/DeleteByQueryRequestTests.java (99%) rename core/src/test/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/ParentBulkByScrollTaskTests.java (99%) rename core/src/test/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/WorkingBulkByScrollTaskTests.java (98%) rename {core => modules/reindex}/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java (97%) rename {core => modules/reindex}/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java (92%) rename {core => modules/reindex}/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java (91%) rename {core => modules/reindex}/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java (90%) rename {core => modules/reindex}/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java (97%) rename {core => modules/reindex}/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java (95%) rename {core => modules/reindex}/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java (95%) rename {core => modules/reindex}/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java (97%) rename {core => modules/reindex}/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java (96%) rename {core/src/main/java/org/elasticsearch/index/reindex => modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote}/RemoteInfo.java (99%) rename {core => modules/reindex}/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java (97%) rename {core => modules/reindex}/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java (97%) rename {modules/reindex/src/test/java/org/elasticsearch/index/reindex => test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll}/AbstractAsyncBulkByScrollActionMetadataTestCase.java (96%) rename test/framework/src/main/java/org/elasticsearch/{index/reindex => action/bulk/byscroll}/AbstractAsyncBulkByScrollActionTestCase.java (97%) rename {core/src/test/java/org/elasticsearch/index/reindex => test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll}/AbstractBulkByScrollRequestTestCase.java (98%) rename {modules/reindex/src/test/java/org/elasticsearch/index/reindex => test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll}/BulkIndexByScrollResponseMatcher.java (98%) diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 9a550740fde36..3e0028032134a 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -127,6 +127,7 @@ + diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollAction.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollAction.java similarity index 98% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollAction.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollAction.java index 2a23823c85843..3ba07ea5538f5 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollAction.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollAction.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; @@ -31,7 +31,7 @@ import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.bulk.Retry; -import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.client.ParentTaskAssigningClient; @@ -77,7 +77,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; import static org.elasticsearch.action.bulk.BackoffPolicy.exponentialBackoff; -import static org.elasticsearch.index.reindex.AbstractBulkByScrollRequest.SIZE_ALL_MATCHES; +import static org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest.SIZE_ALL_MATCHES; import static org.elasticsearch.common.unit.TimeValue.timeValueNanos; import static org.elasticsearch.rest.RestStatus.CONFLICT; import static org.elasticsearch.search.sort.SortBuilders.fieldSort; @@ -116,8 +116,8 @@ public abstract class AbstractAsyncBulkByScrollAction, ScrollableHitSource.Hit, RequestWrapper> scriptApplier; public AbstractAsyncBulkByScrollAction(WorkingBulkByScrollTask task, Logger logger, ParentTaskAssigningClient client, - ThreadPool threadPool, Request mainRequest, ScriptService scriptService, - ClusterState clusterState, ActionListener listener) { + ThreadPool threadPool, Request mainRequest, ScriptService scriptService, ClusterState clusterState, + ActionListener listener) { this(task, logger, client, threadPool, mainRequest, scriptService, clusterState, listener, client.settings()); } @@ -741,7 +741,7 @@ public DeleteRequest self() { /** * Wraps a {@link DeleteRequest} in a {@link RequestWrapper} */ - public static RequestWrapper wrap(DeleteRequest request) { + static RequestWrapper wrap(DeleteRequest request) { return new DeleteRequestWrapper(request); } diff --git a/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequest.java similarity index 99% rename from core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequest.java index a582248af11ae..445808750113a 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.Version; import org.elasticsearch.action.ActionRequest; @@ -355,7 +355,7 @@ public int getSlices() { /** * Build a new request for a slice of the parent request. */ - public abstract Self forSlice(TaskId slicingTask, SearchRequest slice); + protected abstract Self forSlice(TaskId slicingTask, SearchRequest slice); /** * Setup a clone of this request with the information needed to process a slice of it. diff --git a/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestBuilder.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestBuilder.java similarity index 99% rename from core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestBuilder.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestBuilder.java index de3f22f0943a8..4131175f9df09 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestBuilder.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.action.Action; import org.elasticsearch.action.ActionRequestBuilder; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AsyncDeleteByQueryAction.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AsyncDeleteByQueryAction.java similarity index 91% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/AsyncDeleteByQueryAction.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/AsyncDeleteByQueryAction.java index 2608f5715ba3e..cdcfb754fb687 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AsyncDeleteByQueryAction.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AsyncDeleteByQueryAction.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; @@ -32,8 +32,8 @@ */ public class AsyncDeleteByQueryAction extends AbstractAsyncBulkByScrollAction { public AsyncDeleteByQueryAction(WorkingBulkByScrollTask task, Logger logger, ParentTaskAssigningClient client, - ThreadPool threadPool, DeleteByQueryRequest request, ScriptService scriptService, - ClusterState clusterState, ActionListener listener) { + ThreadPool threadPool, DeleteByQueryRequest request, ScriptService scriptService, ClusterState clusterState, + ActionListener listener) { super(task, logger, client, threadPool, request, scriptService, clusterState, listener); } diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelper.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelper.java similarity index 88% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelper.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelper.java index 48f1030645450..f2bd62c233501 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelper.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelper.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.action.Action; import org.elasticsearch.action.ActionListener; @@ -32,13 +32,13 @@ /** * Helps parallelize reindex requests using sliced scrolls. */ -class BulkByScrollParallelizationHelper { +public class BulkByScrollParallelizationHelper { private BulkByScrollParallelizationHelper() {} - public static > void startSlices(Client client, TaskManager taskManager, - Action action, - String localNodeId, ParentBulkByScrollTask task, Request request, - ActionListener listener) { + public static < + Request extends AbstractBulkByScrollRequest + > void startSlices(Client client, TaskManager taskManager, Action action, + String localNodeId, ParentBulkByScrollTask task, Request request, ActionListener listener) { TaskId parentTaskId = new TaskId(localNodeId, task.getId()); for (final SearchRequest slice : sliceIntoSubRequests(request.getSearchRequest(), UidFieldMapper.NAME, request.getSlices())) { // TODO move the request to the correct node. maybe here or somehow do it as part of startup for reindex in general.... diff --git a/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollResponse.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponse.java similarity index 93% rename from core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollResponse.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponse.java index 400baf7b9e2c6..20f2cf2ed06b4 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollResponse.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponse.java @@ -17,10 +17,11 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -44,14 +45,14 @@ public class BulkByScrollResponse extends ActionResponse implements ToXContent { private TimeValue took; private BulkByScrollTask.Status status; private List bulkFailures; - private List searchFailures; + private List searchFailures; private boolean timedOut; public BulkByScrollResponse() { } public BulkByScrollResponse(TimeValue took, BulkByScrollTask.Status status, List bulkFailures, - List searchFailures, boolean timedOut) { + List searchFailures, boolean timedOut) { this.took = took; this.status = requireNonNull(status, "Null status not supported"); this.bulkFailures = bulkFailures; @@ -138,7 +139,7 @@ public List getBulkFailures() { /** * All search failures. */ - public List getSearchFailures() { + public List getSearchFailures() { return searchFailures; } @@ -165,7 +166,7 @@ public void readFrom(StreamInput in) throws IOException { took = new TimeValue(in); status = new BulkByScrollTask.Status(in); bulkFailures = in.readList(Failure::new); - searchFailures = in.readList(ScrollableHitSource.SearchFailure::new); + searchFailures = in.readList(SearchFailure::new); timedOut = in.readBoolean(); } @@ -180,7 +181,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws failure.toXContent(builder, params); builder.endObject(); } - for (ScrollableHitSource.SearchFailure failure: searchFailures) { + for (SearchFailure failure: searchFailures) { failure.toXContent(builder, params); } builder.endArray(); @@ -198,4 +199,4 @@ public String toString() { builder.append(",search_failures=").append(getSearchFailures().subList(0, min(3, getSearchFailures().size()))); return builder.append(']').toString(); } -} +} \ No newline at end of file diff --git a/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollTask.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTask.java similarity index 99% rename from core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollTask.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTask.java index 18c6dac92064a..7c9124057b3c0 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollTask.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTask.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; diff --git a/core/src/main/java/org/elasticsearch/index/reindex/ClientScrollableHitSource.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ClientScrollableHitSource.java similarity index 99% rename from core/src/main/java/org/elasticsearch/index/reindex/ClientScrollableHitSource.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/ClientScrollableHitSource.java index 2f6775a1eae08..3bacc187ebb6d 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/ClientScrollableHitSource.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ClientScrollableHitSource.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; diff --git a/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequest.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequest.java similarity index 97% rename from core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequest.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequest.java index ad70748f3e431..2644d0d94967f 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequest.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.IndicesRequest; @@ -81,7 +81,7 @@ public ActionRequestValidationException validate() { } @Override - public DeleteByQueryRequest forSlice(TaskId slicingTask, SearchRequest slice) { + protected DeleteByQueryRequest forSlice(TaskId slicingTask, SearchRequest slice) { return doForSlice(new DeleteByQueryRequest(slice, false), slicingTask); } diff --git a/core/src/main/java/org/elasticsearch/index/reindex/ParentBulkByScrollTask.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTask.java similarity index 97% rename from core/src/main/java/org/elasticsearch/index/reindex/ParentBulkByScrollTask.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTask.java index bea9bc203653d..a37dc61897c0c 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/ParentBulkByScrollTask.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTask.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.collect.Tuple; @@ -104,7 +104,7 @@ public void onSliceResponse(ActionListener listener, int s /** * Record a failure from a slice and respond to the listener if the request is finished. */ - public void onSliceFailure(ActionListener listener, int sliceId, Exception e) { + void onSliceFailure(ActionListener listener, int sliceId, Exception e) { results.setOnce(sliceId, new Result(sliceId, e)); recordSliceCompletionAndRespondIfAllDone(listener); // TODO cancel when a slice fails? diff --git a/core/src/main/java/org/elasticsearch/index/reindex/ScrollableHitSource.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ScrollableHitSource.java similarity index 99% rename from core/src/main/java/org/elasticsearch/index/reindex/ScrollableHitSource.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/ScrollableHitSource.java index 3d1eb582db879..6426bad592f3e 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/ScrollableHitSource.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ScrollableHitSource.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.apache.logging.log4j.Logger; import org.elasticsearch.ElasticsearchException; @@ -111,7 +111,7 @@ public final void close(Runnable onCompletion) { /** * Set the id of the last scroll. Used for debugging. */ - public final void setScroll(String scrollId) { + final void setScroll(String scrollId) { this.scrollId.set(scrollId); } diff --git a/core/src/main/java/org/elasticsearch/index/reindex/SuccessfullyProcessed.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/SuccessfullyProcessed.java similarity index 96% rename from core/src/main/java/org/elasticsearch/index/reindex/SuccessfullyProcessed.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/SuccessfullyProcessed.java index 6547984900e4b..a0176e3520254 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/SuccessfullyProcessed.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/SuccessfullyProcessed.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; /** * Implemented by {@link BulkByScrollTask} and {@link BulkByScrollTask.Status} to consistently implement diff --git a/core/src/main/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTask.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTask.java similarity index 95% rename from core/src/main/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTask.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTask.java index acb1c0e5547ca..28f08417abbd3 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTask.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTask.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.apache.logging.log4j.Logger; import org.elasticsearch.common.logging.ESLoggerFactory; @@ -115,15 +115,15 @@ TimeValue throttledUntil() { return timeValueNanos(max(0, delayed.future.getDelay(TimeUnit.NANOSECONDS))); } - public void setTotal(long totalHits) { + void setTotal(long totalHits) { total.set(totalHits); } - public void countBatch() { + void countBatch() { batch.incrementAndGet(); } - public void countNoop() { + void countNoop() { noops.incrementAndGet(); } @@ -132,7 +132,7 @@ public long getCreated() { return created.get(); } - public void countCreated() { + void countCreated() { created.incrementAndGet(); } @@ -141,7 +141,7 @@ public long getUpdated() { return updated.get(); } - public void countUpdated() { + void countUpdated() { updated.incrementAndGet(); } @@ -150,15 +150,15 @@ public long getDeleted() { return deleted.get(); } - public void countDeleted() { + void countDeleted() { deleted.incrementAndGet(); } - public void countVersionConflict() { + void countVersionConflict() { versionConflicts.incrementAndGet(); } - public void countBulkRetry() { + void countBulkRetry() { bulkRetries.incrementAndGet(); } @@ -174,8 +174,8 @@ float getRequestsPerSecond() { * Schedule prepareBulkRequestRunnable to run after some delay. This is where throttling plugs into reindexing so the request can be * rescheduled over and over again. */ - public void delayPrepareBulkRequest(ThreadPool threadPool, TimeValue lastBatchStartTime, int lastBatchSize, - AbstractRunnable prepareBulkRequestRunnable) { + void delayPrepareBulkRequest(ThreadPool threadPool, TimeValue lastBatchStartTime, int lastBatchSize, + AbstractRunnable prepareBulkRequestRunnable) { // Synchronize so we are less likely to schedule the same request twice. synchronized (delayedPrepareBulkRequestReference) { TimeValue delay = throttleWaitTime(lastBatchStartTime, timeValueNanos(System.nanoTime()), lastBatchSize); @@ -184,7 +184,7 @@ public void delayPrepareBulkRequest(ThreadPool threadPool, TimeValue lastBatchSt } } - public TimeValue throttleWaitTime(TimeValue lastBatchStartTime, TimeValue now, int lastBatchSize) { + TimeValue throttleWaitTime(TimeValue lastBatchStartTime, TimeValue now, int lastBatchSize) { long earliestNextBatchStartTime = now.nanos() + (long) perfectlyThrottledBatchTime(lastBatchSize); return timeValueNanos(max(0, earliestNextBatchStartTime - System.nanoTime())); } diff --git a/core/src/main/java/org/elasticsearch/index/reindex/package-info.java b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/package-info.java similarity index 87% rename from core/src/main/java/org/elasticsearch/index/reindex/package-info.java rename to core/src/main/java/org/elasticsearch/action/bulk/byscroll/package-info.java index 00cb5106770d1..3a31ea2f6541c 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/package-info.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/byscroll/package-info.java @@ -18,7 +18,6 @@ */ /** - * Infrastructure for actions that modify documents based on the results of a scrolling query - * like reindex, update by query or delete by query. + * Infrastructure for actions that modify documents based on the results of a scrolling query. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/AsyncBulkByScrollActionTests.java similarity index 99% rename from modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java rename to core/src/test/java/org/elasticsearch/action/bulk/byscroll/AsyncBulkByScrollActionTests.java index 5c437da3464ee..fa42573e439ee 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/AsyncBulkByScrollActionTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ExceptionsHelper; @@ -36,8 +36,8 @@ import org.elasticsearch.action.bulk.BulkItemResponse.Failure; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; -import org.elasticsearch.index.reindex.ScrollableHitSource.Hit; -import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Hit; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.index.IndexRequest; @@ -696,7 +696,7 @@ private static class DummyAbstractBulkByScrollRequest extends AbstractBulkByScro } @Override - public DummyAbstractBulkByScrollRequest forSlice(TaskId slicingTask, SearchRequest slice) { + protected DummyAbstractBulkByScrollRequest forSlice(TaskId slicingTask, SearchRequest slice) { throw new UnsupportedOperationException(); } diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelperTests.java b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelperTests.java similarity index 94% rename from modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelperTests.java rename to core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelperTests.java index a64415d08b1b1..498c6bf5286f9 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelperTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelperTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.index.mapper.IdFieldMapper; @@ -27,7 +27,7 @@ import java.io.IOException; import static java.util.Collections.emptyList; -import static org.elasticsearch.index.reindex.BulkByScrollParallelizationHelper.sliceIntoSubRequests; +import static org.elasticsearch.action.bulk.byscroll.BulkByScrollParallelizationHelper.sliceIntoSubRequests; import static org.elasticsearch.search.RandomSearchRequestGenerator.randomSearchRequest; import static org.elasticsearch.search.RandomSearchRequestGenerator.randomSearchSourceBuilder; diff --git a/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollResponseTests.java b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponseTests.java similarity index 90% rename from core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollResponseTests.java rename to core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponseTests.java index a288328391a9d..1d2146c1515d1 100644 --- a/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollResponseTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponseTests.java @@ -17,11 +17,12 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.test.ESTestCase; @@ -55,7 +56,7 @@ private List randomIndexingFailures() { randomSimpleString(random()), new IllegalArgumentException("test"))); } - private List randomSearchFailures() { + private List randomSearchFailures() { if (randomBoolean()) { return emptyList(); } @@ -67,7 +68,7 @@ private List randomSearchFailures() { shardId = randomInt(); nodeId = usually() ? randomAlphaOfLength(5) : null; } - return singletonList(new ScrollableHitSource.SearchFailure(new ElasticsearchException("foo"), index, shardId, nodeId)); + return singletonList(new SearchFailure(new ElasticsearchException("foo"), index, shardId, nodeId)); } private void assertResponseEquals(BulkByScrollResponse expected, BulkByScrollResponse actual) { @@ -85,8 +86,8 @@ private void assertResponseEquals(BulkByScrollResponse expected, BulkByScrollRes } assertEquals(expected.getSearchFailures().size(), actual.getSearchFailures().size()); for (int i = 0; i < expected.getSearchFailures().size(); i++) { - ScrollableHitSource.SearchFailure expectedFailure = expected.getSearchFailures().get(i); - ScrollableHitSource.SearchFailure actualFailure = actual.getSearchFailures().get(i); + SearchFailure expectedFailure = expected.getSearchFailures().get(i); + SearchFailure actualFailure = actual.getSearchFailures().get(i); assertEquals(expectedFailure.getIndex(), actualFailure.getIndex()); assertEquals(expectedFailure.getShardId(), actualFailure.getShardId()); assertEquals(expectedFailure.getNodeId(), actualFailure.getNodeId()); diff --git a/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskStatusTests.java similarity index 97% rename from core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java rename to core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskStatusTests.java index 982198c8fee47..503fe1db7cd32 100644 --- a/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskStatusTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.ElasticsearchException; @@ -26,7 +26,6 @@ import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.test.ESTestCase; -import org.hamcrest.Matchers; import java.io.IOException; import java.util.List; @@ -76,7 +75,7 @@ public static void assertTaskStatusEquals(Version version, BulkByScrollTask.Stat assertEquals(expected.getReasonCancelled(), actual.getReasonCancelled()); assertEquals(expected.getThrottledUntil(), actual.getThrottledUntil()); if (version.onOrAfter(Version.V_5_1_1_UNRELEASED)) { - assertThat(actual.getSliceStatuses(), Matchers.hasSize(expected.getSliceStatuses().size())); + assertThat(actual.getSliceStatuses(), hasSize(expected.getSliceStatuses().size())); for (int i = 0; i < expected.getSliceStatuses().size(); i++) { BulkByScrollTask.StatusOrException sliceStatus = expected.getSliceStatuses().get(i); if (sliceStatus == null) { diff --git a/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskTests.java b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskTests.java rename to core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskTests.java index f4d4ea790bc50..ff0eae5552042 100644 --- a/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.ToXContent; diff --git a/core/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryRequestTests.java b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequestTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryRequestTests.java rename to core/src/test/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequestTests.java index 8c84c8f3f5680..f5c00f63de9c1 100644 --- a/core/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequestTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.search.SearchRequest; diff --git a/core/src/test/java/org/elasticsearch/index/reindex/ParentBulkByScrollTaskTests.java b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTaskTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/index/reindex/ParentBulkByScrollTaskTests.java rename to core/src/test/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTaskTests.java index 6e2d44abed53a..715fcaaad54c8 100644 --- a/core/src/test/java/org/elasticsearch/index/reindex/ParentBulkByScrollTaskTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTaskTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.action.ActionListener; import org.elasticsearch.test.ESTestCase; diff --git a/core/src/test/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTaskTests.java b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTaskTests.java similarity index 98% rename from core/src/test/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTaskTests.java rename to core/src/test/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTaskTests.java index 5d594d080b8b0..7356d626c1023 100644 --- a/core/src/test/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTaskTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTaskTests.java @@ -17,8 +17,10 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; +import org.elasticsearch.action.bulk.byscroll.WorkingBulkByScrollTask; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.tasks.TaskId; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java index 64b02c4be8150..d70b3c9c4ceb0 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java @@ -21,6 +21,9 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.GenericAction; +import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.action.support.ActiveShardCount; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.settings.Settings; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByQueryRestHandler.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByQueryRestHandler.java index 32a252ccc4b19..480ca80e2ee11 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByQueryRestHandler.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByQueryRestHandler.java @@ -21,6 +21,8 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.action.GenericAction; +import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -33,7 +35,7 @@ import java.util.Map; import java.util.function.Consumer; -import static org.elasticsearch.index.reindex.AbstractBulkByScrollRequest.SIZE_ALL_MATCHES; +import static org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest.SIZE_ALL_MATCHES; /** * Rest handler for reindex actions that accepts a search request like Update-By-Query or Delete-By-Query diff --git a/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java similarity index 97% rename from core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java index 62c2635b301c5..6aad6e08a49f0 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.reindex; +import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; diff --git a/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java similarity index 92% rename from core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java index ca1c980995bba..369510083d1da 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java @@ -20,6 +20,8 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; +import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.script.Script; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseContentListener.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseContentListener.java index 8e5dff170d481..a8d321a9fab8d 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseContentListener.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseContentListener.java @@ -21,7 +21,8 @@ import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; -import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.rest.BytesRestResponse; diff --git a/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java similarity index 91% rename from core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java index c1abb16ca3977..b55fe33340e50 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java @@ -20,6 +20,8 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; import org.elasticsearch.client.ElasticsearchClient; public class DeleteByQueryAction extends Action { diff --git a/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java similarity index 90% rename from core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java index e94d1308a74be..3efbc1a562cca 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java @@ -20,6 +20,9 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; +import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java index 1c53a925f0d71..2c84cfc86be6e 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java @@ -20,6 +20,7 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.client.ElasticsearchClient; public class ReindexAction extends Action { diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java index d601f5c06e728..fb203ee5c6dd8 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java @@ -21,6 +21,7 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; diff --git a/core/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java similarity index 97% rename from core/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java index 76944c7b80438..2fa513f9c57a4 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.lucene.uid.Versions; +import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.tasks.TaskId; import java.io.IOException; @@ -127,7 +128,7 @@ public RemoteInfo getRemoteInfo() { } @Override - public ReindexRequest forSlice(TaskId slicingTask, SearchRequest slice) { + protected ReindexRequest forSlice(TaskId slicingTask, SearchRequest slice) { ReindexRequest sliced = doForSlice(new ReindexRequest(slice, destination, false), slicingTask); sliced.setRemoteInfo(remoteInfo); return sliced; diff --git a/core/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java similarity index 95% rename from core/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java index 68bd3f4661828..d7de51c588ec3 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java @@ -20,11 +20,13 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexAction; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; +import org.elasticsearch.index.reindex.remote.RemoteInfo; public class ReindexRequestBuilder extends AbstractBulkIndexByScrollRequestBuilder { diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestDeleteByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestDeleteByQueryAction.java index f906ef7660da8..0e11d64a405ca 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestDeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestDeleteByQueryAction.java @@ -20,6 +20,7 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.settings.Settings; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java index 6c16c31efb153..57d3213da9220 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java @@ -36,6 +36,7 @@ import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.index.VersionType; import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.script.Script; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java index 99e1a9f166dd8..a17b2b81f910f 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java @@ -20,6 +20,12 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.bulk.byscroll.AsyncDeleteByQueryAction; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollParallelizationHelper; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; +import org.elasticsearch.action.bulk.byscroll.ParentBulkByScrollTask; +import org.elasticsearch.action.bulk.byscroll.WorkingBulkByScrollTask; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.client.Client; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportReindexAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportReindexAction.java index 737d885443a1a..b232c50c2b2a8 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportReindexAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportReindexAction.java @@ -37,7 +37,13 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.bulk.BackoffPolicy; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; -import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; +import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollParallelizationHelper; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.ParentBulkByScrollTask; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; +import org.elasticsearch.action.bulk.byscroll.WorkingBulkByScrollTask; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.ActionFilters; @@ -62,6 +68,7 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.VersionType; import org.elasticsearch.index.mapper.VersionFieldMapper; +import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.index.reindex.remote.RemoteScrollableHitSource; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptService; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportRethrottleAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportRethrottleAction.java index 0901e5ade31a5..88329f5cb17b4 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportRethrottleAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportRethrottleAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.action.FailedNodeException; import org.elasticsearch.action.TaskOperationFailure; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.tasks.TransportTasksAction; import org.elasticsearch.client.Client; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java index 8924c7038c99e..12d8696319fb1 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java @@ -21,6 +21,12 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.ParentBulkByScrollTask; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollParallelizationHelper; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource; +import org.elasticsearch.action.bulk.byscroll.WorkingBulkByScrollTask; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; diff --git a/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java similarity index 95% rename from core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java index 1058f7f13078a..cb716e8224871 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java @@ -20,6 +20,7 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.client.ElasticsearchClient; public class UpdateByQueryAction extends diff --git a/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java similarity index 97% rename from core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java index ad0123d76cedf..3e7fac9d4546c 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java @@ -71,7 +71,7 @@ protected UpdateByQueryRequest self() { } @Override - public UpdateByQueryRequest forSlice(TaskId slicingTask, SearchRequest slice) { + protected UpdateByQueryRequest forSlice(TaskId slicingTask, SearchRequest slice) { UpdateByQueryRequest request = doForSlice(new UpdateByQueryRequest(slice, false), slicingTask); request.setPipeline(pipeline); return request; diff --git a/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java similarity index 96% rename from core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java index 06e0426864193..143d66ae6fbcf 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java @@ -20,6 +20,7 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; diff --git a/core/src/main/java/org/elasticsearch/index/reindex/RemoteInfo.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteInfo.java similarity index 99% rename from core/src/main/java/org/elasticsearch/index/reindex/RemoteInfo.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteInfo.java index 878a9c61e4c7c..5fad275cde47a 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/RemoteInfo.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteInfo.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.index.reindex.remote; import org.elasticsearch.Version; import org.elasticsearch.common.Nullable; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteResponseParsers.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteResponseParsers.java index d9a897026d293..e9807bdfa5b81 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteResponseParsers.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteResponseParsers.java @@ -20,10 +20,10 @@ package org.elasticsearch.index.reindex.remote; import org.elasticsearch.Version; -import org.elasticsearch.index.reindex.ScrollableHitSource.BasicHit; -import org.elasticsearch.index.reindex.ScrollableHitSource.Hit; -import org.elasticsearch.index.reindex.ScrollableHitSource.Response; -import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.BasicHit; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Hit; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Response; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.collect.Tuple; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java index 85173b7d89962..f3caeb004c474 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java @@ -30,7 +30,7 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.Version; import org.elasticsearch.action.bulk.BackoffPolicy; -import org.elasticsearch.index.reindex.ScrollableHitSource; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.client.ResponseException; import org.elasticsearch.client.ResponseListener; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java index 6ddf6daa8806c..fd41a6d25f384 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java @@ -20,8 +20,12 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionRequest; -import org.elasticsearch.index.reindex.AbstractAsyncBulkByScrollAction.OpType; -import org.elasticsearch.index.reindex.AbstractAsyncBulkByScrollAction.RequestWrapper; +import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; +import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction.OpType; +import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction.RequestWrapper; +import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollActionTestCase; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.script.CompiledScript; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseTests.java index 6809a02585e1b..791277ad27097 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseTests.java @@ -20,7 +20,9 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.bulk.BulkItemResponse; -import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.test.ESTestCase; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java index a92ceedb0f365..3aa35fb6e19de 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java @@ -22,6 +22,9 @@ import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; +import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.action.ingest.DeletePipelineRequest; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryConcurrentTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryConcurrentTests.java index 8954f1c35c179..b80360f12608f 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryConcurrentTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryConcurrentTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.reindex; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.index.query.MatchQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java index 4f7753fca9abd..c26984e09b649 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.reindex; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import java.util.ArrayList; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java index 746b6adc70e73..96ed13f5b29e6 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.test.junit.annotations.TestLogging; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java index d7d9cfe051b8c..7f8a87c2dad9e 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.client.RestClient; import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.test.ESTestCase; import java.util.ArrayList; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWhitelistTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWhitelistTests.java index 128cd4043e283..1a25e4e5e9c45 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWhitelistTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWhitelistTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.test.ESTestCase; import java.net.UnknownHostException; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java index c4b5c26e5c4ef..6b9aaee8665d4 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java @@ -35,6 +35,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.rest.RestStatus; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java index 4611f9dcbcddb..490480e1ac584 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java @@ -19,7 +19,10 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.index.reindex.ScrollableHitSource.Hit; +import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; +import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollActionMetadataTestCase; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Hit; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.settings.Settings; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java index d0eb1dcd75ef2..17769eeb4ea8b 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilder; diff --git a/core/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java similarity index 97% rename from core/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java rename to modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java index 32b01237375b2..d1bb6f6096c39 100644 --- a/core/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java @@ -20,9 +20,11 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestTestCase; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.search.slice.SliceBuilder; import static java.util.Collections.emptyMap; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java index 4e2834a771a94..8ef99bd18d6ce 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.reindex; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.lucene.uid.Versions; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexSourceTargetValidationTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexSourceTargetValidationTests.java index 28b9febe1c289..f6d3217575d7d 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexSourceTargetValidationTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexSourceTargetValidationTests.java @@ -33,6 +33,7 @@ import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.test.ESTestCase; import static java.util.Collections.emptyMap; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexTestCase.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexTestCase.java index fcf80ea283cf4..e7dc7bf3a7732 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexTestCase.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexTestCase.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.reindex; +import org.elasticsearch.action.bulk.byscroll.BulkIndexByScrollResponseMatcher; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexVersioningTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexVersioningTests.java index 472dec4675f99..b9b99ce163841 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexVersioningTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexVersioningTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.reindex; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.get.GetResponse; import static org.elasticsearch.action.DocWriteRequest.OpType.CREATE; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RestReindexActionTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RestReindexActionTests.java index ffb609843f3b4..b190aee3619a8 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RestReindexActionTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RestReindexActionTests.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.rest.RestController; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.rest.FakeRestRequest; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java index 228bae4ed4a73..f40afce9a4e39 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java @@ -22,6 +22,9 @@ import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.elasticsearch.action.admin.cluster.node.tasks.list.TaskGroup; +import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.tasks.TaskId; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java index 9b19c572c0b3e..86fbd47fd9665 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java @@ -26,12 +26,17 @@ import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.bulk.Retry; +import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; +import org.elasticsearch.action.bulk.byscroll.BulkIndexByScrollResponseMatcher; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.threadpool.ThreadPool; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java index 40377fe96462d..5793db023f086 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java @@ -20,6 +20,8 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.Version; +import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest; +import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.bytes.BytesArray; @@ -29,6 +31,7 @@ import org.elasticsearch.common.io.stream.Streamable; import org.elasticsearch.common.lucene.uid.Versions; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; import org.elasticsearch.tasks.TaskId; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java index 222aedd2e9eeb..b5572c1f34f30 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java @@ -23,6 +23,9 @@ import org.elasticsearch.action.FailedNodeException; import org.elasticsearch.action.TaskOperationFailure; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; +import org.elasticsearch.action.bulk.byscroll.ParentBulkByScrollTask; import org.elasticsearch.client.Client; import org.elasticsearch.tasks.TaskId; import org.elasticsearch.tasks.TaskInfo; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java index b688ce019e3df..174fd67153717 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java @@ -19,7 +19,10 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.index.reindex.ScrollableHitSource.Hit; +import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; +import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollActionMetadataTestCase; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Hit; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.settings.Settings; diff --git a/core/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java similarity index 97% rename from core/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java rename to modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java index b30968cf056b5..700f45b42c515 100644 --- a/core/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.reindex; +import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestTestCase; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.IndicesOptions; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWhileModifyingTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWhileModifyingTests.java index 987830ddd3bc9..cb027d156e661 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWhileModifyingTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWhileModifyingTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.reindex; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.index.engine.VersionConflictEngineException; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java index 9f43ea2d17067..3d478094e8bfe 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.reindex; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.script.ScriptService; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteInfoTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteInfoTests.java index d6ab599b43c2d..fa1f46aa383a3 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteInfoTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteInfoTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex.remote; -import org.elasticsearch.index.reindex.RemoteInfo; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.test.ESTestCase; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSourceTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSourceTests.java index f67a5b627fb4c..f63b05e96beb7 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSourceTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSourceTests.java @@ -40,7 +40,7 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.Version; import org.elasticsearch.action.bulk.BackoffPolicy; -import org.elasticsearch.index.reindex.ScrollableHitSource.Response; +import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Response; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.client.HeapBufferedAsyncResponseConsumer; import org.elasticsearch.client.RestClient; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionMetadataTestCase.java b/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionMetadataTestCase.java similarity index 96% rename from modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionMetadataTestCase.java rename to test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionMetadataTestCase.java index 34da9f56b482c..b68797381d176 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionMetadataTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionMetadataTestCase.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; public abstract class AbstractAsyncBulkByScrollActionMetadataTestCase< Request extends AbstractBulkByScrollRequest, diff --git a/test/framework/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionTestCase.java b/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionTestCase.java similarity index 97% rename from test/framework/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionTestCase.java rename to test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionTestCase.java index b4e01b18a5647..907f8965632dd 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionTestCase.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.tasks.TaskId; diff --git a/core/src/test/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestTestCase.java b/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestTestCase.java similarity index 98% rename from core/src/test/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestTestCase.java rename to test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestTestCase.java index debeab52d979d..c4ac9587e82f7 100644 --- a/core/src/test/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestTestCase.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.ActiveShardCount; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseMatcher.java b/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkIndexByScrollResponseMatcher.java similarity index 98% rename from modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseMatcher.java rename to test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkIndexByScrollResponseMatcher.java index cb2ff1a7ae24a..2902e02a30c8a 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseMatcher.java +++ b/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkIndexByScrollResponseMatcher.java @@ -17,8 +17,9 @@ * under the License. */ -package org.elasticsearch.index.reindex; +package org.elasticsearch.action.bulk.byscroll; +import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; @@ -154,4 +155,4 @@ public void describeTo(Description description) { description.appendText(" and reason cancelled matches ").appendDescriptionOf(reasonCancelledMatcher); description.appendText(" and slices matches ").appendDescriptionOf(slicesMatcher); } -} +} \ No newline at end of file From e7d352b489272a87b05019869857656de7fb82fc Mon Sep 17 00:00:00 2001 From: qwerty4030 Date: Thu, 11 May 2017 10:06:26 -0700 Subject: [PATCH 313/619] Compound order for histogram aggregations. (#22343) This commit adds support for histogram and date_histogram agg compound order by refactoring and reusing terms agg order code. The major change is that the Terms.Order and Histogram.Order classes have been replaced/refactored into a new class BucketOrder. This is a breaking change for the Java Transport API. For backward compatibility with previous ES versions the (date)histogram compound order will use the first order. Also the _term and _time aggregation order keys have been deprecated; replaced by _key. Relates to #20003: now that all these aggregations use the same order code, it should be easier to move validation to parse time (as a follow up PR). Relates to #14771: histogram and date_histogram aggregation order will now be validated at reduce time. Closes #23613: if a single BucketOrder that is not a tie-breaker is added with the Java Transport API, it will be converted into a CompoundOrder with a tie-breaker. --- .../search/aggregations/BucketOrder.java | 127 ++++ .../search/aggregations/InternalOrder.java | 595 ++++++++++++++++++ .../search/aggregations/KeyComparable.java | 41 ++ .../bucket/MultiBucketsAggregation.java | 27 - .../DateHistogramAggregationBuilder.java | 78 +-- .../histogram/DateHistogramAggregator.java | 10 +- .../DateHistogramAggregatorFactory.java | 5 +- .../bucket/histogram/Histogram.java | 81 --- .../HistogramAggregationBuilder.java | 78 +-- .../bucket/histogram/HistogramAggregator.java | 10 +- .../histogram/HistogramAggregatorFactory.java | 7 +- .../histogram/InternalDateHistogram.java | 26 +- .../bucket/histogram/InternalHistogram.java | 26 +- .../bucket/histogram/InternalOrder.java | 135 ---- .../terms/AbstractStringTermsAggregator.java | 3 +- .../bucket/terms/DoubleTerms.java | 7 +- .../bucket/terms/DoubleTermsAggregator.java | 3 +- .../GlobalOrdinalsStringTermsAggregator.java | 23 +- .../bucket/terms/InternalMappedTerms.java | 5 +- .../bucket/terms/InternalOrder.java | 385 ------------ .../bucket/terms/InternalTerms.java | 17 +- .../aggregations/bucket/terms/LongTerms.java | 7 +- .../bucket/terms/LongTermsAggregator.java | 6 +- .../bucket/terms/StringTerms.java | 7 +- .../bucket/terms/StringTermsAggregator.java | 6 +- .../aggregations/bucket/terms/Terms.java | 86 --- .../bucket/terms/TermsAggregationBuilder.java | 76 +-- .../bucket/terms/TermsAggregator.java | 69 +- .../bucket/terms/TermsAggregatorFactory.java | 20 +- .../bucket/terms/UnmappedTerms.java | 3 +- .../aggregations/support/AggregationPath.java | 29 +- .../query/MoreLikeThisQueryBuilderTests.java | 13 + .../FunctionScoreQueryBuilderTests.java | 9 + .../aggregations/InternalOrderTests.java | 158 +++++ .../aggregations/bucket/DateHistogramIT.java | 204 +++++- .../bucket/DateHistogramTests.java | 41 +- .../bucket/DiversifiedSamplerIT.java | 3 +- .../aggregations/bucket/DoubleTermsIT.java | 89 ++- .../aggregations/bucket/HistogramIT.java | 212 ++++++- .../aggregations/bucket/HistogramTests.java | 57 +- .../aggregations/bucket/LongTermsIT.java | 79 ++- .../aggregations/bucket/MinDocCountIT.java | 81 +-- .../aggregations/bucket/NaNSortingIT.java | 5 +- .../aggregations/bucket/ReverseNestedIT.java | 5 +- .../search/aggregations/bucket/SamplerIT.java | 3 +- .../aggregations/bucket/ShardSizeTermsIT.java | 31 +- .../aggregations/bucket/StringTermsIT.java | 111 ++-- .../bucket/TermsDocCountErrorIT.java | 78 +-- .../bucket/TermsShardMinDocCountIT.java | 5 +- .../aggregations/bucket/TermsTests.java | 22 +- .../histogram/InternalDateHistogramTests.java | 3 +- .../histogram/InternalHistogramTests.java | 3 +- .../bucket/terms/DoubleTermsTests.java | 3 +- .../bucket/terms/LongTermsTests.java | 3 +- .../bucket/terms/StringTermsTests.java | 3 +- .../bucket/terms/TermsAggregatorTests.java | 5 +- .../search/aggregations/metrics/AvgIT.java | 4 +- .../aggregations/metrics/ExtendedStatsIT.java | 5 +- .../metrics/HDRPercentileRanksIT.java | 6 +- .../metrics/HDRPercentilesIT.java | 6 +- .../search/aggregations/metrics/MaxIT.java | 4 +- .../search/aggregations/metrics/MinIT.java | 4 +- .../search/aggregations/metrics/StatsIT.java | 4 +- .../search/aggregations/metrics/SumIT.java | 4 +- .../metrics/TDigestPercentileRanksIT.java | 6 +- .../metrics/TDigestPercentilesIT.java | 6 +- .../aggregations/metrics/TopHitsIT.java | 8 +- .../aggregations/pipeline/AvgBucketIT.java | 10 +- .../pipeline/ExtendedStatsBucketIT.java | 12 +- .../aggregations/pipeline/MaxBucketIT.java | 10 +- .../aggregations/pipeline/MinBucketIT.java | 10 +- .../pipeline/PercentilesBucketIT.java | 13 +- .../aggregations/pipeline/StatsBucketIT.java | 10 +- .../aggregations/pipeline/SumBucketIT.java | 10 +- .../bucket/datehistogram-aggregation.asciidoc | 4 + .../bucket/histogram-aggregation.asciidoc | 4 + .../bucket/terms-aggregation.asciidoc | 30 +- .../bucket/datehistogram-aggregation.asciidoc | 7 + .../bucket/histogram-aggregation.asciidoc | 116 +--- .../bucket/terms-aggregation.asciidoc | 3 +- .../migration/migrate_6_0/java.asciidoc | 10 +- .../test/search.aggregation/10_histogram.yaml | 63 ++ .../test/search.aggregation/20_terms.yaml | 54 ++ .../test/AbstractSerializingTestCase.java | 4 +- .../test/AbstractWireSerializingTestCase.java | 6 +- .../org/elasticsearch/test/ESTestCase.java | 34 +- 86 files changed, 2260 insertions(+), 1431 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/BucketOrder.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/InternalOrder.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/KeyComparable.java delete mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalOrder.java delete mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalOrder.java create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/InternalOrderTests.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/BucketOrder.java b/core/src/main/java/org/elasticsearch/search/aggregations/BucketOrder.java new file mode 100644 index 0000000000000..482a90f08526c --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/BucketOrder.java @@ -0,0 +1,127 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations; + +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket; +import org.elasticsearch.search.aggregations.support.AggregationPath; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +/** + * {@link Bucket} Ordering strategy. + */ +public abstract class BucketOrder implements ToXContentObject, Writeable { + + /** + * Creates a bucket ordering strategy that sorts buckets by their document counts (ascending or descending). + * + * @param asc direction to sort by: {@code true} for ascending, {@code false} for descending. + */ + public static BucketOrder count(boolean asc) { + return asc ? InternalOrder.COUNT_ASC : InternalOrder.COUNT_DESC; + } + + /** + * Creates a bucket ordering strategy that sorts buckets by their keys (ascending or descending). This may be + * used as a tie-breaker to avoid non-deterministic ordering. + * + * @param asc direction to sort by: {@code true} for ascending, {@code false} for descending. + */ + public static BucketOrder key(boolean asc) { + return asc ? InternalOrder.KEY_ASC : InternalOrder.KEY_DESC; + } + + /** + * Creates a bucket ordering strategy which sorts buckets based on a single-valued sub-aggregation. + * + * @param path path to the sub-aggregation to sort on. + * @param asc direction to sort by: {@code true} for ascending, {@code false} for descending. + * @see AggregationPath + */ + public static BucketOrder aggregation(String path, boolean asc) { + return new InternalOrder.Aggregation(path, asc); + } + + /** + * Creates a bucket ordering strategy which sorts buckets based on a metric from a multi-valued sub-aggregation. + * + * @param path path to the sub-aggregation to sort on. + * @param metricName name of the value of the multi-value metric to sort on. + * @param asc direction to sort by: {@code true} for ascending, {@code false} for descending. + * @see AggregationPath + */ + public static BucketOrder aggregation(String path, String metricName, boolean asc) { + return new InternalOrder.Aggregation(path + "." + metricName, asc); + } + + /** + * Creates a bucket ordering strategy which sorts buckets based on multiple criteria. A tie-breaker may be added to + * avoid non-deterministic ordering. + * + * @param orders a list of {@link BucketOrder} objects to sort on, in order of priority. + */ + public static BucketOrder compound(List orders) { + return new InternalOrder.CompoundOrder(orders); + } + + /** + * Creates a bucket ordering strategy which sorts buckets based on multiple criteria. A tie-breaker may be added to + * avoid non-deterministic ordering. + * + * @param orders a list of {@link BucketOrder} parameters to sort on, in order of priority. + */ + public static BucketOrder compound(BucketOrder... orders) { + return compound(Arrays.asList(orders)); + } + + /** + * @return A comparator for the bucket based on the given aggregator. The comparator is used in two phases: + *

+ * - aggregation phase, where each shard builds a list of buckets to be sent to the coordinating node. + * In this phase, the passed in aggregator will be the aggregator that aggregates the buckets on the + * shard level. + *

+ * - reduce phase, where the coordinating node gathers all the buckets from all the shards and reduces them + * to a final bucket list. In this case, the passed in aggregator will be {@code null}. + */ + public abstract Comparator comparator(Aggregator aggregator); + + /** + * @return unique internal ID used for reading/writing this order from/to a stream. + * @see InternalOrder.Streams + */ + abstract byte id(); + + @Override + public abstract int hashCode(); + + @Override + public abstract boolean equals(Object obj); + + @Override + public void writeTo(StreamOutput out) throws IOException { + InternalOrder.Streams.writeOrder(this, out); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/InternalOrder.java b/core/src/main/java/org/elasticsearch/search/aggregations/InternalOrder.java new file mode 100644 index 0000000000000..e81c0b1890bdc --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/InternalOrder.java @@ -0,0 +1,595 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations; + +import org.elasticsearch.Version; +import org.elasticsearch.common.ParsingException; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.logging.DeprecationLogger; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.util.Comparators; +import org.elasticsearch.common.xcontent.XContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket; +import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregator; +import org.elasticsearch.search.aggregations.support.AggregationPath; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +/** + * Implementations for {@link Bucket} ordering strategies. + */ +public class InternalOrder extends BucketOrder { + + private final byte id; + private final String key; + protected final boolean asc; + protected final Comparator comparator; + + /** + * Creates an ordering strategy that sorts {@link Bucket}s by some property. + * + * @param id unique ID for this ordering strategy. + * @param key key of the property to sort on. + * @param asc direction to sort by: {@code true} for ascending, {@code false} for descending. + * @param comparator determines how buckets will be ordered. + */ + public InternalOrder(byte id, String key, boolean asc, Comparator comparator) { + this.id = id; + this.key = key; + this.asc = asc; + this.comparator = comparator; + } + + @Override + byte id() { + return id; + } + + @Override + public Comparator comparator(Aggregator aggregator) { + return comparator; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject().field(key, asc ? "asc" : "desc").endObject(); + } + + /** + * Validate a bucket ordering strategy for an {@link Aggregator}. + * + * @param order bucket ordering strategy to sort on. + * @param aggregator aggregator to sort. + * @return unmodified bucket ordering strategy. + * @throws AggregationExecutionException if validation fails + */ + public static BucketOrder validate(BucketOrder order, Aggregator aggregator) throws AggregationExecutionException { + if (order instanceof CompoundOrder) { + for (BucketOrder innerOrder : ((CompoundOrder) order).orderElements) { + validate(innerOrder, aggregator); + } + } else if (order instanceof Aggregation) { + ((Aggregation) order).path().validate(aggregator); + } + return order; + } + + /** + * {@link Bucket} ordering strategy to sort by a sub-aggregation. + */ + public static class Aggregation extends InternalOrder { + + static final byte ID = 0; + + /** + * Create a new ordering strategy to sort by a sub-aggregation. + * + * @param path path to the sub-aggregation to sort on. + * @param asc direction to sort by: {@code true} for ascending, {@code false} for descending. + * @see AggregationPath + */ + Aggregation(String path, boolean asc) { + super(ID, path, asc, new AggregationComparator(path, asc)); + } + + /** + * @return parsed path to the sub-aggregation to sort on. + */ + public AggregationPath path() { + return ((AggregationComparator) comparator).path; + } + + @Override + public Comparator comparator(Aggregator aggregator) { + if (aggregator instanceof TermsAggregator) { + // Internal Optimization for terms aggregation to avoid constructing buckets for ordering purposes + return ((TermsAggregator) aggregator).bucketComparator(path(), asc); + } + return comparator; + } + + /** + * {@link Bucket} ordering strategy to sort by a sub-aggregation. + */ + static class AggregationComparator implements Comparator { + + private final AggregationPath path; + private final boolean asc; + + /** + * Create a new {@link Bucket} ordering strategy to sort by a sub-aggregation. + * + * @param path path to the sub-aggregation to sort on. + * @param asc direction to sort by: {@code true} for ascending, {@code false} for descending. + * @see AggregationPath + */ + AggregationComparator(String path, boolean asc) { + this.asc = asc; + this.path = AggregationPath.parse(path); + } + + @Override + public int compare(Bucket b1, Bucket b2) { + double v1 = path.resolveValue(b1); + double v2 = path.resolveValue(b2); + return Comparators.compareDiscardNaN(v1, v2, asc); + } + } + } + + /** + * {@link Bucket} ordering strategy to sort by multiple criteria. + */ + public static class CompoundOrder extends BucketOrder { + + static final byte ID = -1; + + final List orderElements; + + /** + * Create a new ordering strategy to sort by multiple criteria. A tie-breaker may be added to avoid + * non-deterministic ordering. + * + * @param compoundOrder a list of {@link BucketOrder}s to sort on, in order of priority. + */ + CompoundOrder(List compoundOrder) { + this(compoundOrder, true); + } + + /** + * Create a new ordering strategy to sort by multiple criteria. + * + * @param compoundOrder a list of {@link BucketOrder}s to sort on, in order of priority. + * @param absoluteOrdering {@code true} to add a tie-breaker to avoid non-deterministic ordering if needed, + * {@code false} otherwise. + */ + CompoundOrder(List compoundOrder, boolean absoluteOrdering) { + this.orderElements = new LinkedList<>(compoundOrder); + BucketOrder lastElement = null; + for (BucketOrder order : orderElements) { + if (order instanceof CompoundOrder) { + throw new IllegalArgumentException("nested compound order not supported"); + } + lastElement = order; + } + if (absoluteOrdering && isKeyOrder(lastElement) == false) { + // add key order ascending as a tie-breaker to avoid non-deterministic ordering + // if all user provided comparators return 0. + this.orderElements.add(KEY_ASC); + } + } + + @Override + byte id() { + return ID; + } + + /** + * @return unmodifiable list of {@link BucketOrder}s to sort on. + */ + public List orderElements() { + return Collections.unmodifiableList(orderElements); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startArray(); + for (BucketOrder order : orderElements) { + order.toXContent(builder, params); + } + return builder.endArray(); + } + + @Override + public Comparator comparator(Aggregator aggregator) { + return new CompoundOrderComparator(orderElements, aggregator); + } + + @Override + public int hashCode() { + return Objects.hash(orderElements); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + CompoundOrder other = (CompoundOrder) obj; + return Objects.equals(orderElements, other.orderElements); + } + + /** + * {@code Comparator} for sorting buckets by multiple criteria. + */ + static class CompoundOrderComparator implements Comparator { + + private List compoundOrder; + private Aggregator aggregator; + + /** + * Create a new {@code Comparator} for sorting buckets by multiple criteria. + * + * @param compoundOrder a list of {@link BucketOrder}s to sort on, in order of priority. + * @param aggregator {@link BucketOrder#comparator(Aggregator)} + */ + CompoundOrderComparator(List compoundOrder, Aggregator aggregator) { + this.compoundOrder = compoundOrder; + this.aggregator = aggregator; + } + + @Override + public int compare(Bucket b1, Bucket b2) { + int result = 0; + for (Iterator itr = compoundOrder.iterator(); itr.hasNext() && result == 0; ) { + result = itr.next().comparator(aggregator).compare(b1, b2); + } + return result; + } + } + } + + private static final byte COUNT_DESC_ID = 1; + private static final byte COUNT_ASC_ID = 2; + private static final byte KEY_DESC_ID = 3; + private static final byte KEY_ASC_ID = 4; + + /** + * Order by the (higher) count of each bucket. + */ + static final InternalOrder COUNT_DESC = new InternalOrder(COUNT_DESC_ID, "_count", false, comparingCounts().reversed()); + + /** + * Order by the (lower) count of each bucket. + */ + static final InternalOrder COUNT_ASC = new InternalOrder(COUNT_ASC_ID, "_count", true, comparingCounts()); + + /** + * Order by the key of each bucket descending. + */ + static final InternalOrder KEY_DESC = new InternalOrder(KEY_DESC_ID, "_key", false, comparingKeys().reversed()); + + /** + * Order by the key of each bucket ascending. + */ + static final InternalOrder KEY_ASC = new InternalOrder(KEY_ASC_ID, "_key", true, comparingKeys()); + + /** + * @return compare by {@link Bucket#getDocCount()}. + */ + private static Comparator comparingCounts() { + return Comparator.comparingLong(Bucket::getDocCount); + } + + /** + * @return compare by {@link Bucket#getKey()} from the appropriate implementation. + */ + @SuppressWarnings("unchecked") + private static Comparator comparingKeys() { + return (b1, b2) -> { + if (b1 instanceof KeyComparable) { + return ((KeyComparable) b1).compareKey(b2); + } + throw new IllegalStateException("Unexpected order bucket class [" + b1.getClass() + "]"); + }; + } + + /** + * Determine if the ordering strategy is sorting on bucket count descending. + * + * @param order bucket ordering strategy to check. + * @return {@code true} if the ordering strategy is sorting on bucket count descending, {@code false} otherwise. + */ + public static boolean isCountDesc(BucketOrder order) { + return isOrder(order, COUNT_DESC); + } + + /** + * Determine if the ordering strategy is sorting on bucket key (ascending or descending). + * + * @param order bucket ordering strategy to check. + * @return {@code true} if the ordering strategy is sorting on bucket key, {@code false} otherwise. + */ + public static boolean isKeyOrder(BucketOrder order) { + return isOrder(order, KEY_ASC) || isOrder(order, KEY_DESC); + } + + /** + * Determine if the ordering strategy is sorting on bucket key ascending. + * + * @param order bucket ordering strategy to check. + * @return {@code true} if the ordering strategy is sorting on bucket key ascending, {@code false} otherwise. + */ + public static boolean isKeyAsc(BucketOrder order) { + return isOrder(order, KEY_ASC); + } + + /** + * Determine if the ordering strategy is sorting on bucket key descending. + * + * @param order bucket ordering strategy to check. + * @return {@code true} if the ordering strategy is sorting on bucket key descending, {@code false} otherwise. + */ + public static boolean isKeyDesc(BucketOrder order) { + return isOrder(order, KEY_DESC); + } + + /** + * Determine if the ordering strategy matches the expected one. + * + * @param order bucket ordering strategy to check. If this is a {@link CompoundOrder} the first element will be + * check instead. + * @param expected expected bucket ordering strategy. + * @return {@code true} if the order matches, {@code false} otherwise. + */ + private static boolean isOrder(BucketOrder order, BucketOrder expected) { + if (order == expected) { + return true; + } else if (order instanceof CompoundOrder) { + // check if its a compound order with the first element that matches + List orders = ((CompoundOrder) order).orderElements; + if (orders.size() >= 1) { + return isOrder(orders.get(0), expected); + } + } + return false; + } + + /** + * Contains logic for reading/writing {@link BucketOrder} from/to streams. + */ + public static class Streams { + + /** + * Read a {@link BucketOrder} from a {@link StreamInput}. + * + * @param in stream with order data to read. + * @return order read from the stream + * @throws IOException on error reading from the stream. + */ + public static BucketOrder readOrder(StreamInput in) throws IOException { + byte id = in.readByte(); + switch (id) { + case COUNT_DESC_ID: return COUNT_DESC; + case COUNT_ASC_ID: return COUNT_ASC; + case KEY_DESC_ID: return KEY_DESC; + case KEY_ASC_ID: return KEY_ASC; + case Aggregation.ID: + boolean asc = in.readBoolean(); + String key = in.readString(); + return new Aggregation(key, asc); + case CompoundOrder.ID: + int size = in.readVInt(); + List compoundOrder = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + compoundOrder.add(Streams.readOrder(in)); + } + return new CompoundOrder(compoundOrder, false); + default: + throw new RuntimeException("unknown order id [" + id + "]"); + } + } + + /** + * ONLY FOR HISTOGRAM ORDER: Backwards compatibility logic to read a {@link BucketOrder} from a {@link StreamInput}. + * + * @param in stream with order data to read. + * @param bwcOrderFlag {@code true} to check {@code in.readBoolean()} in the backwards compat logic before reading + * the order. {@code false} to skip this flag (order always present). + * @return order read from the stream + * @throws IOException on error reading from the stream. + */ + public static BucketOrder readHistogramOrder(StreamInput in, boolean bwcOrderFlag) throws IOException { + if (in.getVersion().onOrAfter(Version.V_6_0_0_alpha2_UNRELEASED)) { + return Streams.readOrder(in); + } else { // backwards compat logic + if (bwcOrderFlag == false || in.readBoolean()) { + // translate the old histogram order IDs to the new order objects + byte id = in.readByte(); + switch (id) { + case 1: return KEY_ASC; + case 2: return KEY_DESC; + case 3: return COUNT_ASC; + case 4: return COUNT_DESC; + case 0: // aggregation order stream logic is backwards compatible + boolean asc = in.readBoolean(); + String key = in.readString(); + return new Aggregation(key, asc); + default: // not expecting compound order ID + throw new RuntimeException("unknown histogram order id [" + id + "]"); + } + } else { // default to _key asc if no order specified + return KEY_ASC; + } + } + } + + /** + * Write a {@link BucketOrder} to a {@link StreamOutput}. + * + * @param order order to write to the stream. + * @param out stream to write the order to. + * @throws IOException on error writing to the stream. + */ + public static void writeOrder(BucketOrder order, StreamOutput out) throws IOException { + out.writeByte(order.id()); + if (order instanceof Aggregation) { + Aggregation aggregationOrder = (Aggregation) order; + out.writeBoolean(aggregationOrder.asc); + out.writeString(aggregationOrder.path().toString()); + } else if (order instanceof CompoundOrder) { + CompoundOrder compoundOrder = (CompoundOrder) order; + out.writeVInt(compoundOrder.orderElements.size()); + for (BucketOrder innerOrder : compoundOrder.orderElements) { + innerOrder.writeTo(out); + } + } + } + + /** + * ONLY FOR HISTOGRAM ORDER: Backwards compatibility logic to write a {@link BucketOrder} to a stream. + * + * @param order order to write to the stream. + * @param out stream to write the order to. + * @param bwcOrderFlag {@code true} to always {@code out.writeBoolean(true)} for the backwards compat logic before + * writing the order. {@code false} to skip this flag. + * @throws IOException on error writing to the stream. + */ + public static void writeHistogramOrder(BucketOrder order, StreamOutput out, boolean bwcOrderFlag) throws IOException { + if (out.getVersion().onOrAfter(Version.V_6_0_0_alpha2_UNRELEASED)) { + order.writeTo(out); + } else { // backwards compat logic + if(bwcOrderFlag) { // need to add flag that determines if order exists + out.writeBoolean(true); // order always exists + } + if (order instanceof CompoundOrder) { + // older versions do not support histogram compound order; the best we can do here is use the first order. + order = ((CompoundOrder) order).orderElements.get(0); + } + if (order instanceof Aggregation) { + // aggregation order stream logic is backwards compatible + order.writeTo(out); + } else { + // convert the new order IDs to the old histogram order IDs. + byte id; + switch (order.id()) { + case COUNT_DESC_ID: id = 4; break; + case COUNT_ASC_ID: id = 3; break; + case KEY_DESC_ID: id = 2; break; + case KEY_ASC_ID: id = 1; break; + default: throw new RuntimeException("unknown order id [" + order.id() + "]"); + } + out.writeByte(id); + } + } + } + } + + /** + * Contains logic for parsing a {@link BucketOrder} from a {@link XContentParser}. + */ + public static class Parser { + + private static final DeprecationLogger DEPRECATION_LOGGER = + new DeprecationLogger(Loggers.getLogger(Parser.class)); + + /** + * Parse a {@link BucketOrder} from {@link XContent}. + * + * @param parser for parsing {@link XContent} that contains the order. + * @param context parsing context. + * @return bucket ordering strategy + * @throws IOException on error a {@link XContent} parsing error. + */ + public static BucketOrder parseOrderParam(XContentParser parser, QueryParseContext context) throws IOException { + XContentParser.Token token; + String orderKey = null; + boolean orderAsc = false; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + orderKey = parser.currentName(); + } else if (token == XContentParser.Token.VALUE_STRING) { + String dir = parser.text(); + if ("asc".equalsIgnoreCase(dir)) { + orderAsc = true; + } else if ("desc".equalsIgnoreCase(dir)) { + orderAsc = false; + } else { + throw new ParsingException(parser.getTokenLocation(), + "Unknown order direction [" + dir + "]"); + } + } else { + throw new ParsingException(parser.getTokenLocation(), + "Unexpected token [" + token + "] for [order]"); + } + } + if (orderKey == null) { + throw new ParsingException(parser.getTokenLocation(), + "Must specify at least one field for [order]"); + } + // _term and _time order deprecated in 6.0; replaced by _key + if ("_term".equals(orderKey) || "_time".equals(orderKey)) { + DEPRECATION_LOGGER.deprecated("Deprecated aggregation order key [{}] used, replaced by [_key]", orderKey); + } + switch (orderKey) { + case "_term": + case "_time": + case "_key": + return orderAsc ? KEY_ASC : KEY_DESC; + case "_count": + return orderAsc ? COUNT_ASC : COUNT_DESC; + default: // assume all other orders are sorting on a sub-aggregation. Validation occurs later. + return aggregation(orderKey, orderAsc); + } + } + } + + @Override + public int hashCode() { + return Objects.hash(id, key, asc); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + InternalOrder other = (InternalOrder) obj; + return Objects.equals(id, other.id) + && Objects.equals(key, other.key) + && Objects.equals(asc, other.asc); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/KeyComparable.java b/core/src/main/java/org/elasticsearch/search/aggregations/KeyComparable.java new file mode 100644 index 0000000000000..c69cf0689c608 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/KeyComparable.java @@ -0,0 +1,41 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations; + +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket; + +/** + * Defines behavior for comparing {@link Bucket#getKey() bucket keys} to imposes a total ordering + * of buckets of the same type. + * + * @param {@link Bucket} of the same type that also implements {@link KeyComparable}. + * @see BucketOrder#key(boolean) + */ +public interface KeyComparable> { + + /** + * Compare this {@link Bucket}s {@link Bucket#getKey() key} with another bucket. + * + * @param other the bucket that contains the key to compare to. + * @return a negative integer, zero, or a positive integer as this buckets key + * is less than, equal to, or greater than the other buckets key. + * @see Comparable#compareTo(Object) + */ + int compareKey(T other); +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/MultiBucketsAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/MultiBucketsAggregation.java index fc223916f72c1..24b1894455a4b 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/MultiBucketsAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/MultiBucketsAggregation.java @@ -19,12 +19,10 @@ package org.elasticsearch.search.aggregations.bucket; -import org.elasticsearch.common.util.Comparators; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.Aggregations; import org.elasticsearch.search.aggregations.HasAggregations; -import org.elasticsearch.search.aggregations.support.AggregationPath; import java.util.List; @@ -58,31 +56,6 @@ interface Bucket extends HasAggregations, ToXContent { @Override Aggregations getAggregations(); - class SubAggregationComparator implements java.util.Comparator { - - private final AggregationPath path; - private final boolean asc; - - public SubAggregationComparator(String expression, boolean asc) { - this.asc = asc; - this.path = AggregationPath.parse(expression); - } - - public boolean asc() { - return asc; - } - - public AggregationPath path() { - return path; - } - - @Override - public int compare(B b1, B b2) { - double v1 = path.resolveValue(b1); - double v2 = path.resolveValue(b2); - return Comparators.compareDiscardNaN(v1, v2, asc); - } - } } /** diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregationBuilder.java index 96b08e3bada12..4bbe2c5abe165 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregationBuilder.java @@ -19,7 +19,6 @@ package org.elasticsearch.search.aggregations.bucket.histogram; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.rounding.DateTimeUnit; @@ -28,10 +27,12 @@ import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentParser.Token; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; +import org.elasticsearch.search.aggregations.BucketOrder; +import org.elasticsearch.search.aggregations.InternalOrder; +import org.elasticsearch.search.aggregations.InternalOrder.CompoundOrder; import org.elasticsearch.search.aggregations.support.ValueType; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSource.Numeric; @@ -44,6 +45,7 @@ import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -113,8 +115,8 @@ public class DateHistogramAggregationBuilder PARSER.declareField(DateHistogramAggregationBuilder::extendedBounds, parser -> ExtendedBounds.PARSER.apply(parser, null), ExtendedBounds.EXTENDED_BOUNDS_FIELD, ObjectParser.ValueType.OBJECT); - PARSER.declareField(DateHistogramAggregationBuilder::order, DateHistogramAggregationBuilder::parseOrder, - Histogram.ORDER_FIELD, ObjectParser.ValueType.OBJECT); + PARSER.declareObjectArray(DateHistogramAggregationBuilder::order, InternalOrder.Parser::parseOrderParam, + Histogram.ORDER_FIELD); } public static DateHistogramAggregationBuilder parse(String aggregationName, QueryParseContext context) throws IOException { @@ -125,7 +127,7 @@ public static DateHistogramAggregationBuilder parse(String aggregationName, Quer private DateHistogramInterval dateHistogramInterval; private long offset = 0; private ExtendedBounds extendedBounds; - private InternalOrder order = (InternalOrder) Histogram.Order.KEY_ASC; + private BucketOrder order = BucketOrder.key(true); private boolean keyed = false; private long minDocCount = 0; @@ -137,9 +139,7 @@ public DateHistogramAggregationBuilder(String name) { /** Read from a stream, for internal use only. */ public DateHistogramAggregationBuilder(StreamInput in) throws IOException { super(in, ValuesSourceType.NUMERIC, ValueType.DATE); - if (in.readBoolean()) { - order = InternalOrder.Streams.readOrder(in); - } + order = InternalOrder.Streams.readHistogramOrder(in, true); keyed = in.readBoolean(); minDocCount = in.readVLong(); interval = in.readLong(); @@ -150,11 +150,7 @@ public DateHistogramAggregationBuilder(StreamInput in) throws IOException { @Override protected void innerWriteTo(StreamOutput out) throws IOException { - boolean hasOrder = order != null; - out.writeBoolean(hasOrder); - if (hasOrder) { - InternalOrder.Streams.writeOrder(order, out); - } + InternalOrder.Streams.writeHistogramOrder(order, out, true); out.writeBoolean(keyed); out.writeVLong(minDocCount); out.writeLong(interval); @@ -244,17 +240,34 @@ public DateHistogramAggregationBuilder extendedBounds(ExtendedBounds extendedBou } /** Return the order to use to sort buckets of this histogram. */ - public Histogram.Order order() { + public BucketOrder order() { return order; } /** Set a new order on this builder and return the builder so that calls - * can be chained. */ - public DateHistogramAggregationBuilder order(Histogram.Order order) { + * can be chained. A tie-breaker may be added to avoid non-deterministic ordering. */ + public DateHistogramAggregationBuilder order(BucketOrder order) { if (order == null) { throw new IllegalArgumentException("[order] must not be null: [" + name + "]"); } - this.order = (InternalOrder) order; + if(order instanceof CompoundOrder || InternalOrder.isKeyOrder(order)) { + this.order = order; // if order already contains a tie-breaker we are good to go + } else { // otherwise add a tie-breaker by using a compound order + this.order = BucketOrder.compound(order); + } + return this; + } + + /** + * Sets the order in which the buckets will be returned. A tie-breaker may be added to avoid non-deterministic + * ordering. + */ + public DateHistogramAggregationBuilder order(List orders) { + if (orders == null) { + throw new IllegalArgumentException("[orders] must not be null: [" + name + "]"); + } + // if the list only contains one order use that to avoid inconsistent xcontent + order(orders.size() > 1 ? BucketOrder.compound(orders) : orders.get(0)); return this; } @@ -370,35 +383,4 @@ protected boolean innerEquals(Object obj) { && Objects.equals(offset, other.offset) && Objects.equals(extendedBounds, other.extendedBounds); } - - // similar to the parsing oh histogram orders, but also accepts _time as an alias for _key - private static InternalOrder parseOrder(XContentParser parser, QueryParseContext context) throws IOException { - InternalOrder order = null; - Token token; - String currentFieldName = null; - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - String dir = parser.text(); - boolean asc = "asc".equals(dir); - if (!asc && !"desc".equals(dir)) { - throw new ParsingException(parser.getTokenLocation(), "Unknown order direction: [" + dir - + "]. Should be either [asc] or [desc]"); - } - order = resolveOrder(currentFieldName, asc); - } - } - return order; - } - - static InternalOrder resolveOrder(String key, boolean asc) { - if ("_key".equals(key) || "_time".equals(key)) { - return (InternalOrder) (asc ? InternalOrder.KEY_ASC : InternalOrder.KEY_DESC); - } - if ("_count".equals(key)) { - return (InternalOrder) (asc ? InternalOrder.COUNT_ASC : InternalOrder.COUNT_DESC); - } - return new InternalOrder.Aggregation(key, asc); - } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregator.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregator.java index 871cade50cd71..f5f7877572a16 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregator.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregator.java @@ -33,6 +33,8 @@ import org.elasticsearch.search.aggregations.LeafBucketCollectorBase; import org.elasticsearch.search.aggregations.bucket.BucketsAggregator; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; +import org.elasticsearch.search.aggregations.InternalOrder; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.internal.SearchContext; @@ -53,7 +55,7 @@ class DateHistogramAggregator extends BucketsAggregator { private final ValuesSource.Numeric valuesSource; private final DocValueFormat formatter; private final Rounding rounding; - private final InternalOrder order; + private final BucketOrder order; private final boolean keyed; private final long minDocCount; @@ -62,7 +64,7 @@ class DateHistogramAggregator extends BucketsAggregator { private final LongHash bucketOrds; private long offset; - DateHistogramAggregator(String name, AggregatorFactories factories, Rounding rounding, long offset, InternalOrder order, + DateHistogramAggregator(String name, AggregatorFactories factories, Rounding rounding, long offset, BucketOrder order, boolean keyed, long minDocCount, @Nullable ExtendedBounds extendedBounds, @Nullable ValuesSource.Numeric valuesSource, DocValueFormat formatter, SearchContext aggregationContext, @@ -71,7 +73,7 @@ class DateHistogramAggregator extends BucketsAggregator { super(name, factories, aggregationContext, parent, pipelineAggregators, metaData); this.rounding = rounding; this.offset = offset; - this.order = order; + this.order = InternalOrder.validate(order, this);; this.keyed = keyed; this.minDocCount = minDocCount; this.extendedBounds = extendedBounds; @@ -131,7 +133,7 @@ public InternalAggregation buildAggregation(long owningBucketOrdinal) throws IOE } // the contract of the histogram aggregation is that shards must return buckets ordered by key in ascending order - CollectionUtil.introSort(buckets, InternalOrder.KEY_ASC.comparator()); + CollectionUtil.introSort(buckets, BucketOrder.key(true).comparator(this)); // value source will be null for unmapped fields InternalDateHistogram.EmptyBucketInfo emptyBucketInfo = minDocCount == 0 diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregatorFactory.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregatorFactory.java index 44bb3e02afece..a64e018288879 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregatorFactory.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregatorFactory.java @@ -24,6 +24,7 @@ import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSource.Numeric; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregatorFactory; @@ -40,14 +41,14 @@ public final class DateHistogramAggregatorFactory private final DateHistogramInterval dateHistogramInterval; private final long interval; private final long offset; - private final InternalOrder order; + private final BucketOrder order; private final boolean keyed; private final long minDocCount; private final ExtendedBounds extendedBounds; private Rounding rounding; public DateHistogramAggregatorFactory(String name, ValuesSourceConfig config, long interval, - DateHistogramInterval dateHistogramInterval, long offset, InternalOrder order, boolean keyed, long minDocCount, + DateHistogramInterval dateHistogramInterval, long offset, BucketOrder order, boolean keyed, long minDocCount, Rounding rounding, ExtendedBounds extendedBounds, SearchContext context, AggregatorFactory parent, AggregatorFactories.Builder subFactoriesBuilder, Map metaData) throws IOException { super(name, config, context, parent, subFactoriesBuilder, metaData); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/Histogram.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/Histogram.java index 3ac87de81ed23..07e2eb879623c 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/Histogram.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/Histogram.java @@ -19,10 +19,8 @@ package org.elasticsearch.search.aggregations.bucket.histogram; import org.elasticsearch.common.ParseField; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; -import java.util.Comparator; import java.util.List; /** @@ -50,83 +48,4 @@ interface Bucket extends MultiBucketsAggregation.Bucket { @Override List getBuckets(); - /** - * A strategy defining the order in which the buckets in this histogram are ordered. - */ - abstract class Order implements ToXContent { - - private static int compareKey(Histogram.Bucket b1, Histogram.Bucket b2) { - if (b1 instanceof InternalHistogram.Bucket) { - return Double.compare(((InternalHistogram.Bucket) b1).key, ((InternalHistogram.Bucket) b2).key); - } else if (b1 instanceof InternalDateHistogram.Bucket) { - return Long.compare(((InternalDateHistogram.Bucket) b1).key, ((InternalDateHistogram.Bucket) b2).key); - } else { - throw new IllegalStateException("Unexpected impl: " + b1.getClass()); - } - } - - public static final Order KEY_ASC = new InternalOrder((byte) 1, "_key", true, new Comparator() { - @Override - public int compare(Histogram.Bucket b1, Histogram.Bucket b2) { - return compareKey(b1, b2); - } - }); - - public static final Order KEY_DESC = new InternalOrder((byte) 2, "_key", false, new Comparator() { - @Override - public int compare(Histogram.Bucket b1, Histogram.Bucket b2) { - return compareKey(b2, b1); - } - }); - - public static final Order COUNT_ASC = new InternalOrder((byte) 3, "_count", true, new Comparator() { - @Override - public int compare(Histogram.Bucket b1, Histogram.Bucket b2) { - int cmp = Long.compare(b1.getDocCount(), b2.getDocCount()); - if (cmp == 0) { - cmp = compareKey(b1, b2); - } - return cmp; - } - }); - - - public static final Order COUNT_DESC = new InternalOrder((byte) 4, "_count", false, new Comparator() { - @Override - public int compare(Histogram.Bucket b1, Histogram.Bucket b2) { - int cmp = Long.compare(b2.getDocCount(), b1.getDocCount()); - if (cmp == 0) { - cmp = compareKey(b1, b2); - } - return cmp; - } - }); - - /** - * Creates a bucket ordering strategy that sorts buckets based on a single-valued calc sug-aggregation - * - * @param path the name of the aggregation - * @param asc The direction of the order (ascending or descending) - */ - public static Order aggregation(String path, boolean asc) { - return new InternalOrder.Aggregation(path, asc); - } - - /** - * Creates a bucket ordering strategy that sorts buckets based on a multi-valued calc sug-aggregation - * - * @param aggregationName the name of the aggregation - * @param valueName The name of the value of the multi-value get by which the sorting will be applied - * @param asc The direction of the order (ascending or descending) - */ - public static Order aggregation(String aggregationName, String valueName, boolean asc) { - return new InternalOrder.Aggregation(aggregationName + "." + valueName, asc); - } - - /** - * @return The bucket comparator by which the order will be applied. - */ - abstract Comparator comparator(); - - } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregationBuilder.java index 87c7404c088ba..9362c0b8f779c 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregationBuilder.java @@ -20,16 +20,16 @@ package org.elasticsearch.search.aggregations.bucket.histogram; import org.elasticsearch.common.ParseField; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentParser.Token; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; +import org.elasticsearch.search.aggregations.BucketOrder; +import org.elasticsearch.search.aggregations.InternalOrder; +import org.elasticsearch.search.aggregations.InternalOrder.CompoundOrder; import org.elasticsearch.search.aggregations.support.ValueType; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSource.Numeric; @@ -41,6 +41,7 @@ import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; +import java.util.List; import java.util.Objects; /** @@ -75,8 +76,8 @@ public class HistogramAggregationBuilder histogram.extendedBounds(extendedBounds[0], extendedBounds[1]); }, parser -> EXTENDED_BOUNDS_PARSER.apply(parser, null), ExtendedBounds.EXTENDED_BOUNDS_FIELD, ObjectParser.ValueType.OBJECT); - PARSER.declareField(HistogramAggregationBuilder::order, HistogramAggregationBuilder::parseOrder, - Histogram.ORDER_FIELD, ObjectParser.ValueType.OBJECT); + PARSER.declareObjectArray(HistogramAggregationBuilder::order, InternalOrder.Parser::parseOrderParam, + Histogram.ORDER_FIELD); } public static HistogramAggregationBuilder parse(String aggregationName, QueryParseContext context) throws IOException { @@ -87,7 +88,7 @@ public static HistogramAggregationBuilder parse(String aggregationName, QueryPar private double offset = 0; private double minBound = Double.POSITIVE_INFINITY; private double maxBound = Double.NEGATIVE_INFINITY; - private InternalOrder order = (InternalOrder) Histogram.Order.KEY_ASC; + private BucketOrder order = BucketOrder.key(true); private boolean keyed = false; private long minDocCount = 0; @@ -99,9 +100,7 @@ public HistogramAggregationBuilder(String name) { /** Read from a stream, for internal use only. */ public HistogramAggregationBuilder(StreamInput in) throws IOException { super(in, ValuesSourceType.NUMERIC, ValueType.DOUBLE); - if (in.readBoolean()) { - order = InternalOrder.Streams.readOrder(in); - } + order = InternalOrder.Streams.readHistogramOrder(in, true); keyed = in.readBoolean(); minDocCount = in.readVLong(); interval = in.readDouble(); @@ -112,11 +111,7 @@ public HistogramAggregationBuilder(StreamInput in) throws IOException { @Override protected void innerWriteTo(StreamOutput out) throws IOException { - boolean hasOrder = order != null; - out.writeBoolean(hasOrder); - if (hasOrder) { - InternalOrder.Streams.writeOrder(order, out); - } + InternalOrder.Streams.writeHistogramOrder(order, out, true); out.writeBoolean(keyed); out.writeVLong(minDocCount); out.writeDouble(interval); @@ -185,17 +180,34 @@ public HistogramAggregationBuilder extendedBounds(double minBound, double maxBou } /** Return the order to use to sort buckets of this histogram. */ - public Histogram.Order order() { + public BucketOrder order() { return order; } /** Set a new order on this builder and return the builder so that calls - * can be chained. */ - public HistogramAggregationBuilder order(Histogram.Order order) { + * can be chained. A tie-breaker may be added to avoid non-deterministic ordering. */ + public HistogramAggregationBuilder order(BucketOrder order) { if (order == null) { throw new IllegalArgumentException("[order] must not be null: [" + name + "]"); } - this.order = (InternalOrder) order; + if(order instanceof CompoundOrder || InternalOrder.isKeyOrder(order)) { + this.order = order; // if order already contains a tie-breaker we are good to go + } else { // otherwise add a tie-breaker by using a compound order + this.order = BucketOrder.compound(order); + } + return this; + } + + /** + * Sets the order in which the buckets will be returned. A tie-breaker may be added to avoid non-deterministic + * ordering. + */ + public HistogramAggregationBuilder order(List orders) { + if (orders == null) { + throw new IllegalArgumentException("[orders] must not be null: [" + name + "]"); + } + // if the list only contains one order use that to avoid inconsistent xcontent + order(orders.size() > 1 ? BucketOrder.compound(orders) : orders.get(0)); return this; } @@ -286,34 +298,4 @@ protected boolean innerEquals(Object obj) { && Objects.equals(minBound, other.minBound) && Objects.equals(maxBound, other.maxBound); } - - private static InternalOrder parseOrder(XContentParser parser, QueryParseContext context) throws IOException { - InternalOrder order = null; - Token token; - String currentFieldName = null; - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - String dir = parser.text(); - boolean asc = "asc".equals(dir); - if (!asc && !"desc".equals(dir)) { - throw new ParsingException(parser.getTokenLocation(), "Unknown order direction: [" + dir - + "]. Should be either [asc] or [desc]"); - } - order = resolveOrder(currentFieldName, asc); - } - } - return order; - } - - static InternalOrder resolveOrder(String key, boolean asc) { - if ("_key".equals(key)) { - return (InternalOrder) (asc ? InternalOrder.KEY_ASC : InternalOrder.KEY_DESC); - } - if ("_count".equals(key)) { - return (InternalOrder) (asc ? InternalOrder.COUNT_ASC : InternalOrder.COUNT_DESC); - } - return new InternalOrder.Aggregation(key, asc); - } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregator.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregator.java index a3a038cfa3cae..0c2ba554c0b9f 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregator.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregator.java @@ -34,6 +34,8 @@ import org.elasticsearch.search.aggregations.bucket.BucketsAggregator; import org.elasticsearch.search.aggregations.bucket.histogram.InternalHistogram.EmptyBucketInfo; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; +import org.elasticsearch.search.aggregations.InternalOrder; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.internal.SearchContext; @@ -54,7 +56,7 @@ class HistogramAggregator extends BucketsAggregator { private final ValuesSource.Numeric valuesSource; private final DocValueFormat formatter; private final double interval, offset; - private final InternalOrder order; + private final BucketOrder order; private final boolean keyed; private final long minDocCount; private final double minBound, maxBound; @@ -62,7 +64,7 @@ class HistogramAggregator extends BucketsAggregator { private final LongHash bucketOrds; HistogramAggregator(String name, AggregatorFactories factories, double interval, double offset, - InternalOrder order, boolean keyed, long minDocCount, double minBound, double maxBound, + BucketOrder order, boolean keyed, long minDocCount, double minBound, double maxBound, @Nullable ValuesSource.Numeric valuesSource, DocValueFormat formatter, SearchContext context, Aggregator parent, List pipelineAggregators, Map metaData) throws IOException { @@ -73,7 +75,7 @@ class HistogramAggregator extends BucketsAggregator { } this.interval = interval; this.offset = offset; - this.order = order; + this.order = InternalOrder.validate(order, this); this.keyed = keyed; this.minDocCount = minDocCount; this.minBound = minBound; @@ -137,7 +139,7 @@ public InternalAggregation buildAggregation(long bucket) throws IOException { } // the contract of the histogram aggregation is that shards must return buckets ordered by key in ascending order - CollectionUtil.introSort(buckets, InternalOrder.KEY_ASC.comparator()); + CollectionUtil.introSort(buckets, BucketOrder.key(true).comparator(this)); EmptyBucketInfo emptyBucketInfo = null; if (minDocCount == 0) { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregatorFactory.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregatorFactory.java index 939210b63a699..c478d1262eac9 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregatorFactory.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregatorFactory.java @@ -23,6 +23,7 @@ import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSource.Numeric; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregatorFactory; @@ -36,13 +37,13 @@ public final class HistogramAggregatorFactory extends ValuesSourceAggregatorFactory { private final double interval, offset; - private final InternalOrder order; + private final BucketOrder order; private final boolean keyed; private final long minDocCount; private final double minBound, maxBound; HistogramAggregatorFactory(String name, ValuesSourceConfig config, double interval, double offset, - InternalOrder order, boolean keyed, long minDocCount, double minBound, double maxBound, + BucketOrder order, boolean keyed, long minDocCount, double minBound, double maxBound, SearchContext context, AggregatorFactory parent, AggregatorFactories.Builder subFactoriesBuilder, Map metaData) throws IOException { super(name, config, context, parent, subFactoriesBuilder, metaData); @@ -80,4 +81,4 @@ protected Aggregator createUnmapped(Aggregator parent, List throws IOException { return createAggregator(null, parent, pipelineAggregators, metaData); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogram.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogram.java index dc05ab51e8ad7..c3eab06f28ae8 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogram.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogram.java @@ -31,6 +31,9 @@ import org.elasticsearch.search.aggregations.InternalMultiBucketAggregation; import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; +import org.elasticsearch.search.aggregations.InternalOrder; +import org.elasticsearch.search.aggregations.KeyComparable; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -49,7 +52,7 @@ public final class InternalDateHistogram extends InternalMultiBucketAggregation implements Histogram, HistogramFactory { - public static class Bucket extends InternalMultiBucketAggregation.InternalBucket implements Histogram.Bucket { + public static class Bucket extends InternalMultiBucketAggregation.InternalBucket implements Histogram.Bucket, KeyComparable { final long key; final long docCount; @@ -151,6 +154,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + @Override + public int compareKey(Bucket other) { + return Long.compare(key, other.key); + } + public DocValueFormat getFormatter() { return format; } @@ -206,14 +214,14 @@ public int hashCode() { } private final List buckets; - private final InternalOrder order; + private final BucketOrder order; private final DocValueFormat format; private final boolean keyed; private final long minDocCount; private final long offset; private final EmptyBucketInfo emptyBucketInfo; - InternalDateHistogram(String name, List buckets, InternalOrder order, long minDocCount, long offset, + InternalDateHistogram(String name, List buckets, BucketOrder order, long minDocCount, long offset, EmptyBucketInfo emptyBucketInfo, DocValueFormat formatter, boolean keyed, List pipelineAggregators, Map metaData) { @@ -233,7 +241,7 @@ public int hashCode() { */ public InternalDateHistogram(StreamInput in) throws IOException { super(in); - order = InternalOrder.Streams.readOrder(in); + order = InternalOrder.Streams.readHistogramOrder(in, false); minDocCount = in.readVLong(); if (minDocCount == 0) { emptyBucketInfo = new EmptyBucketInfo(in); @@ -248,7 +256,7 @@ public InternalDateHistogram(StreamInput in) throws IOException { @Override protected void doWriteTo(StreamOutput out) throws IOException { - InternalOrder.Streams.writeOrder(order, out); + InternalOrder.Streams.writeHistogramOrder(order, out, false); out.writeVLong(minDocCount); if (minDocCount == 0) { emptyBucketInfo.writeTo(out); @@ -416,18 +424,18 @@ public InternalAggregation doReduce(List aggregations, Redu addEmptyBuckets(reducedBuckets, reduceContext); } - if (order == InternalOrder.KEY_ASC || reduceContext.isFinalReduce() == false) { + if (InternalOrder.isKeyAsc(order) || reduceContext.isFinalReduce() == false) { // nothing to do, data are already sorted since shards return // sorted buckets and the merge-sort performed by reduceBuckets // maintains order - } else if (order == InternalOrder.KEY_DESC) { + } else if (InternalOrder.isKeyDesc(order)) { // we just need to reverse here... List reverse = new ArrayList<>(reducedBuckets); Collections.reverse(reverse); reducedBuckets = reverse; } else { - // sorted by sub-aggregation, need to fall back to a costly n*log(n) sort - CollectionUtil.introSort(reducedBuckets, order.comparator()); + // sorted by compound order or sub-aggregation, need to fall back to a costly n*log(n) sort + CollectionUtil.introSort(reducedBuckets, order.comparator(null)); } return new InternalDateHistogram(getName(), reducedBuckets, order, minDocCount, offset, emptyBucketInfo, diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogram.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogram.java index c6d2aa9eeb959..035dd9de05539 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogram.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogram.java @@ -30,6 +30,9 @@ import org.elasticsearch.search.aggregations.InternalMultiBucketAggregation; import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; +import org.elasticsearch.search.aggregations.InternalOrder; +import org.elasticsearch.search.aggregations.KeyComparable; import java.io.IOException; import java.util.ArrayList; @@ -45,7 +48,7 @@ */ public final class InternalHistogram extends InternalMultiBucketAggregation implements Histogram, HistogramFactory { - public static class Bucket extends InternalMultiBucketAggregation.InternalBucket implements Histogram.Bucket { + public static class Bucket extends InternalMultiBucketAggregation.InternalBucket implements Histogram.Bucket, KeyComparable { final double key; final long docCount; @@ -147,6 +150,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + @Override + public int compareKey(Bucket other) { + return Double.compare(key, other.key); + } + public DocValueFormat getFormatter() { return format; } @@ -201,13 +209,13 @@ public int hashCode() { } private final List buckets; - private final InternalOrder order; + private final BucketOrder order; private final DocValueFormat format; private final boolean keyed; private final long minDocCount; private final EmptyBucketInfo emptyBucketInfo; - InternalHistogram(String name, List buckets, InternalOrder order, long minDocCount, EmptyBucketInfo emptyBucketInfo, + InternalHistogram(String name, List buckets, BucketOrder order, long minDocCount, EmptyBucketInfo emptyBucketInfo, DocValueFormat formatter, boolean keyed, List pipelineAggregators, Map metaData) { super(name, pipelineAggregators, metaData); @@ -225,7 +233,7 @@ public int hashCode() { */ public InternalHistogram(StreamInput in) throws IOException { super(in); - order = InternalOrder.Streams.readOrder(in); + order = InternalOrder.Streams.readHistogramOrder(in, false); minDocCount = in.readVLong(); if (minDocCount == 0) { emptyBucketInfo = new EmptyBucketInfo(in); @@ -239,7 +247,7 @@ public InternalHistogram(StreamInput in) throws IOException { @Override protected void doWriteTo(StreamOutput out) throws IOException { - InternalOrder.Streams.writeOrder(order, out); + InternalOrder.Streams.writeHistogramOrder(order, out, false); out.writeVLong(minDocCount); if (minDocCount == 0) { emptyBucketInfo.writeTo(out); @@ -400,18 +408,18 @@ public InternalAggregation doReduce(List aggregations, Redu addEmptyBuckets(reducedBuckets, reduceContext); } - if (order == InternalOrder.KEY_ASC || reduceContext.isFinalReduce() == false) { + if (InternalOrder.isKeyAsc(order) || reduceContext.isFinalReduce() == false) { // nothing to do, data are already sorted since shards return // sorted buckets and the merge-sort performed by reduceBuckets // maintains order - } else if (order == InternalOrder.KEY_DESC) { + } else if (InternalOrder.isKeyDesc(order)) { // we just need to reverse here... List reverse = new ArrayList<>(reducedBuckets); Collections.reverse(reverse); reducedBuckets = reverse; } else { - // sorted by sub-aggregation, need to fall back to a costly n*log(n) sort - CollectionUtil.introSort(reducedBuckets, order.comparator()); + // sorted by compound order or sub-aggregation, need to fall back to a costly n*log(n) sort + CollectionUtil.introSort(reducedBuckets, order.comparator(null)); } return new InternalHistogram(getName(), reducedBuckets, order, minDocCount, emptyBucketInfo, format, keyed, pipelineAggregators(), diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalOrder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalOrder.java deleted file mode 100644 index 5cf2f83baa850..0000000000000 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalOrder.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -package org.elasticsearch.search.aggregations.bucket.histogram; - -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; - -import java.io.IOException; -import java.util.Comparator; -import java.util.Objects; - -/** - * An internal {@link Histogram.Order} strategy which is identified by a unique id. - */ -class InternalOrder extends Histogram.Order { - - final byte id; - final String key; - final boolean asc; - final Comparator comparator; - - InternalOrder(byte id, String key, boolean asc, Comparator comparator) { - this.id = id; - this.key = key; - this.asc = asc; - this.comparator = comparator; - } - - byte id() { - return id; - } - - String key() { - return key; - } - - boolean asc() { - return asc; - } - - @Override - Comparator comparator() { - return comparator; - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return builder.startObject().field(key, asc ? "asc" : "desc").endObject(); - } - - @Override - public int hashCode() { - return Objects.hash(id, key, asc); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - InternalOrder other = (InternalOrder) obj; - return Objects.equals(id, other.id) - && Objects.equals(key, other.key) - && Objects.equals(asc, other.asc); - } - - static class Aggregation extends InternalOrder { - - static final byte ID = 0; - - Aggregation(String key, boolean asc) { - super(ID, key, asc, new MultiBucketsAggregation.Bucket.SubAggregationComparator(key, asc)); - } - - } - - static class Streams { - - /** - * Writes the given order to the given output (based on the id of the order). - */ - public static void writeOrder(InternalOrder order, StreamOutput out) throws IOException { - out.writeByte(order.id()); - if (order instanceof InternalOrder.Aggregation) { - out.writeBoolean(order.asc()); - out.writeString(order.key()); - } - } - - /** - * Reads an order from the given input (based on the id of the order). - * - * @see Streams#writeOrder(InternalOrder, org.elasticsearch.common.io.stream.StreamOutput) - */ - public static InternalOrder readOrder(StreamInput in) throws IOException { - byte id = in.readByte(); - switch (id) { - case 1: return (InternalOrder) Histogram.Order.KEY_ASC; - case 2: return (InternalOrder) Histogram.Order.KEY_DESC; - case 3: return (InternalOrder) Histogram.Order.COUNT_ASC; - case 4: return (InternalOrder) Histogram.Order.COUNT_DESC; - case 0: - boolean asc = in.readBoolean(); - String key = in.readString(); - return new InternalOrder.Aggregation(key, asc); - default: - throw new RuntimeException("unknown histogram order"); - } - } - - } - - -} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/AbstractStringTermsAggregator.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/AbstractStringTermsAggregator.java index 9b40fcc42dcc6..edbf2aef25fec 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/AbstractStringTermsAggregator.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/AbstractStringTermsAggregator.java @@ -24,6 +24,7 @@ import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; @@ -37,7 +38,7 @@ abstract class AbstractStringTermsAggregator extends TermsAggregator { protected final boolean showTermDocCountError; AbstractStringTermsAggregator(String name, AggregatorFactories factories, SearchContext context, Aggregator parent, - Terms.Order order, DocValueFormat format, BucketCountThresholds bucketCountThresholds, SubAggCollectionMode subAggCollectMode, + BucketOrder order, DocValueFormat format, BucketCountThresholds bucketCountThresholds, SubAggCollectionMode subAggCollectMode, boolean showTermDocCountError, List pipelineAggregators, Map metaData) throws IOException { super(name, factories, context, parent, bucketCountThresholds, order, format, subAggCollectMode, pipelineAggregators, metaData); this.showTermDocCountError = showTermDocCountError; diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java index e4c7906f21586..e4885bc0539d5 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java @@ -25,6 +25,7 @@ import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; import java.io.IOException; import java.util.ArrayList; @@ -76,8 +77,8 @@ public Number getKeyAsNumber() { } @Override - public int compareTerm(Terms.Bucket other) { - return Double.compare(term, ((Number) other.getKey()).doubleValue()); + public int compareKey(Bucket other) { + return Double.compare(term, other.term); } @Override @@ -105,7 +106,7 @@ public int hashCode() { } } - public DoubleTerms(String name, Terms.Order order, int requiredSize, long minDocCount, List pipelineAggregators, + public DoubleTerms(String name, BucketOrder order, int requiredSize, long minDocCount, List pipelineAggregators, Map metaData, DocValueFormat format, int shardSize, boolean showTermDocCountError, long otherDocCount, List buckets, long docCountError) { super(name, order, requiredSize, minDocCount, pipelineAggregators, metaData, format, shardSize, showTermDocCountError, diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTermsAggregator.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTermsAggregator.java index cf7478e319617..0ae42abd9a4d3 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTermsAggregator.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTermsAggregator.java @@ -27,6 +27,7 @@ import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSource.Numeric; import org.elasticsearch.search.internal.SearchContext; @@ -39,7 +40,7 @@ public class DoubleTermsAggregator extends LongTermsAggregator { public DoubleTermsAggregator(String name, AggregatorFactories factories, ValuesSource.Numeric valuesSource, DocValueFormat format, - Terms.Order order, BucketCountThresholds bucketCountThresholds, SearchContext aggregationContext, Aggregator parent, + BucketOrder order, BucketCountThresholds bucketCountThresholds, SearchContext aggregationContext, Aggregator parent, SubAggCollectionMode collectionMode, boolean showTermDocCountError, IncludeExclude.LongFilter longFilter, List pipelineAggregators, Map metaData) throws IOException { super(name, factories, valuesSource, format, order, bucketCountThresholds, aggregationContext, parent, collectionMode, diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/GlobalOrdinalsStringTermsAggregator.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/GlobalOrdinalsStringTermsAggregator.java index f315d915f0dc6..33bbc370c6e76 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/GlobalOrdinalsStringTermsAggregator.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/GlobalOrdinalsStringTermsAggregator.java @@ -43,6 +43,7 @@ import org.elasticsearch.search.aggregations.bucket.terms.support.BucketPriorityQueue; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.internal.SearchContext; @@ -70,7 +71,7 @@ public class GlobalOrdinalsStringTermsAggregator extends AbstractStringTermsAggr protected SortedSetDocValues globalOrds; public GlobalOrdinalsStringTermsAggregator(String name, AggregatorFactories factories, ValuesSource.Bytes.WithOrdinals valuesSource, - Terms.Order order, DocValueFormat format, BucketCountThresholds bucketCountThresholds, + BucketOrder order, DocValueFormat format, BucketCountThresholds bucketCountThresholds, IncludeExclude.OrdinalsFilter includeExclude, SearchContext context, Aggregator parent, SubAggCollectionMode collectionMode, boolean showTermDocCountError, List pipelineAggregators, Map metaData) throws IOException { @@ -122,8 +123,8 @@ public void collect(int doc, long bucket) throws IOException { public void collect(int doc, long bucket) throws IOException { assert bucket == 0; if (ords.advanceExact(doc)) { - for (long globalOrd = ords.nextOrd(); - globalOrd != SortedSetDocValues.NO_MORE_ORDS; + for (long globalOrd = ords.nextOrd(); + globalOrd != SortedSetDocValues.NO_MORE_ORDS; globalOrd = ords.nextOrd()) { collectExistingBucket(sub, doc, globalOrd); } @@ -218,8 +219,8 @@ static class OrdBucket extends InternalTerms.Bucket { } @Override - public int compareTerm(Terms.Bucket other) { - return Long.compare(globalOrd, ((OrdBucket) other).globalOrd); + public int compareKey(OrdBucket other) { + return Long.compare(globalOrd, other.globalOrd); } @Override @@ -261,7 +262,7 @@ public static class WithHash extends GlobalOrdinalsStringTermsAggregator { private final LongHash bucketOrds; - public WithHash(String name, AggregatorFactories factories, ValuesSource.Bytes.WithOrdinals valuesSource, Terms.Order order, + public WithHash(String name, AggregatorFactories factories, ValuesSource.Bytes.WithOrdinals valuesSource, BucketOrder order, DocValueFormat format, BucketCountThresholds bucketCountThresholds, IncludeExclude.OrdinalsFilter includeExclude, SearchContext context, Aggregator parent, SubAggCollectionMode collectionMode, boolean showTermDocCountError, List pipelineAggregators, Map metaData) @@ -296,8 +297,8 @@ public void collect(int doc, long bucket) throws IOException { @Override public void collect(int doc, long bucket) throws IOException { if (ords.advanceExact(doc)) { - for (long globalOrd = ords.nextOrd(); - globalOrd != SortedSetDocValues.NO_MORE_ORDS; + for (long globalOrd = ords.nextOrd(); + globalOrd != SortedSetDocValues.NO_MORE_ORDS; globalOrd = ords.nextOrd()) { long bucketOrd = bucketOrds.add(globalOrd); if (bucketOrd < 0) { @@ -337,7 +338,7 @@ public static class LowCardinality extends GlobalOrdinalsStringTermsAggregator { private SortedSetDocValues segmentOrds; public LowCardinality(String name, AggregatorFactories factories, ValuesSource.Bytes.WithOrdinals valuesSource, - Terms.Order order, DocValueFormat format, + BucketOrder order, DocValueFormat format, BucketCountThresholds bucketCountThresholds, SearchContext context, Aggregator parent, SubAggCollectionMode collectionMode, boolean showTermDocCountError, List pipelineAggregators, Map metaData) throws IOException { @@ -371,8 +372,8 @@ public void collect(int doc, long bucket) throws IOException { public void collect(int doc, long bucket) throws IOException { assert bucket == 0; if (ords.advanceExact(doc)) { - for (long segmentOrd = ords.nextOrd(); - segmentOrd != SortedSetDocValues.NO_MORE_ORDS; + for (long segmentOrd = ords.nextOrd(); + segmentOrd != SortedSetDocValues.NO_MORE_ORDS; segmentOrd = ords.nextOrd()) { segmentDocCounts.increment(segmentOrd + 1, 1); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalMappedTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalMappedTerms.java index 57c80c5fb40f0..26f8d809fe5fa 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalMappedTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalMappedTerms.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; import java.io.IOException; import java.util.Iterator; @@ -46,7 +47,7 @@ public abstract class InternalMappedTerms, B exten protected long docCountError; - protected InternalMappedTerms(String name, Terms.Order order, int requiredSize, long minDocCount, + protected InternalMappedTerms(String name, BucketOrder order, int requiredSize, long minDocCount, List pipelineAggregators, Map metaData, DocValueFormat format, int shardSize, boolean showTermDocCountError, long otherDocCount, List buckets, long docCountError) { super(name, order, requiredSize, minDocCount, pipelineAggregators, metaData); @@ -83,7 +84,7 @@ protected final void writeTermTypeInfoTo(StreamOutput out) throws IOException { @Override protected void setDocCountError(long docCountError) { - this.docCountError = docCountError; + this.docCountError = docCountError; } @Override diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalOrder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalOrder.java deleted file mode 100644 index 513e7a1ac0ec8..0000000000000 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalOrder.java +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -package org.elasticsearch.search.aggregations.bucket.terms; - -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.util.Comparators; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.search.aggregations.Aggregator; -import org.elasticsearch.search.aggregations.bucket.BucketsAggregator; -import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; -import org.elasticsearch.search.aggregations.bucket.SingleBucketAggregator; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; -import org.elasticsearch.search.aggregations.metrics.NumericMetricsAggregator; -import org.elasticsearch.search.aggregations.support.AggregationPath; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; - -class InternalOrder extends Terms.Order { - - private static final byte COUNT_DESC_ID = 1; - private static final byte COUNT_ASC_ID = 2; - private static final byte TERM_DESC_ID = 3; - private static final byte TERM_ASC_ID = 4; - - /** - * Order by the (higher) count of each term. - */ - public static final InternalOrder COUNT_DESC = new InternalOrder(COUNT_DESC_ID, "_count", false, new Comparator() { - @Override - public int compare(Terms.Bucket o1, Terms.Bucket o2) { - return Long.compare(o2.getDocCount(), o1.getDocCount()); - } - }); - - /** - * Order by the (lower) count of each term. - */ - public static final InternalOrder COUNT_ASC = new InternalOrder(COUNT_ASC_ID, "_count", true, new Comparator() { - - @Override - public int compare(Terms.Bucket o1, Terms.Bucket o2) { - return Long.compare(o1.getDocCount(), o2.getDocCount()); - } - }); - - /** - * Order by the terms. - */ - public static final InternalOrder TERM_DESC = new InternalOrder(TERM_DESC_ID, "_term", false, new Comparator() { - - @Override - public int compare(Terms.Bucket o1, Terms.Bucket o2) { - return o2.compareTerm(o1); - } - }); - - /** - * Order by the terms. - */ - public static final InternalOrder TERM_ASC = new InternalOrder(TERM_ASC_ID, "_term", true, new Comparator() { - - @Override - public int compare(Terms.Bucket o1, Terms.Bucket o2) { - return o1.compareTerm(o2); - } - }); - - public static boolean isCountDesc(Terms.Order order) { - if (order == COUNT_DESC) { - return true; - } else if (order instanceof CompoundOrder) { - // check if its a compound order with count desc and the tie breaker (term asc) - CompoundOrder compoundOrder = (CompoundOrder) order; - if (compoundOrder.orderElements.size() == 2 && compoundOrder.orderElements.get(0) == COUNT_DESC && compoundOrder.orderElements.get(1) == TERM_ASC) { - return true; - } - } - return false; - } - - public static boolean isTermOrder(Terms.Order order) { - if (order == TERM_ASC) { - return true; - } else if (order == TERM_DESC) { - return true; - } else if (order instanceof CompoundOrder) { - // check if its a compound order with only a single element ordering - // by term - CompoundOrder compoundOrder = (CompoundOrder) order; - if (compoundOrder.orderElements.size() == 1 && compoundOrder.orderElements.get(0) == TERM_ASC - || compoundOrder.orderElements.get(0) == TERM_DESC) { - return true; - } - } - return false; - } - - final byte id; - final String key; - final boolean asc; - protected final Comparator comparator; - - InternalOrder(byte id, String key, boolean asc, Comparator comparator) { - this.id = id; - this.key = key; - this.asc = asc; - this.comparator = comparator; - } - - @Override - byte id() { - return id; - } - - @Override - protected Comparator comparator(Aggregator aggregator) { - return comparator; - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return builder.startObject().field(key, asc ? "asc" : "desc").endObject(); - } - - public static Terms.Order validate(Terms.Order order, Aggregator termsAggregator) { - if (order instanceof CompoundOrder) { - for (Terms.Order innerOrder : ((CompoundOrder)order).orderElements) { - validate(innerOrder, termsAggregator); - } - return order; - } else if (!(order instanceof Aggregation)) { - return order; - } - AggregationPath path = ((Aggregation) order).path(); - path.validate(termsAggregator); - return order; - } - - static class Aggregation extends InternalOrder { - - static final byte ID = 0; - - Aggregation(String key, boolean asc) { - super(ID, key, asc, new MultiBucketsAggregation.Bucket.SubAggregationComparator(key, asc)); - } - - AggregationPath path() { - return ((MultiBucketsAggregation.Bucket.SubAggregationComparator) comparator).path(); - } - - @Override - protected Comparator comparator(Aggregator termsAggregator) { - if (termsAggregator == null) { - return comparator; - } - - // Internal Optimization: - // - // in this phase, if the order is based on sub-aggregations, we need to use a different comparator - // to avoid constructing buckets for ordering purposes (we can potentially have a lot of buckets and building - // them will cause loads of redundant object constructions). The "special" comparators here will fetch the - // sub aggregation values directly from the sub aggregators bypassing bucket creation. Note that the comparator - // attached to the order will still be used in the reduce phase of the Aggregation. - - AggregationPath path = path(); - final Aggregator aggregator = path.resolveAggregator(termsAggregator); - final String key = path.lastPathElement().key; - - if (aggregator instanceof SingleBucketAggregator) { - assert key == null : "this should be picked up before the aggregation is executed - on validate"; - return new Comparator() { - @Override - public int compare(Terms.Bucket o1, Terms.Bucket o2) { - int mul = asc ? 1 : -1; - int v1 = ((SingleBucketAggregator) aggregator).bucketDocCount(((InternalTerms.Bucket) o1).bucketOrd); - int v2 = ((SingleBucketAggregator) aggregator).bucketDocCount(((InternalTerms.Bucket) o2).bucketOrd); - return mul * (v1 - v2); - } - }; - } - - // with only support single-bucket aggregators - assert !(aggregator instanceof BucketsAggregator) : "this should be picked up before the aggregation is executed - on validate"; - - if (aggregator instanceof NumericMetricsAggregator.MultiValue) { - assert key != null : "this should be picked up before the aggregation is executed - on validate"; - return new Comparator() { - @Override - public int compare(Terms.Bucket o1, Terms.Bucket o2) { - double v1 = ((NumericMetricsAggregator.MultiValue) aggregator).metric(key, ((InternalTerms.Bucket) o1).bucketOrd); - double v2 = ((NumericMetricsAggregator.MultiValue) aggregator).metric(key, ((InternalTerms.Bucket) o2).bucketOrd); - // some metrics may return NaN (eg. avg, variance, etc...) in which case we'd like to push all of those to - // the bottom - return Comparators.compareDiscardNaN(v1, v2, asc); - } - }; - } - - // single-value metrics agg - return new Comparator() { - @Override - public int compare(Terms.Bucket o1, Terms.Bucket o2) { - double v1 = ((NumericMetricsAggregator.SingleValue) aggregator).metric(((InternalTerms.Bucket) o1).bucketOrd); - double v2 = ((NumericMetricsAggregator.SingleValue) aggregator).metric(((InternalTerms.Bucket) o2).bucketOrd); - // some metrics may return NaN (eg. avg, variance, etc...) in which case we'd like to push all of those to - // the bottom - return Comparators.compareDiscardNaN(v1, v2, asc); - } - }; - } - } - - static class CompoundOrder extends Terms.Order { - - static final byte ID = -1; - - private final List orderElements; - - CompoundOrder(List compoundOrder) { - this(compoundOrder, true); - } - - CompoundOrder(List compoundOrder, boolean absoluteOrdering) { - this.orderElements = new LinkedList<>(compoundOrder); - Terms.Order lastElement = compoundOrder.get(compoundOrder.size() - 1); - if (absoluteOrdering && !(InternalOrder.TERM_ASC == lastElement || InternalOrder.TERM_DESC == lastElement)) { - // add term order ascending as a tie-breaker to avoid non-deterministic ordering - // if all user provided comparators return 0. - this.orderElements.add(Order.term(true)); - } - } - - @Override - byte id() { - return ID; - } - - List orderElements() { - return Collections.unmodifiableList(orderElements); - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startArray(); - for (Terms.Order order : orderElements) { - order.toXContent(builder, params); - } - return builder.endArray(); - } - - @Override - protected Comparator comparator(Aggregator aggregator) { - return new CompoundOrderComparator(orderElements, aggregator); - } - - @Override - public int hashCode() { - return Objects.hash(orderElements); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - CompoundOrder other = (CompoundOrder) obj; - return Objects.equals(orderElements, other.orderElements); - } - - public static class CompoundOrderComparator implements Comparator { - - private List compoundOrder; - private Aggregator aggregator; - - CompoundOrderComparator(List compoundOrder, Aggregator aggregator) { - this.compoundOrder = compoundOrder; - this.aggregator = aggregator; - } - - @Override - public int compare(Bucket o1, Bucket o2) { - int result = 0; - for (Iterator itr = compoundOrder.iterator(); itr.hasNext() && result == 0;) { - result = itr.next().comparator(aggregator).compare(o1, o2); - } - return result; - } - } - } - - public static class Streams { - - public static void writeOrder(Terms.Order order, StreamOutput out) throws IOException { - if (order instanceof Aggregation) { - out.writeByte(order.id()); - Aggregation aggregationOrder = (Aggregation) order; - out.writeBoolean(((MultiBucketsAggregation.Bucket.SubAggregationComparator) aggregationOrder.comparator).asc()); - AggregationPath path = ((Aggregation) order).path(); - out.writeString(path.toString()); - } else if (order instanceof CompoundOrder) { - CompoundOrder compoundOrder = (CompoundOrder) order; - out.writeByte(order.id()); - out.writeVInt(compoundOrder.orderElements.size()); - for (Terms.Order innerOrder : compoundOrder.orderElements) { - Streams.writeOrder(innerOrder, out); - } - } else { - out.writeByte(order.id()); - } - } - - public static Terms.Order readOrder(StreamInput in) throws IOException { - return readOrder(in, false); - } - - public static Terms.Order readOrder(StreamInput in, boolean absoluteOrder) throws IOException { - byte id = in.readByte(); - switch (id) { - case COUNT_DESC_ID: return absoluteOrder ? new CompoundOrder(Collections.singletonList((Terms.Order) InternalOrder.COUNT_DESC)) : InternalOrder.COUNT_DESC; - case COUNT_ASC_ID: return absoluteOrder ? new CompoundOrder(Collections.singletonList((Terms.Order) InternalOrder.COUNT_ASC)) : InternalOrder.COUNT_ASC; - case TERM_DESC_ID: return InternalOrder.TERM_DESC; - case TERM_ASC_ID: return InternalOrder.TERM_ASC; - case Aggregation.ID: - boolean asc = in.readBoolean(); - String key = in.readString(); - return new InternalOrder.Aggregation(key, asc); - case CompoundOrder.ID: - int size = in.readVInt(); - List compoundOrder = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - compoundOrder.add(Streams.readOrder(in, false)); - } - return new CompoundOrder(compoundOrder, absoluteOrder); - default: - throw new RuntimeException("unknown terms order"); - } - } - } - - @Override - public int hashCode() { - return Objects.hash(id, asc); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - InternalOrder other = (InternalOrder) obj; - return Objects.equals(id, other.id) - && Objects.equals(asc, other.asc); - } -} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTerms.java index 3834f9a65be53..24d1d301623db 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalTerms.java @@ -31,6 +31,9 @@ import org.elasticsearch.search.aggregations.InternalMultiBucketAggregation; import org.elasticsearch.search.aggregations.bucket.terms.support.BucketPriorityQueue; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; +import org.elasticsearch.search.aggregations.InternalOrder; +import org.elasticsearch.search.aggregations.KeyComparable; import java.io.IOException; import java.util.ArrayList; @@ -46,8 +49,8 @@ public abstract class InternalTerms, B extends Int protected static final ParseField DOC_COUNT_ERROR_UPPER_BOUND_FIELD_NAME = new ParseField("doc_count_error_upper_bound"); protected static final ParseField SUM_OF_OTHER_DOC_COUNTS = new ParseField("sum_other_doc_count"); - public abstract static class Bucket> extends InternalMultiBucketAggregation.InternalBucket implements Terms.Bucket { - + public abstract static class Bucket> extends InternalMultiBucketAggregation.InternalBucket + implements Terms.Bucket, KeyComparable { /** * Reads a bucket. Should be a constructor reference. */ @@ -177,11 +180,11 @@ public int hashCode() { } } - protected final Terms.Order order; + protected final BucketOrder order; protected final int requiredSize; protected final long minDocCount; - protected InternalTerms(String name, Terms.Order order, int requiredSize, long minDocCount, + protected InternalTerms(String name, BucketOrder order, int requiredSize, long minDocCount, List pipelineAggregators, Map metaData) { super(name, pipelineAggregators, metaData); this.order = order; @@ -201,7 +204,7 @@ protected InternalTerms(StreamInput in) throws IOException { @Override protected final void doWriteTo(StreamOutput out) throws IOException { - InternalOrder.Streams.writeOrder(order, out); + order.writeTo(out); writeSize(requiredSize, out); out.writeVLong(minDocCount); writeTermTypeInfoTo(out); @@ -238,9 +241,9 @@ public InternalAggregation doReduce(List aggregations, Redu } otherDocCount += terms.getSumOfOtherDocCounts(); final long thisAggDocCountError; - if (terms.getBuckets().size() < getShardSize() || InternalOrder.isTermOrder(order)) { + if (terms.getBuckets().size() < getShardSize() || InternalOrder.isKeyOrder(order)) { thisAggDocCountError = 0; - } else if (InternalOrder.isCountDesc(this.order)) { + } else if (InternalOrder.isCountDesc(order)) { if (terms.getDocCountError() > 0) { // If there is an existing docCountError for this agg then // use this as the error for this aggregation diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTerms.java index 0de13a4d98f6b..025c397d3bd00 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTerms.java @@ -25,6 +25,7 @@ import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; import java.io.IOException; import java.util.ArrayList; @@ -76,8 +77,8 @@ public Number getKeyAsNumber() { } @Override - public int compareTerm(Terms.Bucket other) { - return Long.compare(term, ((Number) other.getKey()).longValue()); + public int compareKey(Bucket other) { + return Long.compare(term, other.term); } @Override @@ -105,7 +106,7 @@ public int hashCode() { } } - public LongTerms(String name, Terms.Order order, int requiredSize, long minDocCount, List pipelineAggregators, + public LongTerms(String name, BucketOrder order, int requiredSize, long minDocCount, List pipelineAggregators, Map metaData, DocValueFormat format, int shardSize, boolean showTermDocCountError, long otherDocCount, List buckets, long docCountError) { super(name, order, requiredSize, minDocCount, pipelineAggregators, metaData, format, shardSize, showTermDocCountError, diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsAggregator.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsAggregator.java index 8f8e2f3079b67..752666de67872 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsAggregator.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsAggregator.java @@ -32,6 +32,8 @@ import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude.LongFilter; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; +import org.elasticsearch.search.aggregations.InternalOrder; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.internal.SearchContext; @@ -50,7 +52,7 @@ public class LongTermsAggregator extends TermsAggregator { private LongFilter longFilter; public LongTermsAggregator(String name, AggregatorFactories factories, ValuesSource.Numeric valuesSource, DocValueFormat format, - Terms.Order order, BucketCountThresholds bucketCountThresholds, SearchContext aggregationContext, Aggregator parent, + BucketOrder order, BucketCountThresholds bucketCountThresholds, SearchContext aggregationContext, Aggregator parent, SubAggCollectionMode subAggCollectMode, boolean showTermDocCountError, IncludeExclude.LongFilter longFilter, List pipelineAggregators, Map metaData) throws IOException { super(name, factories, aggregationContext, parent, bucketCountThresholds, order, format, subAggCollectMode, pipelineAggregators, metaData); @@ -106,7 +108,7 @@ public void collect(int doc, long owningBucketOrdinal) throws IOException { public InternalAggregation buildAggregation(long owningBucketOrdinal) throws IOException { assert owningBucketOrdinal == 0; - if (bucketCountThresholds.getMinDocCount() == 0 && (order != InternalOrder.COUNT_DESC || bucketOrds.size() < bucketCountThresholds.getRequiredSize())) { + if (bucketCountThresholds.getMinDocCount() == 0 && (InternalOrder.isCountDesc(order) == false || bucketOrds.size() < bucketCountThresholds.getRequiredSize())) { // we need to fill-in the blanks for (LeafReaderContext ctx : context.searcher().getTopReaderContext().leaves()) { final SortedNumericDocValues values = getValues(valuesSource, ctx); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/StringTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/StringTerms.java index 049d996c08c2e..8c7f09ebe8322 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/StringTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/StringTerms.java @@ -25,6 +25,7 @@ import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; import java.io.IOException; import java.util.List; @@ -75,8 +76,8 @@ public String getKeyAsString() { } @Override - public int compareTerm(Terms.Bucket other) { - return termBytes.compareTo(((Bucket) other).termBytes); + public int compareKey(Bucket other) { + return termBytes.compareTo(other.termBytes); } @Override @@ -100,7 +101,7 @@ public int hashCode() { } } - public StringTerms(String name, Terms.Order order, int requiredSize, long minDocCount, List pipelineAggregators, + public StringTerms(String name, BucketOrder order, int requiredSize, long minDocCount, List pipelineAggregators, Map metaData, DocValueFormat format, int shardSize, boolean showTermDocCountError, long otherDocCount, List buckets, long docCountError) { super(name, order, requiredSize, minDocCount, pipelineAggregators, metaData, format, diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsAggregator.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsAggregator.java index 61c46cdfd68a9..6161f7912a8ad 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsAggregator.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsAggregator.java @@ -33,6 +33,8 @@ import org.elasticsearch.search.aggregations.bucket.terms.support.BucketPriorityQueue; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; +import org.elasticsearch.search.aggregations.InternalOrder; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.internal.SearchContext; @@ -51,7 +53,7 @@ public class StringTermsAggregator extends AbstractStringTermsAggregator { private final IncludeExclude.StringFilter includeExclude; public StringTermsAggregator(String name, AggregatorFactories factories, ValuesSource valuesSource, - Terms.Order order, DocValueFormat format, BucketCountThresholds bucketCountThresholds, + BucketOrder order, DocValueFormat format, BucketCountThresholds bucketCountThresholds, IncludeExclude.StringFilter includeExclude, SearchContext context, Aggregator parent, SubAggCollectionMode collectionMode, boolean showTermDocCountError, List pipelineAggregators, Map metaData) throws IOException { @@ -110,7 +112,7 @@ public void collect(int doc, long bucket) throws IOException { public InternalAggregation buildAggregation(long owningBucketOrdinal) throws IOException { assert owningBucketOrdinal == 0; - if (bucketCountThresholds.getMinDocCount() == 0 && (order != InternalOrder.COUNT_DESC || bucketOrds.size() < bucketCountThresholds.getRequiredSize())) { + if (bucketCountThresholds.getMinDocCount() == 0 && (InternalOrder.isCountDesc(order) == false || bucketOrds.size() < bucketCountThresholds.getRequiredSize())) { // we need to fill-in the blanks for (LeafReaderContext ctx : context.searcher().getTopReaderContext().leaves()) { final SortedBinaryDocValues values = valuesSource.bytesValues(ctx); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/Terms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/Terms.java index 166ece4e1122d..f14ecae7d165c 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/Terms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/Terms.java @@ -18,12 +18,8 @@ */ package org.elasticsearch.search.aggregations.bucket.terms; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; -import java.util.Arrays; -import java.util.Comparator; import java.util.List; /** @@ -39,8 +35,6 @@ interface Bucket extends MultiBucketsAggregation.Bucket { Number getKeyAsNumber(); - int compareTerm(Terms.Bucket other); - long getDocCountError(); } @@ -65,84 +59,4 @@ interface Bucket extends MultiBucketsAggregation.Bucket { * it to the top buckets. */ long getSumOfOtherDocCounts(); - - /** - * Determines the order by which the term buckets will be sorted - */ - abstract class Order implements ToXContent { - - /** - * @return a bucket ordering strategy that sorts buckets by their document counts (ascending or descending) - */ - public static Order count(boolean asc) { - return asc ? InternalOrder.COUNT_ASC : InternalOrder.COUNT_DESC; - } - - /** - * @return a bucket ordering strategy that sorts buckets by their terms (ascending or descending) - */ - public static Order term(boolean asc) { - return asc ? InternalOrder.TERM_ASC : InternalOrder.TERM_DESC; - } - - /** - * Creates a bucket ordering strategy which sorts buckets based on a single-valued calc get - * - * @param path the name of the get - * @param asc The direction of the order (ascending or descending) - */ - public static Order aggregation(String path, boolean asc) { - return new InternalOrder.Aggregation(path, asc); - } - - /** - * Creates a bucket ordering strategy which sorts buckets based on a multi-valued calc get - * - * @param aggregationName the name of the get - * @param metricName The name of the value of the multi-value get by which the sorting will be applied - * @param asc The direction of the order (ascending or descending) - */ - public static Order aggregation(String aggregationName, String metricName, boolean asc) { - return new InternalOrder.Aggregation(aggregationName + "." + metricName, asc); - } - - /** - * Creates a bucket ordering strategy which sorts buckets based multiple criteria - * - * @param orders a list of {@link Order} objects to sort on, in order of priority - */ - public static Order compound(List orders) { - return new InternalOrder.CompoundOrder(orders); - } - - /** - * Creates a bucket ordering strategy which sorts buckets based multiple criteria - * - * @param orders a list of {@link Order} parameters to sort on, in order of priority - */ - public static Order compound(Order... orders) { - return compound(Arrays.asList(orders)); - } - - /** - * @return A comparator for the bucket based on the given terms aggregator. The comparator is used in two phases: - * - * - aggregation phase, where each shard builds a list of term buckets to be sent to the coordinating node. - * In this phase, the passed in aggregator will be the terms aggregator that aggregates the buckets on the - * shard level. - * - * - reduce phase, where the coordinating node gathers all the buckets from all the shards and reduces them - * to a final bucket list. In this case, the passed in aggregator will be {@code null} - */ - protected abstract Comparator comparator(Aggregator aggregator); - - abstract byte id(); - - @Override - public abstract int hashCode(); - - @Override - public abstract boolean equals(Object obj); - - } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregationBuilder.java index 944f9fd96a402..cb239781e3e40 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregationBuilder.java @@ -19,20 +19,20 @@ package org.elasticsearch.search.aggregations.bucket.terms; import org.elasticsearch.common.ParseField; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregator.BucketCountThresholds; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; +import org.elasticsearch.search.aggregations.BucketOrder; +import org.elasticsearch.search.aggregations.InternalOrder; +import org.elasticsearch.search.aggregations.InternalOrder.CompoundOrder; import org.elasticsearch.search.aggregations.support.ValueType; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder; @@ -82,7 +82,7 @@ public class TermsAggregationBuilder extends ValuesSourceAggregationBuilder SubAggCollectionMode.parse(p.text()), SubAggCollectionMode.KEY, ObjectParser.ValueType.STRING); - PARSER.declareObjectArray(TermsAggregationBuilder::order, TermsAggregationBuilder::parseOrderParam, + PARSER.declareObjectArray(TermsAggregationBuilder::order, InternalOrder.Parser::parseOrderParam, TermsAggregationBuilder.ORDER_FIELD); PARSER.declareField((b, v) -> b.includeExclude(IncludeExclude.merge(v, b.includeExclude())), @@ -96,7 +96,7 @@ public static AggregationBuilder parse(String aggregationName, QueryParseContext return PARSER.parse(context.parser(), new TermsAggregationBuilder(aggregationName, null), context); } - private Terms.Order order = Terms.Order.compound(Terms.Order.count(false), Terms.Order.term(true)); + private BucketOrder order = BucketOrder.compound(BucketOrder.count(false)); // automatically adds tie-breaker key asc order private IncludeExclude includeExclude = null; private String executionHint = null; private SubAggCollectionMode collectMode = null; @@ -132,7 +132,7 @@ protected void innerWriteTo(StreamOutput out) throws IOException { out.writeOptionalWriteable(collectMode); out.writeOptionalString(executionHint); out.writeOptionalWriteable(includeExclude); - InternalOrder.Streams.writeOrder(order, out); + order.writeTo(out); out.writeBoolean(showTermDocCountError); } @@ -189,32 +189,37 @@ public TermsAggregationBuilder shardMinDocCount(long shardMinDocCount) { return this; } - /** - * Sets the order in which the buckets will be returned. - */ - public TermsAggregationBuilder order(Terms.Order order) { + /** Set a new order on this builder and return the builder so that calls + * can be chained. A tie-breaker may be added to avoid non-deterministic ordering. */ + public TermsAggregationBuilder order(BucketOrder order) { if (order == null) { throw new IllegalArgumentException("[order] must not be null: [" + name + "]"); } - this.order = order; + if(order instanceof CompoundOrder || InternalOrder.isKeyOrder(order)) { + this.order = order; // if order already contains a tie-breaker we are good to go + } else { // otherwise add a tie-breaker by using a compound order + this.order = BucketOrder.compound(order); + } return this; } /** - * Sets the order in which the buckets will be returned. + * Sets the order in which the buckets will be returned. A tie-breaker may be added to avoid non-deterministic + * ordering. */ - public TermsAggregationBuilder order(List orders) { + public TermsAggregationBuilder order(List orders) { if (orders == null) { throw new IllegalArgumentException("[orders] must not be null: [" + name + "]"); } - order(Terms.Order.compound(orders)); + // if the list only contains one order use that to avoid inconsistent xcontent + order(orders.size() > 1 ? BucketOrder.compound(orders) : orders.get(0)); return this; } /** * Gets the order in which the buckets will be returned. */ - public Terms.Order order() { + public BucketOrder order() { return order; } @@ -327,45 +332,4 @@ public String getType() { return NAME; } - private static Terms.Order parseOrderParam(XContentParser parser, QueryParseContext context) throws IOException { - XContentParser.Token token; - Terms.Order orderParam = null; - String orderKey = null; - boolean orderAsc = false; - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - orderKey = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - String dir = parser.text(); - if ("asc".equalsIgnoreCase(dir)) { - orderAsc = true; - } else if ("desc".equalsIgnoreCase(dir)) { - orderAsc = false; - } else { - throw new ParsingException(parser.getTokenLocation(), - "Unknown terms order direction [" + dir + "]"); - } - } else { - throw new ParsingException(parser.getTokenLocation(), - "Unexpected token " + token + " for [order]"); - } - } - if (orderKey == null) { - throw new ParsingException(parser.getTokenLocation(), - "Must specify at least one field for [order]"); - } else { - orderParam = resolveOrder(orderKey, orderAsc); - } - return orderParam; - } - - static Terms.Order resolveOrder(String key, boolean asc) { - if ("_term".equals(key)) { - return Order.term(asc); - } - if ("_count".equals(key)) { - return Order.count(asc); - } - return Order.aggregation(key, asc); - } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregator.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregator.java index 78d6cde211cde..4cada8f70bf38 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregator.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregator.java @@ -24,19 +24,26 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.util.Comparators; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.bucket.BucketsAggregator; -import org.elasticsearch.search.aggregations.bucket.terms.InternalOrder.Aggregation; -import org.elasticsearch.search.aggregations.bucket.terms.InternalOrder.CompoundOrder; +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket; +import org.elasticsearch.search.aggregations.bucket.SingleBucketAggregator; +import org.elasticsearch.search.aggregations.metrics.NumericMetricsAggregator; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.search.aggregations.support.AggregationPath; +import org.elasticsearch.search.aggregations.BucketOrder; +import org.elasticsearch.search.aggregations.InternalOrder; +import org.elasticsearch.search.aggregations.InternalOrder.Aggregation; +import org.elasticsearch.search.aggregations.InternalOrder.CompoundOrder; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -168,12 +175,12 @@ public boolean equals(Object obj) { protected final DocValueFormat format; protected final BucketCountThresholds bucketCountThresholds; - protected final Terms.Order order; + protected final BucketOrder order; protected final Set aggsUsedForSorting = new HashSet<>(); protected final SubAggCollectionMode collectMode; public TermsAggregator(String name, AggregatorFactories factories, SearchContext context, Aggregator parent, - BucketCountThresholds bucketCountThresholds, Terms.Order order, DocValueFormat format, SubAggCollectionMode collectMode, + BucketCountThresholds bucketCountThresholds, BucketOrder order, DocValueFormat format, SubAggCollectionMode collectMode, List pipelineAggregators, Map metaData) throws IOException { super(name, factories, context, parent, pipelineAggregators, metaData); this.bucketCountThresholds = bucketCountThresholds; @@ -186,7 +193,7 @@ public TermsAggregator(String name, AggregatorFactories factories, SearchContext aggsUsedForSorting.add(path.resolveTopmostAggregator(this)); } else if (order instanceof CompoundOrder) { CompoundOrder compoundOrder = (CompoundOrder) order; - for (Terms.Order orderElement : compoundOrder.orderElements()) { + for (BucketOrder orderElement : compoundOrder.orderElements()) { if (orderElement instanceof Aggregation) { AggregationPath path = ((Aggregation) orderElement).path(); aggsUsedForSorting.add(path.resolveTopmostAggregator(this)); @@ -195,6 +202,58 @@ public TermsAggregator(String name, AggregatorFactories factories, SearchContext } } + /** + * Internal Optimization for ordering {@link InternalTerms.Bucket}s by a sub aggregation. + *

+ * in this phase, if the order is based on sub-aggregations, we need to use a different comparator + * to avoid constructing buckets for ordering purposes (we can potentially have a lot of buckets and building + * them will cause loads of redundant object constructions). The "special" comparators here will fetch the + * sub aggregation values directly from the sub aggregators bypassing bucket creation. Note that the comparator + * attached to the order will still be used in the reduce phase of the Aggregation. + * + * @param path determines which sub aggregation to use for ordering. + * @param asc {@code true} for ascending order, {@code false} for descending. + * @return {@code Comparator} to order {@link InternalTerms.Bucket}s in the desired order. + */ + public Comparator bucketComparator(AggregationPath path, boolean asc) { + + final Aggregator aggregator = path.resolveAggregator(this); + final String key = path.lastPathElement().key; + + if (aggregator instanceof SingleBucketAggregator) { + assert key == null : "this should be picked up before the aggregation is executed - on validate"; + return (b1, b2) -> { + int mul = asc ? 1 : -1; + int v1 = ((SingleBucketAggregator) aggregator).bucketDocCount(((InternalTerms.Bucket) b1).bucketOrd); + int v2 = ((SingleBucketAggregator) aggregator).bucketDocCount(((InternalTerms.Bucket) b2).bucketOrd); + return mul * (v1 - v2); + }; + } + + // with only support single-bucket aggregators + assert !(aggregator instanceof BucketsAggregator) : "this should be picked up before the aggregation is executed - on validate"; + + if (aggregator instanceof NumericMetricsAggregator.MultiValue) { + assert key != null : "this should be picked up before the aggregation is executed - on validate"; + return (b1, b2) -> { + double v1 = ((NumericMetricsAggregator.MultiValue) aggregator).metric(key, ((InternalTerms.Bucket) b1).bucketOrd); + double v2 = ((NumericMetricsAggregator.MultiValue) aggregator).metric(key, ((InternalTerms.Bucket) b2).bucketOrd); + // some metrics may return NaN (eg. avg, variance, etc...) in which case we'd like to push all of those to + // the bottom + return Comparators.compareDiscardNaN(v1, v2, asc); + }; + } + + // single-value metrics agg + return (b1, b2) -> { + double v1 = ((NumericMetricsAggregator.SingleValue) aggregator).metric(((InternalTerms.Bucket) b1).bucketOrd); + double v2 = ((NumericMetricsAggregator.SingleValue) aggregator).metric(((InternalTerms.Bucket) b2).bucketOrd); + // some metrics may return NaN (eg. avg, variance, etc...) in which case we'd like to push all of those to + // the bottom + return Comparators.compareDiscardNaN(v1, v2, asc); + }; + } + @Override protected boolean shouldDefer(Aggregator aggregator) { return collectMode == SubAggCollectionMode.BREADTH_FIRST diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorFactory.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorFactory.java index 10fef55455551..9a06dfe66f592 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorFactory.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorFactory.java @@ -33,6 +33,8 @@ import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregator.BucketCountThresholds; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; +import org.elasticsearch.search.aggregations.InternalOrder; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregatorFactory; import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; @@ -44,14 +46,14 @@ public class TermsAggregatorFactory extends ValuesSourceAggregatorFactory { - private final Terms.Order order; + private final BucketOrder order; private final IncludeExclude includeExclude; private final String executionHint; private final SubAggCollectionMode collectMode; private final TermsAggregator.BucketCountThresholds bucketCountThresholds; private boolean showTermDocCountError; - public TermsAggregatorFactory(String name, ValuesSourceConfig config, Terms.Order order, + public TermsAggregatorFactory(String name, ValuesSourceConfig config, BucketOrder order, IncludeExclude includeExclude, String executionHint, SubAggCollectionMode collectMode, TermsAggregator.BucketCountThresholds bucketCountThresholds, boolean showTermDocCountError, SearchContext context, AggregatorFactory parent, AggregatorFactories.Builder subFactoriesBuilder, Map metaData) throws IOException { @@ -90,7 +92,7 @@ protected Aggregator doCreateInternal(ValuesSource valuesSource, Aggregator pare return asMultiBucketAggregator(this, context, parent); } BucketCountThresholds bucketCountThresholds = new BucketCountThresholds(this.bucketCountThresholds); - if (!(order == InternalOrder.TERM_ASC || order == InternalOrder.TERM_DESC) + if (InternalOrder.isKeyOrder(order) == false && bucketCountThresholds.getShardSize() == TermsAggregationBuilder.DEFAULT_BUCKET_COUNT_THRESHOLDS.getShardSize()) { // The user has not made a shardSize selection. Use default // heuristic to avoid any wrong-ranking caused by distributed @@ -129,7 +131,7 @@ protected Aggregator doCreateInternal(ValuesSource valuesSource, Aggregator pare // to be unbounded and most instances may only aggregate few // documents, so use hashed based // global ordinals to keep the bucket ords dense. - // Additionally, if using partitioned terms the regular global + // Additionally, if using partitioned terms the regular global // ordinals would be sparse so we opt for hash if (Aggregator.descendsFromBucketAggregator(parent) || (includeExclude != null && includeExclude.isPartitionBased())) { @@ -223,7 +225,7 @@ public enum ExecutionMode { MAP(new ParseField("map")) { @Override - Aggregator create(String name, AggregatorFactories factories, ValuesSource valuesSource, Terms.Order order, + Aggregator create(String name, AggregatorFactories factories, ValuesSource valuesSource, BucketOrder order, DocValueFormat format, TermsAggregator.BucketCountThresholds bucketCountThresholds, IncludeExclude includeExclude, SearchContext context, Aggregator parent, SubAggCollectionMode subAggCollectMode, boolean showTermDocCountError, List pipelineAggregators, Map metaData) @@ -242,7 +244,7 @@ boolean needsGlobalOrdinals() { GLOBAL_ORDINALS(new ParseField("global_ordinals")) { @Override - Aggregator create(String name, AggregatorFactories factories, ValuesSource valuesSource, Terms.Order order, + Aggregator create(String name, AggregatorFactories factories, ValuesSource valuesSource, BucketOrder order, DocValueFormat format, TermsAggregator.BucketCountThresholds bucketCountThresholds, IncludeExclude includeExclude, SearchContext context, Aggregator parent, SubAggCollectionMode subAggCollectMode, boolean showTermDocCountError, List pipelineAggregators, Map metaData) @@ -262,7 +264,7 @@ boolean needsGlobalOrdinals() { GLOBAL_ORDINALS_HASH(new ParseField("global_ordinals_hash")) { @Override - Aggregator create(String name, AggregatorFactories factories, ValuesSource valuesSource, Terms.Order order, + Aggregator create(String name, AggregatorFactories factories, ValuesSource valuesSource, BucketOrder order, DocValueFormat format, TermsAggregator.BucketCountThresholds bucketCountThresholds, IncludeExclude includeExclude, SearchContext context, Aggregator parent, SubAggCollectionMode subAggCollectMode, boolean showTermDocCountError, List pipelineAggregators, Map metaData) @@ -281,7 +283,7 @@ boolean needsGlobalOrdinals() { GLOBAL_ORDINALS_LOW_CARDINALITY(new ParseField("global_ordinals_low_cardinality")) { @Override - Aggregator create(String name, AggregatorFactories factories, ValuesSource valuesSource, Terms.Order order, + Aggregator create(String name, AggregatorFactories factories, ValuesSource valuesSource, BucketOrder order, DocValueFormat format, TermsAggregator.BucketCountThresholds bucketCountThresholds, IncludeExclude includeExclude, SearchContext context, Aggregator parent, SubAggCollectionMode subAggCollectMode, boolean showTermDocCountError, List pipelineAggregators, Map metaData) @@ -319,7 +321,7 @@ public static ExecutionMode fromString(String value) { this.parseField = parseField; } - abstract Aggregator create(String name, AggregatorFactories factories, ValuesSource valuesSource, Terms.Order order, + abstract Aggregator create(String name, AggregatorFactories factories, ValuesSource valuesSource, BucketOrder order, DocValueFormat format, TermsAggregator.BucketCountThresholds bucketCountThresholds, IncludeExclude includeExclude, SearchContext context, Aggregator parent, SubAggCollectionMode subAggCollectMode, boolean showTermDocCountError, List pipelineAggregators, Map metaData) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/UnmappedTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/UnmappedTerms.java index 40cbacd37e698..6362f8c347b3a 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/UnmappedTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/UnmappedTerms.java @@ -25,6 +25,7 @@ import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; import java.io.IOException; import java.util.Collections; @@ -50,7 +51,7 @@ private Bucket(long docCount, InternalAggregations aggregations, boolean showDoc } } - public UnmappedTerms(String name, Terms.Order order, int requiredSize, long minDocCount, + public UnmappedTerms(String name, BucketOrder order, int requiredSize, long minDocCount, List pipelineAggregators, Map metaData) { super(name, order, requiredSize, minDocCount, pipelineAggregators, metaData); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/support/AggregationPath.java b/core/src/main/java/org/elasticsearch/search/aggregations/support/AggregationPath.java index 746e0e5e16106..995381373ab40 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/support/AggregationPath.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/support/AggregationPath.java @@ -33,7 +33,7 @@ import java.util.List; /** - * A path that can be used to sort/order buckets (in some multi-bucket aggregations, eg terms & histogram) based on + * A path that can be used to sort/order buckets (in some multi-bucket aggregations, e.g. terms & histogram) based on * sub-aggregations. The path may point to either a single-bucket aggregation or a metrics aggregation. If the path * points to a single-bucket aggregation, the sort will be applied based on the {@code doc_count} of the bucket. If this * path points to a metrics aggregation, if it's a single-value metrics (eg. avg, max, min, etc..) the sort will be @@ -281,14 +281,15 @@ public Aggregator resolveTopmostAggregator(Aggregator root) { /** * Validates this path over the given aggregator as a point of reference. * - * @param root The point of reference of this path + * @param root The point of reference of this path + * @throws AggregationExecutionException on validation error */ - public void validate(Aggregator root) { + public void validate(Aggregator root) throws AggregationExecutionException { Aggregator aggregator = root; for (int i = 0; i < pathElements.size(); i++) { aggregator = aggregator.subAggregator(pathElements.get(i).name); if (aggregator == null) { - throw new AggregationExecutionException("Invalid term-aggregator order path [" + this + "]. Unknown aggregation [" + throw new AggregationExecutionException("Invalid aggregator order path [" + this + "]. Unknown aggregation [" + pathElements.get(i).name + "]"); } if (i < pathElements.size() - 1) { @@ -296,16 +297,16 @@ public void validate(Aggregator root) { // we're in the middle of the path, so the aggregator can only be a single-bucket aggregator if (!(aggregator instanceof SingleBucketAggregator)) { - throw new AggregationExecutionException("Invalid terms aggregation order path [" + this + - "]. Terms buckets can only be sorted on a sub-aggregator path " + + throw new AggregationExecutionException("Invalid aggregation order path [" + this + + "]. Buckets can only be sorted on a sub-aggregator path " + "that is built out of zero or more single-bucket aggregations within the path and a final " + "single-bucket or a metrics aggregation at the path end. Sub-path [" + subPath(0, i + 1) + "] points to non single-bucket aggregation"); } if (pathElements.get(i).key != null) { - throw new AggregationExecutionException("Invalid terms aggregation order path [" + this + - "]. Terms buckets can only be sorted on a sub-aggregator path " + + throw new AggregationExecutionException("Invalid aggregation order path [" + this + + "]. Buckets can only be sorted on a sub-aggregator path " + "that is built out of zero or more single-bucket aggregations within the path and a " + "final single-bucket or a metrics aggregation at the path end. Sub-path [" + subPath(0, i + 1) + "] points to non single-bucket aggregation"); @@ -314,8 +315,8 @@ public void validate(Aggregator root) { } boolean singleBucket = aggregator instanceof SingleBucketAggregator; if (!singleBucket && !(aggregator instanceof NumericMetricsAggregator)) { - throw new AggregationExecutionException("Invalid terms aggregation order path [" + this + - "]. Terms buckets can only be sorted on a sub-aggregator path " + + throw new AggregationExecutionException("Invalid aggregation order path [" + this + + "]. Buckets can only be sorted on a sub-aggregator path " + "that is built out of zero or more single-bucket aggregations within the path and a final " + "single-bucket or a metrics aggregation at the path end."); } @@ -324,7 +325,7 @@ public void validate(Aggregator root) { if (singleBucket) { if (lastToken.key != null && !"doc_count".equals(lastToken.key)) { - throw new AggregationExecutionException("Invalid terms aggregation order path [" + this + + throw new AggregationExecutionException("Invalid aggregation order path [" + this + "]. Ordering on a single-bucket aggregation can only be done on its doc_count. " + "Either drop the key (a la \"" + lastToken.name + "\") or change it to \"doc_count\" (a la \"" + lastToken.name + ".doc_count\")"); } @@ -333,7 +334,7 @@ public void validate(Aggregator root) { if (aggregator instanceof NumericMetricsAggregator.SingleValue) { if (lastToken.key != null && !"value".equals(lastToken.key)) { - throw new AggregationExecutionException("Invalid terms aggregation order path [" + this + + throw new AggregationExecutionException("Invalid aggregation order path [" + this + "]. Ordering on a single-value metrics aggregation can only be done on its value. " + "Either drop the key (a la \"" + lastToken.name + "\") or change it to \"value\" (a la \"" + lastToken.name + ".value\")"); } @@ -342,12 +343,12 @@ public void validate(Aggregator root) { // the aggregator must be of a multi-value metrics type if (lastToken.key == null) { - throw new AggregationExecutionException("Invalid terms aggregation order path [" + this + + throw new AggregationExecutionException("Invalid aggregation order path [" + this + "]. When ordering on a multi-value metrics aggregation a metric name must be specified"); } if (!((NumericMetricsAggregator.MultiValue) aggregator).hasMetric(lastToken.key)) { - throw new AggregationExecutionException("Invalid terms aggregation order path [" + this + + throw new AggregationExecutionException("Invalid aggregation order path [" + this + "]. Unknown metric name [" + lastToken.key + "] on multi-value metrics aggregation [" + lastToken.name + "]"); } } diff --git a/core/src/test/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilderTests.java index f2d66409e9e42..c321ffa965a57 100644 --- a/core/src/test/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilderTests.java @@ -32,6 +32,7 @@ import org.elasticsearch.action.termvectors.MultiTermVectorsResponse; import org.elasticsearch.action.termvectors.TermVectorsRequest; import org.elasticsearch.action.termvectors.TermVectorsResponse; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.lucene.search.MoreLikeThisQuery; @@ -64,6 +65,8 @@ public class MoreLikeThisQueryBuilderTests extends AbstractQueryTestCase { + private static final String[] SHUFFLE_PROTECTED_FIELDS = new String[]{Item.Field.DOC.getPreferredName()}; + private static String[] randomFields; private static Item[] randomLikeItems; private static Item[] randomUnlikeItems; @@ -204,6 +207,16 @@ protected MoreLikeThisQueryBuilder doCreateTestQueryBuilder() { return queryBuilder; } + /** + * we don't want to shuffle the "doc" field internally in {@link #testFromXContent()} because even though the + * documents would be functionally the same, their {@link BytesReference} representation isn't and thats what we + * compare when check for equality of the original and the shuffled builder + */ + @Override + protected String[] shuffleProtectedFields() { + return SHUFFLE_PROTECTED_FIELDS; + } + @Override protected Set getObjectsHoldingArbitraryContent() { //doc contains arbitrary content, anything can be added to it and no exception will be thrown diff --git a/core/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java index 67ddcbd6b0e5a..90d11efb11c22 100644 --- a/core/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java @@ -83,6 +83,9 @@ public class FunctionScoreQueryBuilderTests extends AbstractQueryTestCase { + private static final String[] SHUFFLE_PROTECTED_FIELDS = new String[] {Script.PARAMS_PARSE_FIELD.getPreferredName(), + ExponentialDecayFunctionBuilder.NAME, LinearDecayFunctionBuilder.NAME, GaussDecayFunctionBuilder.NAME}; + @Override protected Collection> getPlugins() { return Collections.singleton(TestPlugin.class); @@ -106,6 +109,12 @@ protected FunctionScoreQueryBuilder doCreateTestQueryBuilder() { return functionScoreQueryBuilder; } + @Override + protected String[] shuffleProtectedFields() { + // do not shuffle fields that may contain arbitrary content + return SHUFFLE_PROTECTED_FIELDS; + } + @Override protected Set getObjectsHoldingArbitraryContent() { //script_score.script.params can contain arbitrary parameters. no error is expected when adding additional objects diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalOrderTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalOrderTests.java new file mode 100644 index 0000000000000..43d10af99fbe9 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalOrderTests.java @@ -0,0 +1,158 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.search.aggregations; + +import org.elasticsearch.Version; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.BytesStreamOutput; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParser.Token; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.search.aggregations.InternalOrder.CompoundOrder; +import org.elasticsearch.test.AbstractSerializingTestCase; +import org.elasticsearch.test.VersionUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class InternalOrderTests extends AbstractSerializingTestCase { + + @Override + protected BucketOrder createTestInstance() { + if (randomBoolean()) { + return getRandomOrder(); + } else { + List orders = new ArrayList<>(); + for (int i = 0; i < randomInt(3); i++) { + orders.add(getRandomOrder()); + } + return BucketOrder.compound(orders); + } + } + + private BucketOrder getRandomOrder() { + switch(randomInt(2)) { + case 0: return BucketOrder.key(randomBoolean()); + case 1: return BucketOrder.count(randomBoolean()); + default: return BucketOrder.aggregation(randomAlphaOfLength(10), randomBoolean()); + } + } + + @Override + protected Reader instanceReader() { + return InternalOrder.Streams::readOrder; + } + + @Override + protected BucketOrder doParseInstance(XContentParser parser) throws IOException { + Token token = parser.nextToken(); + if (token == Token.START_OBJECT) { + return InternalOrder.Parser.parseOrderParam(parser, null); + } + if (token == Token.START_ARRAY) { + List orders = new ArrayList<>(); + while (parser.nextToken() == Token.START_OBJECT) { + orders.add(InternalOrder.Parser.parseOrderParam(parser, null)); + } + return BucketOrder.compound(orders); + } + return null; + } + + @Override + protected BucketOrder assertSerialization(BucketOrder testInstance) throws IOException { + // identical behavior to AbstractWireSerializingTestCase, except assertNotSame is only called for + // compound and aggregation order because _key and _count orders are static instances. + BucketOrder deserializedInstance = copyInstance(testInstance); + assertEquals(testInstance, deserializedInstance); + assertEquals(testInstance.hashCode(), deserializedInstance.hashCode()); + if(testInstance instanceof CompoundOrder || testInstance instanceof InternalOrder.Aggregation) { + assertNotSame(testInstance, deserializedInstance); + } + return deserializedInstance; + } + + @Override + protected void assertParsedInstance(XContentType xContentType, BytesReference instanceAsBytes, BucketOrder expectedInstance) + throws IOException { + // identical behavior to AbstractSerializingTestCase, except assertNotSame is only called for + // compound and aggregation order because _key and _count orders are static instances. + XContentParser parser = createParser(XContentFactory.xContent(xContentType), instanceAsBytes); + BucketOrder newInstance = parseInstance(parser); + assertEquals(expectedInstance, newInstance); + assertEquals(expectedInstance.hashCode(), newInstance.hashCode()); + if(expectedInstance instanceof CompoundOrder || expectedInstance instanceof InternalOrder.Aggregation) { + assertNotSame(newInstance, expectedInstance); + } + } + + public void testHistogramOrderBwc() throws IOException { + for (int runs = 0; runs < NUMBER_OF_TEST_RUNS; runs++) { + BucketOrder order = createTestInstance(); + Version bwcVersion = VersionUtils.randomVersionBetween(random(), VersionUtils.getFirstVersion(), + VersionUtils.getPreviousVersion(Version.V_6_0_0_alpha2_UNRELEASED)); + boolean bwcOrderFlag = randomBoolean(); + try (BytesStreamOutput out = new BytesStreamOutput()) { + out.setVersion(bwcVersion); + InternalOrder.Streams.writeHistogramOrder(order, out, bwcOrderFlag); + try (StreamInput in = out.bytes().streamInput()) { + in.setVersion(bwcVersion); + BucketOrder actual = InternalOrder.Streams.readHistogramOrder(in, bwcOrderFlag); + BucketOrder expected = order; + if (order instanceof CompoundOrder) { + expected = ((CompoundOrder) order).orderElements.get(0); + } + assertEquals(expected, actual); + } + } + } + } + + public void testAggregationOrderEqualsAndHashCode() { + String path = randomAlphaOfLength(10); + boolean asc = randomBoolean(); + BucketOrder o1 = BucketOrder.aggregation(path, asc); + BucketOrder o2 = BucketOrder.aggregation(path + "test", asc); + BucketOrder o3 = BucketOrder.aggregation(path, !asc); + BucketOrder o4 = BucketOrder.aggregation(path, asc); + assertNotEquals(o1, o2); + assertNotEquals(o1.hashCode(), o2.hashCode()); + assertNotEquals(o1, o3); + assertNotEquals(o1.hashCode(), o3.hashCode()); + assertEquals(o1, o4); + assertEquals(o1.hashCode(), o4.hashCode()); + + o1 = InternalOrder.compound(o1); + o2 = InternalOrder.compound(o2); + o3 = InternalOrder.compound(o3); + assertNotEquals(o1, o2); + assertNotEquals(o1.hashCode(), o2.hashCode()); + assertNotEquals(o1, o2); + assertNotEquals(o1.hashCode(), o2.hashCode()); + assertNotEquals(o1, o3); + assertNotEquals(o1.hashCode(), o3.hashCode()); + assertNotEquals(o1, o4); + assertNotEquals(o1.hashCode(), o4.hashCode()); + } + +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java index cef4cb07f884f..bedd8610a401e 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java @@ -18,7 +18,9 @@ */ package org.elasticsearch.search.aggregations.bucket; +import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.joda.DateMathParser; import org.elasticsearch.common.joda.Joda; @@ -30,13 +32,16 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; +import org.elasticsearch.search.aggregations.AggregationExecutionException; import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.bucket.DateScriptMocks.DateScriptsMockPlugin; import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval; import org.elasticsearch.search.aggregations.bucket.histogram.ExtendedBounds; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Bucket; +import org.elasticsearch.search.aggregations.metrics.avg.Avg; import org.elasticsearch.search.aggregations.metrics.sum.Sum; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import org.hamcrest.Matchers; import org.joda.time.DateTime; @@ -57,6 +62,7 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; +import static org.elasticsearch.search.aggregations.AggregationBuilders.avg; import static org.elasticsearch.search.aggregations.AggregationBuilders.dateHistogram; import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; import static org.elasticsearch.search.aggregations.AggregationBuilders.max; @@ -73,6 +79,8 @@ @ESIntegTestCase.SuiteScopeTestCase public class DateHistogramIT extends ESIntegTestCase { + static Map> expectedMultiSortBuckets; + private DateTime date(int month, int day) { return new DateTime(2012, month, day, 0, 0, DateTimeZone.UTC); } @@ -98,6 +106,7 @@ private IndexRequestBuilder indexDoc(int month, int day, int value) throws Excep return client().prepareIndex("idx", "type").setSource(jsonBuilder() .startObject() .field("value", value) + .field("constant", 1) .field("date", date(month, day)) .startArray("dates").value(date(month, day)).value(date(month + 1, day + 1)).endArray() .endObject()); @@ -115,6 +124,9 @@ public void setupSuiteScopeCluster() throws Exception { .field("value", i * 2) .endObject())); } + + getMultiSortDocs(builders); + builders.addAll(Arrays.asList( indexDoc(1, 2, 1), // date: Jan 2, dates: Jan 2, Feb 3 indexDoc(2, 2, 2), // date: Feb 2, dates: Feb 2, Mar 3 @@ -126,6 +138,50 @@ public void setupSuiteScopeCluster() throws Exception { ensureSearchable(); } + private void addExpectedBucket(DateTime key, long docCount, double avg, double sum) { + Map bucketProps = new HashMap<>(); + bucketProps.put("_count", docCount); + bucketProps.put("avg_l", avg); + bucketProps.put("sum_d", sum); + expectedMultiSortBuckets.put(key, bucketProps); + } + + private void getMultiSortDocs(List builders) throws IOException { + expectedMultiSortBuckets = new HashMap<>(); + addExpectedBucket(date(1, 1), 3, 1, 6); + addExpectedBucket(date(1, 2), 3, 2, 6); + addExpectedBucket(date(1, 3), 2, 3, 3); + addExpectedBucket(date(1, 4), 2, 3, 4); + addExpectedBucket(date(1, 5), 2, 5, 3); + addExpectedBucket(date(1, 6), 1, 5, 1); + addExpectedBucket(date(1, 7), 1, 5, 1); + + assertAcked(client().admin().indices().prepareCreate("sort_idx") + .addMapping("type", "date", "type=date").get()); + for (int i = 1; i <= 3; i++) { + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field("date", date(1, 1)).field("l", 1).field("d", i).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field("date", date(1, 2)).field("l", 2).field("d", i).endObject())); + } + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field("date", date(1, 3)).field("l", 3).field("d", 1).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field("date", date(1, 3).plusHours(1)).field("l", 3).field("d", 2).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field("date", date(1, 4)).field("l", 3).field("d", 1).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field("date", date(1, 4).plusHours(2)).field("l", 3).field("d", 3).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field("date", date(1, 5)).field("l", 5).field("d", 1).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field("date", date(1, 5).plusHours(12)).field("l", 5).field("d", 2).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field("date", date(1, 6)).field("l", 5).field("d", 1).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field("date", date(1, 7)).field("l", 5).field("d", 1).endObject())); + } + @Override protected Collection> nodePlugins() { return Arrays.asList( @@ -281,7 +337,7 @@ public void testSingleValuedFieldOrderedByKeyAsc() throws Exception { .addAggregation(dateHistogram("histo") .field("date") .dateHistogramInterval(DateHistogramInterval.MONTH) - .order(Histogram.Order.KEY_ASC)) + .order(BucketOrder.key(true))) .execute().actionGet(); assertSearchResponse(response); @@ -304,7 +360,7 @@ public void testSingleValuedFieldOrderedByKeyDesc() throws Exception { .addAggregation(dateHistogram("histo") .field("date") .dateHistogramInterval(DateHistogramInterval.MONTH) - .order(Histogram.Order.KEY_DESC)) + .order(BucketOrder.key(false))) .execute().actionGet(); assertSearchResponse(response); @@ -326,7 +382,7 @@ public void testSingleValuedFieldOrderedByCountAsc() throws Exception { .addAggregation(dateHistogram("histo") .field("date") .dateHistogramInterval(DateHistogramInterval.MONTH) - .order(Histogram.Order.COUNT_ASC)) + .order(BucketOrder.count(true))) .execute().actionGet(); assertSearchResponse(response); @@ -348,7 +404,7 @@ public void testSingleValuedFieldOrderedByCountDesc() throws Exception { .addAggregation(dateHistogram("histo") .field("date") .dateHistogramInterval(DateHistogramInterval.MONTH) - .order(Histogram.Order.COUNT_DESC)) + .order(BucketOrder.count(false))) .execute().actionGet(); assertSearchResponse(response); @@ -428,7 +484,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAsc() throws Exception { .addAggregation(dateHistogram("histo") .field("date") .dateHistogramInterval(DateHistogramInterval.MONTH) - .order(Histogram.Order.aggregation("sum", true)) + .order(BucketOrder.aggregation("sum", true)) .subAggregation(max("sum").field("value"))) .execute().actionGet(); @@ -451,7 +507,7 @@ public void testSingleValuedFieldOrderedBySubAggregationDesc() throws Exception .addAggregation(dateHistogram("histo") .field("date") .dateHistogramInterval(DateHistogramInterval.MONTH) - .order(Histogram.Order.aggregation("sum", false)) + .order(BucketOrder.aggregation("sum", false)) .subAggregation(max("sum").field("value"))) .execute().actionGet(); @@ -474,7 +530,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationDesc() throws .addAggregation(dateHistogram("histo") .field("date") .dateHistogramInterval(DateHistogramInterval.MONTH) - .order(Histogram.Order.aggregation("stats", "sum", false)) + .order(BucketOrder.aggregation("stats", "sum", false)) .subAggregation(stats("stats").field("value"))) .execute().actionGet(); @@ -492,6 +548,60 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationDesc() throws } } + public void testSingleValuedFieldOrderedByTieBreaker() throws Exception { + SearchResponse response = client().prepareSearch("idx") + .addAggregation(dateHistogram("histo") + .field("date") + .dateHistogramInterval(DateHistogramInterval.MONTH) + .order(BucketOrder.aggregation("max_constant", randomBoolean())) + .subAggregation(max("max_constant").field("constant"))) + .execute().actionGet(); + + assertSearchResponse(response); + + Histogram histo = response.getAggregations().get("histo"); + assertThat(histo, notNullValue()); + assertThat(histo.getName(), equalTo("histo")); + assertThat(histo.getBuckets().size(), equalTo(3)); + + int i = 1; + for (Histogram.Bucket bucket : histo.getBuckets()) { + assertThat(bucket.getKey(), equalTo(date(i, 1))); + i++; + } + } + + public void testSingleValuedFieldOrderedByIllegalAgg() throws Exception { + boolean asc = true; + try { + client() + .prepareSearch("idx") + .addAggregation( + dateHistogram("histo").field("date") + .dateHistogramInterval(DateHistogramInterval.MONTH) + .order(BucketOrder.aggregation("inner_histo>avg", asc)) + .subAggregation(dateHistogram("inner_histo") + .dateHistogramInterval(DateHistogramInterval.MONTH) + .field("dates") + .subAggregation(avg("avg").field("value")))) + .execute().actionGet(); + fail("Expected an exception"); + } catch (SearchPhaseExecutionException e) { + ElasticsearchException[] rootCauses = e.guessRootCauses(); + if (rootCauses.length == 1) { + ElasticsearchException rootCause = rootCauses[0]; + if (rootCause instanceof AggregationExecutionException) { + AggregationExecutionException aggException = (AggregationExecutionException) rootCause; + assertThat(aggException.getMessage(), Matchers.startsWith("Invalid aggregation order path")); + } else { + throw e; + } + } else { + throw e; + } + } + } + public void testSingleValuedFieldWithValueScript() throws Exception { Map params = new HashMap<>(); params.put("fieldname", "date"); @@ -583,12 +693,12 @@ public void testMultiValuedField() throws Exception { assertThat(bucket.getDocCount(), equalTo(3L)); } - public void testMultiValuedFieldOrderedByKeyDesc() throws Exception { + public void testMultiValuedFieldOrderedByCountDesc() throws Exception { SearchResponse response = client().prepareSearch("idx") .addAggregation(dateHistogram("histo") .field("dates") .dateHistogramInterval(DateHistogramInterval.MONTH) - .order(Histogram.Order.COUNT_DESC)) + .order(BucketOrder.count(false))) .execute().actionGet(); assertSearchResponse(response); @@ -598,23 +708,26 @@ public void testMultiValuedFieldOrderedByKeyDesc() throws Exception { assertThat(histo.getName(), equalTo("histo")); assertThat(histo.getBuckets().size(), equalTo(4)); - // TODO: use diamond once JI-9019884 is fixed List buckets = new ArrayList<>(histo.getBuckets()); Histogram.Bucket bucket = buckets.get(0); assertThat(bucket, notNullValue()); + assertThat(bucket.getKey(), equalTo(date(3, 1))); assertThat(bucket.getDocCount(), equalTo(5L)); bucket = buckets.get(1); assertThat(bucket, notNullValue()); + assertThat(bucket.getKey(), equalTo(date(2, 1))); assertThat(bucket.getDocCount(), equalTo(3L)); bucket = buckets.get(2); assertThat(bucket, notNullValue()); + assertThat(bucket.getKey(), equalTo(date(4, 1))); assertThat(bucket.getDocCount(), equalTo(3L)); bucket = buckets.get(3); assertThat(bucket, notNullValue()); + assertThat(bucket.getKey(), equalTo(date(1, 1))); assertThat(bucket.getDocCount(), equalTo(1L)); } @@ -1236,4 +1349,75 @@ public void testDontCacheScripts() throws Exception { assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache() .getMissCount(), equalTo(1L)); } + + public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndKeyDesc() throws Exception { + int[] expectedDays = new int[] { 1, 2, 4, 3, 7, 6, 5 }; + assertMultiSortResponse(expectedDays, BucketOrder.aggregation("avg_l", true), BucketOrder.key(false)); + } + + public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndKeyAsc() throws Exception { + int[] expectedDays = new int[] { 1, 2, 3, 4, 5, 6, 7 }; + assertMultiSortResponse(expectedDays, BucketOrder.aggregation("avg_l", true), BucketOrder.key(true)); + } + + public void testSingleValuedFieldOrderedBySingleValueSubAggregationDescAndKeyAsc() throws Exception { + int[] expectedDays = new int[] { 5, 6, 7, 3, 4, 2, 1 }; + assertMultiSortResponse(expectedDays, BucketOrder.aggregation("avg_l", false), BucketOrder.key(true)); + } + + public void testSingleValuedFieldOrderedByCountAscAndSingleValueSubAggregationAsc() throws Exception { + int[] expectedDays = new int[] { 6, 7, 3, 4, 5, 1, 2 }; + assertMultiSortResponse(expectedDays, BucketOrder.count(true), BucketOrder.aggregation("avg_l", true)); + } + + public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscSingleValueSubAggregationAsc() throws Exception { + int[] expectedDays = new int[] { 6, 7, 3, 5, 4, 1, 2 }; + assertMultiSortResponse(expectedDays, BucketOrder.aggregation("sum_d", true), BucketOrder.aggregation("avg_l", true)); + } + + public void testSingleValuedFieldOrderedByThreeCriteria() throws Exception { + int[] expectedDays = new int[] { 2, 1, 4, 5, 3, 6, 7 }; + assertMultiSortResponse(expectedDays, BucketOrder.count(false), BucketOrder.aggregation("sum_d", false), + BucketOrder.aggregation("avg_l", false)); + } + + public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAsCompound() throws Exception { + int[] expectedDays = new int[] { 1, 2, 3, 4, 5, 6, 7 }; + assertMultiSortResponse(expectedDays, BucketOrder.aggregation("avg_l", true)); + } + + private void assertMultiSortResponse(int[] expectedDays, BucketOrder... order) { + DateTime[] expectedKeys = Arrays.stream(expectedDays).mapToObj(d -> date(1, d)).toArray(DateTime[]::new); + SearchResponse response = client() + .prepareSearch("sort_idx") + .setTypes("type") + .addAggregation( + dateHistogram("histo").field("date").dateHistogramInterval(DateHistogramInterval.DAY).order(BucketOrder.compound(order)) + .subAggregation(avg("avg_l").field("l")).subAggregation(sum("sum_d").field("d"))).execute().actionGet(); + + assertSearchResponse(response); + + Histogram histogram = response.getAggregations().get("histo"); + assertThat(histogram, notNullValue()); + assertThat(histogram.getName(), equalTo("histo")); + assertThat(histogram.getBuckets().size(), equalTo(expectedKeys.length)); + + int i = 0; + for (Histogram.Bucket bucket : histogram.getBuckets()) { + assertThat(bucket, notNullValue()); + assertThat(key(bucket), equalTo(expectedKeys[i])); + assertThat(bucket.getDocCount(), equalTo(expectedMultiSortBuckets.get(expectedKeys[i]).get("_count"))); + Avg avg = bucket.getAggregations().get("avg_l"); + assertThat(avg, notNullValue()); + assertThat(avg.getValue(), equalTo(expectedMultiSortBuckets.get(expectedKeys[i]).get("avg_l"))); + Sum sum = bucket.getAggregations().get("sum_d"); + assertThat(sum, notNullValue()); + assertThat(sum.getValue(), equalTo(expectedMultiSortBuckets.get(expectedKeys[i]).get("sum_d"))); + i++; + } + } + + private DateTime key(Histogram.Bucket bucket) { + return (DateTime) bucket.getKey(); + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramTests.java index 76e58c715bfc8..e86b3a553e9c4 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramTests.java @@ -20,10 +20,13 @@ package org.elasticsearch.search.aggregations.bucket; import org.elasticsearch.search.aggregations.BaseAggregationTestCase; -import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Order; import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval; import org.elasticsearch.search.aggregations.bucket.histogram.ExtendedBoundsTests; +import org.elasticsearch.search.aggregations.BucketOrder; + +import java.util.ArrayList; +import java.util.List; public class DateHistogramTests extends BaseAggregationTestCase { @@ -80,29 +83,41 @@ protected DateHistogramAggregationBuilder createTestAggregatorBuilder() { factory.offset(randomIntBetween(0, 100000)); } if (randomBoolean()) { - int branch = randomInt(5); - switch (branch) { + List order = randomOrder(); + if(order.size() == 1 && randomBoolean()) { + factory.order(order.get(0)); + } else { + factory.order(order); + } + } + return factory; + } + + private List randomOrder() { + List orders = new ArrayList<>(); + switch (randomInt(4)) { case 0: - factory.order(Order.COUNT_ASC); + orders.add(BucketOrder.key(randomBoolean())); break; case 1: - factory.order(Order.COUNT_DESC); + orders.add(BucketOrder.count(randomBoolean())); break; case 2: - factory.order(Order.KEY_ASC); + orders.add(BucketOrder.aggregation(randomAlphaOfLengthBetween(3, 20), randomBoolean())); break; case 3: - factory.order(Order.KEY_DESC); + orders.add(BucketOrder.aggregation(randomAlphaOfLengthBetween(3, 20), randomAlphaOfLengthBetween(3, 20), randomBoolean())); break; case 4: - factory.order(Order.aggregation("foo", true)); - break; - case 5: - factory.order(Order.aggregation("foo", false)); + int numOrders = randomIntBetween(1, 3); + for (int i = 0; i < numOrders; i++) { + orders.addAll(randomOrder()); + } break; - } + default: + fail(); } - return factory; + return orders; } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java index c8803b7e790e1..6710bcdb23168 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java @@ -29,6 +29,7 @@ import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.max.Max; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import java.util.Collection; @@ -103,7 +104,7 @@ public void testIssue10719() throws Exception { SearchResponse response = client().prepareSearch("test").setTypes("book").setSearchType(SearchType.QUERY_THEN_FETCH) .addAggregation(terms("genres") .field("genre") - .order(Terms.Order.aggregation("sample>max_price.value", asc)) + .order(BucketOrder.aggregation("sample>max_price.value", asc)) .subAggregation(sampler("sample").shardSize(100) .subAggregation(max("max_price").field("price"))) ).execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java index ca106721fcc99..2363c21c7d112 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java @@ -41,6 +41,7 @@ import org.elasticsearch.search.aggregations.metrics.stats.Stats; import org.elasticsearch.search.aggregations.metrics.stats.extended.ExtendedStats; import org.elasticsearch.search.aggregations.metrics.sum.Sum; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import org.hamcrest.Matchers; @@ -134,6 +135,7 @@ public void setupSuiteScopeCluster() throws Exception { .startObject() .field(SINGLE_VALUED_FIELD_NAME, (double) i) .field("num_tag", i < NUM_DOCS/2 + 1 ? 1 : 0) // used to test order by single-bucket sub agg + .field("constant", 1) .startArray(MULTI_VALUED_FIELD_NAME).value((double) i).value(i + 1d).endArray() .endObject())); @@ -315,7 +317,7 @@ public void testSingleValueFieldWithMaxSize() throws Exception { .field(SINGLE_VALUED_FIELD_NAME) .size(20) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.term(true))) // we need to sort by terms cause we're checking the first 20 values + .order(BucketOrder.key(true))) // we need to sort by terms cause we're checking the first 20 values .execute().actionGet(); assertSearchResponse(response); @@ -363,15 +365,15 @@ private void testIncludeExcludeResults(double[] includes, double[] excludes, dou assertThat(bucket.getDocCount(), equalTo(1L)); } } - + public void testSingleValueFieldWithPartitionedFiltering() throws Exception { runTestFieldWithPartitionedFiltering(SINGLE_VALUED_FIELD_NAME); } - + public void testMultiValueFieldWithPartitionedFiltering() throws Exception { runTestFieldWithPartitionedFiltering(MULTI_VALUED_FIELD_NAME); } - + private void runTestFieldWithPartitionedFiltering(String field) throws Exception { // Find total number of unique terms SearchResponse allResponse = client().prepareSearch("idx") @@ -399,14 +401,14 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception } } assertEquals(expectedCardinality, foundTerms.size()); - } + } public void testSingleValueFieldOrderedByTermAsc() throws Exception { SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.term(true))) + .order(BucketOrder.key(true))) .execute().actionGet(); assertSearchResponse(response); @@ -432,7 +434,7 @@ public void testSingleValueFieldOrderedByTermDesc() throws Exception { .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.term(false))) + .order(BucketOrder.key(false))) .execute().actionGet(); assertSearchResponse(response); @@ -453,6 +455,33 @@ public void testSingleValueFieldOrderedByTermDesc() throws Exception { } } + public void testSingleValueFieldOrderedByTieBreaker() throws Exception { + SearchResponse response = client().prepareSearch("idx").setTypes("type") + .addAggregation(terms("terms") + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("max_constant", randomBoolean())) + .subAggregation(max("max_constant").field("constant"))) + .execute().actionGet(); + + assertSearchResponse(response); + + + Terms terms = response.getAggregations().get("terms"); + assertThat(terms, notNullValue()); + assertThat(terms.getName(), equalTo("terms")); + assertThat(terms.getBuckets().size(), equalTo(5)); + + int i = 0; + for (Terms.Bucket bucket : terms.getBuckets()) { + assertThat(bucket, notNullValue()); + assertThat(key(bucket), equalTo("" + (double)i)); + assertThat(bucket.getKeyAsNumber().intValue(), equalTo(i)); + assertThat(bucket.getDocCount(), equalTo(1L)); + i++; + } + } + public void testSingleValuedFieldWithSubAggregation() throws Exception { SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") @@ -759,7 +788,7 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAsc() throws .prepareSearch("idx") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("avg_i", asc)).subAggregation(avg("avg_i").field(SINGLE_VALUED_FIELD_NAME))) + .order(BucketOrder.aggregation("avg_i", asc)).subAggregation(avg("avg_i").field(SINGLE_VALUED_FIELD_NAME))) .execute().actionGet(); @@ -789,7 +818,7 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscWithSubTer terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("avg_i", asc)) + .order(BucketOrder.aggregation("avg_i", asc)) .subAggregation(avg("avg_i").field(SINGLE_VALUED_FIELD_NAME)) .subAggregation( terms("subTerms").field(MULTI_VALUED_FIELD_NAME).collectMode( @@ -831,7 +860,7 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws .prepareSearch("idx") .addAggregation( terms("num_tags").field("num_tag").collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("filter", asc)) + .order(BucketOrder.aggregation("filter", asc)) .subAggregation(filter("filter", QueryBuilders.matchAllQuery()))).execute().actionGet(); @@ -869,7 +898,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels( terms("tags") .field("num_tag") .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("filter1>filter2>max", asc)) + .order(BucketOrder.aggregation("filter1>filter2>max", asc)) .subAggregation( filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( filter("filter2", QueryBuilders.matchAllQuery()).subAggregation( @@ -923,7 +952,7 @@ public void testSingleValuedFieldOrderedByMissingSubAggregation() throws Excepti client().prepareSearch(index) .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("avg_i", true))).execute().actionGet(); + .order(BucketOrder.aggregation("avg_i", true))).execute().actionGet(); fail("Expected search to fail when trying to sort terms aggregation by sug-aggregation that doesn't exist"); @@ -941,7 +970,7 @@ public void testSingleValuedFieldOrderedByNonMetricsOrMultiBucketSubAggregation( terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("num_tags", true)) + .order(BucketOrder.aggregation("num_tags", true)) .subAggregation( terms("num_tags").field("num_tags").collectMode(randomFrom(SubAggCollectionMode.values())))) .execute().actionGet(); @@ -960,7 +989,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithUknownMet client().prepareSearch(index) .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME + "2").collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats.foo", true)) + .order(BucketOrder.aggregation("stats.foo", true)) .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME))).execute().actionGet(); fail("Expected search to fail when trying to sort terms aggregation by multi-valued sug-aggregation " + @@ -978,7 +1007,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithoutMetric client().prepareSearch(index) .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats", true)) + .order(BucketOrder.aggregation("stats", true)) .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME))).execute().actionGet(); fail("Expected search to fail when trying to sort terms aggregation by multi-valued sug-aggregation " + @@ -996,7 +1025,7 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationDesc() throws .prepareSearch("idx") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("avg_i", asc)).subAggregation(avg("avg_i").field(SINGLE_VALUED_FIELD_NAME))) + .order(BucketOrder.aggregation("avg_i", asc)).subAggregation(avg("avg_i").field(SINGLE_VALUED_FIELD_NAME))) .execute().actionGet(); @@ -1026,7 +1055,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws E .prepareSearch("idx") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats.avg", asc)) + .order(BucketOrder.aggregation("stats.avg", asc)) .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME))).execute().actionGet(); assertSearchResponse(response); @@ -1054,7 +1083,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws .prepareSearch("idx") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats.avg", asc)) + .order(BucketOrder.aggregation("stats.avg", asc)) .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME))).execute().actionGet(); assertSearchResponse(response); @@ -1082,7 +1111,7 @@ public void testSingleValuedFieldOrderedByMultiValueExtendedStatsAsc() throws Ex .prepareSearch("idx") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats.variance", asc)) + .order(BucketOrder.aggregation("stats.variance", asc)) .subAggregation(extendedStats("stats").field(SINGLE_VALUED_FIELD_NAME))).execute().actionGet(); assertSearchResponse(response); @@ -1139,48 +1168,48 @@ public void testScriptScore() { public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndTermsDesc() throws Exception { double[] expectedKeys = new double[] { 1, 2, 4, 3, 7, 6, 5 }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("avg_l", true), Terms.Order.term(false)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", true), BucketOrder.key(false)); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndTermsAsc() throws Exception { double[] expectedKeys = new double[] { 1, 2, 3, 4, 5, 6, 7 }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("avg_l", true), Terms.Order.term(true)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", true), BucketOrder.key(true)); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationDescAndTermsAsc() throws Exception { double[] expectedKeys = new double[] { 5, 6, 7, 3, 4, 2, 1 }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("avg_l", false), Terms.Order.term(true)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", false), BucketOrder.key(true)); } public void testSingleValuedFieldOrderedByCountAscAndSingleValueSubAggregationAsc() throws Exception { double[] expectedKeys = new double[] { 6, 7, 3, 4, 5, 1, 2 }; - assertMultiSortResponse(expectedKeys, Terms.Order.count(true), Terms.Order.aggregation("avg_l", true)); + assertMultiSortResponse(expectedKeys, BucketOrder.count(true), BucketOrder.aggregation("avg_l", true)); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscSingleValueSubAggregationAsc() throws Exception { double[] expectedKeys = new double[] { 6, 7, 3, 5, 4, 1, 2 }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("sum_d", true), Terms.Order.aggregation("avg_l", true)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("sum_d", true), BucketOrder.aggregation("avg_l", true)); } public void testSingleValuedFieldOrderedByThreeCriteria() throws Exception { double[] expectedKeys = new double[] { 2, 1, 4, 5, 3, 6, 7 }; - assertMultiSortResponse(expectedKeys, Terms.Order.count(false), - Terms.Order.aggregation("sum_d", false), - Terms.Order.aggregation("avg_l", false)); + assertMultiSortResponse(expectedKeys, BucketOrder.count(false), + BucketOrder.aggregation("sum_d", false), + BucketOrder.aggregation("avg_l", false)); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAsCompound() throws Exception { double[] expectedKeys = new double[] { 1, 2, 3, 4, 5, 6, 7 }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("avg_l", true)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", true)); } - private void assertMultiSortResponse(double[] expectedKeys, Terms.Order... order) { + private void assertMultiSortResponse(double[] expectedKeys, BucketOrder... order) { SearchResponse response = client() .prepareSearch("sort_idx") .setTypes("multi_sort_type") .addAggregation( terms("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.compound(order)).subAggregation(avg("avg_l").field("l")) + .order(BucketOrder.compound(order)).subAggregation(avg("avg_l").field("l")) .subAggregation(sum("sum_d").field("d"))).execute().actionGet(); assertSearchResponse(response); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java index 683e7924419a3..d7bd069f2ba3d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java @@ -19,7 +19,9 @@ package org.elasticsearch.search.aggregations.bucket; import com.carrotsearch.hppc.LongHashSet; +import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.query.QueryBuilders; @@ -27,16 +29,20 @@ import org.elasticsearch.script.MockScriptPlugin; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; +import org.elasticsearch.search.aggregations.AggregationExecutionException; import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.bucket.filter.Filter; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Bucket; +import org.elasticsearch.search.aggregations.metrics.avg.Avg; import org.elasticsearch.search.aggregations.metrics.max.Max; import org.elasticsearch.search.aggregations.metrics.stats.Stats; import org.elasticsearch.search.aggregations.metrics.sum.Sum; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import org.hamcrest.Matchers; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -48,6 +54,7 @@ import static java.util.Collections.emptyMap; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; +import static org.elasticsearch.search.aggregations.AggregationBuilders.avg; import static org.elasticsearch.search.aggregations.AggregationBuilders.filter; import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; import static org.elasticsearch.search.aggregations.AggregationBuilders.max; @@ -72,6 +79,7 @@ public class HistogramIT extends ESIntegTestCase { static int interval; static int numValueBuckets, numValuesBuckets; static long[] valueCounts, valuesCounts; + static Map> expectedMultiSortBuckets; @Override protected Collection> nodePlugins() { @@ -130,16 +138,18 @@ public void setupSuiteScopeCluster() throws Exception { } List builders = new ArrayList<>(); - for (int i = 0; i < numDocs; i++) { builders.add(client().prepareIndex("idx", "type").setSource(jsonBuilder() .startObject() .field(SINGLE_VALUED_FIELD_NAME, i + 1) .startArray(MULTI_VALUED_FIELD_NAME).value(i + 1).value(i + 2).endArray() .field("tag", "tag" + i) + .field("constant", 1) .endObject())); } + getMultiSortDocs(builders); + assertAcked(prepareCreate("empty_bucket_idx").addMapping("type", SINGLE_VALUED_FIELD_NAME, "type=integer")); for (int i = 0; i < 2; i++) { builders.add(client().prepareIndex("empty_bucket_idx", "type", "" + i).setSource(jsonBuilder() @@ -151,6 +161,51 @@ public void setupSuiteScopeCluster() throws Exception { ensureSearchable(); } + private void addExpectedBucket(long key, long docCount, double avg, double sum) { + Map bucketProps = new HashMap<>(); + bucketProps.put("key", key); + bucketProps.put("_count", docCount); + bucketProps.put("avg_l", avg); + bucketProps.put("sum_d", sum); + expectedMultiSortBuckets.put(key, bucketProps); + } + + private void getMultiSortDocs(List builders) throws IOException { + expectedMultiSortBuckets = new HashMap<>(); + addExpectedBucket(1, 3, 1, 6); + addExpectedBucket(2, 3, 2, 6); + addExpectedBucket(3, 2, 3, 3); + addExpectedBucket(4, 2, 3, 4); + addExpectedBucket(5, 2, 5, 3); + addExpectedBucket(6, 1, 5, 1); + addExpectedBucket(7, 1, 5, 1); + + assertAcked(client().admin().indices().prepareCreate("sort_idx") + .addMapping("type", SINGLE_VALUED_FIELD_NAME, "type=double").get()); + for (int i = 1; i <= 3; i++) { + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, 1).field("l", 1).field("d", i).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, 2).field("l", 2).field("d", i).endObject())); + } + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, 3).field("l", 3).field("d", 1).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, 3.8).field("l", 3).field("d", 2).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, 4).field("l", 3).field("d", 1).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, 4.4).field("l", 3).field("d", 3).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, 5).field("l", 5).field("d", 1).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, 5.1).field("l", 5).field("d", 2).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, 6).field("l", 5).field("d", 1).endObject())); + builders.add(client().prepareIndex("sort_idx", "type").setSource( + jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, 7).field("l", 5).field("d", 1).endObject())); + } + public void testSingleValuedField() throws Exception { SearchResponse response = client().prepareSearch("idx") .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval)) @@ -241,7 +296,7 @@ public void testSingleValuedFieldWithRandomOffset() throws Exception { public void testSingleValuedFieldOrderedByKeyAsc() throws Exception { SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(Histogram.Order.KEY_ASC)) + .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.key(true))) .execute().actionGet(); assertSearchResponse(response); @@ -252,7 +307,6 @@ public void testSingleValuedFieldOrderedByKeyAsc() throws Exception { assertThat(histo.getName(), equalTo("histo")); assertThat(histo.getBuckets().size(), equalTo(numValueBuckets)); - // TODO: use diamond once JI-9019884 is fixed List buckets = new ArrayList<>(histo.getBuckets()); for (int i = 0; i < numValueBuckets; ++i) { Histogram.Bucket bucket = buckets.get(i); @@ -264,7 +318,7 @@ public void testSingleValuedFieldOrderedByKeyAsc() throws Exception { public void testsingleValuedFieldOrderedByKeyDesc() throws Exception { SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(Histogram.Order.KEY_DESC)) + .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.key(false))) .execute().actionGet(); assertSearchResponse(response); @@ -275,7 +329,6 @@ public void testsingleValuedFieldOrderedByKeyDesc() throws Exception { assertThat(histo.getName(), equalTo("histo")); assertThat(histo.getBuckets().size(), equalTo(numValueBuckets)); - // TODO: use diamond once JI-9019884 is fixed List buckets = new ArrayList<>(histo.getBuckets()); for (int i = 0; i < numValueBuckets; ++i) { Histogram.Bucket bucket = buckets.get(numValueBuckets - i - 1); @@ -287,7 +340,7 @@ public void testsingleValuedFieldOrderedByKeyDesc() throws Exception { public void testSingleValuedFieldOrderedByCountAsc() throws Exception { SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(Histogram.Order.COUNT_ASC)) + .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.count(true))) .execute().actionGet(); assertSearchResponse(response); @@ -299,7 +352,6 @@ public void testSingleValuedFieldOrderedByCountAsc() throws Exception { assertThat(histo.getBuckets().size(), equalTo(numValueBuckets)); LongHashSet buckets = new LongHashSet(); - // TODO: use diamond once JI-9019884 is fixed List histoBuckets = new ArrayList<>(histo.getBuckets()); long previousCount = Long.MIN_VALUE; for (int i = 0; i < numValueBuckets; ++i) { @@ -316,7 +368,7 @@ public void testSingleValuedFieldOrderedByCountAsc() throws Exception { public void testSingleValuedFieldOrderedByCountDesc() throws Exception { SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(Histogram.Order.COUNT_DESC)) + .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.count(false))) .execute().actionGet(); assertSearchResponse(response); @@ -328,7 +380,6 @@ public void testSingleValuedFieldOrderedByCountDesc() throws Exception { assertThat(histo.getBuckets().size(), equalTo(numValueBuckets)); LongHashSet buckets = new LongHashSet(); - // TODO: use diamond once JI-9019884 is fixed List histoBuckets = new ArrayList<>(histo.getBuckets()); long previousCount = Long.MAX_VALUE; for (int i = 0; i < numValueBuckets; ++i) { @@ -361,7 +412,6 @@ public void testSingleValuedFieldWithSubAggregation() throws Exception { Object[] propertiesDocCounts = (Object[]) ((InternalAggregation)histo).getProperty("_count"); Object[] propertiesCounts = (Object[]) ((InternalAggregation)histo).getProperty("sum.value"); - // TODO: use diamond once JI-9019884 is fixed List buckets = new ArrayList<>(histo.getBuckets()); for (int i = 0; i < numValueBuckets; ++i) { Histogram.Bucket bucket = buckets.get(i); @@ -390,7 +440,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAsc() throws Exception { histogram("histo") .field(SINGLE_VALUED_FIELD_NAME) .interval(interval) - .order(Histogram.Order.aggregation("sum", true)) + .order(BucketOrder.aggregation("sum", true)) .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME))) .execute().actionGet(); @@ -404,7 +454,6 @@ public void testSingleValuedFieldOrderedBySubAggregationAsc() throws Exception { LongHashSet visited = new LongHashSet(); double previousSum = Double.NEGATIVE_INFINITY; - // TODO: use diamond once JI-9019884 is fixed List buckets = new ArrayList<>(histo.getBuckets()); for (int i = 0; i < numValueBuckets; ++i) { Histogram.Bucket bucket = buckets.get(i); @@ -434,7 +483,7 @@ public void testSingleValuedFieldOrderedBySubAggregationDesc() throws Exception histogram("histo") .field(SINGLE_VALUED_FIELD_NAME) .interval(interval) - .order(Histogram.Order.aggregation("sum", false)) + .order(BucketOrder.aggregation("sum", false)) .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME))) .execute().actionGet(); @@ -448,7 +497,6 @@ public void testSingleValuedFieldOrderedBySubAggregationDesc() throws Exception LongHashSet visited = new LongHashSet(); double previousSum = Double.POSITIVE_INFINITY; - // TODO: use diamond once JI-9019884 is fixed List buckets = new ArrayList<>(histo.getBuckets()); for (int i = 0; i < numValueBuckets; ++i) { Histogram.Bucket bucket = buckets.get(i); @@ -478,7 +526,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationDesc() throws histogram("histo") .field(SINGLE_VALUED_FIELD_NAME) .interval(interval) - .order(Histogram.Order.aggregation("stats.sum", false)) + .order(BucketOrder.aggregation("stats.sum", false)) .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME))) .execute().actionGet(); @@ -492,7 +540,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationDesc() throws LongHashSet visited = new LongHashSet(); double previousSum = Double.POSITIVE_INFINITY; - // TODO: use diamond once JI-9019884 is fixed + List buckets = new ArrayList<>(histo.getBuckets()); for (int i = 0; i < numValueBuckets; ++i) { Histogram.Bucket bucket = buckets.get(i); @@ -523,7 +571,7 @@ public void testSingleValuedFieldOrderedBySubAggregationDescDeepOrderPath() thro histogram("histo") .field(SINGLE_VALUED_FIELD_NAME) .interval(interval) - .order(Histogram.Order.aggregation("filter>max", asc)) + .order(BucketOrder.aggregation("filter>max", asc)) .subAggregation(filter("filter", matchAllQuery()) .subAggregation(max("max").field(SINGLE_VALUED_FIELD_NAME)))) .execute().actionGet(); @@ -538,7 +586,6 @@ public void testSingleValuedFieldOrderedBySubAggregationDescDeepOrderPath() thro LongHashSet visited = new LongHashSet(); double prevMax = asc ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; - // TODO: use diamond once JI-9019884 is fixed List buckets = new ArrayList<>(histo.getBuckets()); for (int i = 0; i < numValueBuckets; ++i) { Histogram.Bucket bucket = buckets.get(i); @@ -558,6 +605,62 @@ public void testSingleValuedFieldOrderedBySubAggregationDescDeepOrderPath() thro } } + public void testSingleValuedFieldOrderedByTieBreaker() throws Exception { + SearchResponse response = client().prepareSearch("idx") + .addAggregation(histogram("histo") + .field(SINGLE_VALUED_FIELD_NAME) + .interval(interval) + .order(BucketOrder.aggregation("max_constant", randomBoolean())) + .subAggregation(max("max_constant").field("constant"))) + .execute().actionGet(); + + assertSearchResponse(response); + + Histogram histo = response.getAggregations().get("histo"); + assertThat(histo, notNullValue()); + assertThat(histo.getName(), equalTo("histo")); + assertThat(histo.getBuckets().size(), equalTo(numValueBuckets)); + + List buckets = new ArrayList<>(histo.getBuckets()); + for (int i = 0; i < numValueBuckets; ++i) { + Histogram.Bucket bucket = buckets.get(i); + assertThat(bucket, notNullValue()); + assertThat(((Number) bucket.getKey()).longValue(), equalTo((long) i * interval)); + assertThat(bucket.getDocCount(), equalTo(valueCounts[i])); + } + } + + public void testSingleValuedFieldOrderedByIllegalAgg() throws Exception { + boolean asc = true; + try { + client() + .prepareSearch("idx") + .addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME) + .interval(interval) + .order(BucketOrder.aggregation("inner_histo>avg", asc)) + .subAggregation(histogram("inner_histo") + .interval(interval) + .field(MULTI_VALUED_FIELD_NAME) + .subAggregation(avg("avg").field("value")))) + .execute().actionGet(); + fail("Expected an exception"); + } catch (SearchPhaseExecutionException e) { + ElasticsearchException[] rootCauses = e.guessRootCauses(); + if (rootCauses.length == 1) { + ElasticsearchException rootCause = rootCauses[0]; + if (rootCause instanceof AggregationExecutionException) { + AggregationExecutionException aggException = (AggregationExecutionException) rootCause; + assertThat(aggException.getMessage(), Matchers.startsWith("Invalid aggregation order path")); + } else { + throw e; + } + } else { + throw e; + } + } + } + public void testSingleValuedFieldWithValueScript() throws Exception { SearchResponse response = client().prepareSearch("idx") .addAggregation( @@ -614,7 +717,7 @@ public void testMultiValuedField() throws Exception { public void testMultiValuedFieldOrderedByKeyDesc() throws Exception { SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(MULTI_VALUED_FIELD_NAME).interval(interval).order(Histogram.Order.KEY_DESC)) + .addAggregation(histogram("histo").field(MULTI_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.key(false))) .execute().actionGet(); assertSearchResponse(response); @@ -625,7 +728,6 @@ public void testMultiValuedFieldOrderedByKeyDesc() throws Exception { assertThat(histo.getName(), equalTo("histo")); assertThat(histo.getBuckets().size(), equalTo(numValuesBuckets)); - // TODO: use diamond once JI-9019884 is fixed List buckets = new ArrayList<>(histo.getBuckets()); for (int i = 0; i < numValuesBuckets; ++i) { Histogram.Bucket bucket = buckets.get(numValuesBuckets - i - 1); @@ -1036,4 +1138,74 @@ public void testDontCacheScripts() throws Exception { assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache() .getMissCount(), equalTo(1L)); } + + public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndKeyDesc() throws Exception { + long[] expectedKeys = new long[] { 1, 2, 4, 3, 7, 6, 5 }; + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", true), BucketOrder.key(false)); + } + + public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndKeyAsc() throws Exception { + long[] expectedKeys = new long[] { 1, 2, 3, 4, 5, 6, 7 }; + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", true), BucketOrder.key(true)); + } + + public void testSingleValuedFieldOrderedBySingleValueSubAggregationDescAndKeyAsc() throws Exception { + long[] expectedKeys = new long[] { 5, 6, 7, 3, 4, 2, 1 }; + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", false), BucketOrder.key(true)); + } + + public void testSingleValuedFieldOrderedByCountAscAndSingleValueSubAggregationAsc() throws Exception { + long[] expectedKeys = new long[] { 6, 7, 3, 4, 5, 1, 2 }; + assertMultiSortResponse(expectedKeys, BucketOrder.count(true), BucketOrder.aggregation("avg_l", true)); + } + + public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscSingleValueSubAggregationAsc() throws Exception { + long[] expectedKeys = new long[] { 6, 7, 3, 5, 4, 1, 2 }; + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("sum_d", true), BucketOrder.aggregation("avg_l", true)); + } + + public void testSingleValuedFieldOrderedByThreeCriteria() throws Exception { + long[] expectedKeys = new long[] { 2, 1, 4, 5, 3, 6, 7 }; + assertMultiSortResponse(expectedKeys, BucketOrder.count(false), BucketOrder.aggregation("sum_d", false), + BucketOrder.aggregation("avg_l", false)); + } + + public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAsCompound() throws Exception { + long[] expectedKeys = new long[] { 1, 2, 3, 4, 5, 6, 7 }; + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", true)); + } + + private void assertMultiSortResponse(long[] expectedKeys, BucketOrder... order) { + SearchResponse response = client() + .prepareSearch("sort_idx") + .setTypes("type") + .addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(1).order(BucketOrder.compound(order)) + .subAggregation(avg("avg_l").field("l")).subAggregation(sum("sum_d").field("d"))).execute().actionGet(); + + assertSearchResponse(response); + + Histogram histogram = response.getAggregations().get("histo"); + assertThat(histogram, notNullValue()); + assertThat(histogram.getName(), equalTo("histo")); + assertThat(histogram.getBuckets().size(), equalTo(expectedKeys.length)); + + int i = 0; + for (Histogram.Bucket bucket : histogram.getBuckets()) { + assertThat(bucket, notNullValue()); + assertThat(key(bucket), equalTo(expectedKeys[i])); + assertThat(bucket.getDocCount(), equalTo(expectedMultiSortBuckets.get(expectedKeys[i]).get("_count"))); + Avg avg = bucket.getAggregations().get("avg_l"); + assertThat(avg, notNullValue()); + assertThat(avg.getValue(), equalTo(expectedMultiSortBuckets.get(expectedKeys[i]).get("avg_l"))); + Sum sum = bucket.getAggregations().get("sum_d"); + assertThat(sum, notNullValue()); + assertThat(sum.getValue(), equalTo(expectedMultiSortBuckets.get(expectedKeys[i]).get("sum_d"))); + i++; + } + } + + private long key(Histogram.Bucket bucket) { + return ((Number) bucket.getKey()).longValue(); + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/HistogramTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/HistogramTests.java index ea61a8168adef..ee22b2291773d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/HistogramTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/HistogramTests.java @@ -21,7 +21,10 @@ import org.elasticsearch.search.aggregations.BaseAggregationTestCase; import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Order; +import org.elasticsearch.search.aggregations.BucketOrder; + +import java.util.ArrayList; +import java.util.List; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.startsWith; @@ -54,26 +57,11 @@ protected HistogramAggregationBuilder createTestAggregatorBuilder() { factory.offset(randomIntBetween(0, 100000)); } if (randomBoolean()) { - int branch = randomInt(5); - switch (branch) { - case 0: - factory.order(Order.COUNT_ASC); - break; - case 1: - factory.order(Order.COUNT_DESC); - break; - case 2: - factory.order(Order.KEY_ASC); - break; - case 3: - factory.order(Order.KEY_DESC); - break; - case 4: - factory.order(Order.aggregation("foo", true)); - break; - case 5: - factory.order(Order.aggregation("foo", false)); - break; + List order = randomOrder(); + if(order.size() == 1 && randomBoolean()) { + factory.order(order.get(0)); + } else { + factory.order(order); } } return factory; @@ -102,4 +90,31 @@ public void testInvalidBounds() { assertThat(ex.getMessage(), equalTo("maxBound [0.4] must be greater than minBound [0.5]")); } + private List randomOrder() { + List orders = new ArrayList<>(); + switch (randomInt(4)) { + case 0: + orders.add(BucketOrder.key(randomBoolean())); + break; + case 1: + orders.add(BucketOrder.count(randomBoolean())); + break; + case 2: + orders.add(BucketOrder.aggregation(randomAlphaOfLengthBetween(3, 20), randomBoolean())); + break; + case 3: + orders.add(BucketOrder.aggregation(randomAlphaOfLengthBetween(3, 20), randomAlphaOfLengthBetween(3, 20), randomBoolean())); + break; + case 4: + int numOrders = randomIntBetween(1, 3); + for (int i = 0; i < numOrders; i++) { + orders.addAll(randomOrder()); + } + break; + default: + fail(); + } + return orders; + } + } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java index a54dc3e2f5edf..565cdaaa87e0a 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java @@ -40,6 +40,7 @@ import org.elasticsearch.search.aggregations.metrics.stats.Stats; import org.elasticsearch.search.aggregations.metrics.stats.extended.ExtendedStats; import org.elasticsearch.search.aggregations.metrics.sum.Sum; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import org.hamcrest.Matchers; @@ -121,6 +122,7 @@ public void setupSuiteScopeCluster() throws Exception { .field(SINGLE_VALUED_FIELD_NAME, i) .startArray(MULTI_VALUED_FIELD_NAME).value(i).value(i + 1).endArray() .field("num_tag", i < lowCardBuilders.length / 2 + 1 ? 1 : 0) // used to test order by single-bucket sub agg + .field("constant", 1) .endObject()); } indexRandom(true, lowCardBuilders); @@ -392,7 +394,7 @@ public void testSingleValueFieldWithMaxSize() throws Exception { .field(SINGLE_VALUED_FIELD_NAME) .size(20) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.term(true))) // we need to sort by terms cause we're checking the first 20 values + .order(BucketOrder.key(true))) // we need to sort by terms cause we're checking the first 20 values .execute().actionGet(); assertSearchResponse(response); @@ -417,7 +419,7 @@ public void testSingleValueFieldOrderedByTermAsc() throws Exception { .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.term(true))) + .order(BucketOrder.key(true))) .execute().actionGet(); assertSearchResponse(response); @@ -441,7 +443,7 @@ public void testSingleValueFieldOrderedByTermDesc() throws Exception { .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.term(false))) + .order(BucketOrder.key(false))) .execute().actionGet(); assertSearchResponse(response); @@ -462,6 +464,31 @@ public void testSingleValueFieldOrderedByTermDesc() throws Exception { } } + public void testSingleValueFieldOrderedByTieBreaker() throws Exception { + SearchResponse response = client().prepareSearch("idx").setTypes("type") + .addAggregation(terms("terms") + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("max_constant", randomBoolean())) + .subAggregation(max("max_constant").field("constant"))) + .execute().actionGet(); + assertSearchResponse(response); + + Terms terms = response.getAggregations().get("terms"); + assertThat(terms, notNullValue()); + assertThat(terms.getName(), equalTo("terms")); + assertThat(terms.getBuckets().size(), equalTo(5)); + + int i = 0; + for (Terms.Bucket bucket : terms.getBuckets()) { + assertThat(bucket, notNullValue()); + assertThat(key(bucket), equalTo("" + i)); + assertThat(bucket.getKeyAsNumber().intValue(), equalTo(i)); + assertThat(bucket.getDocCount(), equalTo(1L)); + i++; + } + } + public void testSingleValuedFieldWithSubAggregation() throws Exception { SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("terms") @@ -769,7 +796,7 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAsc() throws .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("avg_i", asc)) + .order(BucketOrder.aggregation("avg_i", asc)) .subAggregation(avg("avg_i").field(SINGLE_VALUED_FIELD_NAME)) ).execute().actionGet(); @@ -798,7 +825,7 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscWithTermsS .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("avg_i", asc)) + .order(BucketOrder.aggregation("avg_i", asc)) .subAggregation( avg("avg_i").field(SINGLE_VALUED_FIELD_NAME)) .subAggregation( @@ -842,7 +869,7 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws .addAggregation(terms("num_tags") .field("num_tag") .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("filter", asc)) + .order(BucketOrder.aggregation("filter", asc)) .subAggregation(filter("filter", QueryBuilders.matchAllQuery())) ).get(); @@ -879,7 +906,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels( .addAggregation(terms("tags") .field("num_tag") .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("filter1>filter2>max", asc)) + .order(BucketOrder.aggregation("filter1>filter2>max", asc)) .subAggregation(filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( filter("filter2", QueryBuilders.matchAllQuery()) .subAggregation(max("max").field(SINGLE_VALUED_FIELD_NAME)))) @@ -934,7 +961,7 @@ public void testSingleValuedFieldOrderedByMissingSubAggregation() throws Excepti .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("avg_i", true)) + .order(BucketOrder.aggregation("avg_i", true)) ).execute().actionGet(); fail("Expected search to fail when trying to sort terms aggregation by sug-aggregation that doesn't exist"); @@ -952,7 +979,7 @@ public void testSingleValuedFieldOrderedByNonMetricsOrMultiBucketSubAggregation( .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("num_tags", true)) + .order(BucketOrder.aggregation("num_tags", true)) .subAggregation(terms("num_tags").field("num_tags") .collectMode(randomFrom(SubAggCollectionMode.values()))) ).execute().actionGet(); @@ -972,7 +999,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithUknownMet .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats.foo", true)) + .order(BucketOrder.aggregation("stats.foo", true)) .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) ).execute().actionGet(); @@ -992,7 +1019,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithoutMetric .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats", true)) + .order(BucketOrder.aggregation("stats", true)) .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) ).execute().actionGet(); @@ -1011,7 +1038,7 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationDesc() throws .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("avg_i", asc)) + .order(BucketOrder.aggregation("avg_i", asc)) .subAggregation(avg("avg_i").field(SINGLE_VALUED_FIELD_NAME)) ).execute().actionGet(); @@ -1043,7 +1070,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws E .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats.avg", asc)) + .order(BucketOrder.aggregation("stats.avg", asc)) .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) ).execute().actionGet(); @@ -1073,7 +1100,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats.avg", asc)) + .order(BucketOrder.aggregation("stats.avg", asc)) .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) ).execute().actionGet(); @@ -1103,7 +1130,7 @@ public void testSingleValuedFieldOrderedByMultiValueExtendedStatsAsc() throws Ex .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats.variance", asc)) + .order(BucketOrder.aggregation("stats.variance", asc)) .subAggregation(extendedStats("stats").field(SINGLE_VALUED_FIELD_NAME)) ).execute().actionGet(); @@ -1129,47 +1156,47 @@ public void testSingleValuedFieldOrderedByMultiValueExtendedStatsAsc() throws Ex public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndTermsDesc() throws Exception { long[] expectedKeys = new long[] { 1, 2, 4, 3, 7, 6, 5 }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("avg_l", true), Terms.Order.term(false)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", true), BucketOrder.key(false)); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndTermsAsc() throws Exception { long[] expectedKeys = new long[] { 1, 2, 3, 4, 5, 6, 7 }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("avg_l", true), Terms.Order.term(true)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", true), BucketOrder.key(true)); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationDescAndTermsAsc() throws Exception { long[] expectedKeys = new long[] { 5, 6, 7, 3, 4, 2, 1 }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("avg_l", false), Terms.Order.term(true)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", false), BucketOrder.key(true)); } public void testSingleValuedFieldOrderedByCountAscAndSingleValueSubAggregationAsc() throws Exception { long[] expectedKeys = new long[] { 6, 7, 3, 4, 5, 1, 2 }; - assertMultiSortResponse(expectedKeys, Terms.Order.count(true), Terms.Order.aggregation("avg_l", true)); + assertMultiSortResponse(expectedKeys, BucketOrder.count(true), BucketOrder.aggregation("avg_l", true)); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscSingleValueSubAggregationAsc() throws Exception { long[] expectedKeys = new long[] { 6, 7, 3, 5, 4, 1, 2 }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("sum_d", true), Terms.Order.aggregation("avg_l", true)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("sum_d", true), BucketOrder.aggregation("avg_l", true)); } public void testSingleValuedFieldOrderedByThreeCriteria() throws Exception { long[] expectedKeys = new long[] { 2, 1, 4, 5, 3, 6, 7 }; - assertMultiSortResponse(expectedKeys, Terms.Order.count(false), - Terms.Order.aggregation("sum_d", false), - Terms.Order.aggregation("avg_l", false)); + assertMultiSortResponse(expectedKeys, BucketOrder.count(false), + BucketOrder.aggregation("sum_d", false), + BucketOrder.aggregation("avg_l", false)); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAsCompound() throws Exception { long[] expectedKeys = new long[] { 1, 2, 3, 4, 5, 6, 7 }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("avg_l", true)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", true)); } - private void assertMultiSortResponse(long[] expectedKeys, Terms.Order... order) { + private void assertMultiSortResponse(long[] expectedKeys, BucketOrder... order) { SearchResponse response = client().prepareSearch("sort_idx").setTypes("multi_sort_type") .addAggregation(terms("terms") .field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.compound(order)) + .order(BucketOrder.compound(order)) .subAggregation(avg("avg_l").field("l")) .subAggregation(sum("sum_d").field("d")) ).execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java index e1e8f1ba660cd..038227239cc0f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java @@ -37,6 +37,7 @@ import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -190,122 +191,122 @@ private void assertSubset(Histogram histo1, Histogram histo2, long minDocCount) } public void testStringTermAsc() throws Exception { - testMinDocCountOnTerms("s", Script.NO, Terms.Order.term(true)); + testMinDocCountOnTerms("s", Script.NO, BucketOrder.key(true)); } public void testStringScriptTermAsc() throws Exception { - testMinDocCountOnTerms("s", Script.YES, Terms.Order.term(true)); + testMinDocCountOnTerms("s", Script.YES, BucketOrder.key(true)); } public void testStringTermDesc() throws Exception { - testMinDocCountOnTerms("s", Script.NO, Terms.Order.term(false)); + testMinDocCountOnTerms("s", Script.NO, BucketOrder.key(false)); } public void testStringScriptTermDesc() throws Exception { - testMinDocCountOnTerms("s", Script.YES, Terms.Order.term(false)); + testMinDocCountOnTerms("s", Script.YES, BucketOrder.key(false)); } public void testStringCountAsc() throws Exception { - testMinDocCountOnTerms("s", Script.NO, Terms.Order.count(true)); + testMinDocCountOnTerms("s", Script.NO, BucketOrder.count(true)); } public void testStringScriptCountAsc() throws Exception { - testMinDocCountOnTerms("s", Script.YES, Terms.Order.count(true)); + testMinDocCountOnTerms("s", Script.YES, BucketOrder.count(true)); } public void testStringCountDesc() throws Exception { - testMinDocCountOnTerms("s", Script.NO, Terms.Order.count(false)); + testMinDocCountOnTerms("s", Script.NO, BucketOrder.count(false)); } public void testStringScriptCountDesc() throws Exception { - testMinDocCountOnTerms("s", Script.YES, Terms.Order.count(false)); + testMinDocCountOnTerms("s", Script.YES, BucketOrder.count(false)); } public void testStringCountAscWithInclude() throws Exception { - testMinDocCountOnTerms("s", Script.NO, Terms.Order.count(true), ".*a.*", true); + testMinDocCountOnTerms("s", Script.NO, BucketOrder.count(true), ".*a.*", true); } public void testStringScriptCountAscWithInclude() throws Exception { - testMinDocCountOnTerms("s", Script.YES, Terms.Order.count(true), ".*a.*", true); + testMinDocCountOnTerms("s", Script.YES, BucketOrder.count(true), ".*a.*", true); } public void testStringCountDescWithInclude() throws Exception { - testMinDocCountOnTerms("s", Script.NO, Terms.Order.count(false), ".*a.*", true); + testMinDocCountOnTerms("s", Script.NO, BucketOrder.count(false), ".*a.*", true); } public void testStringScriptCountDescWithInclude() throws Exception { - testMinDocCountOnTerms("s", Script.YES, Terms.Order.count(false), ".*a.*", true); + testMinDocCountOnTerms("s", Script.YES, BucketOrder.count(false), ".*a.*", true); } public void testLongTermAsc() throws Exception { - testMinDocCountOnTerms("l", Script.NO, Terms.Order.term(true)); + testMinDocCountOnTerms("l", Script.NO, BucketOrder.key(true)); } public void testLongScriptTermAsc() throws Exception { - testMinDocCountOnTerms("l", Script.YES, Terms.Order.term(true)); + testMinDocCountOnTerms("l", Script.YES, BucketOrder.key(true)); } public void testLongTermDesc() throws Exception { - testMinDocCountOnTerms("l", Script.NO, Terms.Order.term(false)); + testMinDocCountOnTerms("l", Script.NO, BucketOrder.key(false)); } public void testLongScriptTermDesc() throws Exception { - testMinDocCountOnTerms("l", Script.YES, Terms.Order.term(false)); + testMinDocCountOnTerms("l", Script.YES, BucketOrder.key(false)); } public void testLongCountAsc() throws Exception { - testMinDocCountOnTerms("l", Script.NO, Terms.Order.count(true)); + testMinDocCountOnTerms("l", Script.NO, BucketOrder.count(true)); } public void testLongScriptCountAsc() throws Exception { - testMinDocCountOnTerms("l", Script.YES, Terms.Order.count(true)); + testMinDocCountOnTerms("l", Script.YES, BucketOrder.count(true)); } public void testLongCountDesc() throws Exception { - testMinDocCountOnTerms("l", Script.NO, Terms.Order.count(false)); + testMinDocCountOnTerms("l", Script.NO, BucketOrder.count(false)); } public void testLongScriptCountDesc() throws Exception { - testMinDocCountOnTerms("l", Script.YES, Terms.Order.count(false)); + testMinDocCountOnTerms("l", Script.YES, BucketOrder.count(false)); } public void testDoubleTermAsc() throws Exception { - testMinDocCountOnTerms("d", Script.NO, Terms.Order.term(true)); + testMinDocCountOnTerms("d", Script.NO, BucketOrder.key(true)); } public void testDoubleScriptTermAsc() throws Exception { - testMinDocCountOnTerms("d", Script.YES, Terms.Order.term(true)); + testMinDocCountOnTerms("d", Script.YES, BucketOrder.key(true)); } public void testDoubleTermDesc() throws Exception { - testMinDocCountOnTerms("d", Script.NO, Terms.Order.term(false)); + testMinDocCountOnTerms("d", Script.NO, BucketOrder.key(false)); } public void testDoubleScriptTermDesc() throws Exception { - testMinDocCountOnTerms("d", Script.YES, Terms.Order.term(false)); + testMinDocCountOnTerms("d", Script.YES, BucketOrder.key(false)); } public void testDoubleCountAsc() throws Exception { - testMinDocCountOnTerms("d", Script.NO, Terms.Order.count(true)); + testMinDocCountOnTerms("d", Script.NO, BucketOrder.count(true)); } public void testDoubleScriptCountAsc() throws Exception { - testMinDocCountOnTerms("d", Script.YES, Terms.Order.count(true)); + testMinDocCountOnTerms("d", Script.YES, BucketOrder.count(true)); } public void testDoubleCountDesc() throws Exception { - testMinDocCountOnTerms("d", Script.NO, Terms.Order.count(false)); + testMinDocCountOnTerms("d", Script.NO, BucketOrder.count(false)); } public void testDoubleScriptCountDesc() throws Exception { - testMinDocCountOnTerms("d", Script.YES, Terms.Order.count(false)); + testMinDocCountOnTerms("d", Script.YES, BucketOrder.count(false)); } - private void testMinDocCountOnTerms(String field, Script script, Terms.Order order) throws Exception { + private void testMinDocCountOnTerms(String field, Script script, BucketOrder order) throws Exception { testMinDocCountOnTerms(field, script, order, null, true); } - private void testMinDocCountOnTerms(String field, Script script, Terms.Order order, String include, boolean retry) throws Exception { + private void testMinDocCountOnTerms(String field, Script script, BucketOrder order, String include, boolean retry) throws Exception { // all terms final SearchResponse allTermsResponse = client().prepareSearch("idx").setTypes("type") .setSize(0) @@ -342,38 +343,38 @@ private void testMinDocCountOnTerms(String field, Script script, Terms.Order ord } public void testHistogramCountAsc() throws Exception { - testMinDocCountOnHistogram(Histogram.Order.COUNT_ASC); + testMinDocCountOnHistogram(BucketOrder.count(true)); } public void testHistogramCountDesc() throws Exception { - testMinDocCountOnHistogram(Histogram.Order.COUNT_DESC); + testMinDocCountOnHistogram(BucketOrder.count(false)); } public void testHistogramKeyAsc() throws Exception { - testMinDocCountOnHistogram(Histogram.Order.KEY_ASC); + testMinDocCountOnHistogram(BucketOrder.key(true)); } public void testHistogramKeyDesc() throws Exception { - testMinDocCountOnHistogram(Histogram.Order.KEY_DESC); + testMinDocCountOnHistogram(BucketOrder.key(false)); } public void testDateHistogramCountAsc() throws Exception { - testMinDocCountOnDateHistogram(Histogram.Order.COUNT_ASC); + testMinDocCountOnDateHistogram(BucketOrder.count(true)); } public void testDateHistogramCountDesc() throws Exception { - testMinDocCountOnDateHistogram(Histogram.Order.COUNT_DESC); + testMinDocCountOnDateHistogram(BucketOrder.count(false)); } public void testDateHistogramKeyAsc() throws Exception { - testMinDocCountOnDateHistogram(Histogram.Order.KEY_ASC); + testMinDocCountOnDateHistogram(BucketOrder.key(true)); } public void testDateHistogramKeyDesc() throws Exception { - testMinDocCountOnDateHistogram(Histogram.Order.KEY_DESC); + testMinDocCountOnDateHistogram(BucketOrder.key(false)); } - private void testMinDocCountOnHistogram(Histogram.Order order) throws Exception { + private void testMinDocCountOnHistogram(BucketOrder order) throws Exception { final int interval = randomIntBetween(1, 3); final SearchResponse allResponse = client().prepareSearch("idx").setTypes("type") .setSize(0) @@ -393,7 +394,7 @@ private void testMinDocCountOnHistogram(Histogram.Order order) throws Exception } } - private void testMinDocCountOnDateHistogram(Histogram.Order order) throws Exception { + private void testMinDocCountOnDateHistogram(BucketOrder order) throws Exception { final SearchResponse allResponse = client().prepareSearch("idx").setTypes("type") .setSize(0) .setQuery(QUERY) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/NaNSortingIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/NaNSortingIT.java index f6db12a7f6d8c..5b8c3b878c19a 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/NaNSortingIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/NaNSortingIT.java @@ -30,6 +30,7 @@ import org.elasticsearch.search.aggregations.metrics.avg.AvgAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.stats.extended.ExtendedStats; import org.elasticsearch.search.aggregations.metrics.stats.extended.ExtendedStatsAggregationBuilder; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder; import org.elasticsearch.test.ESIntegTestCase; @@ -150,7 +151,7 @@ public void testTerms(String fieldName) { final boolean asc = randomBoolean(); SubAggregation agg = randomFrom(SubAggregation.values()); SearchResponse response = client().prepareSearch("idx") - .addAggregation(terms("terms").field(fieldName).collectMode(randomFrom(SubAggCollectionMode.values())).subAggregation(agg.builder()).order(Terms.Order.aggregation(agg.sortKey(), asc))) + .addAggregation(terms("terms").field(fieldName).collectMode(randomFrom(SubAggCollectionMode.values())).subAggregation(agg.builder()).order(BucketOrder.aggregation(agg.sortKey(), asc))) .execute().actionGet(); assertSearchResponse(response); @@ -175,7 +176,7 @@ public void testLongHistogram() { SubAggregation agg = randomFrom(SubAggregation.values()); SearchResponse response = client().prepareSearch("idx") .addAggregation(histogram("histo") - .field("long_value").interval(randomIntBetween(1, 2)).subAggregation(agg.builder()).order(Histogram.Order.aggregation(agg.sortKey(), asc))) + .field("long_value").interval(randomIntBetween(1, 2)).subAggregation(agg.builder()).order(BucketOrder.aggregation(agg.sortKey(), asc))) .execute().actionGet(); assertSearchResponse(response); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java index 6118cb69ee747..aaf366c7c7b06 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java @@ -29,6 +29,7 @@ import org.elasticsearch.search.aggregations.bucket.nested.ReverseNested; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.metrics.valuecount.ValueCount; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import java.util.ArrayList; @@ -349,13 +350,13 @@ public void testSimpleReverseNestedToNested1() throws Exception { SearchResponse response = client().prepareSearch("idx2") .addAggregation(nested("nested1", "nested1.nested2") .subAggregation( - terms("field2").field("nested1.nested2.field2").order(Terms.Order.term(true)) + terms("field2").field("nested1.nested2.field2").order(BucketOrder.key(true)) .collectMode(randomFrom(SubAggCollectionMode.values())) .size(10000) .subAggregation( reverseNested("nested1_to_field1").path("nested1") .subAggregation( - terms("field1").field("nested1.field1").order(Terms.Order.term(true)) + terms("field1").field("nested1.field1").order(BucketOrder.key(true)) .collectMode(randomFrom(SubAggCollectionMode.values())) ) ) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java index ebd078de67402..328ce538feb98 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java @@ -28,6 +28,7 @@ import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket; import org.elasticsearch.search.aggregations.metrics.max.Max; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import java.util.Collection; @@ -99,7 +100,7 @@ public void testIssue10719() throws Exception { SearchResponse response = client().prepareSearch("test").setTypes("book").setSearchType(SearchType.QUERY_THEN_FETCH) .addAggregation(terms("genres") .field("genre") - .order(Terms.Order.aggregation("sample>max_price.value", asc)) + .order(BucketOrder.aggregation("sample>max_price.value", asc)) .subAggregation(sampler("sample").shardSize(100) .subAggregation(max("max_price").field("price"))) ).execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTermsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTermsIT.java index 748c5f886f669..4c03ca9a84ea6 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTermsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTermsIT.java @@ -21,6 +21,7 @@ import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.elasticsearch.search.aggregations.bucket.terms.Terms; +import org.elasticsearch.search.aggregations.BucketOrder; import java.util.HashMap; import java.util.List; @@ -39,7 +40,7 @@ public void testNoShardSizeString() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type") .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.count(false))) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.count(false))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -62,7 +63,7 @@ public void testShardSizeEqualsSizeString() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type") .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3).shardSize(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.count(false))) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.count(false))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -86,7 +87,7 @@ public void testWithShardSizeString() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type") .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).shardSize(5).order(Terms.Order.count(false))) + .collectMode(randomFrom(SubAggCollectionMode.values())).shardSize(5).order(BucketOrder.count(false))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -110,7 +111,7 @@ public void testWithShardSizeStringSingleShard() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type").setRouting(routing1) .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).shardSize(5).order(Terms.Order.count(false))) + .collectMode(randomFrom(SubAggCollectionMode.values())).shardSize(5).order(BucketOrder.count(false))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -133,7 +134,7 @@ public void testNoShardSizeTermOrderString() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type") .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.term(true))) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.key(true))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -156,7 +157,7 @@ public void testNoShardSizeLong() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type") .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.count(false))) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.count(false))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -179,7 +180,7 @@ public void testShardSizeEqualsSizeLong() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type") .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3).shardSize(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.count(false))) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.count(false))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -202,7 +203,7 @@ public void testWithShardSizeLong() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type") .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).shardSize(5).order(Terms.Order.count(false))) + .collectMode(randomFrom(SubAggCollectionMode.values())).shardSize(5).order(BucketOrder.count(false))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -226,7 +227,7 @@ public void testWithShardSizeLongSingleShard() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type").setRouting(routing1) .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).shardSize(5).order(Terms.Order.count(false))) + .collectMode(randomFrom(SubAggCollectionMode.values())).shardSize(5).order(BucketOrder.count(false))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -249,7 +250,7 @@ public void testNoShardSizeTermOrderLong() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type") .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.term(true))) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.key(true))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -272,7 +273,7 @@ public void testNoShardSizeDouble() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type") .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.count(false))) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.count(false))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -295,7 +296,7 @@ public void testShardSizeEqualsSizeDouble() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type") .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3).shardSize(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.count(false))) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.count(false))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -318,7 +319,7 @@ public void testWithShardSizeDouble() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type") .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).shardSize(5).order(Terms.Order.count(false))) + .collectMode(randomFrom(SubAggCollectionMode.values())).shardSize(5).order(BucketOrder.count(false))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -341,7 +342,7 @@ public void testWithShardSizeDoubleSingleShard() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type").setRouting(routing1) .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).shardSize(5).order(Terms.Order.count(false))) + .collectMode(randomFrom(SubAggCollectionMode.values())).shardSize(5).order(BucketOrder.count(false))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); @@ -364,7 +365,7 @@ public void testNoShardSizeTermOrderDouble() throws Exception { SearchResponse response = client().prepareSearch("idx").setTypes("type") .setQuery(matchAllQuery()) .addAggregation(terms("keys").field("key").size(3) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.term(true))) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.key(true))) .execute().actionGet(); Terms terms = response.getAggregations().get("keys"); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/StringTermsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/StringTermsIT.java index df69cfcfa9383..0c93ff2f6bbd5 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/StringTermsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/StringTermsIT.java @@ -42,10 +42,12 @@ import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregatorFactory.ExecutionMode; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; import org.elasticsearch.search.aggregations.metrics.avg.Avg; +import org.elasticsearch.search.aggregations.metrics.max.Max; import org.elasticsearch.search.aggregations.metrics.stats.Stats; import org.elasticsearch.search.aggregations.metrics.stats.extended.ExtendedStats; import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.search.aggregations.metrics.valuecount.ValueCount; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import org.hamcrest.Matchers; @@ -72,6 +74,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.extendedStats; import static org.elasticsearch.search.aggregations.AggregationBuilders.filter; import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; +import static org.elasticsearch.search.aggregations.AggregationBuilders.max; import static org.elasticsearch.search.aggregations.AggregationBuilders.stats; import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; @@ -129,9 +132,15 @@ public void setupSuiteScopeCluster() throws Exception { List builders = new ArrayList<>(); for (int i = 0; i < 5; i++) { builders.add(client().prepareIndex("idx", "type").setSource( - jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val" + i).field("i", i) - .field("tag", i < 5 / 2 + 1 ? "more" : "less").startArray(MULTI_VALUED_FIELD_NAME).value("val" + i) - .value("val" + (i + 1)).endArray().endObject())); + jsonBuilder().startObject() + .field(SINGLE_VALUED_FIELD_NAME, "val" + i) + .field("i", i) + .field("constant", 1) + .field("tag", i < 5 / 2 + 1 ? "more" : "less") + .startArray(MULTI_VALUED_FIELD_NAME) + .value("val" + i) + .value("val" + (i + 1)) + .endArray().endObject())); } getMultiSortDocs(builders); @@ -456,15 +465,15 @@ public void testSingleValueFieldWithExactTermFiltering() throws Exception { } } - + public void testSingleValueFieldWithPartitionedFiltering() throws Exception { runTestFieldWithPartitionedFiltering(SINGLE_VALUED_FIELD_NAME); } - + public void testMultiValueFieldWithPartitionedFiltering() throws Exception { runTestFieldWithPartitionedFiltering(MULTI_VALUED_FIELD_NAME); } - + private void runTestFieldWithPartitionedFiltering(String field) throws Exception { // Find total number of unique terms SearchResponse allResponse = client().prepareSearch("idx").setTypes("type") @@ -492,8 +501,8 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception } } assertEquals(expectedCardinality, foundTerms.size()); - } - + } + public void testSingleValueFieldWithMaxSize() throws Exception { SearchResponse response = client() @@ -503,7 +512,7 @@ public void testSingleValueFieldWithMaxSize() throws Exception { .executionHint(randomExecutionHint()) .field(SINGLE_VALUED_FIELD_NAME).size(20) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.term(true))) // we need to sort by terms cause we're checking the first 20 values + .order(BucketOrder.key(true))) // we need to sort by terms cause we're checking the first 20 values .execute().actionGet(); assertSearchResponse(response); @@ -527,7 +536,7 @@ public void testSingleValueFieldOrderedByTermAsc() throws Exception { .setTypes("type") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.term(true))).execute() + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.key(true))).execute() .actionGet(); assertSearchResponse(response); @@ -552,7 +561,7 @@ public void testSingleValueFieldOrderedByTermDesc() throws Exception { .setTypes("type") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.term(false))).execute() + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.key(false))).execute() .actionGet(); assertSearchResponse(response); @@ -944,7 +953,7 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAsc() throws .setTypes("type") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.aggregation("avg_i", asc)) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.aggregation("avg_i", asc)) .subAggregation(avg("avg_i").field("i"))).execute().actionGet(); assertSearchResponse(response); @@ -966,6 +975,34 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAsc() throws } } + public void testSingleValuedFieldOrderedByTieBreaker() throws Exception { + SearchResponse response = client() + .prepareSearch("idx") + .setTypes("type") + .addAggregation( + terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.aggregation("max_constant", randomBoolean())) + .subAggregation(max("max_constant").field("constant"))).execute().actionGet(); + + assertSearchResponse(response); + + Terms terms = response.getAggregations().get("terms"); + assertThat(terms, notNullValue()); + assertThat(terms.getName(), equalTo("terms")); + assertThat(terms.getBuckets().size(), equalTo(5)); + + int i = 0; + for (Terms.Bucket bucket : terms.getBuckets()) { + assertThat(bucket, notNullValue()); + assertThat(key(bucket), equalTo("val" + i)); + assertThat(bucket.getDocCount(), equalTo(1L)); + Max max = bucket.getAggregations().get("max_constant"); + assertThat(max, notNullValue()); + assertThat(max.getValue(), equalTo((double) 1)); + i++; + } + } + public void testSingleValuedFieldOrderedByIllegalAgg() throws Exception { boolean asc = true; try { @@ -975,7 +1012,7 @@ public void testSingleValuedFieldOrderedByIllegalAgg() throws Exception { .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("inner_terms>avg", asc)) + .order(BucketOrder.aggregation("inner_terms>avg", asc)) .subAggregation(terms("inner_terms").field(MULTI_VALUED_FIELD_NAME).subAggregation(avg("avg").field("i")))) .execute().actionGet(); fail("Expected an exception"); @@ -985,7 +1022,7 @@ public void testSingleValuedFieldOrderedByIllegalAgg() throws Exception { ElasticsearchException rootCause = rootCauses[0]; if (rootCause instanceof AggregationExecutionException) { AggregationExecutionException aggException = (AggregationExecutionException) rootCause; - assertThat(aggException.getMessage(), Matchers.startsWith("Invalid terms aggregation order path")); + assertThat(aggException.getMessage(), Matchers.startsWith("Invalid aggregation order path")); } else { throw e; } @@ -1002,7 +1039,7 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws .setTypes("type") .addAggregation( terms("tags").executionHint(randomExecutionHint()).field("tag") - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.aggregation("filter", asc)) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.aggregation("filter", asc)) .subAggregation(filter("filter", QueryBuilders.matchAllQuery()))).execute().actionGet(); assertSearchResponse(response); @@ -1041,7 +1078,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels( .executionHint(randomExecutionHint()) .field("tag") .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("filter1>filter2>stats.max", asc)) + .order(BucketOrder.aggregation("filter1>filter2>stats.max", asc)) .subAggregation( filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( filter("filter2", QueryBuilders.matchAllQuery()).subAggregation( @@ -1104,7 +1141,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevelsS .executionHint(randomExecutionHint()) .field("tag") .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("filter1>" + filter2Name + ">" + statsName + ".max", asc)) + .order(BucketOrder.aggregation("filter1>" + filter2Name + ">" + statsName + ".max", asc)) .subAggregation( filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( filter(filter2Name, QueryBuilders.matchAllQuery()).subAggregation( @@ -1167,7 +1204,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevelsS .executionHint(randomExecutionHint()) .field("tag") .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("filter1>" + filter2Name + ">" + statsName + "[max]", asc)) + .order(BucketOrder.aggregation("filter1>" + filter2Name + ">" + statsName + "[max]", asc)) .subAggregation( filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( filter(filter2Name, QueryBuilders.matchAllQuery()).subAggregation( @@ -1222,7 +1259,7 @@ public void testSingleValuedFieldOrderedByMissingSubAggregation() throws Excepti .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("avg_i", true))).execute().actionGet(); + .order(BucketOrder.aggregation("avg_i", true))).execute().actionGet(); fail("Expected search to fail when trying to sort terms aggregation by sug-aggregation that doesn't exist"); @@ -1240,7 +1277,7 @@ public void testSingleValuedFieldOrderedByNonMetricsOrMultiBucketSubAggregation( .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("values", true)) + .order(BucketOrder.aggregation("values", true)) .subAggregation(terms("values").field("i").collectMode(randomFrom(SubAggCollectionMode.values())))) .execute().actionGet(); @@ -1262,7 +1299,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithUknownMet .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats.foo", true)).subAggregation(stats("stats").field("i"))) + .order(BucketOrder.aggregation("stats.foo", true)).subAggregation(stats("stats").field("i"))) .execute().actionGet(); fail("Expected search to fail when trying to sort terms aggregation by multi-valued sug-aggregation " + "with an unknown specified metric to order by. response had " + response.getFailedShards() + " failed shards."); @@ -1281,7 +1318,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithoutMetric .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats", true)).subAggregation(stats("stats").field("i"))).execute() + .order(BucketOrder.aggregation("stats", true)).subAggregation(stats("stats").field("i"))).execute() .actionGet(); fail("Expected search to fail when trying to sort terms aggregation by multi-valued sug-aggregation " @@ -1300,7 +1337,7 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationDesc() throws .setTypes("type") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.aggregation("avg_i", asc)) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.aggregation("avg_i", asc)) .subAggregation(avg("avg_i").field("i"))).execute().actionGet(); assertSearchResponse(response); @@ -1331,7 +1368,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws E .setTypes("type") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.aggregation("stats.avg", asc)) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.aggregation("stats.avg", asc)) .subAggregation(stats("stats").field("i"))).execute().actionGet(); assertSearchResponse(response); @@ -1361,7 +1398,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws .setTypes("type") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.aggregation("stats.avg", asc)) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.aggregation("stats.avg", asc)) .subAggregation(stats("stats").field("i"))).execute().actionGet(); assertSearchResponse(response); @@ -1393,7 +1430,7 @@ public void testSingleValuedFieldOrderedByMultiValueExtendedStatsAsc() throws Ex .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats.sum_of_squares", asc)) + .order(BucketOrder.aggregation("stats.sum_of_squares", asc)) .subAggregation(extendedStats("stats").field("i"))).execute().actionGet(); assertSearchResponse(response); @@ -1425,7 +1462,7 @@ public void testSingleValuedFieldOrderedByStatsAggAscWithTermsSubAgg() throws Ex .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(Terms.Order.aggregation("stats.sum_of_squares", asc)) + .order(BucketOrder.aggregation("stats.sum_of_squares", asc)) .subAggregation(extendedStats("stats").field("i")) .subAggregation(terms("subTerms").field("s_values").collectMode(randomFrom(SubAggCollectionMode.values())))) .execute().actionGet(); @@ -1464,46 +1501,46 @@ public void testSingleValuedFieldOrderedByStatsAggAscWithTermsSubAgg() throws Ex public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndTermsDesc() throws Exception { String[] expectedKeys = new String[] { "val1", "val2", "val4", "val3", "val7", "val6", "val5" }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("avg_l", true), Terms.Order.term(false)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", true), BucketOrder.key(false)); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndTermsAsc() throws Exception { String[] expectedKeys = new String[] { "val1", "val2", "val3", "val4", "val5", "val6", "val7" }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("avg_l", true), Terms.Order.term(true)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", true), BucketOrder.key(true)); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationDescAndTermsAsc() throws Exception { String[] expectedKeys = new String[] { "val5", "val6", "val7", "val3", "val4", "val2", "val1" }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("avg_l", false), Terms.Order.term(true)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", false), BucketOrder.key(true)); } public void testSingleValuedFieldOrderedByCountAscAndSingleValueSubAggregationAsc() throws Exception { String[] expectedKeys = new String[] { "val6", "val7", "val3", "val4", "val5", "val1", "val2" }; - assertMultiSortResponse(expectedKeys, Terms.Order.count(true), Terms.Order.aggregation("avg_l", true)); + assertMultiSortResponse(expectedKeys, BucketOrder.count(true), BucketOrder.aggregation("avg_l", true)); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscSingleValueSubAggregationAsc() throws Exception { String[] expectedKeys = new String[] { "val6", "val7", "val3", "val5", "val4", "val1", "val2" }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("sum_d", true), Terms.Order.aggregation("avg_l", true)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("sum_d", true), BucketOrder.aggregation("avg_l", true)); } public void testSingleValuedFieldOrderedByThreeCriteria() throws Exception { String[] expectedKeys = new String[] { "val2", "val1", "val4", "val5", "val3", "val6", "val7" }; - assertMultiSortResponse(expectedKeys, Terms.Order.count(false), Terms.Order.aggregation("sum_d", false), - Terms.Order.aggregation("avg_l", false)); + assertMultiSortResponse(expectedKeys, BucketOrder.count(false), BucketOrder.aggregation("sum_d", false), + BucketOrder.aggregation("avg_l", false)); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAsCompound() throws Exception { String[] expectedKeys = new String[] { "val1", "val2", "val3", "val4", "val5", "val6", "val7" }; - assertMultiSortResponse(expectedKeys, Terms.Order.aggregation("avg_l", true)); + assertMultiSortResponse(expectedKeys, BucketOrder.aggregation("avg_l", true)); } - private void assertMultiSortResponse(String[] expectedKeys, Terms.Order... order) { + private void assertMultiSortResponse(String[] expectedKeys, BucketOrder... order) { SearchResponse response = client() .prepareSearch("sort_idx") .addAggregation( terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())).order(Terms.Order.compound(order)) + .collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.compound(order)) .subAggregation(avg("avg_l").field("l")).subAggregation(sum("sum_d").field("d"))).execute().actionGet(); assertSearchResponse(response); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java index 9ed32ca2e7b34..204de33440d7b 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java @@ -27,8 +27,8 @@ import org.elasticsearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregatorFactory.ExecutionMode; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import java.util.ArrayList; @@ -347,7 +347,7 @@ public void testStringValueFieldDocCountAsc() throws Exception { .field(STRING_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.count(true)) + .order(BucketOrder.count(true)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -360,7 +360,7 @@ public void testStringValueFieldDocCountAsc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.count(true)) + .order(BucketOrder.count(true)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -378,7 +378,7 @@ public void testStringValueFieldTermSortAsc() throws Exception { .field(STRING_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.term(true)) + .order(BucketOrder.key(true)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -391,7 +391,7 @@ public void testStringValueFieldTermSortAsc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.term(true)) + .order(BucketOrder.key(true)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -409,7 +409,7 @@ public void testStringValueFieldTermSortDesc() throws Exception { .field(STRING_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.term(false)) + .order(BucketOrder.key(false)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -422,7 +422,7 @@ public void testStringValueFieldTermSortDesc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.term(false)) + .order(BucketOrder.key(false)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -440,7 +440,7 @@ public void testStringValueFieldSubAggAsc() throws Exception { .field(STRING_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.aggregation("sortAgg", true)) + .order(BucketOrder.aggregation("sortAgg", true)) .collectMode(randomFrom(SubAggCollectionMode.values())) .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME))) .execute().actionGet(); @@ -454,7 +454,7 @@ public void testStringValueFieldSubAggAsc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.aggregation("sortAgg", true)) + .order(BucketOrder.aggregation("sortAgg", true)) .collectMode(randomFrom(SubAggCollectionMode.values())) .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME))) .execute().actionGet(); @@ -473,7 +473,7 @@ public void testStringValueFieldSubAggDesc() throws Exception { .field(STRING_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.aggregation("sortAgg", false)) + .order(BucketOrder.aggregation("sortAgg", false)) .collectMode(randomFrom(SubAggCollectionMode.values())) .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME))) .execute().actionGet(); @@ -487,7 +487,7 @@ public void testStringValueFieldSubAggDesc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.aggregation("sortAgg", false)) + .order(BucketOrder.aggregation("sortAgg", false)) .collectMode(randomFrom(SubAggCollectionMode.values())) .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME))) .execute().actionGet(); @@ -583,7 +583,7 @@ public void testLongValueFieldDocCountAsc() throws Exception { .field(LONG_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.count(true)) + .order(BucketOrder.count(true)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -596,7 +596,7 @@ public void testLongValueFieldDocCountAsc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.count(true)) + .order(BucketOrder.count(true)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -614,7 +614,7 @@ public void testLongValueFieldTermSortAsc() throws Exception { .field(LONG_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.term(true)) + .order(BucketOrder.key(true)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -627,7 +627,7 @@ public void testLongValueFieldTermSortAsc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.term(true)) + .order(BucketOrder.key(true)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -645,7 +645,7 @@ public void testLongValueFieldTermSortDesc() throws Exception { .field(LONG_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.term(false)) + .order(BucketOrder.key(false)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -658,7 +658,7 @@ public void testLongValueFieldTermSortDesc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.term(false)) + .order(BucketOrder.key(false)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -676,7 +676,7 @@ public void testLongValueFieldSubAggAsc() throws Exception { .field(LONG_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.aggregation("sortAgg", true)) + .order(BucketOrder.aggregation("sortAgg", true)) .collectMode(randomFrom(SubAggCollectionMode.values())) .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME))) .execute().actionGet(); @@ -690,7 +690,7 @@ public void testLongValueFieldSubAggAsc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.aggregation("sortAgg", true)) + .order(BucketOrder.aggregation("sortAgg", true)) .collectMode(randomFrom(SubAggCollectionMode.values())) .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME))) .execute().actionGet(); @@ -709,7 +709,7 @@ public void testLongValueFieldSubAggDesc() throws Exception { .field(LONG_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.aggregation("sortAgg", false)) + .order(BucketOrder.aggregation("sortAgg", false)) .collectMode(randomFrom(SubAggCollectionMode.values())) .subAggregation(sum("sortAgg").field(DOUBLE_FIELD_NAME))) .execute().actionGet(); @@ -723,7 +723,7 @@ public void testLongValueFieldSubAggDesc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.aggregation("sortAgg", false)) + .order(BucketOrder.aggregation("sortAgg", false)) .collectMode(randomFrom(SubAggCollectionMode.values())) .subAggregation(sum("sortAgg").field(DOUBLE_FIELD_NAME))) .execute().actionGet(); @@ -819,7 +819,7 @@ public void testDoubleValueFieldDocCountAsc() throws Exception { .field(DOUBLE_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.count(true)) + .order(BucketOrder.count(true)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -832,7 +832,7 @@ public void testDoubleValueFieldDocCountAsc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.count(true)) + .order(BucketOrder.count(true)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -850,7 +850,7 @@ public void testDoubleValueFieldTermSortAsc() throws Exception { .field(DOUBLE_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.term(true)) + .order(BucketOrder.key(true)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -863,7 +863,7 @@ public void testDoubleValueFieldTermSortAsc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.term(true)) + .order(BucketOrder.key(true)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -881,7 +881,7 @@ public void testDoubleValueFieldTermSortDesc() throws Exception { .field(DOUBLE_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.term(false)) + .order(BucketOrder.key(false)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -894,7 +894,7 @@ public void testDoubleValueFieldTermSortDesc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.term(false)) + .order(BucketOrder.key(false)) .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); @@ -912,7 +912,7 @@ public void testDoubleValueFieldSubAggAsc() throws Exception { .field(DOUBLE_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.aggregation("sortAgg", true)) + .order(BucketOrder.aggregation("sortAgg", true)) .collectMode(randomFrom(SubAggCollectionMode.values())) .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME))) .execute().actionGet(); @@ -926,7 +926,7 @@ public void testDoubleValueFieldSubAggAsc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.aggregation("sortAgg", true)) + .order(BucketOrder.aggregation("sortAgg", true)) .collectMode(randomFrom(SubAggCollectionMode.values())) .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME))) .execute().actionGet(); @@ -945,7 +945,7 @@ public void testDoubleValueFieldSubAggDesc() throws Exception { .field(DOUBLE_FIELD_NAME) .showTermDocCountError(true) .size(10000).shardSize(10000) - .order(Order.aggregation("sortAgg", false)) + .order(BucketOrder.aggregation("sortAgg", false)) .collectMode(randomFrom(SubAggCollectionMode.values())) .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME))) .execute().actionGet(); @@ -959,7 +959,7 @@ public void testDoubleValueFieldSubAggDesc() throws Exception { .showTermDocCountError(true) .size(size) .shardSize(shardSize) - .order(Order.aggregation("sortAgg", false)) + .order(BucketOrder.aggregation("sortAgg", false)) .collectMode(randomFrom(SubAggCollectionMode.values())) .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME))) .execute().actionGet(); @@ -968,7 +968,7 @@ public void testDoubleValueFieldSubAggDesc() throws Exception { assertUnboundedDocCountError(size, accurateResponse, testResponse); } - + /** * Test a case where we know exactly how many of each term is on each shard * so we know the exact error value for each term. To do this we search over @@ -984,39 +984,39 @@ public void testFixedDocs() throws Exception { .collectMode(randomFrom(SubAggCollectionMode.values()))) .execute().actionGet(); assertSearchResponse(response); - + Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getDocCountError(), equalTo(46L)); List buckets = terms.getBuckets(); assertThat(buckets, notNullValue()); assertThat(buckets.size(), equalTo(5)); - + Bucket bucket = buckets.get(0); assertThat(bucket, notNullValue()); assertThat(bucket.getKey(), equalTo("A")); assertThat(bucket.getDocCount(), equalTo(100L)); assertThat(bucket.getDocCountError(), equalTo(0L)); - + bucket = buckets.get(1); assertThat(bucket, notNullValue()); assertThat(bucket.getKey(), equalTo("Z")); assertThat(bucket.getDocCount(), equalTo(52L)); assertThat(bucket.getDocCountError(), equalTo(2L)); - + bucket = buckets.get(2); assertThat(bucket, notNullValue()); assertThat(bucket.getKey(), equalTo("C")); assertThat(bucket.getDocCount(), equalTo(50L)); assertThat(bucket.getDocCountError(), equalTo(15L)); - - + + bucket = buckets.get(3); assertThat(bucket, notNullValue()); assertThat(bucket.getKey(), equalTo("G")); assertThat(bucket.getDocCount(), equalTo(45L)); assertThat(bucket.getDocCountError(), equalTo(2L)); - + bucket = buckets.get(4); assertThat(bucket, notNullValue()); assertThat(bucket.getKey(), equalTo("B")); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsShardMinDocCountIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsShardMinDocCountIT.java index f7e3d9a61b598..c094c245dac03 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsShardMinDocCountIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsShardMinDocCountIT.java @@ -26,6 +26,7 @@ import org.elasticsearch.search.aggregations.bucket.significant.SignificantTerms; import org.elasticsearch.search.aggregations.bucket.significant.SignificantTermsAggregatorFactory; import org.elasticsearch.search.aggregations.bucket.terms.Terms; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import java.util.ArrayList; @@ -129,7 +130,7 @@ public void testShardMinDocCountTermsTest() throws Exception { // first, check that indeed when not setting the shardMinDocCount parameter 0 terms are returned SearchResponse response = client().prepareSearch(index) .addAggregation( - terms("myTerms").field("text").minDocCount(2).size(2).executionHint(randomExecutionHint()).order(Terms.Order.term(true)) + terms("myTerms").field("text").minDocCount(2).size(2).executionHint(randomExecutionHint()).order(BucketOrder.key(true)) ) .execute() .actionGet(); @@ -140,7 +141,7 @@ public void testShardMinDocCountTermsTest() throws Exception { response = client().prepareSearch(index) .addAggregation( - terms("myTerms").field("text").minDocCount(2).shardMinDocCount(2).size(2).executionHint(randomExecutionHint()).order(Terms.Order.term(true)) + terms("myTerms").field("text").minDocCount(2).shardMinDocCount(2).size(2).executionHint(randomExecutionHint()).order(BucketOrder.key(true)) ) .execute() .actionGet(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsTests.java index 42f6ef78f4b17..73c275cfd235d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsTests.java @@ -23,10 +23,10 @@ import org.apache.lucene.util.automaton.RegExp; import org.elasticsearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.elasticsearch.search.aggregations.BaseAggregationTestCase; -import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregatorFactory.ExecutionMode; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; +import org.elasticsearch.search.aggregations.BucketOrder; import java.util.ArrayList; import java.util.List; @@ -155,8 +155,12 @@ protected TermsAggregationBuilder createTestAggregatorBuilder() { factory.includeExclude(incExc); } if (randomBoolean()) { - List order = randomOrder(); - factory.order(order); + List order = randomOrder(); + if(order.size() == 1 && randomBoolean()) { + factory.order(order.get(0)); + } else { + factory.order(order); + } } if (randomBoolean()) { factory.showTermDocCountError(randomBoolean()); @@ -164,20 +168,20 @@ protected TermsAggregationBuilder createTestAggregatorBuilder() { return factory; } - private List randomOrder() { - List orders = new ArrayList<>(); + private List randomOrder() { + List orders = new ArrayList<>(); switch (randomInt(4)) { case 0: - orders.add(Terms.Order.term(randomBoolean())); + orders.add(BucketOrder.key(randomBoolean())); break; case 1: - orders.add(Terms.Order.count(randomBoolean())); + orders.add(BucketOrder.count(randomBoolean())); break; case 2: - orders.add(Terms.Order.aggregation(randomAlphaOfLengthBetween(3, 20), randomBoolean())); + orders.add(BucketOrder.aggregation(randomAlphaOfLengthBetween(3, 20), randomBoolean())); break; case 3: - orders.add(Terms.Order.aggregation(randomAlphaOfLengthBetween(3, 20), randomAlphaOfLengthBetween(3, 20), randomBoolean())); + orders.add(BucketOrder.aggregation(randomAlphaOfLengthBetween(3, 20), randomAlphaOfLengthBetween(3, 20), randomBoolean())); break; case 4: int numOrders = randomIntBetween(1, 3); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java index aafad557bc681..17c29dc1ae609 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.InternalAggregationTestCase; import org.joda.time.DateTime; @@ -55,7 +56,7 @@ protected InternalDateHistogram createTestInstance(String name, List pipelineAggregators, Map metaData) { - Terms.Order order = Terms.Order.count(false); + BucketOrder order = BucketOrder.count(false); long minDocCount = 1; int requiredSize = 3; int shardSize = requiredSize + 2; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsTests.java index ff95984bc3206..96833091a11b2 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; import java.util.ArrayList; import java.util.HashSet; @@ -37,7 +38,7 @@ public class LongTermsTests extends InternalTermsTestCase { String name, List pipelineAggregators, Map metaData) { - Terms.Order order = Terms.Order.count(false); + BucketOrder order = BucketOrder.count(false); long minDocCount = 1; int requiredSize = 3; int shardSize = requiredSize + 2; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsTests.java index 64e814bd8192a..abac0f0b03a98 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsTests.java @@ -24,6 +24,7 @@ import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.BucketOrder; import java.util.ArrayList; import java.util.HashSet; @@ -38,7 +39,7 @@ public class StringTermsTests extends InternalTermsTestCase { String name, List pipelineAggregators, Map metaData) { - Terms.Order order = Terms.Order.count(false); + BucketOrder order = BucketOrder.count(false); long minDocCount = 1; int requiredSize = 3; int shardSize = requiredSize + 2; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java index 1648d8ede9fdf..7b93653fff868 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java @@ -37,6 +37,7 @@ import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.search.aggregations.AggregatorTestCase; import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.aggregations.support.ValueType; import java.io.IOException; @@ -70,7 +71,7 @@ public void testTermsAggregator() throws Exception { TermsAggregationBuilder aggregationBuilder = new TermsAggregationBuilder("_name", ValueType.STRING) .executionHint(executionMode.toString()) .field("string") - .order(Terms.Order.term(true)); + .order(BucketOrder.key(true)); MappedFieldType fieldType = new KeywordFieldMapper.KeywordFieldType(); fieldType.setName("string"); fieldType.setHasDocValues(true ); @@ -99,7 +100,7 @@ public void testMixLongAndDouble() throws Exception { TermsAggregationBuilder aggregationBuilder = new TermsAggregationBuilder("_name", ValueType.LONG) .executionHint(executionMode.toString()) .field("number") - .order(Terms.Order.term(true)); + .order(BucketOrder.key(true)); List aggs = new ArrayList<> (); int numLongs = randomIntBetween(1, 3); for (int i = 0; i < numLongs; i++) { diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AvgIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AvgIT.java index c952f43eb3032..c51c0aec4bb61 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AvgIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/AvgIT.java @@ -36,8 +36,8 @@ import org.elasticsearch.search.aggregations.bucket.global.Global; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.metrics.avg.Avg; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.lookup.LeafSearchLookup; import org.elasticsearch.search.lookup.SearchLookup; @@ -326,7 +326,7 @@ public void testScriptMultiValuedWithParams() throws Exception { @Override public void testOrderByEmptyAggregation() throws Exception { SearchResponse searchResponse = client().prepareSearch("idx").setQuery(matchAllQuery()) - .addAggregation(terms("terms").field("value").order(Order.compound(Order.aggregation("filter>avg", true))) + .addAggregation(terms("terms").field("value").order(BucketOrder.compound(BucketOrder.aggregation("filter>avg", true))) .subAggregation(filter("filter", termQuery("value", 100)).subAggregation(avg("avg").field("value")))) .get(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java index 3903dd8b0bc76..7de333e8127ca 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java @@ -30,9 +30,9 @@ import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.missing.Missing; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.metrics.stats.extended.ExtendedStats; import org.elasticsearch.search.aggregations.metrics.stats.extended.ExtendedStats.Bounds; +import org.elasticsearch.search.aggregations.BucketOrder; import java.util.Collection; import java.util.Collections; @@ -595,7 +595,8 @@ public void testEmptySubAggregation() { @Override public void testOrderByEmptyAggregation() throws Exception { SearchResponse searchResponse = client().prepareSearch("idx").setQuery(matchAllQuery()) - .addAggregation(terms("terms").field("value").order(Order.compound(Order.aggregation("filter>extendedStats.avg", true))) + .addAggregation(terms("terms").field("value") + .order(BucketOrder.compound(BucketOrder.aggregation("filter>extendedStats.avg", true))) .subAggregation( filter("filter", termQuery("value", 100)).subAggregation(extendedStats("extendedStats").field("value")))) .get(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java index 5b56e6b7efbf2..586af22755cd6 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java @@ -29,11 +29,11 @@ import org.elasticsearch.search.aggregations.bucket.filter.Filter; import org.elasticsearch.search.aggregations.bucket.global.Global; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; -import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Order; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; import org.elasticsearch.search.aggregations.metrics.percentiles.PercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.PercentilesMethod; +import org.elasticsearch.search.aggregations.BucketOrder; import java.util.Arrays; import java.util.Collection; @@ -485,7 +485,7 @@ public void testOrderBySubAggregation() { .subAggregation( percentileRanks("percentile_ranks").field("value").method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits).values(99)) - .order(Order.aggregation("percentile_ranks", "99", asc))).execute().actionGet(); + .order(BucketOrder.aggregation("percentile_ranks", "99", asc))).execute().actionGet(); assertHitCount(searchResponse, 10); @@ -506,7 +506,7 @@ public void testOrderBySubAggregation() { @Override public void testOrderByEmptyAggregation() throws Exception { SearchResponse searchResponse = client().prepareSearch("idx").setQuery(matchAllQuery()) - .addAggregation(terms("terms").field("value").order(Terms.Order.compound(Terms.Order.aggregation("filter>ranks.99", true))) + .addAggregation(terms("terms").field("value").order(BucketOrder.compound(BucketOrder.aggregation("filter>ranks.99", true))) .subAggregation(filter("filter", termQuery("value", 100)) .subAggregation(percentileRanks("ranks").method(PercentilesMethod.HDR).values(99).field("value")))) .get(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java index 56fb14402ad5d..ae745e1f1ad03 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java @@ -30,11 +30,11 @@ import org.elasticsearch.search.aggregations.bucket.filter.Filter; import org.elasticsearch.search.aggregations.bucket.global.Global; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; -import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Order; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; import org.elasticsearch.search.aggregations.metrics.percentiles.Percentiles; import org.elasticsearch.search.aggregations.metrics.percentiles.PercentilesMethod; +import org.elasticsearch.search.aggregations.BucketOrder; import java.util.Arrays; import java.util.Collection; @@ -474,7 +474,7 @@ public void testOrderBySubAggregation() { .method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) .percentiles(99)) - .order(Order.aggregation("percentiles", "99", asc))).execute().actionGet(); + .order(BucketOrder.aggregation("percentiles", "99", asc))).execute().actionGet(); assertHitCount(searchResponse, 10); @@ -497,7 +497,7 @@ public void testOrderByEmptyAggregation() throws Exception { SearchResponse searchResponse = client().prepareSearch("idx") .setQuery(matchAllQuery()) .addAggregation( - terms("terms").field("value").order(Terms.Order.compound(Terms.Order.aggregation("filter>percentiles.99", true))) + terms("terms").field("value").order(BucketOrder.compound(BucketOrder.aggregation("filter>percentiles.99", true))) .subAggregation(filter("filter", termQuery("value", 100)) .subAggregation(percentiles("percentiles").method(PercentilesMethod.HDR).field("value")))) .get(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MaxIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MaxIT.java index 03eb9a092372a..a192b3c4a12c1 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MaxIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MaxIT.java @@ -29,8 +29,8 @@ import org.elasticsearch.search.aggregations.bucket.global.Global; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.metrics.max.Max; +import org.elasticsearch.search.aggregations.BucketOrder; import java.util.Collection; import java.util.Collections; @@ -328,7 +328,7 @@ public void testScriptMultiValuedWithParams() throws Exception { @Override public void testOrderByEmptyAggregation() throws Exception { SearchResponse searchResponse = client().prepareSearch("idx").setQuery(matchAllQuery()) - .addAggregation(terms("terms").field("value").order(Order.compound(Order.aggregation("filter>max", true))) + .addAggregation(terms("terms").field("value").order(BucketOrder.compound(BucketOrder.aggregation("filter>max", true))) .subAggregation(filter("filter", termQuery("value", 100)).subAggregation(max("max").field("value")))) .get(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MinIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MinIT.java index cba2ba9eb97c8..7f2522c04bb50 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MinIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/MinIT.java @@ -29,8 +29,8 @@ import org.elasticsearch.search.aggregations.bucket.global.Global; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.metrics.min.Min; +import org.elasticsearch.search.aggregations.BucketOrder; import java.util.Collection; import java.util.Collections; @@ -340,7 +340,7 @@ public void testScriptMultiValuedWithParams() throws Exception { @Override public void testOrderByEmptyAggregation() throws Exception { SearchResponse searchResponse = client().prepareSearch("idx").setQuery(matchAllQuery()) - .addAggregation(terms("terms").field("value").order(Order.compound(Order.aggregation("filter>min", true))) + .addAggregation(terms("terms").field("value").order(BucketOrder.compound(BucketOrder.aggregation("filter>min", true))) .subAggregation(filter("filter", termQuery("value", 100)).subAggregation(min("min").field("value")))) .get(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java index 9231f09396307..0fcf794ee1d83 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java @@ -32,8 +32,8 @@ import org.elasticsearch.search.aggregations.bucket.global.Global; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.metrics.stats.Stats; +import org.elasticsearch.search.aggregations.BucketOrder; import java.util.Collection; import java.util.Collections; @@ -447,7 +447,7 @@ public void testScriptMultiValuedWithParams() throws Exception { @Override public void testOrderByEmptyAggregation() throws Exception { SearchResponse searchResponse = client().prepareSearch("idx").setQuery(matchAllQuery()) - .addAggregation(terms("terms").field("value").order(Order.compound(Order.aggregation("filter>stats.avg", true))) + .addAggregation(terms("terms").field("value").order(BucketOrder.compound(BucketOrder.aggregation("filter>stats.avg", true))) .subAggregation(filter("filter", termQuery("value", 100)).subAggregation(stats("stats").field("value")))) .get(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java index 227ffc7251bb3..86f59659ebc18 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/SumIT.java @@ -36,8 +36,8 @@ import org.elasticsearch.search.aggregations.bucket.global.Global; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.metrics.sum.Sum; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.lookup.LeafSearchLookup; import org.elasticsearch.search.lookup.SearchLookup; @@ -325,7 +325,7 @@ public void testMultiValuedFieldWithValueScriptWithParams() throws Exception { @Override public void testOrderByEmptyAggregation() throws Exception { SearchResponse searchResponse = client().prepareSearch("idx").setQuery(matchAllQuery()) - .addAggregation(terms("terms").field("value").order(Order.compound(Order.aggregation("filter>sum", true))) + .addAggregation(terms("terms").field("value").order(BucketOrder.compound(BucketOrder.aggregation("filter>sum", true))) .subAggregation(filter("filter", termQuery("value", 100)).subAggregation(sum("sum").field("value")))) .get(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java index f1943747ceb43..11ff1edbc537e 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java @@ -30,12 +30,12 @@ import org.elasticsearch.search.aggregations.bucket.filter.Filter; import org.elasticsearch.search.aggregations.bucket.global.Global; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; -import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Order; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; import org.elasticsearch.search.aggregations.metrics.percentiles.PercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.PercentileRanksAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.percentiles.PercentilesMethod; +import org.elasticsearch.search.aggregations.BucketOrder; import java.util.Arrays; import java.util.Collection; @@ -435,7 +435,7 @@ public void testOrderBySubAggregation() { .addAggregation( histogram("histo").field("value").interval(2L) .subAggregation(randomCompression(percentileRanks("percentile_ranks").field("value").values(99))) - .order(Order.aggregation("percentile_ranks", "99", asc))) + .order(BucketOrder.aggregation("percentile_ranks", "99", asc))) .execute().actionGet(); assertHitCount(searchResponse, 10); @@ -457,7 +457,7 @@ public void testOrderBySubAggregation() { @Override public void testOrderByEmptyAggregation() throws Exception { SearchResponse searchResponse = client().prepareSearch("idx").setQuery(matchAllQuery()) - .addAggregation(terms("terms").field("value").order(Terms.Order.compound(Terms.Order.aggregation("filter>ranks.99", true))) + .addAggregation(terms("terms").field("value").order(BucketOrder.compound(BucketOrder.aggregation("filter>ranks.99", true))) .subAggregation(filter("filter", termQuery("value", 100)) .subAggregation(percentileRanks("ranks").method(PercentilesMethod.TDIGEST).values(99).field("value")))) .get(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java index 2589e9977a6c9..89c7d12c746fa 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java @@ -30,12 +30,12 @@ import org.elasticsearch.search.aggregations.bucket.filter.Filter; import org.elasticsearch.search.aggregations.bucket.global.Global; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; -import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Order; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; import org.elasticsearch.search.aggregations.metrics.percentiles.Percentiles; import org.elasticsearch.search.aggregations.metrics.percentiles.PercentilesAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.percentiles.PercentilesMethod; +import org.elasticsearch.search.aggregations.BucketOrder; import java.util.Arrays; import java.util.Collection; @@ -419,7 +419,7 @@ public void testOrderBySubAggregation() { .addAggregation( histogram("histo").field("value").interval(2L) .subAggregation(randomCompression(percentiles("percentiles").field("value").percentiles(99))) - .order(Order.aggregation("percentiles", "99", asc))) + .order(BucketOrder.aggregation("percentiles", "99", asc))) .execute().actionGet(); assertHitCount(searchResponse, 10); @@ -442,7 +442,7 @@ public void testOrderBySubAggregation() { public void testOrderByEmptyAggregation() throws Exception { SearchResponse searchResponse = client().prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( - terms("terms").field("value").order(Terms.Order.compound(Terms.Order.aggregation("filter>percentiles.99", true))) + terms("terms").field("value").order(BucketOrder.compound(BucketOrder.aggregation("filter>percentiles.99", true))) .subAggregation(filter("filter", termQuery("value", 100)) .subAggregation(percentiles("percentiles").method(PercentilesMethod.TDIGEST).field("value")))) .get(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java index 6819fddf3e3e5..563fac1ba7df7 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java @@ -47,6 +47,7 @@ import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregatorFactory.ExecutionMode; import org.elasticsearch.search.aggregations.metrics.max.Max; import org.elasticsearch.search.aggregations.metrics.tophits.TopHits; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightField; import org.elasticsearch.search.rescore.RescoreBuilder; @@ -398,7 +399,7 @@ public void testBreadthFirstWithAggOrderAndScoreNeeded() throws Exception { .executionHint(randomExecutionHint()) .collectMode(SubAggCollectionMode.BREADTH_FIRST) .field(TERMS_AGGS_FIELD) - .order(Terms.Order.aggregation("max", false)) + .order(BucketOrder.aggregation("max", false)) .subAggregation(max("max").field(SORT_FIELD)) .subAggregation(topHits("hits").size(3)) ).get(); @@ -494,7 +495,7 @@ public void testSortByBucket() throws Exception { .addAggregation(terms("terms") .executionHint(randomExecutionHint()) .field(TERMS_AGGS_FIELD) - .order(Terms.Order.aggregation("max_sort", false)) + .order(BucketOrder.aggregation("max_sort", false)) .subAggregation( topHits("hits").sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC)).trackScores(true) ) @@ -535,7 +536,7 @@ public void testFieldCollapsing() throws Exception { .setQuery(matchQuery("text", "term rare")) .addAggregation( terms("terms").executionHint(randomExecutionHint()).field("group") - .order(Terms.Order.aggregation("max_score", false)).subAggregation(topHits("hits").size(1)) + .order(BucketOrder.aggregation("max_score", false)).subAggregation(topHits("hits").size(1)) .subAggregation(max("max_score").field("value"))).get(); assertSearchResponse(response); @@ -908,7 +909,6 @@ public void testTopHitsInNested() throws Exception { histogram("dates") .field("date") .interval(5) - .order(Histogram.Order.aggregation("to-comments", true)) .subAggregation( nested("to-comments", "comments") .subAggregation(topHits("comments") diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/AvgBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/AvgBucketIT.java index 4f6ff0e32eda8..1d29518a300e6 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/AvgBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/AvgBucketIT.java @@ -24,10 +24,10 @@ import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Bucket; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import java.util.ArrayList; @@ -129,7 +129,7 @@ public void testDocCountAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) @@ -211,7 +211,7 @@ public void testMetricAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -264,7 +264,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -337,7 +337,7 @@ public void testNested() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java index 607124ecb159d..fea143bddccfd 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java @@ -26,12 +26,12 @@ import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Bucket; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; import org.elasticsearch.search.aggregations.metrics.stats.extended.ExtendedStats.Bounds; import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.extended.ExtendedStatsBucket; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import java.util.ArrayList; @@ -200,7 +200,7 @@ public void testDocCountAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) @@ -300,7 +300,7 @@ public void testMetricAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -362,7 +362,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -445,7 +445,7 @@ public void testBadSigmaAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -475,7 +475,7 @@ public void testNested() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MaxBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MaxBucketIT.java index 632f11f7ec73d..50b512ee1944b 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MaxBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MaxBucketIT.java @@ -25,11 +25,11 @@ import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Bucket; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.InternalBucketMetricValue; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import java.util.ArrayList; @@ -138,7 +138,7 @@ public void testDocCountAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) @@ -230,7 +230,7 @@ public void testMetricAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -335,7 +335,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -413,7 +413,7 @@ public void testNested() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MinBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MinBucketIT.java index 04fdd0c313354..33678f146dbc2 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MinBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/MinBucketIT.java @@ -24,11 +24,11 @@ import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Bucket; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.InternalBucketMetricValue; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import java.util.ArrayList; @@ -135,7 +135,7 @@ public void testDocCountAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) @@ -227,7 +227,7 @@ public void testMetricAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -285,7 +285,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -363,7 +363,7 @@ public void testNested() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java index e23e5441431eb..62f9ad462e902 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java @@ -29,6 +29,7 @@ import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile; import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.percentile.PercentilesBucket; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import java.util.ArrayList; @@ -133,7 +134,7 @@ public void testDocCountAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Terms.Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) @@ -248,7 +249,7 @@ public void testMetricAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Terms.Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -301,7 +302,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Terms.Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -435,7 +436,7 @@ public void testBadPercents_asSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Terms.Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) @@ -466,7 +467,7 @@ public void testNested() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Terms.Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) @@ -525,7 +526,7 @@ public void testNestedWithDecimal() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Terms.Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/StatsBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/StatsBucketIT.java index 231005f1b5b62..ff5a85f198ef4 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/StatsBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/StatsBucketIT.java @@ -24,11 +24,11 @@ import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Bucket; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.StatsBucket; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import java.util.ArrayList; @@ -136,7 +136,7 @@ public void testDocCountAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) @@ -230,7 +230,7 @@ public void testMetricAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -289,7 +289,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -368,7 +368,7 @@ public void testNested() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/SumBucketIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/SumBucketIT.java index 048dfac8648d4..69451435d58b5 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/SumBucketIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/SumBucketIT.java @@ -24,10 +24,10 @@ import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Bucket; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; import java.util.ArrayList; @@ -126,7 +126,7 @@ public void testDocCountAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) @@ -202,7 +202,7 @@ public void testMetricAsSubAgg() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -252,7 +252,7 @@ public void testMetricAsSubAggWithInsertZeros() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue) @@ -322,7 +322,7 @@ public void testNested() throws Exception { .addAggregation( terms("terms") .field("tag") - .order(Order.term(true)) + .order(BucketOrder.key(true)) .subAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) .extendedBounds(minRandomValue, maxRandomValue)) diff --git a/docs/java-api/aggregations/bucket/datehistogram-aggregation.asciidoc b/docs/java-api/aggregations/bucket/datehistogram-aggregation.asciidoc index 99b871730e6d8..1fe945077fdb7 100644 --- a/docs/java-api/aggregations/bucket/datehistogram-aggregation.asciidoc +++ b/docs/java-api/aggregations/bucket/datehistogram-aggregation.asciidoc @@ -67,3 +67,7 @@ key [2005-01-01T00:00:00.000Z], date [2005], doc_count [1] key [2007-01-01T00:00:00.000Z], date [2007], doc_count [2] key [2008-01-01T00:00:00.000Z], date [2008], doc_count [3] -------------------------------------------------- + +===== Order + +Supports the same order functionality as the <>. diff --git a/docs/java-api/aggregations/bucket/histogram-aggregation.asciidoc b/docs/java-api/aggregations/bucket/histogram-aggregation.asciidoc index 28e9cd3ecd0ff..59bb555401c5b 100644 --- a/docs/java-api/aggregations/bucket/histogram-aggregation.asciidoc +++ b/docs/java-api/aggregations/bucket/histogram-aggregation.asciidoc @@ -42,3 +42,7 @@ for (Histogram.Bucket entry : agg.getBuckets()) { logger.info("key [{}], doc_count [{}]", key, docCount); } -------------------------------------------------- + +===== Order + +Supports the same order functionality as the <>. diff --git a/docs/java-api/aggregations/bucket/terms-aggregation.asciidoc b/docs/java-api/aggregations/bucket/terms-aggregation.asciidoc index ad83faccd31c5..db584fd4cedd2 100644 --- a/docs/java-api/aggregations/bucket/terms-aggregation.asciidoc +++ b/docs/java-api/aggregations/bucket/terms-aggregation.asciidoc @@ -39,7 +39,14 @@ for (Terms.Bucket entry : genders.getBuckets()) { } -------------------------------------------------- -==== Order +===== Order + +Import bucket ordering strategy classes: + +[source,java] +-------------------------------------------------- +import org.elasticsearch.search.aggregations.BucketOrder; +-------------------------------------------------- Ordering the buckets by their `doc_count` in an ascending manner: @@ -48,7 +55,7 @@ Ordering the buckets by their `doc_count` in an ascending manner: AggregationBuilders .terms("genders") .field("gender") - .order(Terms.Order.count(true)) + .order(BucketOrder.count(true)) -------------------------------------------------- Ordering the buckets alphabetically by their terms in an ascending manner: @@ -58,7 +65,7 @@ Ordering the buckets alphabetically by their terms in an ascending manner: AggregationBuilders .terms("genders") .field("gender") - .order(Terms.Order.term(true)) + .order(BucketOrder.key(true)) -------------------------------------------------- Ordering the buckets by single value metrics sub-aggregation (identified by the aggregation name): @@ -68,7 +75,22 @@ Ordering the buckets by single value metrics sub-aggregation (identified by the AggregationBuilders .terms("genders") .field("gender") - .order(Terms.Order.aggregation("avg_height", false)) + .order(BucketOrder.aggregation("avg_height", false)) + .subAggregation( + AggregationBuilders.avg("avg_height").field("height") + ) +-------------------------------------------------- + +Ordering the buckets by multiple criteria: + +[source,java] +-------------------------------------------------- +AggregationBuilders + .terms("genders") + .field("gender") + .order(BucketOrder.compound( // in order of priority: + BucketOrder.aggregation("avg_height", false), // sort by sub-aggregation first + BucketOrder.count(true))) // then bucket count as a tie-breaker .subAggregation( AggregationBuilders.avg("avg_height").field("height") ) diff --git a/docs/reference/aggregations/bucket/datehistogram-aggregation.asciidoc b/docs/reference/aggregations/bucket/datehistogram-aggregation.asciidoc index b7619b175df3d..47265a0b224cd 100644 --- a/docs/reference/aggregations/bucket/datehistogram-aggregation.asciidoc +++ b/docs/reference/aggregations/bucket/datehistogram-aggregation.asciidoc @@ -390,3 +390,10 @@ POST /sales/_search?size=0 // TEST[setup:sales] <1> Documents without a value in the `publish_date` field will fall into the same bucket as documents that have the value `2000-01-01`. + +==== Order + +By default the returned buckets are sorted by their `key` ascending, though the order behaviour can be controlled using +the `order` setting. Supports the same `order` functionality as the <>. + +deprecated[6.0.0, Use `_key` instead of `_time` to order buckets by their dates/keys] diff --git a/docs/reference/aggregations/bucket/histogram-aggregation.asciidoc b/docs/reference/aggregations/bucket/histogram-aggregation.asciidoc index de828c62aa9a7..380d06258da76 100644 --- a/docs/reference/aggregations/bucket/histogram-aggregation.asciidoc +++ b/docs/reference/aggregations/bucket/histogram-aggregation.asciidoc @@ -179,120 +179,8 @@ POST /sales/_search?size=0 ==== Order -By default the returned buckets are sorted by their `key` ascending, though the order behaviour can be controlled -using the `order` setting. - -Ordering the buckets by their key - descending: - -[source,js] --------------------------------------------------- -POST /sales/_search?size=0 -{ - "aggs" : { - "prices" : { - "histogram" : { - "field" : "price", - "interval" : 50, - "order" : { "_key" : "desc" } - } - } - } -} --------------------------------------------------- -// CONSOLE -// TEST[setup:sales] - -Ordering the buckets by their `doc_count` - ascending: - -[source,js] --------------------------------------------------- -POST /sales/_search?size=0 -{ - "aggs" : { - "prices" : { - "histogram" : { - "field" : "price", - "interval" : 50, - "order" : { "_count" : "asc" } - } - } - } -} --------------------------------------------------- -// CONSOLE -// TEST[setup:sales] - -If the histogram aggregation has a direct metrics sub-aggregation, the latter can determine the order of the buckets: - -[source,js] --------------------------------------------------- -POST /sales/_search?size=0 -{ - "aggs" : { - "prices" : { - "histogram" : { - "field" : "price", - "interval" : 50, - "order" : { "price_stats.min" : "asc" } <1> - }, - "aggs" : { - "price_stats" : { "stats" : {"field" : "price"} } - } - } - } -} --------------------------------------------------- -// CONSOLE -// TEST[setup:sales] - -<1> The `{ "price_stats.min" : asc" }` will sort the buckets based on `min` value of their `price_stats` sub-aggregation. - -It is also possible to order the buckets based on a "deeper" aggregation in the hierarchy. This is supported as long -as the aggregations path are of a single-bucket type, where the last aggregation in the path may either by a single-bucket -one or a metrics one. If it's a single-bucket type, the order will be defined by the number of docs in the bucket (i.e. `doc_count`), -in case it's a metrics one, the same rules as above apply (where the path must indicate the metric name to sort by in case of -a multi-value metrics aggregation, and in case of a single-value metrics aggregation the sort will be applied on that value). - -The path must be defined in the following form: - -// https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form -[source,ebnf] --------------------------------------------------- -AGG_SEPARATOR = '>' ; -METRIC_SEPARATOR = '.' ; -AGG_NAME = ; -METRIC = ; -PATH = [ , ]* [ , ] ; --------------------------------------------------- - -[source,js] --------------------------------------------------- -POST /sales/_search?size=0 -{ - "aggs" : { - "prices" : { - "histogram" : { - "field" : "price", - "interval" : 50, - "order" : { "promoted_products>rating_stats.avg" : "desc" } <1> - }, - "aggs" : { - "promoted_products" : { - "filter" : { "term" : { "promoted" : true }}, - "aggs" : { - "rating_stats" : { "stats" : { "field" : "rating" }} - } - } - } - } - } -} --------------------------------------------------- -// CONSOLE -// TEST[setup:sales] - -The above will sort the buckets based on the avg rating among the promoted products - +By default the returned buckets are sorted by their `key` ascending, though the order behaviour can be controlled using +the `order` setting. Supports the same `order` functionality as the <>. ==== Offset diff --git a/docs/reference/aggregations/bucket/terms-aggregation.asciidoc b/docs/reference/aggregations/bucket/terms-aggregation.asciidoc index 0b028c1a940ce..90a5586d9e4fd 100644 --- a/docs/reference/aggregations/bucket/terms-aggregation.asciidoc +++ b/docs/reference/aggregations/bucket/terms-aggregation.asciidoc @@ -280,13 +280,14 @@ Ordering the buckets alphabetically by their terms in an ascending manner: "genres" : { "terms" : { "field" : "genre", - "order" : { "_term" : "asc" } + "order" : { "_key" : "asc" } } } } } -------------------------------------------------- +deprecated[6.0.0, Use `_key` instead of `_term` to order buckets by their term] Ordering the buckets by single value metrics sub-aggregation (identified by the aggregation name): diff --git a/docs/reference/migration/migrate_6_0/java.asciidoc b/docs/reference/migration/migrate_6_0/java.asciidoc index 5693d50852649..43feb15f84b50 100644 --- a/docs/reference/migration/migrate_6_0/java.asciidoc +++ b/docs/reference/migration/migrate_6_0/java.asciidoc @@ -26,4 +26,12 @@ When sending a request through the request builders e.g. client.prepareSearch(). be possible to call `addListener` against the returned `ListenableActionFuture`. With this change an `ActionFuture` is returned instead, which is consistent with what the `Client` methods return, hence it is not possible to associate the future with listeners. The `execute` method that accept a listener -as an argument can be used instead. \ No newline at end of file +as an argument can be used instead. + +==== `Terms.Order` and `Histogram.Order` classes replace by `BucketOrder` + +The `terms`, `histogram`, and `date_histogram` aggregation code has been refactored to use common +code for ordering buckets. The `BucketOrder` class must be used instead of `Terms.Order` and +`Histogram.Order`. The `static` methods in the `BucketOrder` class must be called instead of directly +accessing internal order instances, e.g. `BucketOrder.count(boolean)` and `BucketOrder.aggregation(String, boolean)`. +Use `BucketOrder.key(boolean)` to order the `terms` aggregation buckets by `_term`. diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/10_histogram.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/10_histogram.yaml index 63deebcd87053..4955dcfb4daa0 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/10_histogram.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/10_histogram.yaml @@ -10,6 +10,8 @@ setup: "properties": "number": "type" : "integer" + "date": + "type" : "date" - do: cluster.health: wait_for_status: green @@ -143,3 +145,64 @@ setup: - match: { aggregations.histo.buckets.3.key_as_string: "Value is 150.0" } - match: { aggregations.histo.buckets.3.doc_count: 1 } + +--- +"Deprecated _time order": + + - skip: + version: " - 5.99.99" + reason: _time order deprecated in 6.0, replaced by _key + features: "warnings" + + - do: + index: + index: test_1 + type: test + id: 1 + body: { "date" : "2016-01-01" } + + - do: + index: + index: test_1 + type: test + id: 2 + body: { "date" : "2016-01-02" } + + - do: + index: + index: test_1 + type: test + id: 3 + body: { "date" : "2016-02-01" } + + - do: + index: + index: test_1 + type: test + id: 4 + body: { "date" : "2016-03-01" } + + - do: + indices.refresh: {} + + - do: + search: + body: { "aggs" : { "histo" : { "date_histogram" : { "field" : "date", "interval" : "month", "order" : { "_time" : "desc" } } } } } + warnings: + - "Deprecated aggregation order key [_time] used, replaced by [_key]" + + - match: { hits.total: 4 } + + - length: { aggregations.histo.buckets: 3 } + + - match: { aggregations.histo.buckets.0.key_as_string: "2016-03-01T00:00:00.000Z" } + + - match: { aggregations.histo.buckets.0.doc_count: 1 } + + - match: { aggregations.histo.buckets.1.key_as_string: "2016-02-01T00:00:00.000Z" } + + - match: { aggregations.histo.buckets.1.doc_count: 1 } + + - match: { aggregations.histo.buckets.2.key_as_string: "2016-01-01T00:00:00.000Z" } + + - match: { aggregations.histo.buckets.2.doc_count: 2 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/20_terms.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/20_terms.yaml index c9ba94cf61521..9cc30bbcd1b45 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/20_terms.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/20_terms.yaml @@ -747,3 +747,57 @@ setup: - match: { aggregations.number_terms.buckets.2.key: 14.6 } - match: { aggregations.number_terms.buckets.2.doc_count: 1 } + +--- +"Deprecated _term order": + + - skip: + version: " - 5.99.99" + reason: _term order deprecated in 6.0, replaced by _key + features: "warnings" + + - do: + index: + index: test_1 + type: test + id: 1 + body: { "str": "abc" } + + - do: + index: + index: test_1 + type: test + id: 2 + body: { "str": "abc" } + + - do: + index: + index: test_1 + type: test + id: 3 + body: { "str": "bcd" } + + - do: + indices.refresh: {} + + - do: + search: + body: { "size" : 0, "aggs" : { "str_terms" : { "terms" : { "field" : "str", "order" : { "_term" : "desc" } } } } } + warnings: + - "Deprecated aggregation order key [_term] used, replaced by [_key]" + + - match: { hits.total: 3 } + + - length: { aggregations.str_terms.buckets: 2 } + + - match: { aggregations.str_terms.buckets.0.key: "bcd" } + + - is_false: aggregations.str_terms.buckets.0.key_as_string + + - match: { aggregations.str_terms.buckets.0.doc_count: 1 } + + - match: { aggregations.str_terms.buckets.1.key: "abc" } + + - is_false: aggregations.str_terms.buckets.1.key_as_string + + - match: { aggregations.str_terms.buckets.1.doc_count: 2 } diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractSerializingTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractSerializingTestCase.java index 392dcf1542a1c..178eed2dcef5a 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractSerializingTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractSerializingTestCase.java @@ -51,7 +51,7 @@ public void testFromXContent() throws IOException { } } - private void assertParsedInstance(XContentType xContentType, BytesReference instanceAsBytes, T expectedInstance) + protected void assertParsedInstance(XContentType xContentType, BytesReference instanceAsBytes, T expectedInstance) throws IOException { XContentParser parser = createParser(XContentFactory.xContent(xContentType), instanceAsBytes); @@ -61,7 +61,7 @@ private void assertParsedInstance(XContentType xContentType, BytesReference inst assertEquals(expectedInstance.hashCode(), newInstance.hashCode()); } - private T parseInstance(XContentParser parser) throws IOException { + protected T parseInstance(XContentParser parser) throws IOException { T parsedInstance = doParseInstance(parser); assertNull(parser.nextToken()); return parsedInstance; diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractWireSerializingTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractWireSerializingTestCase.java index bf65a7f4bdd4a..c38efca52037a 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractWireSerializingTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractWireSerializingTestCase.java @@ -100,7 +100,7 @@ protected T assertSerialization(T testInstance) throws IOException { return deserializedInstance; } - private T copyInstance(T instance) throws IOException { + protected T copyInstance(T instance) throws IOException { try (BytesStreamOutput output = new BytesStreamOutput()) { instance.writeTo(output); try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), @@ -112,9 +112,9 @@ private T copyInstance(T instance) throws IOException { /** * Get the {@link NamedWriteableRegistry} to use when de-serializing the object. - * + * * Override this method if you need to register {@link NamedWriteable}s for the test object to de-serialize. - * + * * By default this will return a {@link NamedWriteableRegistry} with no registered {@link NamedWriteable}s */ protected NamedWriteableRegistry getNamedWriteableRegistry() { diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index 9e6bb080b1604..cdab659bdc459 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -72,6 +72,7 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParser.Token; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.env.Environment; @@ -924,18 +925,39 @@ protected final XContentBuilder shuffleXContent(XContentBuilder builder, String. * recursive shuffling behavior can be made by passing in the names of fields which * internally should stay untouched. */ - protected static XContentBuilder shuffleXContent(XContentParser parser, boolean prettyPrint, String... exceptFieldNames) - throws IOException { - //we need a sorted map for reproducibility, as we are going to shuffle its keys and write XContent back - Map shuffledMap = shuffleMap((LinkedHashMap)parser.mapOrdered(), - new HashSet<>(Arrays.asList(exceptFieldNames))); + public XContentBuilder shuffleXContent(XContentParser parser, boolean prettyPrint, String... exceptFieldNames) throws IOException { XContentBuilder xContentBuilder = XContentFactory.contentBuilder(parser.contentType()); if (prettyPrint) { xContentBuilder.prettyPrint(); } + Token token = parser.currentToken() == null ? parser.nextToken() : parser.currentToken(); + if (token == Token.START_ARRAY) { + List shuffledList = shuffleList(parser.listOrderedMap(), new HashSet<>(Arrays.asList(exceptFieldNames))); + return xContentBuilder.value(shuffledList); + } + //we need a sorted map for reproducibility, as we are going to shuffle its keys and write XContent back + Map shuffledMap = shuffleMap((LinkedHashMap)parser.mapOrdered(), + new HashSet<>(Arrays.asList(exceptFieldNames))); return xContentBuilder.map(shuffledMap); } + // shuffle fields of objects in the list, but not the list itself + private static List shuffleList(List list, Set exceptFields) { + List targetList = new ArrayList<>(); + for(Object value : list) { + if (value instanceof Map) { + @SuppressWarnings("unchecked") + LinkedHashMap valueMap = (LinkedHashMap) value; + targetList.add(shuffleMap(valueMap, exceptFields)); + } else if(value instanceof List) { + targetList.add(shuffleList((List) value, exceptFields)); + } else { + targetList.add(value); + } + } + return targetList; + } + public static LinkedHashMap shuffleMap(LinkedHashMap map, Set exceptFields) { List keys = new ArrayList<>(map.keySet()); LinkedHashMap targetMap = new LinkedHashMap<>(); @@ -946,6 +968,8 @@ public static LinkedHashMap shuffleMap(LinkedHashMap valueMap = (LinkedHashMap) value; targetMap.put(key, shuffleMap(valueMap, exceptFields)); + } else if(value instanceof List && exceptFields.contains(key) == false) { + targetMap.put(key, shuffleList((List) value, exceptFields)); } else { targetMap.put(key, value); } From 657686cefb736999b2231b4caee318da14e9462a Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Thu, 11 May 2017 13:56:43 -0400 Subject: [PATCH 314/619] Fix runtime class cast exception in TRA If we fail to acquire the shard lock, need to retry and wait for the new cluster state, we were sending the wrong kind of request for the replica action. This commit fixes this issue. --- .../action/support/replication/TransportReplicationAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java b/core/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java index ae107e9515731..a8ee1677bbdeb 100644 --- a/core/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java +++ b/core/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java @@ -542,7 +542,7 @@ public void onNewClusterState(ClusterState state) { new TransportChannelResponseHandler<>(logger, channel, extraMessage, () -> TransportResponse.Empty.INSTANCE); transportService.sendRequest(clusterService.localNode(), transportReplicaAction, - new ConcreteShardRequest<>(request, targetAllocationID), + new ConcreteReplicaRequest<>(request, targetAllocationID, globalCheckpoint), handler); } From 11556155364e09fc601a12ef53021fd99fd03562 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Thu, 11 May 2017 18:27:05 +0200 Subject: [PATCH 315/619] Move DeleteByQuery and Reindex requests into core (#24578) This allows other plugins to use a client to call the functionality that is in the core modules without duplicating the logic. Plugins can now safely send the request and response classes via the client even if the requests are executed locally. All relevant classes are loaded by the core classloader such that plugins can share them. This is re-adds this commit that was revered in 952feb58e41bd0220784c44136b840ea4a740e26 --- .../resources/checkstyle_suppressions.xml | 1 - .../reindex}/AbstractBulkByScrollRequest.java | 4 ++-- .../AbstractBulkByScrollRequestBuilder.java | 2 +- .../AbstractBulkIndexByScrollRequest.java | 1 - ...stractBulkIndexByScrollRequestBuilder.java | 2 -- .../reindex}/BulkByScrollResponse.java | 15 ++++++------ .../reindex}/BulkByScrollTask.java | 2 +- .../reindex}/ClientScrollableHitSource.java | 2 +- .../index/reindex/DeleteByQueryAction.java | 2 -- .../reindex}/DeleteByQueryRequest.java | 4 ++-- .../reindex/DeleteByQueryRequestBuilder.java | 3 --- .../reindex}/ParentBulkByScrollTask.java | 4 ++-- .../index/reindex/ReindexRequest.java | 3 +-- .../index/reindex/ReindexRequestBuilder.java | 2 -- .../index/reindex}/RemoteInfo.java | 2 +- .../reindex}/ScrollableHitSource.java | 4 ++-- .../reindex}/SuccessfullyProcessed.java | 2 +- .../index/reindex/UpdateByQueryAction.java | 1 - .../index/reindex/UpdateByQueryRequest.java | 2 +- .../reindex/UpdateByQueryRequestBuilder.java | 1 - .../reindex}/WorkingBulkByScrollTask.java | 24 +++++++++---------- .../reindex}/package-info.java | 5 ++-- .../AbstractBulkByScrollRequestTestCase.java | 2 +- .../reindex}/BulkByScrollResponseTests.java | 11 ++++----- .../reindex}/BulkByScrollTaskStatusTests.java | 5 ++-- .../reindex}/BulkByScrollTaskTests.java | 2 +- .../reindex}/DeleteByQueryRequestTests.java | 2 +- .../reindex}/ParentBulkByScrollTaskTests.java | 2 +- .../index/reindex/ReindexRequestTests.java | 2 -- .../reindex/UpdateByQueryRequestTests.java | 1 - .../WorkingBulkByScrollTaskTests.java | 4 +--- .../AbstractAsyncBulkByScrollAction.java | 12 +++++----- .../AbstractBaseReindexRestHandler.java | 3 --- .../AbstractBulkByQueryRestHandler.java | 4 +--- .../reindex}/AsyncDeleteByQueryAction.java | 6 ++--- .../BulkByScrollParallelizationHelper.java | 12 +++++----- ...kIndexByScrollResponseContentListener.java | 3 +-- .../index/reindex/ReindexAction.java | 1 - .../index/reindex/ReindexPlugin.java | 1 - .../reindex/RestDeleteByQueryAction.java | 1 - .../index/reindex/RestReindexAction.java | 1 - .../reindex/TransportDeleteByQueryAction.java | 6 ----- .../index/reindex/TransportReindexAction.java | 9 +------ .../reindex/TransportRethrottleAction.java | 1 - .../reindex/TransportUpdateByQueryAction.java | 6 ----- .../reindex/remote/RemoteResponseParsers.java | 8 +++---- .../remote/RemoteScrollableHitSource.java | 2 +- ...yncBulkByScrollActionMetadataTestCase.java | 2 +- ...AsyncBulkByScrollActionScriptTestCase.java | 8 ++----- .../AsyncBulkByScrollActionTests.java | 8 +++---- ...ulkByScrollParallelizationHelperTests.java | 4 ++-- .../BulkIndexByScrollResponseMatcher.java | 5 ++-- .../BulkIndexByScrollResponseTests.java | 4 +--- .../index/reindex/CancelTests.java | 3 --- .../reindex/DeleteByQueryConcurrentTests.java | 1 - .../index/reindex/ReindexBasicTests.java | 1 - .../index/reindex/ReindexFailureTests.java | 1 - ...ReindexFromRemoteBuildRestClientTests.java | 1 - .../ReindexFromRemoteWhitelistTests.java | 1 - .../ReindexFromRemoteWithAuthTests.java | 1 - .../index/reindex/ReindexMetadataTests.java | 5 +--- .../reindex/ReindexParentChildTests.java | 1 - .../index/reindex/ReindexScriptTests.java | 1 - .../ReindexSourceTargetValidationTests.java | 1 - .../index/reindex/ReindexTestCase.java | 1 - .../index/reindex/ReindexVersioningTests.java | 1 - .../index/reindex/RestReindexActionTests.java | 1 - .../index/reindex/RethrottleTests.java | 3 --- .../index/reindex/RetryTests.java | 5 ---- .../index/reindex/RoundTripTests.java | 3 --- .../TransportRethrottleActionTests.java | 3 --- .../reindex/UpdateByQueryMetadataTests.java | 5 +--- .../UpdateByQueryWhileModifyingTests.java | 1 - .../reindex/UpdateByQueryWithScriptTests.java | 1 - .../index/reindex/remote/RemoteInfoTests.java | 1 + .../RemoteScrollableHitSourceTests.java | 2 +- ...stractAsyncBulkByScrollActionTestCase.java | 2 +- 77 files changed, 90 insertions(+), 182 deletions(-) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/AbstractBulkByScrollRequest.java (99%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/AbstractBulkByScrollRequestBuilder.java (99%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java (97%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java (92%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/BulkByScrollResponse.java (93%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/BulkByScrollTask.java (99%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/ClientScrollableHitSource.java (99%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java (91%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/DeleteByQueryRequest.java (97%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java (90%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/ParentBulkByScrollTask.java (97%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java (97%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java (95%) rename {modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote => core/src/main/java/org/elasticsearch/index/reindex}/RemoteInfo.java (99%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/ScrollableHitSource.java (99%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/SuccessfullyProcessed.java (96%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java (95%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java (97%) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java (96%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/WorkingBulkByScrollTask.java (95%) rename core/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/package-info.java (87%) rename {test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll => core/src/test/java/org/elasticsearch/index/reindex}/AbstractBulkByScrollRequestTestCase.java (98%) rename core/src/test/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/BulkByScrollResponseTests.java (90%) rename core/src/test/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/BulkByScrollTaskStatusTests.java (97%) rename core/src/test/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/BulkByScrollTaskTests.java (99%) rename core/src/test/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/DeleteByQueryRequestTests.java (99%) rename core/src/test/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/ParentBulkByScrollTaskTests.java (99%) rename {modules/reindex => core}/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java (97%) rename {modules/reindex => core}/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java (97%) rename core/src/test/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/WorkingBulkByScrollTaskTests.java (98%) rename {core/src/main/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/main/java/org/elasticsearch/index/reindex}/AbstractAsyncBulkByScrollAction.java (98%) rename {core/src/main/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/main/java/org/elasticsearch/index/reindex}/AsyncDeleteByQueryAction.java (91%) rename {core/src/main/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/main/java/org/elasticsearch/index/reindex}/BulkByScrollParallelizationHelper.java (88%) rename {test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/test/java/org/elasticsearch/index/reindex}/AbstractAsyncBulkByScrollActionMetadataTestCase.java (96%) rename {core/src/test/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/test/java/org/elasticsearch/index/reindex}/AsyncBulkByScrollActionTests.java (99%) rename {core/src/test/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/test/java/org/elasticsearch/index/reindex}/BulkByScrollParallelizationHelperTests.java (94%) rename {test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll => modules/reindex/src/test/java/org/elasticsearch/index/reindex}/BulkIndexByScrollResponseMatcher.java (98%) rename test/framework/src/main/java/org/elasticsearch/{action/bulk/byscroll => index/reindex}/AbstractAsyncBulkByScrollActionTestCase.java (97%) diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 3e0028032134a..9a550740fde36 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -127,7 +127,6 @@ - diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequest.java b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java similarity index 99% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequest.java rename to core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java index 445808750113a..a582248af11ae 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequest.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.Version; import org.elasticsearch.action.ActionRequest; @@ -355,7 +355,7 @@ public int getSlices() { /** * Build a new request for a slice of the parent request. */ - protected abstract Self forSlice(TaskId slicingTask, SearchRequest slice); + public abstract Self forSlice(TaskId slicingTask, SearchRequest slice); /** * Setup a clone of this request with the information needed to process a slice of it. diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestBuilder.java b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestBuilder.java similarity index 99% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestBuilder.java rename to core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestBuilder.java index 4131175f9df09..de3f22f0943a8 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestBuilder.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; import org.elasticsearch.action.ActionRequestBuilder; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java similarity index 97% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java rename to core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java index 6aad6e08a49f0..62c2635b301c5 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequest.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java similarity index 92% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java rename to core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java index 369510083d1da..ca1c980995bba 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkIndexByScrollRequestBuilder.java @@ -20,8 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.script.Script; diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponse.java b/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollResponse.java similarity index 93% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponse.java rename to core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollResponse.java index 20f2cf2ed06b4..400baf7b9e2c6 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponse.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollResponse.java @@ -17,11 +17,10 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -45,14 +44,14 @@ public class BulkByScrollResponse extends ActionResponse implements ToXContent { private TimeValue took; private BulkByScrollTask.Status status; private List bulkFailures; - private List searchFailures; + private List searchFailures; private boolean timedOut; public BulkByScrollResponse() { } public BulkByScrollResponse(TimeValue took, BulkByScrollTask.Status status, List bulkFailures, - List searchFailures, boolean timedOut) { + List searchFailures, boolean timedOut) { this.took = took; this.status = requireNonNull(status, "Null status not supported"); this.bulkFailures = bulkFailures; @@ -139,7 +138,7 @@ public List getBulkFailures() { /** * All search failures. */ - public List getSearchFailures() { + public List getSearchFailures() { return searchFailures; } @@ -166,7 +165,7 @@ public void readFrom(StreamInput in) throws IOException { took = new TimeValue(in); status = new BulkByScrollTask.Status(in); bulkFailures = in.readList(Failure::new); - searchFailures = in.readList(SearchFailure::new); + searchFailures = in.readList(ScrollableHitSource.SearchFailure::new); timedOut = in.readBoolean(); } @@ -181,7 +180,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws failure.toXContent(builder, params); builder.endObject(); } - for (SearchFailure failure: searchFailures) { + for (ScrollableHitSource.SearchFailure failure: searchFailures) { failure.toXContent(builder, params); } builder.endArray(); @@ -199,4 +198,4 @@ public String toString() { builder.append(",search_failures=").append(getSearchFailures().subList(0, min(3, getSearchFailures().size()))); return builder.append(']').toString(); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTask.java b/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollTask.java similarity index 99% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTask.java rename to core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollTask.java index 7c9124057b3c0..18c6dac92064a 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTask.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollTask.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ClientScrollableHitSource.java b/core/src/main/java/org/elasticsearch/index/reindex/ClientScrollableHitSource.java similarity index 99% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/ClientScrollableHitSource.java rename to core/src/main/java/org/elasticsearch/index/reindex/ClientScrollableHitSource.java index 3bacc187ebb6d..2f6775a1eae08 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ClientScrollableHitSource.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/ClientScrollableHitSource.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java b/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java similarity index 91% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java rename to core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java index b55fe33340e50..c1abb16ca3977 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryAction.java @@ -20,8 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; import org.elasticsearch.client.ElasticsearchClient; public class DeleteByQueryAction extends Action { diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequest.java b/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequest.java similarity index 97% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequest.java rename to core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequest.java index 2644d0d94967f..ad70748f3e431 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequest.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.IndicesRequest; @@ -81,7 +81,7 @@ public ActionRequestValidationException validate() { } @Override - protected DeleteByQueryRequest forSlice(TaskId slicingTask, SearchRequest slice) { + public DeleteByQueryRequest forSlice(TaskId slicingTask, SearchRequest slice) { return doForSlice(new DeleteByQueryRequest(slice, false), slicingTask); } diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java b/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java similarity index 90% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java rename to core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java index 3efbc1a562cca..e94d1308a74be 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/DeleteByQueryRequestBuilder.java @@ -20,9 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTask.java b/core/src/main/java/org/elasticsearch/index/reindex/ParentBulkByScrollTask.java similarity index 97% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTask.java rename to core/src/main/java/org/elasticsearch/index/reindex/ParentBulkByScrollTask.java index a37dc61897c0c..bea9bc203653d 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTask.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/ParentBulkByScrollTask.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.collect.Tuple; @@ -104,7 +104,7 @@ public void onSliceResponse(ActionListener listener, int s /** * Record a failure from a slice and respond to the listener if the request is finished. */ - void onSliceFailure(ActionListener listener, int sliceId, Exception e) { + public void onSliceFailure(ActionListener listener, int sliceId, Exception e) { results.setOnce(sliceId, new Result(sliceId, e)); recordSliceCompletionAndRespondIfAllDone(listener); // TODO cancel when a slice fails? diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java b/core/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java similarity index 97% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java rename to core/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java index 2fa513f9c57a4..76944c7b80438 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/ReindexRequest.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.lucene.uid.Versions; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.tasks.TaskId; import java.io.IOException; @@ -128,7 +127,7 @@ public RemoteInfo getRemoteInfo() { } @Override - protected ReindexRequest forSlice(TaskId slicingTask, SearchRequest slice) { + public ReindexRequest forSlice(TaskId slicingTask, SearchRequest slice) { ReindexRequest sliced = doForSlice(new ReindexRequest(slice, destination, false), slicingTask); sliced.setRemoteInfo(remoteInfo); return sliced; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java b/core/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java similarity index 95% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java rename to core/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java index d7de51c588ec3..68bd3f4661828 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/ReindexRequestBuilder.java @@ -20,13 +20,11 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexAction; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; -import org.elasticsearch.index.reindex.remote.RemoteInfo; public class ReindexRequestBuilder extends AbstractBulkIndexByScrollRequestBuilder { diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteInfo.java b/core/src/main/java/org/elasticsearch/index/reindex/RemoteInfo.java similarity index 99% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteInfo.java rename to core/src/main/java/org/elasticsearch/index/reindex/RemoteInfo.java index 5fad275cde47a..878a9c61e4c7c 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteInfo.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/RemoteInfo.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.reindex.remote; +package org.elasticsearch.index.reindex; import org.elasticsearch.Version; import org.elasticsearch.common.Nullable; diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ScrollableHitSource.java b/core/src/main/java/org/elasticsearch/index/reindex/ScrollableHitSource.java similarity index 99% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/ScrollableHitSource.java rename to core/src/main/java/org/elasticsearch/index/reindex/ScrollableHitSource.java index 6426bad592f3e..3d1eb582db879 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/ScrollableHitSource.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/ScrollableHitSource.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.apache.logging.log4j.Logger; import org.elasticsearch.ElasticsearchException; @@ -111,7 +111,7 @@ public final void close(Runnable onCompletion) { /** * Set the id of the last scroll. Used for debugging. */ - final void setScroll(String scrollId) { + public final void setScroll(String scrollId) { this.scrollId.set(scrollId); } diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/SuccessfullyProcessed.java b/core/src/main/java/org/elasticsearch/index/reindex/SuccessfullyProcessed.java similarity index 96% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/SuccessfullyProcessed.java rename to core/src/main/java/org/elasticsearch/index/reindex/SuccessfullyProcessed.java index a0176e3520254..6547984900e4b 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/SuccessfullyProcessed.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/SuccessfullyProcessed.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; /** * Implemented by {@link BulkByScrollTask} and {@link BulkByScrollTask.Status} to consistently implement diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java b/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java similarity index 95% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java rename to core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java index cb716e8224871..1058f7f13078a 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryAction.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.client.ElasticsearchClient; public class UpdateByQueryAction extends diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java b/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java similarity index 97% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java rename to core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java index 3e7fac9d4546c..ad0123d76cedf 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequest.java @@ -71,7 +71,7 @@ protected UpdateByQueryRequest self() { } @Override - protected UpdateByQueryRequest forSlice(TaskId slicingTask, SearchRequest slice) { + public UpdateByQueryRequest forSlice(TaskId slicingTask, SearchRequest slice) { UpdateByQueryRequest request = doForSlice(new UpdateByQueryRequest(slice, false), slicingTask); request.setPipeline(pipeline); return request; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java b/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java similarity index 96% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java rename to core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java index 143d66ae6fbcf..06e0426864193 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/UpdateByQueryRequestBuilder.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTask.java b/core/src/main/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTask.java similarity index 95% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTask.java rename to core/src/main/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTask.java index 28f08417abbd3..acb1c0e5547ca 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTask.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTask.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.apache.logging.log4j.Logger; import org.elasticsearch.common.logging.ESLoggerFactory; @@ -115,15 +115,15 @@ TimeValue throttledUntil() { return timeValueNanos(max(0, delayed.future.getDelay(TimeUnit.NANOSECONDS))); } - void setTotal(long totalHits) { + public void setTotal(long totalHits) { total.set(totalHits); } - void countBatch() { + public void countBatch() { batch.incrementAndGet(); } - void countNoop() { + public void countNoop() { noops.incrementAndGet(); } @@ -132,7 +132,7 @@ public long getCreated() { return created.get(); } - void countCreated() { + public void countCreated() { created.incrementAndGet(); } @@ -141,7 +141,7 @@ public long getUpdated() { return updated.get(); } - void countUpdated() { + public void countUpdated() { updated.incrementAndGet(); } @@ -150,15 +150,15 @@ public long getDeleted() { return deleted.get(); } - void countDeleted() { + public void countDeleted() { deleted.incrementAndGet(); } - void countVersionConflict() { + public void countVersionConflict() { versionConflicts.incrementAndGet(); } - void countBulkRetry() { + public void countBulkRetry() { bulkRetries.incrementAndGet(); } @@ -174,8 +174,8 @@ float getRequestsPerSecond() { * Schedule prepareBulkRequestRunnable to run after some delay. This is where throttling plugs into reindexing so the request can be * rescheduled over and over again. */ - void delayPrepareBulkRequest(ThreadPool threadPool, TimeValue lastBatchStartTime, int lastBatchSize, - AbstractRunnable prepareBulkRequestRunnable) { + public void delayPrepareBulkRequest(ThreadPool threadPool, TimeValue lastBatchStartTime, int lastBatchSize, + AbstractRunnable prepareBulkRequestRunnable) { // Synchronize so we are less likely to schedule the same request twice. synchronized (delayedPrepareBulkRequestReference) { TimeValue delay = throttleWaitTime(lastBatchStartTime, timeValueNanos(System.nanoTime()), lastBatchSize); @@ -184,7 +184,7 @@ void delayPrepareBulkRequest(ThreadPool threadPool, TimeValue lastBatchStartTime } } - TimeValue throttleWaitTime(TimeValue lastBatchStartTime, TimeValue now, int lastBatchSize) { + public TimeValue throttleWaitTime(TimeValue lastBatchStartTime, TimeValue now, int lastBatchSize) { long earliestNextBatchStartTime = now.nanos() + (long) perfectlyThrottledBatchTime(lastBatchSize); return timeValueNanos(max(0, earliestNextBatchStartTime - System.nanoTime())); } diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/package-info.java b/core/src/main/java/org/elasticsearch/index/reindex/package-info.java similarity index 87% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/package-info.java rename to core/src/main/java/org/elasticsearch/index/reindex/package-info.java index 3a31ea2f6541c..00cb5106770d1 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/package-info.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/package-info.java @@ -18,6 +18,7 @@ */ /** - * Infrastructure for actions that modify documents based on the results of a scrolling query. + * Infrastructure for actions that modify documents based on the results of a scrolling query + * like reindex, update by query or delete by query. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; diff --git a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestTestCase.java b/core/src/test/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestTestCase.java similarity index 98% rename from test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestTestCase.java rename to core/src/test/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestTestCase.java index c4ac9587e82f7..debeab52d979d 100644 --- a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractBulkByScrollRequestTestCase.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestTestCase.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.ActiveShardCount; diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponseTests.java b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollResponseTests.java similarity index 90% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponseTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollResponseTests.java index 1d2146c1515d1..a288328391a9d 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollResponseTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollResponseTests.java @@ -17,12 +17,11 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.test.ESTestCase; @@ -56,7 +55,7 @@ private List randomIndexingFailures() { randomSimpleString(random()), new IllegalArgumentException("test"))); } - private List randomSearchFailures() { + private List randomSearchFailures() { if (randomBoolean()) { return emptyList(); } @@ -68,7 +67,7 @@ private List randomSearchFailures() { shardId = randomInt(); nodeId = usually() ? randomAlphaOfLength(5) : null; } - return singletonList(new SearchFailure(new ElasticsearchException("foo"), index, shardId, nodeId)); + return singletonList(new ScrollableHitSource.SearchFailure(new ElasticsearchException("foo"), index, shardId, nodeId)); } private void assertResponseEquals(BulkByScrollResponse expected, BulkByScrollResponse actual) { @@ -86,8 +85,8 @@ private void assertResponseEquals(BulkByScrollResponse expected, BulkByScrollRes } assertEquals(expected.getSearchFailures().size(), actual.getSearchFailures().size()); for (int i = 0; i < expected.getSearchFailures().size(); i++) { - SearchFailure expectedFailure = expected.getSearchFailures().get(i); - SearchFailure actualFailure = actual.getSearchFailures().get(i); + ScrollableHitSource.SearchFailure expectedFailure = expected.getSearchFailures().get(i); + ScrollableHitSource.SearchFailure actualFailure = actual.getSearchFailures().get(i); assertEquals(expectedFailure.getIndex(), actualFailure.getIndex()); assertEquals(expectedFailure.getShardId(), actualFailure.getShardId()); assertEquals(expectedFailure.getNodeId(), actualFailure.getNodeId()); diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskStatusTests.java b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java similarity index 97% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskStatusTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java index 503fe1db7cd32..982198c8fee47 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskStatusTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.ElasticsearchException; @@ -26,6 +26,7 @@ import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.test.ESTestCase; +import org.hamcrest.Matchers; import java.io.IOException; import java.util.List; @@ -75,7 +76,7 @@ public static void assertTaskStatusEquals(Version version, BulkByScrollTask.Stat assertEquals(expected.getReasonCancelled(), actual.getReasonCancelled()); assertEquals(expected.getThrottledUntil(), actual.getThrottledUntil()); if (version.onOrAfter(Version.V_5_1_1_UNRELEASED)) { - assertThat(actual.getSliceStatuses(), hasSize(expected.getSliceStatuses().size())); + assertThat(actual.getSliceStatuses(), Matchers.hasSize(expected.getSliceStatuses().size())); for (int i = 0; i < expected.getSliceStatuses().size(); i++) { BulkByScrollTask.StatusOrException sliceStatus = expected.getSliceStatuses().get(i); if (sliceStatus == null) { diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskTests.java b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskTests.java index ff0eae5552042..f4d4ea790bc50 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollTaskTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.ToXContent; diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequestTests.java b/core/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryRequestTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequestTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryRequestTests.java index f5c00f63de9c1..8c84c8f3f5680 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/DeleteByQueryRequestTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryRequestTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.search.SearchRequest; diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTaskTests.java b/core/src/test/java/org/elasticsearch/index/reindex/ParentBulkByScrollTaskTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTaskTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/ParentBulkByScrollTaskTests.java index 715fcaaad54c8..6e2d44abed53a 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/ParentBulkByScrollTaskTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/ParentBulkByScrollTaskTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionListener; import org.elasticsearch.test.ESTestCase; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java b/core/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java similarity index 97% rename from modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java index d1bb6f6096c39..32b01237375b2 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/ReindexRequestTests.java @@ -20,11 +20,9 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionRequestValidationException; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestTestCase; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.search.slice.SliceBuilder; import static java.util.Collections.emptyMap; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java b/core/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java similarity index 97% rename from modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java index 700f45b42c515..b30968cf056b5 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryRequestTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestTestCase; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.IndicesOptions; diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTaskTests.java b/core/src/test/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTaskTests.java similarity index 98% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTaskTests.java rename to core/src/test/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTaskTests.java index 7356d626c1023..5d594d080b8b0 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/WorkingBulkByScrollTaskTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/WorkingBulkByScrollTaskTests.java @@ -17,10 +17,8 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.WorkingBulkByScrollTask; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.tasks.TaskId; diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollAction.java similarity index 98% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollAction.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollAction.java index 3ba07ea5538f5..2a23823c85843 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollAction.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; @@ -31,7 +31,7 @@ import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.bulk.Retry; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; +import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.client.ParentTaskAssigningClient; @@ -77,7 +77,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; import static org.elasticsearch.action.bulk.BackoffPolicy.exponentialBackoff; -import static org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest.SIZE_ALL_MATCHES; +import static org.elasticsearch.index.reindex.AbstractBulkByScrollRequest.SIZE_ALL_MATCHES; import static org.elasticsearch.common.unit.TimeValue.timeValueNanos; import static org.elasticsearch.rest.RestStatus.CONFLICT; import static org.elasticsearch.search.sort.SortBuilders.fieldSort; @@ -116,8 +116,8 @@ public abstract class AbstractAsyncBulkByScrollAction, ScrollableHitSource.Hit, RequestWrapper> scriptApplier; public AbstractAsyncBulkByScrollAction(WorkingBulkByScrollTask task, Logger logger, ParentTaskAssigningClient client, - ThreadPool threadPool, Request mainRequest, ScriptService scriptService, ClusterState clusterState, - ActionListener listener) { + ThreadPool threadPool, Request mainRequest, ScriptService scriptService, + ClusterState clusterState, ActionListener listener) { this(task, logger, client, threadPool, mainRequest, scriptService, clusterState, listener, client.settings()); } @@ -741,7 +741,7 @@ public DeleteRequest self() { /** * Wraps a {@link DeleteRequest} in a {@link RequestWrapper} */ - static RequestWrapper wrap(DeleteRequest request) { + public static RequestWrapper wrap(DeleteRequest request) { return new DeleteRequestWrapper(request); } diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java index d70b3c9c4ceb0..64b02c4be8150 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java @@ -21,9 +21,6 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.GenericAction; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.action.support.ActiveShardCount; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.settings.Settings; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByQueryRestHandler.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByQueryRestHandler.java index 480ca80e2ee11..32a252ccc4b19 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByQueryRestHandler.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByQueryRestHandler.java @@ -21,8 +21,6 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.action.GenericAction; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -35,7 +33,7 @@ import java.util.Map; import java.util.function.Consumer; -import static org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest.SIZE_ALL_MATCHES; +import static org.elasticsearch.index.reindex.AbstractBulkByScrollRequest.SIZE_ALL_MATCHES; /** * Rest handler for reindex actions that accepts a search request like Update-By-Query or Delete-By-Query diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AsyncDeleteByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AsyncDeleteByQueryAction.java similarity index 91% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/AsyncDeleteByQueryAction.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/AsyncDeleteByQueryAction.java index cdcfb754fb687..2608f5715ba3e 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/AsyncDeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AsyncDeleteByQueryAction.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; @@ -32,8 +32,8 @@ */ public class AsyncDeleteByQueryAction extends AbstractAsyncBulkByScrollAction { public AsyncDeleteByQueryAction(WorkingBulkByScrollTask task, Logger logger, ParentTaskAssigningClient client, - ThreadPool threadPool, DeleteByQueryRequest request, ScriptService scriptService, ClusterState clusterState, - ActionListener listener) { + ThreadPool threadPool, DeleteByQueryRequest request, ScriptService scriptService, + ClusterState clusterState, ActionListener listener) { super(task, logger, client, threadPool, request, scriptService, clusterState, listener); } diff --git a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelper.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelper.java similarity index 88% rename from core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelper.java rename to modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelper.java index f2bd62c233501..48f1030645450 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelper.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelper.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; import org.elasticsearch.action.ActionListener; @@ -32,13 +32,13 @@ /** * Helps parallelize reindex requests using sliced scrolls. */ -public class BulkByScrollParallelizationHelper { +class BulkByScrollParallelizationHelper { private BulkByScrollParallelizationHelper() {} - public static < - Request extends AbstractBulkByScrollRequest - > void startSlices(Client client, TaskManager taskManager, Action action, - String localNodeId, ParentBulkByScrollTask task, Request request, ActionListener listener) { + public static > void startSlices(Client client, TaskManager taskManager, + Action action, + String localNodeId, ParentBulkByScrollTask task, Request request, + ActionListener listener) { TaskId parentTaskId = new TaskId(localNodeId, task.getId()); for (final SearchRequest slice : sliceIntoSubRequests(request.getSearchRequest(), UidFieldMapper.NAME, request.getSlices())) { // TODO move the request to the correct node. maybe here or somehow do it as part of startup for reindex in general.... diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseContentListener.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseContentListener.java index a8d321a9fab8d..8e5dff170d481 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseContentListener.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseContentListener.java @@ -21,8 +21,7 @@ import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; +import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.rest.BytesRestResponse; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java index 2c84cfc86be6e..1c53a925f0d71 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.Action; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.client.ElasticsearchClient; public class ReindexAction extends Action { diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java index fb203ee5c6dd8..d601f5c06e728 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java @@ -21,7 +21,6 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestDeleteByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestDeleteByQueryAction.java index 0e11d64a405ca..f906ef7660da8 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestDeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestDeleteByQueryAction.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.settings.Settings; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java index 57d3213da9220..6c16c31efb153 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java @@ -36,7 +36,6 @@ import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.index.VersionType; import org.elasticsearch.index.query.QueryParseContext; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.script.Script; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java index a17b2b81f910f..99e1a9f166dd8 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java @@ -20,12 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.bulk.byscroll.AsyncDeleteByQueryAction; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollParallelizationHelper; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; -import org.elasticsearch.action.bulk.byscroll.ParentBulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.WorkingBulkByScrollTask; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.client.Client; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportReindexAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportReindexAction.java index b232c50c2b2a8..737d885443a1a 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportReindexAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportReindexAction.java @@ -37,13 +37,7 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.bulk.BackoffPolicy; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollParallelizationHelper; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.ParentBulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; -import org.elasticsearch.action.bulk.byscroll.WorkingBulkByScrollTask; +import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.ActionFilters; @@ -68,7 +62,6 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.VersionType; import org.elasticsearch.index.mapper.VersionFieldMapper; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.index.reindex.remote.RemoteScrollableHitSource; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptService; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportRethrottleAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportRethrottleAction.java index 88329f5cb17b4..0901e5ade31a5 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportRethrottleAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportRethrottleAction.java @@ -24,7 +24,6 @@ import org.elasticsearch.action.FailedNodeException; import org.elasticsearch.action.TaskOperationFailure; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.tasks.TransportTasksAction; import org.elasticsearch.client.Client; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java index 12d8696319fb1..8924c7038c99e 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java @@ -21,12 +21,6 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.ParentBulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollParallelizationHelper; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource; -import org.elasticsearch.action.bulk.byscroll.WorkingBulkByScrollTask; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteResponseParsers.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteResponseParsers.java index e9807bdfa5b81..d9a897026d293 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteResponseParsers.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteResponseParsers.java @@ -20,10 +20,10 @@ package org.elasticsearch.index.reindex.remote; import org.elasticsearch.Version; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.BasicHit; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Hit; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Response; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; +import org.elasticsearch.index.reindex.ScrollableHitSource.BasicHit; +import org.elasticsearch.index.reindex.ScrollableHitSource.Hit; +import org.elasticsearch.index.reindex.ScrollableHitSource.Response; +import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.collect.Tuple; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java index f3caeb004c474..85173b7d89962 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSource.java @@ -30,7 +30,7 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.Version; import org.elasticsearch.action.bulk.BackoffPolicy; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource; +import org.elasticsearch.index.reindex.ScrollableHitSource; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.client.ResponseException; import org.elasticsearch.client.ResponseListener; diff --git a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionMetadataTestCase.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionMetadataTestCase.java similarity index 96% rename from test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionMetadataTestCase.java rename to modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionMetadataTestCase.java index b68797381d176..34da9f56b482c 100644 --- a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionMetadataTestCase.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionMetadataTestCase.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; public abstract class AbstractAsyncBulkByScrollActionMetadataTestCase< Request extends AbstractBulkByScrollRequest, diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java index fd41a6d25f384..6ddf6daa8806c 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java @@ -20,12 +20,8 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.ActionRequest; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction.OpType; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction.RequestWrapper; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollActionTestCase; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource; +import org.elasticsearch.index.reindex.AbstractAsyncBulkByScrollAction.OpType; +import org.elasticsearch.index.reindex.AbstractAsyncBulkByScrollAction.RequestWrapper; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.script.CompiledScript; diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/AsyncBulkByScrollActionTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/AsyncBulkByScrollActionTests.java rename to modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java index fa42573e439ee..5c437da3464ee 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/AsyncBulkByScrollActionTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ExceptionsHelper; @@ -36,8 +36,8 @@ import org.elasticsearch.action.bulk.BulkItemResponse.Failure; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Hit; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; +import org.elasticsearch.index.reindex.ScrollableHitSource.Hit; +import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.index.IndexRequest; @@ -696,7 +696,7 @@ private static class DummyAbstractBulkByScrollRequest extends AbstractBulkByScro } @Override - protected DummyAbstractBulkByScrollRequest forSlice(TaskId slicingTask, SearchRequest slice) { + public DummyAbstractBulkByScrollRequest forSlice(TaskId slicingTask, SearchRequest slice) { throw new UnsupportedOperationException(); } diff --git a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelperTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelperTests.java similarity index 94% rename from core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelperTests.java rename to modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelperTests.java index 498c6bf5286f9..a64415d08b1b1 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/byscroll/BulkByScrollParallelizationHelperTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkByScrollParallelizationHelperTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.index.mapper.IdFieldMapper; @@ -27,7 +27,7 @@ import java.io.IOException; import static java.util.Collections.emptyList; -import static org.elasticsearch.action.bulk.byscroll.BulkByScrollParallelizationHelper.sliceIntoSubRequests; +import static org.elasticsearch.index.reindex.BulkByScrollParallelizationHelper.sliceIntoSubRequests; import static org.elasticsearch.search.RandomSearchRequestGenerator.randomSearchRequest; import static org.elasticsearch.search.RandomSearchRequestGenerator.randomSearchSourceBuilder; diff --git a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkIndexByScrollResponseMatcher.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseMatcher.java similarity index 98% rename from test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkIndexByScrollResponseMatcher.java rename to modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseMatcher.java index 2902e02a30c8a..cb2ff1a7ae24a 100644 --- a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/BulkIndexByScrollResponseMatcher.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseMatcher.java @@ -17,9 +17,8 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; @@ -155,4 +154,4 @@ public void describeTo(Description description) { description.appendText(" and reason cancelled matches ").appendDescriptionOf(reasonCancelledMatcher); description.appendText(" and slices matches ").appendDescriptionOf(slicesMatcher); } -} \ No newline at end of file +} diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseTests.java index 791277ad27097..6809a02585e1b 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/BulkIndexByScrollResponseTests.java @@ -20,9 +20,7 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.bulk.BulkItemResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.SearchFailure; +import org.elasticsearch.index.reindex.ScrollableHitSource.SearchFailure; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.test.ESTestCase; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java index 3aa35fb6e19de..a92ceedb0f365 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/CancelTests.java @@ -22,9 +22,6 @@ import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.action.ingest.DeletePipelineRequest; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryConcurrentTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryConcurrentTests.java index b80360f12608f..8954f1c35c179 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryConcurrentTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryConcurrentTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.index.query.MatchQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java index c26984e09b649..4f7753fca9abd 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexBasicTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import java.util.ArrayList; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java index 96ed13f5b29e6..746b6adc70e73 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.test.junit.annotations.TestLogging; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java index 7f8a87c2dad9e..d7d9cfe051b8c 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java @@ -21,7 +21,6 @@ import org.elasticsearch.client.RestClient; import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.test.ESTestCase; import java.util.ArrayList; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWhitelistTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWhitelistTests.java index 1a25e4e5e9c45..128cd4043e283 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWhitelistTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWhitelistTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.test.ESTestCase; import java.net.UnknownHostException; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java index 6b9aaee8665d4..c4b5c26e5c4ef 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java @@ -35,7 +35,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.util.concurrent.ThreadContext; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.rest.RestStatus; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java index 490480e1ac584..4611f9dcbcddb 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java @@ -19,10 +19,7 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollActionMetadataTestCase; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Hit; +import org.elasticsearch.index.reindex.ScrollableHitSource.Hit; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.settings.Settings; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java index 17769eeb4ea8b..d0eb1dcd75ef2 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilder; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java index 8ef99bd18d6ce..4e2834a771a94 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.lucene.uid.Versions; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexSourceTargetValidationTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexSourceTargetValidationTests.java index f6d3217575d7d..28b9febe1c289 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexSourceTargetValidationTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexSourceTargetValidationTests.java @@ -33,7 +33,6 @@ import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.test.ESTestCase; import static java.util.Collections.emptyMap; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexTestCase.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexTestCase.java index e7dc7bf3a7732..fcf80ea283cf4 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexTestCase.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexTestCase.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkIndexByScrollResponseMatcher; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexVersioningTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexVersioningTests.java index b9b99ce163841..472dec4675f99 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexVersioningTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexVersioningTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.get.GetResponse; import static org.elasticsearch.action.DocWriteRequest.OpType.CREATE; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RestReindexActionTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RestReindexActionTests.java index b190aee3619a8..ffb609843f3b4 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RestReindexActionTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RestReindexActionTests.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.rest.RestController; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.rest.FakeRestRequest; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java index f40afce9a4e39..228bae4ed4a73 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RethrottleTests.java @@ -22,9 +22,6 @@ import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.elasticsearch.action.admin.cluster.node.tasks.list.TaskGroup; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.tasks.TaskId; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java index 86fbd47fd9665..9b19c572c0b3e 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java @@ -26,17 +26,12 @@ import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.bulk.Retry; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequestBuilder; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.BulkIndexByScrollResponseMatcher; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.threadpool.ThreadPool; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java index 5793db023f086..40377fe96462d 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java @@ -20,8 +20,6 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.Version; -import org.elasticsearch.action.bulk.byscroll.AbstractBulkByScrollRequest; -import org.elasticsearch.action.bulk.byscroll.DeleteByQueryRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.bytes.BytesArray; @@ -31,7 +29,6 @@ import org.elasticsearch.common.io.stream.Streamable; import org.elasticsearch.common.lucene.uid.Versions; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.index.reindex.remote.RemoteInfo; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; import org.elasticsearch.tasks.TaskId; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java index b5572c1f34f30..222aedd2e9eeb 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java @@ -23,9 +23,6 @@ import org.elasticsearch.action.FailedNodeException; import org.elasticsearch.action.TaskOperationFailure; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollTask; -import org.elasticsearch.action.bulk.byscroll.ParentBulkByScrollTask; import org.elasticsearch.client.Client; import org.elasticsearch.tasks.TaskId; import org.elasticsearch.tasks.TaskInfo; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java index 174fd67153717..b688ce019e3df 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java @@ -19,10 +19,7 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollAction; -import org.elasticsearch.action.bulk.byscroll.AbstractAsyncBulkByScrollActionMetadataTestCase; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Hit; +import org.elasticsearch.index.reindex.ScrollableHitSource.Hit; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.settings.Settings; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWhileModifyingTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWhileModifyingTests.java index cb027d156e661..987830ddd3bc9 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWhileModifyingTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWhileModifyingTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.index.engine.VersionConflictEngineException; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java index 3d478094e8bfe..9f43ea2d17067 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import org.elasticsearch.action.bulk.byscroll.BulkByScrollResponse; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.script.ScriptService; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteInfoTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteInfoTests.java index fa1f46aa383a3..d6ab599b43c2d 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteInfoTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteInfoTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.reindex.remote; +import org.elasticsearch.index.reindex.RemoteInfo; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.test.ESTestCase; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSourceTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSourceTests.java index f63b05e96beb7..f67a5b627fb4c 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSourceTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteScrollableHitSourceTests.java @@ -40,7 +40,7 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.Version; import org.elasticsearch.action.bulk.BackoffPolicy; -import org.elasticsearch.action.bulk.byscroll.ScrollableHitSource.Response; +import org.elasticsearch.index.reindex.ScrollableHitSource.Response; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.client.HeapBufferedAsyncResponseConsumer; import org.elasticsearch.client.RestClient; diff --git a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionTestCase.java similarity index 97% rename from test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionTestCase.java rename to test/framework/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionTestCase.java index 907f8965632dd..b4e01b18a5647 100644 --- a/test/framework/src/main/java/org/elasticsearch/action/bulk/byscroll/AbstractAsyncBulkByScrollActionTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionTestCase.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.action.bulk.byscroll; +package org.elasticsearch.index.reindex; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.tasks.TaskId; From 17d01550c2d8736c3ed38c5eeface1d292c70717 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 11 May 2017 12:14:23 -0700 Subject: [PATCH 316/619] S3 Repository: Add back repository level credentials (#24609) Specifying s3 access and secret keys inside repository settings are not secure. However, until there is a way to dynamically update secure settings, this is the only way to dynamically add repositories with credentials that are not known at node startup time. This commit adds back `access_key` and `secret_key` s3 repository settings, but protects it with a required system property `allow_insecure_settings`. --- .../common/settings/SecureSetting.java | 27 +++++++++--- plugins/repository-s3/build.gradle | 10 +++++ .../repositories/s3/InternalAwsS3Service.java | 39 ++++++++++++++++-- .../repositories/s3/S3Repository.java | 10 ++++- .../s3/AwsS3ServiceImplTests.java | 39 +++++++++++++++--- .../RepositorySettingsCredentialsTests.java | 41 +++++++++++++++++++ 6 files changed, 149 insertions(+), 17 deletions(-) create mode 100644 plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/RepositorySettingsCredentialsTests.java diff --git a/core/src/main/java/org/elasticsearch/common/settings/SecureSetting.java b/core/src/main/java/org/elasticsearch/common/settings/SecureSetting.java index e93240b9a32ba..7b8f8bbdff3ab 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/SecureSetting.java +++ b/core/src/main/java/org/elasticsearch/common/settings/SecureSetting.java @@ -24,25 +24,25 @@ import java.util.EnumSet; import java.util.Set; +import org.elasticsearch.common.Booleans; import org.elasticsearch.common.util.ArrayUtils; - /** * A secure setting. * * This class allows access to settings from the Elasticsearch keystore. */ public abstract class SecureSetting extends Setting { + + /** Determines whether legacy settings with sensitive values should be allowed. */ + private static final boolean ALLOW_INSECURE_SETTINGS = Booleans.parseBoolean(System.getProperty("es.allow_insecure_settings", "false")); + private static final Set ALLOWED_PROPERTIES = EnumSet.of(Property.Deprecated, Property.Shared); private static final Property[] FIXED_PROPERTIES = { Property.NodeScope }; - private static final Property[] LEGACY_PROPERTIES = { - Property.NodeScope, Property.Deprecated, Property.Filtered - }; - private SecureSetting(String key, Property... properties) { super(key, (String)null, null, ArrayUtils.concat(properties, FIXED_PROPERTIES, Property.class)); assert assertAllowedProperties(properties); @@ -133,6 +133,23 @@ SecureString getFallback(Settings settings) { }; } + /** + * A setting which contains a sensitive string, but which for legacy reasons must be found outside secure settings. + * @see #secureString(String, Setting, Property...) + */ + public static Setting insecureString(String name) { + return new Setting(name, "", SecureString::new, Property.Deprecated, Property.Filtered, Property.NodeScope) { + @Override + public SecureString get(Settings settings) { + if (ALLOW_INSECURE_SETTINGS == false && exists(settings)) { + throw new IllegalArgumentException("Setting [" + name + "] is insecure, " + + "but property [allow_insecure_settings] is not set"); + } + return super.get(settings); + } + }; + } + /** * A setting which contains a file. Reading the setting opens an input stream to the file. * diff --git a/plugins/repository-s3/build.gradle b/plugins/repository-s3/build.gradle index 50f2ac571a424..9db78e5cbb2ca 100644 --- a/plugins/repository-s3/build.gradle +++ b/plugins/repository-s3/build.gradle @@ -54,6 +54,16 @@ bundlePlugin { } } +additionalTest('testRepositoryCreds'){ + include '**/RepositorySettingsCredentialsTests.class' + systemProperty 'es.allow_insecure_settings', 'true' +} + +test { + // these are tested explicitly in separate test tasks + exclude '**/*CredentialsTests.class' +} + integTestCluster { keystoreSetting 's3.client.default.access_key', 'myaccesskey' keystoreSetting 's3.client.default.secret_key', 'mysecretkey' diff --git a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/InternalAwsS3Service.java b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/InternalAwsS3Service.java index c76280f116ca8..58a130bcd95c2 100644 --- a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/InternalAwsS3Service.java +++ b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/InternalAwsS3Service.java @@ -26,6 +26,7 @@ import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.auth.InstanceProfileCredentialsProvider; import com.amazonaws.http.IdleConnectionReaper; import com.amazonaws.internal.StaticCredentialsProvider; @@ -35,6 +36,8 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.component.AbstractLifecycleComponent; +import org.elasticsearch.common.logging.DeprecationLogger; +import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; @@ -69,7 +72,7 @@ public synchronized AmazonS3 client(Settings repositorySettings) { logger.debug("creating S3 client with client_name [{}], endpoint [{}]", clientName, clientSettings.endpoint); - AWSCredentialsProvider credentials = buildCredentials(logger, clientSettings); + AWSCredentialsProvider credentials = buildCredentials(logger, deprecationLogger, clientSettings, repositorySettings); ClientConfiguration configuration = buildConfiguration(clientSettings, repositorySettings); client = new AmazonS3Client(credentials, configuration); @@ -106,14 +109,42 @@ static ClientConfiguration buildConfiguration(S3ClientSettings clientSettings, S } // pkg private for tests - static AWSCredentialsProvider buildCredentials(Logger logger, S3ClientSettings clientSettings) { - if (clientSettings.credentials == null) { + static AWSCredentialsProvider buildCredentials(Logger logger, DeprecationLogger deprecationLogger, + S3ClientSettings clientSettings, Settings repositorySettings) { + + + BasicAWSCredentials credentials = clientSettings.credentials; + if (S3Repository.ACCESS_KEY_SETTING.exists(repositorySettings)) { + if (S3Repository.SECRET_KEY_SETTING.exists(repositorySettings) == false) { + throw new IllegalArgumentException("Repository setting [" + S3Repository.ACCESS_KEY_SETTING.getKey() + + " must be accompanied by setting [" + S3Repository.SECRET_KEY_SETTING.getKey() + "]"); + } + try (SecureString key = S3Repository.ACCESS_KEY_SETTING.get(repositorySettings); + SecureString secret = S3Repository.SECRET_KEY_SETTING.get(repositorySettings)) { + credentials = new BasicAWSCredentials(key.toString(), secret.toString()); + } + // backcompat for reading keys out of repository settings + deprecationLogger.deprecated("Using s3 access/secret key from repository settings. Instead " + + "store these in named clients and the elasticsearch keystore for secure settings."); + } else if (S3Repository.SECRET_KEY_SETTING.exists(repositorySettings)) { + throw new IllegalArgumentException("Repository setting [" + S3Repository.SECRET_KEY_SETTING.getKey() + + " must be accompanied by setting [" + S3Repository.ACCESS_KEY_SETTING.getKey() + "]"); + } + if (credentials == null) { logger.debug("Using instance profile credentials"); return new PrivilegedInstanceProfileCredentialsProvider(); } else { logger.debug("Using basic key/secret credentials"); - return new StaticCredentialsProvider(clientSettings.credentials); + return new StaticCredentialsProvider(credentials); + } + } + + /** Returns the value for a given setting from the repository, or returns the fallback value. */ + private static T getRepoValue(Settings repositorySettings, Setting repositorySetting, T fallback) { + if (repositorySetting.exists(repositorySettings)) { + return repositorySetting.get(repositorySettings); } + return fallback; } @Override diff --git a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java index b73848eed600f..eeca906ff4998 100644 --- a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java +++ b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java @@ -21,14 +21,14 @@ import java.io.IOException; -import com.amazonaws.ClientConfiguration; import com.amazonaws.services.s3.AmazonS3; import org.elasticsearch.cluster.metadata.RepositoryMetaData; import org.elasticsearch.common.Strings; import org.elasticsearch.common.blobstore.BlobPath; import org.elasticsearch.common.blobstore.BlobStore; +import org.elasticsearch.common.settings.SecureSetting; +import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Setting; -import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; @@ -53,6 +53,12 @@ class S3Repository extends BlobStoreRepository { static final String TYPE = "s3"; + /** The access key to authenticate with s3. This setting is insecure because cluster settings are stored in cluster state */ + static final Setting ACCESS_KEY_SETTING = SecureSetting.insecureString("access_key"); + + /** The secret key to authenticate with s3. This setting is insecure because cluster settings are stored in cluster state */ + static final Setting SECRET_KEY_SETTING = SecureSetting.insecureString("secret_key"); + /** * Default is to use 100MB (S3 defaults) for heaps above 2GB and 5% of * the available memory for smaller heaps. diff --git a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java index 85398571dc1d1..9863402ec37c1 100644 --- a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java +++ b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java @@ -24,10 +24,12 @@ import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; import org.elasticsearch.common.settings.MockSecureSettings; +import org.elasticsearch.common.settings.SecureSetting; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; @@ -35,7 +37,8 @@ public class AwsS3ServiceImplTests extends ESTestCase { public void testAWSCredentialsWithSystemProviders() { S3ClientSettings clientSettings = S3ClientSettings.getClientSettings(Settings.EMPTY, "default"); - AWSCredentialsProvider credentialsProvider = InternalAwsS3Service.buildCredentials(logger, clientSettings); + AWSCredentialsProvider credentialsProvider = + InternalAwsS3Service.buildCredentials(logger, deprecationLogger, clientSettings, Settings.EMPTY); assertThat(credentialsProvider, instanceOf(InternalAwsS3Service.PrivilegedInstanceProfileCredentialsProvider.class)); } @@ -44,7 +47,7 @@ public void testAwsCredsDefaultSettings() { secureSettings.setString("s3.client.default.access_key", "aws_key"); secureSettings.setString("s3.client.default.secret_key", "aws_secret"); Settings settings = Settings.builder().setSecureSettings(secureSettings).build(); - launchAWSCredentialsWithElasticsearchSettingsTest(Settings.EMPTY, settings, "aws_key", "aws_secret"); + assertCredentials(Settings.EMPTY, settings, "aws_key", "aws_secret"); } public void testAwsCredsExplicitConfigSettings() { @@ -55,14 +58,38 @@ public void testAwsCredsExplicitConfigSettings() { secureSettings.setString("s3.client.default.access_key", "wrong_key"); secureSettings.setString("s3.client.default.secret_key", "wrong_secret"); Settings settings = Settings.builder().setSecureSettings(secureSettings).build(); - launchAWSCredentialsWithElasticsearchSettingsTest(repositorySettings, settings, "aws_key", "aws_secret"); + assertCredentials(repositorySettings, settings, "aws_key", "aws_secret"); } - private void launchAWSCredentialsWithElasticsearchSettingsTest(Settings singleRepositorySettings, Settings settings, - String expectedKey, String expectedSecret) { + public void testRepositorySettingsCredentialsDisallowed() { + Settings repositorySettings = Settings.builder() + .put(S3Repository.ACCESS_KEY_SETTING.getKey(), "aws_key") + .put(S3Repository.SECRET_KEY_SETTING.getKey(), "aws_secret").build(); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> + assertCredentials(repositorySettings, Settings.EMPTY, "aws_key", "aws_secret")); + assertThat(e.getMessage(), containsString("Setting [access_key] is insecure")); + } + + public void testRepositorySettingsCredentialsMissingKey() { + Settings repositorySettings = Settings.builder().put(S3Repository.SECRET_KEY_SETTING.getKey(), "aws_secret").build(); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> + assertCredentials(repositorySettings, Settings.EMPTY, "aws_key", "aws_secret")); + assertThat(e.getMessage(), containsString("must be accompanied by setting [access_key]")); + } + + public void testRepositorySettingsCredentialsMissingSecret() { + Settings repositorySettings = Settings.builder().put(S3Repository.ACCESS_KEY_SETTING.getKey(), "aws_key").build(); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> + assertCredentials(repositorySettings, Settings.EMPTY, "aws_key", "aws_secret")); + assertThat(e.getMessage(), containsString("must be accompanied by setting [secret_key]")); + } + + private void assertCredentials(Settings singleRepositorySettings, Settings settings, + String expectedKey, String expectedSecret) { String configName = InternalAwsS3Service.CLIENT_NAME.get(singleRepositorySettings); S3ClientSettings clientSettings = S3ClientSettings.getClientSettings(settings, configName); - AWSCredentials credentials = InternalAwsS3Service.buildCredentials(logger, clientSettings).getCredentials(); + AWSCredentials credentials = InternalAwsS3Service.buildCredentials(logger, deprecationLogger, + clientSettings, singleRepositorySettings).getCredentials(); assertThat(credentials.getAWSAccessKeyId(), is(expectedKey)); assertThat(credentials.getAWSSecretKey(), is(expectedSecret)); } diff --git a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/RepositorySettingsCredentialsTests.java b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/RepositorySettingsCredentialsTests.java new file mode 100644 index 0000000000000..c3e7069fdfd65 --- /dev/null +++ b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/RepositorySettingsCredentialsTests.java @@ -0,0 +1,41 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.repositories.s3; + +import com.amazonaws.auth.AWSCredentials; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.test.ESTestCase; + +public class RepositorySettingsCredentialsTests extends ESTestCase { + + public void testRepositorySettingsCredentials() { + Settings repositorySettings = Settings.builder() + .put(S3Repository.ACCESS_KEY_SETTING.getKey(), "aws_key") + .put(S3Repository.SECRET_KEY_SETTING.getKey(), "aws_secret").build(); + AWSCredentials credentials = InternalAwsS3Service.buildCredentials(logger, deprecationLogger, + S3ClientSettings.getClientSettings(Settings.EMPTY, "default"), repositorySettings).getCredentials(); + assertEquals("aws_key", credentials.getAWSAccessKeyId()); + assertEquals("aws_secret", credentials.getAWSSecretKey()); + assertSettingDeprecationsAndWarnings(new Setting[] { S3Repository.ACCESS_KEY_SETTING, S3Repository.SECRET_KEY_SETTING }, + "Using s3 access/secret key from repository settings. " + + "Instead store these in named clients and the elasticsearch keystore for secure settings."); + } +} From c1f1f665093c393463e32fe85f07845ebfd9b468 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 11 May 2017 12:15:16 -0700 Subject: [PATCH 317/619] Scripting: Replace advanced and native scripts with ScriptEngine docs (#24603) This commit documents how to write a `ScriptEngine` in order to use expert internal apis, such as using Lucene directly to find index term statistics. These documents prepare the way to remove both native scripts and IndexLookup. The example java code is actually compiled and tested under a new gradle subproject for example plugins. This change does not yet breakup jvm-example into the new examples dir, which should be done separately. relates #19359 relates #19966 --- .../script/LeafSearchScript.java | 35 +++- .../elasticsearch/script/ScriptEngine.java | 4 +- docs/reference/modules/scripting.asciidoc | 6 +- .../scripting/advanced-scripting.asciidoc | 189 ------------------ .../modules/scripting/engine.asciidoc | 57 ++++++ .../modules/scripting/native.asciidoc | 86 -------- docs/reference/redirects.asciidoc | 10 + plugins/examples/build.gradle | 0 .../script-expert-scoring/build.gradle | 28 +++ .../expertscript/ExpertScriptPlugin.java | 145 ++++++++++++++ .../ExpertScriptClientYamlTestSuiteIT.java | 38 ++++ .../test/script_expert_scoring/10_basic.yaml | 13 ++ .../test/script_expert_scoring/20_score.yaml | 53 +++++ settings.gradle | 14 ++ 14 files changed, 394 insertions(+), 284 deletions(-) delete mode 100644 docs/reference/modules/scripting/advanced-scripting.asciidoc create mode 100644 docs/reference/modules/scripting/engine.asciidoc delete mode 100644 docs/reference/modules/scripting/native.asciidoc create mode 100644 plugins/examples/build.gradle create mode 100644 plugins/examples/script-expert-scoring/build.gradle create mode 100644 plugins/examples/script-expert-scoring/src/main/java/org/elasticsearch/example/expertscript/ExpertScriptPlugin.java create mode 100644 plugins/examples/script-expert-scoring/src/test/java/org/elasticsearch/example/expertscript/ExpertScriptClientYamlTestSuiteIT.java create mode 100644 plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/10_basic.yaml create mode 100644 plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/20_score.yaml diff --git a/core/src/main/java/org/elasticsearch/script/LeafSearchScript.java b/core/src/main/java/org/elasticsearch/script/LeafSearchScript.java index 0bf9e0d50e072..762168d3c9086 100644 --- a/core/src/main/java/org/elasticsearch/script/LeafSearchScript.java +++ b/core/src/main/java/org/elasticsearch/script/LeafSearchScript.java @@ -19,18 +19,30 @@ package org.elasticsearch.script; +import org.apache.lucene.search.Scorer; import org.elasticsearch.common.lucene.ScorerAware; import java.util.Map; /** * A per-segment {@link SearchScript}. + * + * This is effectively a functional interface, requiring at least implementing {@link #runAsDouble()}. */ public interface LeafSearchScript extends ScorerAware, ExecutableScript { - void setDocument(int doc); + /** + * Set the document this script will process next. + */ + default void setDocument(int doc) {} + + @Override + default void setScorer(Scorer scorer) {} - void setSource(Map source); + /** + * Set the source for the current document. + */ + default void setSource(Map source) {} /** * Sets per-document aggregation {@code _value}. @@ -44,8 +56,23 @@ default void setNextAggregationValue(Object value) { setNextVar("_value", value); } - long runAsLong(); + @Override + default void setNextVar(String field, Object value) {} - double runAsDouble(); + /** + * Return the result as a long. This is used by aggregation scripts over long fields. + */ + default long runAsLong() { + throw new UnsupportedOperationException("runAsLong is not implemented"); + } + + @Override + default Object run() { + return runAsDouble(); + } + /** + * Return the result as a double. This is the main use case of search script, used for document scoring. + */ + double runAsDouble(); } diff --git a/core/src/main/java/org/elasticsearch/script/ScriptEngine.java b/core/src/main/java/org/elasticsearch/script/ScriptEngine.java index 89ded962567aa..78a1478a9c9c9 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptEngine.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptEngine.java @@ -38,7 +38,9 @@ public interface ScriptEngine extends Closeable { /** * The extension for file scripts in this language. */ - String getExtension(); + default String getExtension() { + return getType(); + } /** * Compiles a script. diff --git a/docs/reference/modules/scripting.asciidoc b/docs/reference/modules/scripting.asciidoc index 460b3be461dbf..c6a8a252b9334 100644 --- a/docs/reference/modules/scripting.asciidoc +++ b/docs/reference/modules/scripting.asciidoc @@ -51,7 +51,7 @@ certain tasks. |built-in |templates -|<> +|<> |n/a |you write it! |expert API @@ -83,6 +83,4 @@ include::scripting/painless-debugging.asciidoc[] include::scripting/expression.asciidoc[] -include::scripting/native.asciidoc[] - -include::scripting/advanced-scripting.asciidoc[] +include::scripting/engine.asciidoc[] diff --git a/docs/reference/modules/scripting/advanced-scripting.asciidoc b/docs/reference/modules/scripting/advanced-scripting.asciidoc deleted file mode 100644 index a5fcc12777dec..0000000000000 --- a/docs/reference/modules/scripting/advanced-scripting.asciidoc +++ /dev/null @@ -1,189 +0,0 @@ -[[modules-advanced-scripting]] -=== Advanced text scoring in scripts - -experimental[The functionality described on this page is considered experimental and may be changed or removed in a future release] - -Text features, such as term or document frequency for a specific term can be -accessed in scripts with the `_index` variable. This can be useful if, for -example, you want to implement your own scoring model using for example a -script inside a <>. -Statistics over the document collection are computed *per shard*, not per -index. - -It should be noted that the `_index` variable is not supported in the painless language, but `_index` is defined when using the groovy language. - -[float] -=== Nomenclature: - - -[horizontal] -`df`:: - - document frequency. The number of documents a term appears in. Computed - per field. - - -`tf`:: - - term frequency. The number times a term appears in a field in one specific - document. - -`ttf`:: - - total term frequency. The number of times this term appears in all - documents, that is, the sum of `tf` over all documents. Computed per - field. - -`df` and `ttf` are computed per shard and therefore these numbers can vary -depending on the shard the current document resides in. - - -[float] -=== Shard statistics: - -`_index.numDocs()`:: - - Number of documents in shard. - -`_index.maxDoc()`:: - - Maximal document number in shard. - -`_index.numDeletedDocs()`:: - - Number of deleted documents in shard. - - -[float] -=== Field statistics: - -Field statistics can be accessed with a subscript operator like this: -`_index['FIELD']`. - - -`_index['FIELD'].docCount()`:: - - Number of documents containing the field `FIELD`. Does not take deleted documents into account. - -`_index['FIELD'].sumttf()`:: - - Sum of `ttf` over all terms that appear in field `FIELD` in all documents. - -`_index['FIELD'].sumdf()`:: - - The sum of `df` s over all terms that appear in field `FIELD` in all - documents. - - -Field statistics are computed per shard and therefore these numbers can vary -depending on the shard the current document resides in. -The number of terms in a field cannot be accessed using the `_index` variable. See <> for how to do that. - -[float] -=== Term statistics: - -Term statistics for a field can be accessed with a subscript operator like -this: `_index['FIELD']['TERM']`. This will never return null, even if term or field does not exist. -If you do not need the term frequency, call `_index['FIELD'].get('TERM', 0)` -to avoid unnecessary initialization of the frequencies. The flag will have only -affect is your set the <> to `docs`. - - -`_index['FIELD']['TERM'].df()`:: - - `df` of term `TERM` in field `FIELD`. Will be returned, even if the term - is not present in the current document. - -`_index['FIELD']['TERM'].ttf()`:: - - The sum of term frequencies of term `TERM` in field `FIELD` over all - documents. Will be returned, even if the term is not present in the - current document. - -`_index['FIELD']['TERM'].tf()`:: - - `tf` of term `TERM` in field `FIELD`. Will be 0 if the term is not present - in the current document. - - -[float] -=== Term positions, offsets and payloads: - -If you need information on the positions of terms in a field, call -`_index['FIELD'].get('TERM', flag)` where flag can be - -[horizontal] -`_POSITIONS`:: if you need the positions of the term -`_OFFSETS`:: if you need the offsets of the term -`_PAYLOADS`:: if you need the payloads of the term -`_CACHE`:: if you need to iterate over all positions several times - -The iterator uses the underlying lucene classes to iterate over positions. For efficiency reasons, you can only iterate over positions once. If you need to iterate over the positions several times, set the `_CACHE` flag. - -You can combine the operators with a `|` if you need more than one info. For -example, the following will return an object holding the positions and payloads, -as well as all statistics: - - - `_index['FIELD'].get('TERM', _POSITIONS | _PAYLOADS)` - - -Positions can be accessed with an iterator that returns an object -(`POS_OBJECT`) holding position, offsets and payload for each term position. - -`POS_OBJECT.position`:: - - The position of the term. - -`POS_OBJECT.startOffset`:: - - The start offset of the term. - -`POS_OBJECT.endOffset`:: - - The end offset of the term. - -`POS_OBJECT.payload`:: - - The payload of the term. - -`POS_OBJECT.payloadAsInt(missingValue)`:: - - The payload of the term converted to integer. If the current position has - no payload, the `missingValue` will be returned. Call this only if you - know that your payloads are integers. - -`POS_OBJECT.payloadAsFloat(missingValue)`:: - - The payload of the term converted to float. If the current position has no - payload, the `missingValue` will be returned. Call this only if you know - that your payloads are floats. - -`POS_OBJECT.payloadAsString()`:: - - The payload of the term converted to string. If the current position has - no payload, `null` will be returned. Call this only if you know that your - payloads are strings. - - -Example: sums up all payloads for the term `foo`. - -[source,groovy] ---------------------------------------------------------- -termInfo = _index['my_field'].get('foo',_PAYLOADS); -score = 0; -for (pos in termInfo) { - score = score + pos.payloadAsInt(0); -} -return score; ---------------------------------------------------------- - - -[float] -=== Term vectors: - -The `_index` variable can only be used to gather statistics for single terms. If you want to use information on all terms in a field, you must store the term vectors (see <>). To access them, call -`_index.termVectors()` to get a -https://lucene.apache.org/core/4_0_0/core/org/apache/lucene/index/Fields.html[Fields] -instance. This object can then be used as described in https://lucene.apache.org/core/4_0_0/core/org/apache/lucene/index/Fields.html[lucene doc] to iterate over fields and then for each field iterate over each term in the field. -The method will return null if the term vectors were not stored. diff --git a/docs/reference/modules/scripting/engine.asciidoc b/docs/reference/modules/scripting/engine.asciidoc new file mode 100644 index 0000000000000..207722d8febf7 --- /dev/null +++ b/docs/reference/modules/scripting/engine.asciidoc @@ -0,0 +1,57 @@ +[[modules-scripting-engine]] +=== Advanced scripts using script engines + +A `ScriptEngine` is a backend for implementing a scripting language. It may also +be used to write scripts that need to use advanced internals of scripting. For example, +a script that wants to use term frequencies while scoring. + +The plugin {plugins}/plugin-authors.html[documentation] has more information on +how to write a plugin so that Elasticsearch will properly load it. To register +the `ScriptEngine`, your plugin should implement the `ScriptPlugin` interface +and override the `getScriptEngine(Settings settings)` method. + +The following is an example of a custom `ScriptEngine` which uses the language +name `expert_scripts`. It implements a single script called `pure_df` which +may be used as a search script to override each document's score as +the document frequency of a provided term. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{docdir}/../../plugins/examples/script-expert-scoring/src/main/java/org/elasticsearch/example/expertscript/ExpertScriptPlugin.java[expert_engine] +-------------------------------------------------- + +You can execute the script by specifying its `lang` as `expert_scripts`, and the name +of the script as the the script source: + + +[source,js] +-------------------------------------------------- +POST /_search +{ + "query": { + "function_score": { + "query": { + "match": { + "body": "foo" + } + }, + "functions": [ + { + "script_score": { + "script": { + "inline": "pure_df", + "lang" : "expert_scripts", + "params": { + "field": "body", + "term": "foo" + } + } + } + } + ] + } + } +} +-------------------------------------------------- +// CONSOLE +// TEST[skip:we don't have an expert script plugin installed to test this] diff --git a/docs/reference/modules/scripting/native.asciidoc b/docs/reference/modules/scripting/native.asciidoc deleted file mode 100644 index bc0ad3bcaf9ba..0000000000000 --- a/docs/reference/modules/scripting/native.asciidoc +++ /dev/null @@ -1,86 +0,0 @@ -[[modules-scripting-native]] -=== Native (Java) Scripts - -Sometimes `painless` and <> aren't enough. For those times you can -implement a native script. - -The best way to implement a native script is to write a plugin and install it. -The plugin {plugins}/plugin-authors.html[documentation] has more information on -how to write a plugin so that Elasticsearch will properly load it. - -To register the actual script you'll need to implement `NativeScriptFactory` -to construct the script. The actual script will extend either -`AbstractExecutableScript` or `AbstractSearchScript`. The second one is likely -the most useful and has several helpful subclasses you can extend like -`AbstractLongSearchScript` and `AbstractDoubleSearchScript`. -Finally, your plugin should register the native script by implementing the -`ScriptPlugin` interface. - -If you squashed the whole thing into one class it'd look like: - -[source,java] --------------------------------------------------- -public class MyNativeScriptPlugin extends Plugin implements ScriptPlugin { - - @Override - public List getNativeScripts() { - return Collections.singletonList(new MyNativeScriptFactory()); - } - - public static class MyNativeScriptFactory implements NativeScriptFactory { - @Override - public ExecutableScript newScript(@Nullable Map params) { - return new MyNativeScript(); - } - @Override - public boolean needsScores() { - return false; - } - @Override - public String getName() { - return "my_script"; - } - } - - public static class MyNativeScript extends AbstractDoubleSearchScript { - @Override - public double runAsDouble() { - double a = (double) source().get("a"); - double b = (double) source().get("b"); - return a * b; - } - } -} --------------------------------------------------- - -You can execute the script by specifying its `lang` as `native`, and the name -of the script as the `id`: - - -[source,js] --------------------------------------------------- -POST /_search -{ - "query": { - "function_score": { - "query": { - "match": { - "body": "foo" - } - }, - "functions": [ - { - "script_score": { - "script": { - "inline": "my_script", - "lang" : "native" - } - } - } - ] - } - } -} --------------------------------------------------- -// CONSOLE -// TEST[skip:we don't have a native plugin installed to test this] diff --git a/docs/reference/redirects.asciidoc b/docs/reference/redirects.asciidoc index cf5d1ef5f4d6b..0644b5498128b 100644 --- a/docs/reference/redirects.asciidoc +++ b/docs/reference/redirects.asciidoc @@ -463,3 +463,13 @@ index that make warmers not necessary anymore. === Index time boosting The index time boost mapping has been replaced with query time boost (see <>). + +[role="exclude",id="modules-scripting-native"] +=== Native scripting + +Native scripts have been replaced with writing custom `ScriptEngine` backends (see <>). + +[role="exclude",id="modules-advanced-scripting"] +=== Advanced scripting + +Using `_index` in scripts has been replaced with writing `ScriptEngine` backends (see <>). diff --git a/plugins/examples/build.gradle b/plugins/examples/build.gradle new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/examples/script-expert-scoring/build.gradle b/plugins/examples/script-expert-scoring/build.gradle new file mode 100644 index 0000000000000..7c602d9bc027d --- /dev/null +++ b/plugins/examples/script-expert-scoring/build.gradle @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +apply plugin: 'elasticsearch.esplugin' + +esplugin { + name 'script-expert-scoring' + description 'An example script engine to use low level Lucene internals for expert scoring' + classname 'org.elasticsearch.example.expertscript.ExpertScriptPlugin' +} + +test.enabled = false diff --git a/plugins/examples/script-expert-scoring/src/main/java/org/elasticsearch/example/expertscript/ExpertScriptPlugin.java b/plugins/examples/script-expert-scoring/src/main/java/org/elasticsearch/example/expertscript/ExpertScriptPlugin.java new file mode 100644 index 0000000000000..d56e7932499c6 --- /dev/null +++ b/plugins/examples/script-expert-scoring/src/main/java/org/elasticsearch/example/expertscript/ExpertScriptPlugin.java @@ -0,0 +1,145 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.example.expertscript; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.PostingsEnum; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Scorer; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.plugins.ScriptPlugin; +import org.elasticsearch.script.CompiledScript; +import org.elasticsearch.script.ExecutableScript; +import org.elasticsearch.script.LeafSearchScript; +import org.elasticsearch.script.ScriptEngine; +import org.elasticsearch.script.SearchScript; +import org.elasticsearch.search.lookup.SearchLookup; + +/** + * An example script plugin that adds a {@link ScriptEngine} implementing expert scoring. + */ +public class ExpertScriptPlugin extends Plugin implements ScriptPlugin { + + @Override + public ScriptEngine getScriptEngine(Settings settings) { + return new MyExpertScriptEngine(); + } + + /** An example {@link ScriptEngine} that uses Lucene segment details to implement pure document frequency scoring. */ + // tag::expert_engine + private static class MyExpertScriptEngine implements ScriptEngine { + @Override + public String getType() { + return "expert_scripts"; + } + + @Override + public Function,SearchScript> compile(String scriptName, String scriptSource, Map params) { + // we use the script "source" as the script identifier + if ("pure_df".equals(scriptSource)) { + return p -> new SearchScript() { + final String field; + final String term; + { + if (p.containsKey("field") == false) { + throw new IllegalArgumentException("Missing parameter [field]"); + } + if (p.containsKey("term") == false) { + throw new IllegalArgumentException("Missing parameter [term]"); + } + field = p.get("field").toString(); + term = p.get("term").toString(); + } + + @Override + public LeafSearchScript getLeafSearchScript(LeafReaderContext context) throws IOException { + PostingsEnum postings = context.reader().postings(new Term(field, term)); + if (postings == null) { + // the field and/or term don't exist in this segment, so always return 0 + return () -> 0.0d; + } + return new LeafSearchScript() { + int currentDocid = -1; + @Override + public void setDocument(int docid) { + // advance has undefined behavior calling with a docid <= its current docid + if (postings.docID() < docid) { + try { + postings.advance(docid); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + currentDocid = docid; + } + @Override + public double runAsDouble() { + if (postings.docID() != currentDocid) { + // advance moved past the current doc, so this doc has no occurrences of the term + return 0.0d; + } + try { + return postings.freq(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + }; + } + + @Override + public boolean needsScores() { + return false; + } + }; + } + throw new IllegalArgumentException("Unknown script name " + scriptSource); + } + + @Override + @SuppressWarnings("unchecked") + public SearchScript search(CompiledScript compiledScript, SearchLookup lookup, @Nullable Map params) { + Function,SearchScript> scriptFactory = (Function,SearchScript>) compiledScript.compiled(); + return scriptFactory.apply(params); + } + + @Override + public ExecutableScript executable(CompiledScript compiledScript, @Nullable Map params) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isInlineScriptEnabled() { + return true; + } + + @Override + public void close() {} + } + // end::expert_engine +} diff --git a/plugins/examples/script-expert-scoring/src/test/java/org/elasticsearch/example/expertscript/ExpertScriptClientYamlTestSuiteIT.java b/plugins/examples/script-expert-scoring/src/test/java/org/elasticsearch/example/expertscript/ExpertScriptClientYamlTestSuiteIT.java new file mode 100644 index 0000000000000..977ca9cf843a9 --- /dev/null +++ b/plugins/examples/script-expert-scoring/src/test/java/org/elasticsearch/example/expertscript/ExpertScriptClientYamlTestSuiteIT.java @@ -0,0 +1,38 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.example.expertscript; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate; +import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase; + +public class ExpertScriptClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase { + + public ExpertScriptClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) { + super(testCandidate); + } + + @ParametersFactory + public static Iterable parameters() throws Exception { + return ESClientYamlSuiteTestCase.createParameters(); + } +} + diff --git a/plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/10_basic.yaml b/plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/10_basic.yaml new file mode 100644 index 0000000000000..b4fafd69dd4ab --- /dev/null +++ b/plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/10_basic.yaml @@ -0,0 +1,13 @@ +# Integration tests for the expert scoring script example plugin +# +"Plugin loaded": + - do: + cluster.state: {} + + # Get master node id + - set: { master_node: master } + + - do: + nodes.info: {} + + - match: { nodes.$master.plugins.0.name: script-expert-scoring } diff --git a/plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/20_score.yaml b/plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/20_score.yaml new file mode 100644 index 0000000000000..1a0134b154b69 --- /dev/null +++ b/plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/20_score.yaml @@ -0,0 +1,53 @@ +# Integration tests for the expert scoring script example plugin +# +--- +setup: + - do: + indices.create: + index: test + + - do: + index: + index: test + type: test + id: 1 + body: { "important_field": "foo" } + - do: + index: + index: test + type: test + id: 2 + body: { "important_field": "foo foo foo" } + - do: + index: + index: test + type: test + id: 3 + body: { "important_field": "foo foo" } + + - do: + indices.refresh: {} +--- +"document scoring": + - do: + search: + index: test + body: + query: + function_score: + query: + match: + important_field: "foo" + functions: + - script_score: + script: + inline: "pure_df" + lang: "expert_scripts" + params: + field: "important_field" + term: "foo" + + - length: { hits.hits: 3 } + - match: {hits.hits.0._id: "2" } + - match: {hits.hits.1._id: "3" } + - match: {hits.hits.2._id: "1" } diff --git a/settings.gradle b/settings.gradle index 6e16b5a8b38da..7acedd9c98bc0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -78,6 +78,15 @@ List projects = [ 'qa:wildfly' ] +File examplePluginsDir = new File(rootProject.projectDir, 'plugins/examples') +List examplePlugins = [] +for (File example : examplePluginsDir.listFiles()) { + if (example.isDirectory() == false) continue; + if (example.name.startsWith('build') || example.name.startsWith('.')) continue; + projects.add("example-plugins:${example.name}".toString()) + examplePlugins.add(example.name) +} + boolean isEclipse = System.getProperty("eclipse.launcher") != null || gradle.startParameter.taskNames.contains('eclipse') || gradle.startParameter.taskNames.contains('cleanEclipse') if (isEclipse) { // eclipse cannot handle an intermediate dependency between main and test, so we must create separate projects @@ -88,6 +97,11 @@ if (isEclipse) { include projects.toArray(new String[0]) project(':build-tools').projectDir = new File(rootProject.projectDir, 'buildSrc') +project(':example-plugins').projectDir = new File(rootProject.projectDir, 'plugins/examples') + +for (String example : examplePlugins) { + project(":example-plugins:${example}").projectDir = new File(rootProject.projectDir, "plugins/examples/${example}") +} if (isEclipse) { project(":core").projectDir = new File(rootProject.projectDir, 'core/src/main') From 2ffdd4468d8a2671a160278a3a94714e72347786 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 11 May 2017 15:32:31 -0400 Subject: [PATCH 318/619] Skip reindex tests from old es if we can't run it Two of the versions of Elasticsearch we need to run for these tests can't run in Java 9 so we skip the entire test if we are running in java 9. For now. I'd like to reenable it to run against java 8 if there is one available, but that can wait for another time. Relates to #24561 --- qa/reindex-from-old/build.gradle | 63 ++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/qa/reindex-from-old/build.gradle b/qa/reindex-from-old/build.gradle index ca05e6919856a..a87cea6112b00 100644 --- a/qa/reindex-from-old/build.gradle +++ b/qa/reindex-from-old/build.gradle @@ -49,34 +49,41 @@ dependencies { es090 'org.elasticsearch:elasticsearch:0.90.13@zip' } -/* Set up tasks to unzip and run the old versions of ES before running the - * integration tests. */ -for (String version : ['2', '1', '090']) { - Task unzip = task("unzipEs${version}", type: Sync) { - Configuration oldEsDependency = configurations['es' + version] - dependsOn oldEsDependency - // Use a closure here to delay resolution of the dependency until we need it - from { - oldEsDependency.collect { zipTree(it) } +if (project.javaVersion == JavaVersion.VERSION_1_9) { + /* We can't run the dependencies with Java 9 so for now we'll skip the whole + * thing. */ + integTest.enabled = false +} else { + /* Set up tasks to unzip and run the old versions of ES before running the + * integration tests. */ + for (String version : ['2', '1', '090']) { + Task unzip = task("unzipEs${version}", type: Sync) { + Configuration oldEsDependency = configurations['es' + version] + dependsOn oldEsDependency + /* Use a closure here to delay resolution of the dependency until we need + * it */ + from { + oldEsDependency.collect { zipTree(it) } + } + into temporaryDir + } + Task fixture = task("oldEs${version}Fixture", + type: org.elasticsearch.gradle.test.AntFixture) { + dependsOn project.configurations.oldesFixture + dependsOn unzip + executable = new File(project.javaHome, 'bin/java') + env 'CLASSPATH', "${ -> project.configurations.oldesFixture.asPath }" + args 'oldes.OldElasticsearch', + baseDir, + unzip.temporaryDir, + version == '090' + } + integTest.dependsOn fixture + integTestRunner { + /* Use a closure on the string to delay evaluation until right before we + * run the integration tests so that we can be sure that the file is + * ready. */ + systemProperty "es${version}.port", "${ -> fixture.addressAndPort }" } - into temporaryDir - } - Task fixture = task("oldEs${version}Fixture", - type: org.elasticsearch.gradle.test.AntFixture) { - dependsOn project.configurations.oldesFixture - dependsOn unzip - executable = new File(project.javaHome, 'bin/java') - env 'CLASSPATH', "${ -> project.configurations.oldesFixture.asPath }" - args 'oldes.OldElasticsearch', - baseDir, - unzip.temporaryDir, - version == '090' - } - integTest.dependsOn fixture - integTestRunner { - /* Use a closure on the string to delay evaluation until right before we - * run the integration tests so that we can be sure that the file is ready. - */ - systemProperty "es${version}.port", "${ -> fixture.addressAndPort }" } } From f477a6472d523b64a94c496e08f27a307099682e Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 11 May 2017 13:11:18 -0700 Subject: [PATCH 319/619] Settings: Deprecate settings in .yml and .json (#24059) This commit adds a deprecation warning when elasticsearch.yml or elasticsearch.json is read during startup. relates #19391 --- .../elasticsearch/bootstrap/Bootstrap.java | 10 +++++ .../org/elasticsearch/env/Environment.java | 16 ++++++- .../node/InternalSettingsPreparer.java | 19 ++++---- .../bootstrap/BootstrapTests.java | 33 ++++++++++++++ .../node/InternalSettingsPreparerTests.java | 43 ++++++++++++------- .../org/elasticsearch/test/ESTestCase.java | 5 ++- 6 files changed, 99 insertions(+), 27 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/bootstrap/BootstrapTests.java diff --git a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index 1e53faa9efc4e..d0462efc391bc 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -35,6 +35,7 @@ import org.elasticsearch.common.PidFile; import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.inject.CreationException; +import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.logging.LogConfigurator; import org.elasticsearch.common.logging.Loggers; @@ -297,6 +298,7 @@ static void init( throw new BootstrapException(e); } checkForCustomConfFile(); + checkConfigExtension(environment.configExtension()); if (environment.pidFile() != null) { try { @@ -412,6 +414,14 @@ private static void checkUnsetAndMaybeExit(String confFileSetting, String settin } } + // pkg private for tests + static void checkConfigExtension(String extension) { + if (".yml".equals(extension) || ".json".equals(extension)) { + final DeprecationLogger deprecationLogger = new DeprecationLogger(Loggers.getLogger(Bootstrap.class)); + deprecationLogger.deprecated("elasticsearch{} is deprecated; rename your configuration file to elasticsearch.yaml", extension); + } + } + @SuppressForbidden(reason = "Allowed to exit explicitly in bootstrap phase") private static void exit(int status) { System.exit(status); diff --git a/core/src/main/java/org/elasticsearch/env/Environment.java b/core/src/main/java/org/elasticsearch/env/Environment.java index d1ad668e0eb31..df859ab7deb1f 100644 --- a/core/src/main/java/org/elasticsearch/env/Environment.java +++ b/core/src/main/java/org/elasticsearch/env/Environment.java @@ -71,6 +71,8 @@ public class Environment { private final Settings settings; + private final String configExtension; + private final Path[] dataFiles; private final Path[] dataWithClusterFiles; @@ -102,6 +104,12 @@ public class Environment { private final Path tmpFile = PathUtils.get(System.getProperty("java.io.tmpdir")); public Environment(Settings settings) { + this(settings, null); + } + + // Note: Do not use this ctor, it is for correct deprecation logging in 5.5 and will be removed + public Environment(Settings settings, String configExtension) { + this.configExtension = configExtension; final Path homeFile; if (PATH_HOME_SETTING.exists(settings)) { homeFile = PathUtils.get(cleanPath(PATH_HOME_SETTING.get(settings))); @@ -273,8 +281,14 @@ public URL resolveRepoURL(URL url) { } } + /** Return then extension of the config file that was loaded, or*/ + public String configExtension() { + return configExtension; + } + + // TODO: rename all these "file" methods to "dir" /** - * The config location. + * The config directory. */ public Path configFile() { return configFile; diff --git a/core/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java b/core/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java index d9cefc6b22e12..b8862fb20e570 100644 --- a/core/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java +++ b/core/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java @@ -19,14 +19,6 @@ package org.elasticsearch.node; -import org.elasticsearch.cli.Terminal; -import org.elasticsearch.cluster.ClusterName; -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.collect.Tuple; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.settings.SettingsException; -import org.elasticsearch.env.Environment; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -38,6 +30,14 @@ import java.util.Set; import java.util.function.Function; +import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.settings.SettingsException; +import org.elasticsearch.env.Environment; + import static org.elasticsearch.common.Strings.cleanPath; public class InternalSettingsPreparer { @@ -116,7 +116,8 @@ public static Environment prepareEnvironment(Settings input, Terminal terminal, // we put back the path.logs so we can use it in the logging configuration file output.put(Environment.PATH_LOGS_SETTING.getKey(), cleanPath(environment.logsFile().toAbsolutePath().toString())); - return new Environment(output.build()); + String configExtension = foundSuffixes.isEmpty() ? null : foundSuffixes.iterator().next(); + return new Environment(output.build(), configExtension); } /** diff --git a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapTests.java b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapTests.java new file mode 100644 index 0000000000000..b2fab7746fbf7 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapTests.java @@ -0,0 +1,33 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.bootstrap; + +import org.elasticsearch.test.ESTestCase; + +public class BootstrapTests extends ESTestCase { + + public void testConfigDeprecation() { + Bootstrap.checkConfigExtension(".json"); + assertWarnings("elasticsearch.json is deprecated; rename your configuration file to elasticsearch.yaml"); + Bootstrap.checkConfigExtension(".yml"); + assertWarnings("elasticsearch.yml is deprecated; rename your configuration file to elasticsearch.yaml"); + Bootstrap.checkConfigExtension(".yaml"); // no warnings, will be checked in @After + } +} diff --git a/core/src/test/java/org/elasticsearch/node/InternalSettingsPreparerTests.java b/core/src/test/java/org/elasticsearch/node/InternalSettingsPreparerTests.java index 033fa9cdf2011..5db397ab16d93 100644 --- a/core/src/test/java/org/elasticsearch/node/InternalSettingsPreparerTests.java +++ b/core/src/test/java/org/elasticsearch/node/InternalSettingsPreparerTests.java @@ -28,17 +28,14 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsException; import org.elasticsearch.env.Environment; -import org.elasticsearch.node.InternalSettingsPreparer; import org.elasticsearch.test.ESTestCase; import org.junit.After; import org.junit.Before; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.Collections; import java.util.Map; @@ -155,22 +152,36 @@ public void testGarbageIsNotSwallowed() throws IOException { public void testMultipleSettingsFileNotAllowed() throws IOException { InputStream yaml = getClass().getResourceAsStream("/config/elasticsearch.yaml"); - InputStream properties = getClass().getResourceAsStream("/config/elasticsearch.properties"); - Path home = createTempDir(); - Path config = home.resolve("config"); + InputStream json = getClass().getResourceAsStream("/config/elasticsearch.json"); + Path config = homeDir.resolve("config"); Files.createDirectory(config); Files.copy(yaml, config.resolve("elasticsearch.yaml")); - Files.copy(properties, config.resolve("elasticsearch.properties")); + Files.copy(json, config.resolve("elasticsearch.json")); + + SettingsException e = expectThrows(SettingsException.class, () -> + InternalSettingsPreparer.prepareEnvironment(Settings.builder().put(baseEnvSettings).build(), null) + ); + assertTrue(e.getMessage(), e.getMessage().contains("multiple settings files found with suffixes")); + assertTrue(e.getMessage(), e.getMessage().contains(".yaml")); + assertTrue(e.getMessage(), e.getMessage().contains(".json")); + } - try { - InternalSettingsPreparer.prepareEnvironment(Settings.builder() - .put(baseEnvSettings) - .build(), null); - } catch (SettingsException e) { - assertTrue(e.getMessage(), e.getMessage().contains("multiple settings files found with suffixes")); - assertTrue(e.getMessage(), e.getMessage().contains(".yaml")); - assertTrue(e.getMessage(), e.getMessage().contains(".properties")); - } + public void testYmlExtension() throws IOException { + InputStream yaml = getClass().getResourceAsStream("/config/elasticsearch.yaml"); + Path config = homeDir.resolve("config"); + Files.createDirectory(config); + Files.copy(yaml, config.resolve("elasticsearch.yml")); + Environment env = InternalSettingsPreparer.prepareEnvironment(Settings.builder().put(baseEnvSettings).build(), null); + assertEquals(".yml", env.configExtension()); + } + + public void testJsonExtension() throws IOException { + InputStream yaml = getClass().getResourceAsStream("/config/elasticsearch.json"); + Path config = homeDir.resolve("config"); + Files.createDirectory(config); + Files.copy(yaml, config.resolve("elasticsearch.json")); + Environment env = InternalSettingsPreparer.prepareEnvironment(Settings.builder().put(baseEnvSettings).build(), null); + assertEquals(".json", env.configExtension()); } public void testSecureSettings() { diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index cdab659bdc459..56f898b9009f0 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -140,8 +140,11 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static org.elasticsearch.common.util.CollectionUtils.arrayAsArrayList; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; /** * Base testcase for randomized unit testing with Elasticsearch @@ -336,7 +339,7 @@ protected final void assertWarnings(String... expectedWarnings) { final Set actualWarningValues = actualWarnings.stream().map(DeprecationLogger::extractWarningValueFromWarningHeader).collect(Collectors.toSet()); for (String msg : expectedWarnings) { - assertTrue(msg, actualWarningValues.contains(DeprecationLogger.escape(msg))); + assertThat(actualWarningValues, hasItem(DeprecationLogger.escape(msg))); } assertEquals("Expected " + expectedWarnings.length + " warnings but found " + actualWarnings.size() + "\nExpected: " + Arrays.asList(expectedWarnings) + "\nActual: " + actualWarnings, From a40c3a99c9ca2fa576c72ee2f441cbac3df55424 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 11 May 2017 15:57:11 -0400 Subject: [PATCH 320/619] Reindex: don't duplicate _source parameter (#24629) If the request asks for the `_source` stored field then don't duplicate it when forcing the `_source` parameter to onto the request for reindex-from-remote from versions before 1.0. Closes #24628 --- .../reindex/remote/RemoteRequestBuilders.java | 11 ++++++- .../remote/RemoteRequestBuildersTests.java | 33 +++++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuilders.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuilders.java index b81d504b9f362..50769cc92310b 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuilders.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuilders.java @@ -45,6 +45,13 @@ import static java.util.Collections.singletonMap; import static org.elasticsearch.common.unit.TimeValue.timeValueMillis; +/** + * Builds requests for remote version of Elasticsearch. Note that unlike most of the + * rest of Elasticsearch this file needs to be compatible with very old versions of + * Elasticsearch. Thus is often uses identifiers for versions like {@code 2000099} + * for {@code 2.0.0-alpha1}. Do not drop support for features from this file just + * because the version constants have been removed. + */ final class RemoteRequestBuilders { private RemoteRequestBuilders() {} @@ -103,7 +110,9 @@ static Map initialSearchParams(SearchRequest searchRequest, Vers searchRequest.source().storedField("_parent").storedField("_routing").storedField("_ttl"); if (remoteVersion.before(Version.fromId(1000099))) { // Versions before 1.0.0 don't support `"_source": true` so we have to ask for the _source in a funny way. - searchRequest.source().storedField("_source"); + if (false == searchRequest.source().storedFields().fieldNames().contains("_source")) { + searchRequest.source().storedField("_source"); + } } } if (searchRequest.source().storedFields() != null && false == searchRequest.source().storedFields().fieldNames().isEmpty()) { diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuildersTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuildersTests.java index 5f30318351f13..9cb644162da40 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuildersTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuildersTests.java @@ -28,7 +28,6 @@ import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.VersionUtils; import java.io.IOException; import java.io.InputStreamReader; @@ -49,6 +48,13 @@ import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.not; +/** + * Tests for {@link RemoteRequestBuilders} which builds requests for remote version of + * Elasticsearch. Note that unlike most of the rest of Elasticsearch this file needs to + * be compatible with very old versions of Elasticsearch. Thus is often uses identifiers + * for versions like {@code 2000099} for {@code 2.0.0-alpha1}. Do not drop support for + * features from this file just because the version constants have been removed. + */ public class RemoteRequestBuildersTests extends ESTestCase { public void testIntialSearchPath() { SearchRequest searchRequest = new SearchRequest().source(new SearchSourceBuilder()); @@ -117,20 +123,33 @@ public void testInitialSearchParamsFields() { SearchRequest searchRequest = new SearchRequest().source(new SearchSourceBuilder()); // Test request without any fields - Version remoteVersion = VersionUtils.randomVersion(random()); + Version remoteVersion = Version.fromId(between(2000099, Version.CURRENT.id)); assertThat(initialSearchParams(searchRequest, remoteVersion), not(either(hasKey("stored_fields")).or(hasKey("fields")))); - // Setup some fields for the next two tests - searchRequest.source().storedField("_source").storedField("_id"); - // Test stored_fields for versions that support it - remoteVersion = VersionUtils.randomVersionBetween(random(), Version.V_5_0_0_alpha4, null); + searchRequest = new SearchRequest().source(new SearchSourceBuilder()); + searchRequest.source().storedField("_source").storedField("_id"); + remoteVersion = Version.fromId(between(Version.V_5_0_0_alpha4_ID, Version.CURRENT.id)); assertThat(initialSearchParams(searchRequest, remoteVersion), hasEntry("stored_fields", "_source,_id")); // Test fields for versions that support it - remoteVersion = VersionUtils.randomVersionBetween(random(), null, Version.V_5_0_0_alpha3); + searchRequest = new SearchRequest().source(new SearchSourceBuilder()); + searchRequest.source().storedField("_source").storedField("_id"); + remoteVersion = Version.fromId(between(2000099, Version.V_5_0_0_alpha4_ID - 1)); assertThat(initialSearchParams(searchRequest, remoteVersion), hasEntry("fields", "_source,_id")); + + // Test extra fields for versions that need it + searchRequest = new SearchRequest().source(new SearchSourceBuilder()); + searchRequest.source().storedField("_source").storedField("_id"); + remoteVersion = Version.fromId(between(0, 2000099 - 1)); + assertThat(initialSearchParams(searchRequest, remoteVersion), hasEntry("fields", "_source,_id,_parent,_routing,_ttl")); + + // But only versions before 1.0 force _source to be in the list + searchRequest = new SearchRequest().source(new SearchSourceBuilder()); + searchRequest.source().storedField("_id"); + remoteVersion = Version.fromId(between(1000099, 2000099 - 1)); + assertThat(initialSearchParams(searchRequest, remoteVersion), hasEntry("fields", "_id,_parent,_routing,_ttl")); } public void testInitialSearchParamsMisc() { From 4d86679f5a00ae3eda0d88680705b712a6e5eb46 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 11 May 2017 17:36:03 -0400 Subject: [PATCH 321/619] Disable reindex-from-old tests in windows In windows we can't reliable git the pid so we skip the reindex-from-remote tests from old versions of elasticsearch. This is OK because we aren't really testing windows here anyway. It isn't great, but should be safe. --- qa/reindex-from-old/build.gradle | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qa/reindex-from-old/build.gradle b/qa/reindex-from-old/build.gradle index a87cea6112b00..72927842e037b 100644 --- a/qa/reindex-from-old/build.gradle +++ b/qa/reindex-from-old/build.gradle @@ -24,6 +24,8 @@ should be able to use the standard launching mechanism which is more flexible and reliable. """ +import org.apache.tools.ant.taskdefs.condition.Os + apply plugin: 'elasticsearch.standalone-rest-test' apply plugin: 'elasticsearch.rest-test' @@ -49,9 +51,9 @@ dependencies { es090 'org.elasticsearch:elasticsearch:0.90.13@zip' } -if (project.javaVersion == JavaVersion.VERSION_1_9) { +if (project.javaVersion == JavaVersion.VERSION_1_9 || Os.isFamily(Os.FAMILY_WINDOWS)) { /* We can't run the dependencies with Java 9 so for now we'll skip the whole - * thing. */ + * thing. We can't get the pid files in windows so we skip that as well.... */ integTest.enabled = false } else { /* Set up tasks to unzip and run the old versions of ES before running the From 7b7e15023a2d7cd4dd0e86a4a0b64615cae43a3b Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Fri, 12 May 2017 00:07:24 +0200 Subject: [PATCH 322/619] Add rest test for sliced scroll (#24630) --- .../rest-api-spec/test/scroll/12_slices.yaml | 78 ++++++++++++++++++- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/12_slices.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/12_slices.yaml index 1695bdb2352e0..ac66af0095e2d 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/12_slices.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/12_slices.yaml @@ -1,5 +1,5 @@ --- -"Sliced scroll": +setup: - do: indices.create: index: test_sliced_scroll @@ -8,31 +8,101 @@ index: index: test_sliced_scroll type: test - id: 42 + id: 1 body: { foo: 1 } + - do: + index: + index: test_sliced_scroll + type: test + id: 2 + body: { foo: 2 } + + - do: + index: + index: test_sliced_scroll + type: test + id: 3 + body: { foo: 3 } + + - do: + index: + index: test_sliced_scroll + type: test + id: 4 + body: { foo: 4 } + - do: indices.refresh: {} +--- +"Sliced scroll": + - skip: + version: " - 5.3.0" + reason: Prior version uses a random seed per node to compute the hash of the keys. + - do: search: index: test_sliced_scroll - size: 1 scroll: 1m sort: foo body: slice: id: 0 - max: 3 + max: 2 + query: + match_all: {} + + - set: {_scroll_id: scroll_id} + - match: {hits.total: 3 } + - length: {hits.hits: 3 } + - match: {hits.hits.0._id: "2" } + - match: {hits.hits.1._id: "3" } + - match: {hits.hits.2._id: "4" } + + - do: + scroll: + scroll_id: $scroll_id + scroll: 1m + + - match: {hits.total: 3 } + - length: {hits.hits: 0 } + + - do: + clear_scroll: + scroll_id: $scroll_id + + - do: + search: + index: test_sliced_scroll + scroll: 1m + sort: foo + body: + slice: + id: 1 + max: 2 query: match_all: {} - set: {_scroll_id: scroll_id} + - match: {hits.total: 1 } + - length: {hits.hits: 1 } + - match: {hits.hits.0._id: "1" } + + - do: + scroll: + scroll_id: $scroll_id + scroll: 1m + + - match: {hits.total: 1 } + - length: {hits.hits: 0 } - do: clear_scroll: scroll_id: $scroll_id +--- +"Sliced scroll with invalid arguments": - do: catch: /query_phase_execution_exception.*The number of slices.*index.max_slices_per_scroll/ search: From 220bd2de6e5ad817d952bd43d8d3f99f1480dba9 Mon Sep 17 00:00:00 2001 From: Koen De Groote Date: Fri, 12 May 2017 04:44:31 +0200 Subject: [PATCH 323/619] Make a few class level fields static This commit converts some final constant instance fields to class fields. --- .../org/elasticsearch/bootstrap/BootstrapChecks.java | 12 ++++++------ .../common/geo/builders/ShapeBuilder.java | 12 ++++++------ .../indices/recovery/RecoveryTarget.java | 2 +- .../suggest/phrase/DirectCandidateGenerator.java | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java index 54a1c5fa44483..96ff8fbf90d10 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java @@ -315,11 +315,11 @@ boolean isMemoryLocked() { static class MaxNumberOfThreadsCheck implements BootstrapCheck { // this should be plenty for machines up to 256 cores - private final long maxNumberOfThreadsThreshold = 1 << 12; + private static final long MAX_NUMBER_OF_THREADS_THRESHOLD = 1 << 12; @Override public boolean check() { - return getMaxNumberOfThreads() != -1 && getMaxNumberOfThreads() < maxNumberOfThreadsThreshold; + return getMaxNumberOfThreads() != -1 && getMaxNumberOfThreads() < MAX_NUMBER_OF_THREADS_THRESHOLD; } @Override @@ -329,7 +329,7 @@ public String errorMessage() { "max number of threads [%d] for user [%s] is too low, increase to at least [%d]", getMaxNumberOfThreads(), BootstrapInfo.getSystemProperties().get("user.name"), - maxNumberOfThreadsThreshold); + MAX_NUMBER_OF_THREADS_THRESHOLD); } // visible for testing @@ -369,11 +369,11 @@ long getMaxSizeVirtualMemory() { static class MaxMapCountCheck implements BootstrapCheck { - private final long limit = 1 << 18; + private static final long LIMIT = 1 << 18; @Override public boolean check() { - return getMaxMapCount() != -1 && getMaxMapCount() < limit; + return getMaxMapCount() != -1 && getMaxMapCount() < LIMIT; } @Override @@ -382,7 +382,7 @@ public String errorMessage() { Locale.ROOT, "max virtual memory areas vm.max_map_count [%d] is too low, increase to at least [%d]", getMaxMapCount(), - limit); + LIMIT); } // visible for testing diff --git a/core/src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilder.java b/core/src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilder.java index fbe47a17826d0..64902266d427c 100644 --- a/core/src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilder.java +++ b/core/src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilder.java @@ -79,21 +79,21 @@ public abstract class ShapeBuilder extends ToXContentToBytes implements NamedWri /** It's possible that some geometries in a MULTI* shape might overlap. With the possible exception of GeometryCollection, * this normally isn't allowed. */ - protected final boolean multiPolygonMayOverlap = false; + protected static final boolean MULTI_POLYGON_MAY_OVERLAP = false; /** @see org.locationtech.spatial4j.shape.jts.JtsGeometry#validate() */ - protected final boolean autoValidateJtsGeometry = true; + protected static final boolean AUTO_VALIDATE_JTS_GEOMETRY = true; /** @see org.locationtech.spatial4j.shape.jts.JtsGeometry#index() */ - protected final boolean autoIndexJtsGeometry = true;//may want to turn off once SpatialStrategy impls do it. + protected static final boolean AUTO_INDEX_JTS_GEOMETRY = true;//may want to turn off once SpatialStrategy impls do it. protected ShapeBuilder() { } protected JtsGeometry jtsGeometry(Geometry geom) { //dateline180Check is false because ElasticSearch does it's own dateline wrapping - JtsGeometry jtsGeometry = new JtsGeometry(geom, SPATIAL_CONTEXT, false, multiPolygonMayOverlap); - if (autoValidateJtsGeometry) + JtsGeometry jtsGeometry = new JtsGeometry(geom, SPATIAL_CONTEXT, false, MULTI_POLYGON_MAY_OVERLAP); + if (AUTO_VALIDATE_JTS_GEOMETRY) jtsGeometry.validate(); - if (autoIndexJtsGeometry) + if (AUTO_INDEX_JTS_GEOMETRY) jtsGeometry.index(); return jtsGeometry; } diff --git a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java index d9540019e803a..18557b5c7b84c 100644 --- a/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java +++ b/core/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java @@ -70,7 +70,7 @@ public class RecoveryTarget extends AbstractRefCounted implements RecoveryTarget private static final AtomicLong idGenerator = new AtomicLong(); - private final String RECOVERY_PREFIX = "recovery."; + private static final String RECOVERY_PREFIX = "recovery."; private final ShardId shardId; private final long recoveryId; diff --git a/core/src/main/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGenerator.java b/core/src/main/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGenerator.java index f6faaaeea5cef..24f2647167e22 100644 --- a/core/src/main/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGenerator.java +++ b/core/src/main/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGenerator.java @@ -58,7 +58,7 @@ public final class DirectCandidateGenerator extends CandidateGenerator { private final TermsEnum termsEnum; private final IndexReader reader; private final long dictSize; - private final double logBase = 5; + private static final double LOG_BASE = 5; private final long frequencyPlateau; private final Analyzer preFilter; private final Analyzer postFilter; @@ -189,7 +189,7 @@ private double score(long frequency, double errorScore, long dictionarySize) { protected long thresholdFrequency(long termFrequency, long dictionarySize) { if (termFrequency > 0) { - return max(0, round(termFrequency * (log10(termFrequency - frequencyPlateau) * (1.0 / log10(logBase))) + 1)); + return max(0, round(termFrequency * (log10(termFrequency - frequencyPlateau) * (1.0 / log10(LOG_BASE))) + 1)); } return 0; From 70e14d5e3fb78a74a89e812fc019882e781c61e4 Mon Sep 17 00:00:00 2001 From: Byron Voorbach Date: Fri, 12 May 2017 09:38:16 +0200 Subject: [PATCH 324/619] Fix Source filtering in new field collapsing feature Update ValueType of FetchSourceContext to OBJECT_ARRAY_BOOLEAN_OR_STRING to be in line with other source_filtering implementations --- .../elasticsearch/common/xcontent/ObjectParser.java | 1 + .../org/elasticsearch/index/query/InnerHitBuilder.java | 2 +- .../index/query/InnerHitBuilderTests.java | 10 +++++++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java b/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java index 7e3001b75a2d6..c07f4d11d8405 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java @@ -409,6 +409,7 @@ public enum ValueType { OBJECT_ARRAY(START_OBJECT, START_ARRAY), OBJECT_OR_BOOLEAN(START_OBJECT, VALUE_BOOLEAN), OBJECT_OR_STRING(START_OBJECT, VALUE_STRING), + OBJECT_ARRAY_BOOLEAN_OR_STRING(START_OBJECT, START_ARRAY, VALUE_BOOLEAN, VALUE_STRING), OBJECT_ARRAY_OR_STRING(START_OBJECT, START_ARRAY, VALUE_STRING), VALUE(VALUE_BOOLEAN, VALUE_NULL, VALUE_EMBEDDED_OBJECT, VALUE_NUMBER, VALUE_STRING); diff --git a/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java b/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java index c1b240066abe2..dd23429dbdb72 100644 --- a/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java @@ -101,7 +101,7 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl } catch (IOException e) { throw new ParsingException(p.getTokenLocation(), "Could not parse inner _source definition", e); } - }, SearchSourceBuilder._SOURCE_FIELD, ObjectParser.ValueType.OBJECT_OR_BOOLEAN); + }, SearchSourceBuilder._SOURCE_FIELD, ObjectParser.ValueType.OBJECT_ARRAY_BOOLEAN_OR_STRING); PARSER.declareObject(InnerHitBuilder::setHighlightBuilder, (p, c) -> HighlightBuilder.fromXContent(c), SearchSourceBuilder.HIGHLIGHT_FIELD); PARSER.declareObject(InnerHitBuilder::setChildInnerHits, (p, c) -> { diff --git a/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java index 6de9cbab62071..cd6622368c931 100644 --- a/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.query; import org.apache.lucene.search.join.ScoreMode; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Settings; @@ -311,13 +312,16 @@ public static InnerHitBuilder randomInnerHits(boolean recursive, boolean include } innerHits.setScriptFields(new HashSet<>(scriptFields.values())); FetchSourceContext randomFetchSourceContext; - if (randomBoolean()) { - randomFetchSourceContext = new FetchSourceContext(randomBoolean()); - } else { + int randomInt = randomIntBetween(0, 2); + if (randomInt == 0) { + randomFetchSourceContext = new FetchSourceContext(true, Strings.EMPTY_ARRAY, Strings.EMPTY_ARRAY); + } else if (randomInt == 1) { randomFetchSourceContext = new FetchSourceContext(true, generateRandomStringArray(12, 16, false), generateRandomStringArray(12, 16, false) ); + } else { + randomFetchSourceContext = new FetchSourceContext(randomBoolean()); } innerHits.setFetchSourceContext(randomFetchSourceContext); if (randomBoolean()) { From f06467b6a07d7ff4197cdb4c165bdaa1c072d7e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Fri, 12 May 2017 11:48:44 +0200 Subject: [PATCH 325/619] [Tests] Add unit tests for InternalFilters (#24624) Relates to #22278 --- .../bucket/filters/InternalFilters.java | 42 +++++++++- .../bucket/filters/InternalFiltersTests.java | 83 +++++++++++++++++++ 2 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/bucket/filters/InternalFiltersTests.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filters/InternalFilters.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filters/InternalFilters.java index 5153122272564..2cc146ba2f02c 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filters/InternalFilters.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filters/InternalFilters.java @@ -33,6 +33,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; public class InternalFilters extends InternalMultiBucketAggregation implements Filters { public static class InternalBucket extends InternalMultiBucketAggregation.InternalBucket implements Filters.Bucket { @@ -113,13 +114,35 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.endObject(); return builder; } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + InternalBucket that = (InternalBucket) other; + return Objects.equals(key, that.key) + && Objects.equals(keyed, keyed) + && Objects.equals(docCount, that.docCount) + && Objects.equals(aggregations, that.aggregations); + } + + @Override + public int hashCode() { + return Objects.hash(getClass(), key, keyed, docCount, aggregations); + } } private final List buckets; private final boolean keyed; - private Map bucketMap; + // bucketMap gets lazily initialized from buckets in getBucketByKey() + private transient Map bucketMap; - public InternalFilters(String name, List buckets, boolean keyed, List pipelineAggregators, Map metaData) { + public InternalFilters(String name, List buckets, boolean keyed, List pipelineAggregators, + Map metaData) { super(name, pipelineAggregators, metaData); this.buckets = buckets; this.keyed = keyed; @@ -200,7 +223,8 @@ public InternalAggregation doReduce(List aggregations, Redu } } - InternalFilters reduced = new InternalFilters(name, new ArrayList(bucketsList.size()), keyed, pipelineAggregators(), getMetaData()); + InternalFilters reduced = new InternalFilters(name, new ArrayList(bucketsList.size()), keyed, pipelineAggregators(), + getMetaData()); for (List sameRangeList : bucketsList) { reduced.buckets.add((sameRangeList.get(0)).reduce(sameRangeList, reduceContext)); } @@ -225,4 +249,16 @@ public XContentBuilder doXContentBody(XContentBuilder builder, Params params) th return builder; } + @Override + protected int doHashCode() { + return Objects.hash(buckets, keyed); + } + + @Override + protected boolean doEquals(Object obj) { + InternalFilters that = (InternalFilters) obj; + return Objects.equals(buckets, that.buckets) + && Objects.equals(keyed, that.keyed); + } + } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filters/InternalFiltersTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filters/InternalFiltersTests.java new file mode 100644 index 0000000000000..b21f201e79bec --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filters/InternalFiltersTests.java @@ -0,0 +1,83 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.filters; + +import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; +import org.junit.Before; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class InternalFiltersTests extends InternalAggregationTestCase { + + private boolean keyed; + private final List keys = new ArrayList<>(); + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + keyed = randomBoolean(); + int numKeys = randomIntBetween(1,10); + for (int i = 0; i < numKeys; i++) { + keys.add(randomAlphaOfLength(5)); + } + + } + + @Override + protected InternalFilters createTestInstance(String name, List pipelineAggregators, Map metaData) { + final List buckets = new ArrayList<>(); + for (int i = 0; i < keys.size(); ++i) { + String key = keys.get(i); + int docCount = randomIntBetween(0, 1000); + buckets.add( new InternalFilters.InternalBucket(key, docCount, InternalAggregations.EMPTY, keyed)); + } + return new InternalFilters(name, buckets, keyed, pipelineAggregators, metaData); + } + + @Override + protected void assertReduced(InternalFilters reduced, List inputs) { + final Map expectedCounts = new TreeMap<>(); + for (InternalFilters input : inputs) { + for (InternalFilters.InternalBucket bucket : input.getBuckets()) { + expectedCounts.compute(bucket.getKeyAsString(), + (key, oldValue) -> (oldValue == null ? 0 : oldValue) + bucket.getDocCount()); + } + } + final Map actualCounts = new TreeMap<>(); + for (InternalFilters.InternalBucket bucket : reduced.getBuckets()) { + actualCounts.compute(bucket.getKeyAsString(), + (key, oldValue) -> (oldValue == null ? 0 : oldValue) + bucket.getDocCount()); + } + assertEquals(expectedCounts, actualCounts); + } + + @Override + protected Reader instanceReader() { + return InternalFilters::new; + } + +} From b7976bd5369ca6b19fd868491b11b7930d859e38 Mon Sep 17 00:00:00 2001 From: Dimitris Athanasiou Date: Fri, 12 May 2017 12:58:30 +0100 Subject: [PATCH 326/619] [TEST] Temporarily disable the secure fixture for hdfs tests (#24643) This keeps failing the build so I am temporarily disabling it until #24636 gets merged. --- plugins/repository-hdfs/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/repository-hdfs/build.gradle b/plugins/repository-hdfs/build.gradle index 2a94b569278f7..2848f1b2479ac 100644 --- a/plugins/repository-hdfs/build.gradle +++ b/plugins/repository-hdfs/build.gradle @@ -180,7 +180,7 @@ if (fixtureSupported) { } // Create a Integration Test suite just for security based tests -if (secureFixtureSupported) { +if (secureFixtureSupported && false) { // This fails due to a vagrant configuration issue - remove the false check to re-enable // This must execute before the afterEvaluate block from integTestSecure project.afterEvaluate { Path elasticsearchKT = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("keytabs").resolve("elasticsearch.keytab").toAbsolutePath() From 04e08f5e491477f573b9e3ac19ee4b57cf7886db Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Fri, 12 May 2017 14:08:14 +0200 Subject: [PATCH 327/619] Simplify Discovery interface (#24608) - Removes clusterState, getInitialClusterState and getMinimumMasterNodes methods from Discovery interface. - Sets PingContextProvider in ZenPing constructor - Renames state in ZenDiscovery to committedState --- .../cluster/service/ClusterApplier.java | 7 +- .../service/ClusterApplierService.java | 1 + .../elasticsearch/discovery/Discovery.java | 18 ----- .../discovery/DiscoveryModule.java | 4 +- .../discovery/TribeDiscovery.java | 30 ++++---- .../discovery/single/SingleNodeDiscovery.java | 55 +++++++-------- .../discovery/zen/UnicastZenPing.java | 8 +-- .../discovery/zen/ZenDiscovery.java | 69 ++++++++----------- .../elasticsearch/discovery/zen/ZenPing.java | 2 +- .../org/elasticsearch/gateway/Gateway.java | 11 ++- .../elasticsearch/gateway/GatewayService.java | 9 +-- .../java/org/elasticsearch/node/Node.java | 4 +- .../single/SingleNodeDiscoveryIT.java | 18 ++--- .../single/SingleNodeDiscoveryTests.java | 30 ++++++-- .../discovery/zen/UnicastZenPingTests.java | 56 +++++++-------- .../discovery/zen/ZenDiscoveryUnitTests.java | 25 +++++-- .../gateway/GatewayServiceTests.java | 3 +- .../org/elasticsearch/test/NoopDiscovery.java | 17 ----- .../test/discovery/MockZenPing.java | 9 ++- .../test/discovery/TestZenDiscovery.java | 2 +- 20 files changed, 174 insertions(+), 204 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplier.java b/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplier.java index ef3135af24d4a..aa5c74e15e854 100644 --- a/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplier.java +++ b/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplier.java @@ -24,8 +24,13 @@ import java.util.function.Supplier; -@FunctionalInterface public interface ClusterApplier { + /** + * Sets the initial state for this applier. Should only be called once. + * @param initialState the initial state to set + */ + void setInitialState(ClusterState initialState); + /** * Method to invoke when a new cluster state is available to be applied * diff --git a/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplierService.java b/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplierService.java index 540881718fc34..b029f10f5f033 100644 --- a/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplierService.java +++ b/core/src/main/java/org/elasticsearch/cluster/service/ClusterApplierService.java @@ -116,6 +116,7 @@ public synchronized void setNodeConnectionsService(NodeConnectionsService nodeCo this.nodeConnectionsService = nodeConnectionsService; } + @Override public void setInitialState(ClusterState initialState) { if (lifecycle.started()) { throw new IllegalStateException("can't set initial state when started"); diff --git a/core/src/main/java/org/elasticsearch/discovery/Discovery.java b/core/src/main/java/org/elasticsearch/discovery/Discovery.java index 7f68f417fcb73..3842e68d1006b 100644 --- a/core/src/main/java/org/elasticsearch/discovery/Discovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/Discovery.java @@ -21,7 +21,6 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.cluster.ClusterChangedEvent; -import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.component.LifecycleComponent; @@ -48,18 +47,6 @@ public interface Discovery extends LifecycleComponent { */ void publish(ClusterChangedEvent clusterChangedEvent, AckListener ackListener); - /** - * Returns the initial cluster state provided by the discovery module. Used by - * {@link org.elasticsearch.cluster.service.ClusterApplierService} as initial applied state. - */ - ClusterState getInitialClusterState(); - - /** - * Returns latest cluster state used by the discovery module. Used by {@link org.elasticsearch.cluster.service.MasterService} to - * calculate the next prospective state to publish. - */ - ClusterState clusterState(); - interface AckListener { void onNodeAck(DiscoveryNode node, @Nullable Exception e); void onTimeout(); @@ -90,9 +77,4 @@ public FailedToCommitClusterStateException(String msg, Throwable cause, Object.. */ void startInitialJoin(); - /*** - * @return the current value of minimum master nodes, or -1 for not set - */ - int getMinimumMasterNodes(); - } diff --git a/core/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java b/core/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java index b2367c6e95377..c410cb88d691b 100644 --- a/core/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java +++ b/core/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java @@ -86,8 +86,8 @@ public DiscoveryModule(Settings settings, ThreadPool threadPool, TransportServic discoveryTypes.put("zen", () -> new ZenDiscovery(settings, threadPool, transportService, namedWriteableRegistry, masterService, clusterApplier, clusterSettings, hostsProvider, allocationService)); - discoveryTypes.put("tribe", () -> new TribeDiscovery(settings, transportService, clusterApplier)); - discoveryTypes.put("single-node", () -> new SingleNodeDiscovery(settings, transportService, clusterApplier)); + discoveryTypes.put("tribe", () -> new TribeDiscovery(settings, transportService, masterService, clusterApplier)); + discoveryTypes.put("single-node", () -> new SingleNodeDiscovery(settings, transportService, masterService, clusterApplier)); for (DiscoveryPlugin plugin : plugins) { plugin.getDiscoveryTypes(threadPool, transportService, namedWriteableRegistry, masterService, clusterApplier, clusterSettings, hostsProvider, allocationService).entrySet().forEach(entry -> { diff --git a/core/src/main/java/org/elasticsearch/discovery/TribeDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/TribeDiscovery.java index 9f802cc270adf..f3200be4562e8 100644 --- a/core/src/main/java/org/elasticsearch/discovery/TribeDiscovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/TribeDiscovery.java @@ -25,6 +25,7 @@ import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterApplier; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.discovery.single.SingleNodeDiscovery; @@ -44,26 +45,23 @@ public class TribeDiscovery extends SingleNodeDiscovery implements Discovery { @Inject - public TribeDiscovery(Settings settings, TransportService transportService, ClusterApplier clusterApplier) { - super(settings, transportService, clusterApplier); + public TribeDiscovery(Settings settings, TransportService transportService, + MasterService masterService, ClusterApplier clusterApplier) { + super(settings, transportService, masterService, clusterApplier); } @Override - public synchronized ClusterState getInitialClusterState() { - if (initialState == null) { - ClusterBlocks.Builder clusterBlocks = ClusterBlocks.builder(); // don't add no_master / state recovery block - if (BLOCKS_WRITE_SETTING.get(settings)) { - clusterBlocks.addGlobalBlock(TRIBE_WRITE_BLOCK); - } - if (BLOCKS_METADATA_SETTING.get(settings)) { - clusterBlocks.addGlobalBlock(TRIBE_METADATA_BLOCK); - } - DiscoveryNode localNode = transportService.getLocalNode(); - initialState = ClusterState.builder(ClusterName.CLUSTER_NAME_SETTING.get(settings)) - .nodes(DiscoveryNodes.builder().add(localNode).localNodeId(localNode.getId()).build()) - .blocks(clusterBlocks).build(); + protected ClusterState createInitialState(DiscoveryNode localNode) { + ClusterBlocks.Builder clusterBlocks = ClusterBlocks.builder(); // don't add no_master / state recovery block + if (BLOCKS_WRITE_SETTING.get(settings)) { + clusterBlocks.addGlobalBlock(TRIBE_WRITE_BLOCK); } - return initialState; + if (BLOCKS_METADATA_SETTING.get(settings)) { + clusterBlocks.addGlobalBlock(TRIBE_METADATA_BLOCK); + } + return ClusterState.builder(ClusterName.CLUSTER_NAME_SETTING.get(settings)) + .nodes(DiscoveryNodes.builder().add(localNode).localNodeId(localNode.getId()).build()) + .blocks(clusterBlocks).build(); } @Override diff --git a/core/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java index 11526961797c7..a61253b7c2a4b 100644 --- a/core/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java @@ -28,6 +28,7 @@ import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterApplier; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.discovery.Discovery; @@ -48,13 +49,13 @@ public class SingleNodeDiscovery extends AbstractLifecycleComponent implements D protected final TransportService transportService; private final ClusterApplier clusterApplier; - protected volatile ClusterState initialState; private volatile ClusterState clusterState; public SingleNodeDiscovery(final Settings settings, final TransportService transportService, - ClusterApplier clusterApplier) { + final MasterService masterService, final ClusterApplier clusterApplier) { super(Objects.requireNonNull(settings)); this.transportService = Objects.requireNonNull(transportService); + masterService.setClusterStateSupplier(() -> clusterState); this.clusterApplier = clusterApplier; } @@ -82,7 +83,7 @@ public void onFailure(String source, Exception e) { e); } }; - clusterApplier.onNewClusterState("apply-locally-on-node[" + event.source() + "]", this::clusterState, listener); + clusterApplier.onNewClusterState("apply-locally-on-node[" + event.source() + "]", () -> clusterState, listener); try { latch.await(); @@ -91,27 +92,6 @@ public void onFailure(String source, Exception e) { } } - @Override - public synchronized ClusterState getInitialClusterState() { - if (initialState == null) { - DiscoveryNode localNode = transportService.getLocalNode(); - initialState = ClusterState.builder(ClusterName.CLUSTER_NAME_SETTING.get(settings)) - .nodes(DiscoveryNodes.builder().add(localNode) - .localNodeId(localNode.getId()) - .masterNodeId(localNode.getId()) - .build()) - .blocks(ClusterBlocks.builder() - .addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)) - .build(); - } - return initialState; - } - - @Override - public ClusterState clusterState() { - return clusterState; - } - @Override public DiscoveryStats stats() { return new DiscoveryStats((PendingClusterStateStats) null); @@ -119,21 +99,32 @@ public DiscoveryStats stats() { @Override public synchronized void startInitialJoin() { + if (lifecycle.started() == false) { + throw new IllegalStateException("can't start initial join when not started"); + } // apply a fresh cluster state just so that state recovery gets triggered by GatewayService // TODO: give discovery module control over GatewayService - clusterState = ClusterState.builder(getInitialClusterState()).build(); - clusterApplier.onNewClusterState("single-node-start-initial-join", this::clusterState, (source, e) -> {}); + clusterState = ClusterState.builder(clusterState).build(); + clusterApplier.onNewClusterState("single-node-start-initial-join", () -> clusterState, (source, e) -> {}); } @Override - public int getMinimumMasterNodes() { - return 1; + protected synchronized void doStart() { + // set initial state + DiscoveryNode localNode = transportService.getLocalNode(); + clusterState = createInitialState(localNode); + clusterApplier.setInitialState(clusterState); } - @Override - protected synchronized void doStart() { - initialState = getInitialClusterState(); - clusterState = initialState; + protected ClusterState createInitialState(DiscoveryNode localNode) { + return ClusterState.builder(ClusterName.CLUSTER_NAME_SETTING.get(settings)) + .nodes(DiscoveryNodes.builder().add(localNode) + .localNodeId(localNode.getId()) + .masterNodeId(localNode.getId()) + .build()) + .blocks(ClusterBlocks.builder() + .addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)) + .build(); } @Override diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/UnicastZenPing.java b/core/src/main/java/org/elasticsearch/discovery/zen/UnicastZenPing.java index 3a68b2b4cd8a3..d0a9a212bdd14 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/UnicastZenPing.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/UnicastZenPing.java @@ -116,7 +116,7 @@ public class UnicastZenPing extends AbstractComponent implements ZenPing { private final int limitPortCounts; - private volatile PingContextProvider contextProvider; + private final PingContextProvider contextProvider; private final AtomicInteger pingingRoundIdGenerator = new AtomicInteger(); @@ -137,12 +137,13 @@ public class UnicastZenPing extends AbstractComponent implements ZenPing { private volatile boolean closed = false; public UnicastZenPing(Settings settings, ThreadPool threadPool, TransportService transportService, - UnicastHostsProvider unicastHostsProvider) { + UnicastHostsProvider unicastHostsProvider, PingContextProvider contextProvider) { super(settings); this.threadPool = threadPool; this.transportService = transportService; this.clusterName = ClusterName.CLUSTER_NAME_SETTING.get(settings); this.hostsProvider = unicastHostsProvider; + this.contextProvider = contextProvider; final int concurrentConnects = DISCOVERY_ZEN_PING_UNICAST_CONCURRENT_CONNECTS_SETTING.get(settings); if (DISCOVERY_ZEN_PING_UNICAST_HOSTS_SETTING.exists(settings)) { @@ -260,8 +261,7 @@ public void close() { } @Override - public void start(PingContextProvider contextProvider) { - this.contextProvider = contextProvider; + public void start() { } /** diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java index d08b148554b4b..09e6357ba5629 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java @@ -143,9 +143,8 @@ public class ZenDiscovery extends AbstractLifecycleComponent implements Discover private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; private final ClusterApplier clusterApplier; - private final AtomicReference state; // last committed cluster state + private final AtomicReference committedState; // last committed cluster state private final Object stateMutex = new Object(); - private volatile ClusterState initialState; // set lazily when discovery layer is started public ZenDiscovery(Settings settings, ThreadPool threadPool, TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, MasterService masterService, ClusterApplier clusterApplier, @@ -165,7 +164,7 @@ public ZenDiscovery(Settings settings, ThreadPool threadPool, TransportService t this.sendLeaveRequest = SEND_LEAVE_REQUEST_SETTING.get(settings); this.threadPool = threadPool; this.clusterName = ClusterName.CLUSTER_NAME_SETTING.get(settings); - this.state = new AtomicReference<>(); + this.committedState = new AtomicReference<>(); this.masterElectionIgnoreNonMasters = MASTER_ELECTION_IGNORE_NON_MASTER_PINGS_SETTING.get(settings); this.masterElectionWaitForJoinsTimeout = MASTER_ELECTION_WAIT_FOR_JOINS_TIMEOUT_SETTING.get(settings); @@ -214,6 +213,8 @@ public ZenDiscovery(Settings settings, ThreadPool threadPool, TransportService t this.nodeJoinController = new NodeJoinController(masterService, allocationService, electMaster, settings); this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, electMaster, this::submitRejoin, logger); + masterService.setClusterStateSupplier(this::clusterState); + transportService.registerRequestHandler( DISCOVERY_REJOIN_ACTION_NAME, RejoinClusterRequest::new, ThreadPool.Names.SAME, new RejoinClusterRequestHandler()); } @@ -221,7 +222,7 @@ public ZenDiscovery(Settings settings, ThreadPool threadPool, TransportService t // protected to allow overriding in tests protected ZenPing newZenPing(Settings settings, ThreadPool threadPool, TransportService transportService, UnicastHostsProvider hostsProvider) { - return new UnicastZenPing(settings, threadPool, transportService, hostsProvider); + return new UnicastZenPing(settings, threadPool, transportService, hostsProvider, this); } @Override @@ -229,12 +230,21 @@ protected void doStart() { DiscoveryNode localNode = transportService.getLocalNode(); assert localNode != null; synchronized (stateMutex) { - initialState = getInitialClusterState(); - state.set(initialState); + // set initial state + assert committedState.get() == null; + assert localNode != null; + ClusterState initialState = ClusterState.builder(clusterName) + .blocks(ClusterBlocks.builder() + .addGlobalBlock(STATE_NOT_RECOVERED_BLOCK) + .addGlobalBlock(discoverySettings.getNoMasterBlock())) + .nodes(DiscoveryNodes.builder().add(localNode).localNodeId(localNode.getId())) + .build(); + committedState.set(initialState); + clusterApplier.setInitialState(initialState); nodesFD.setLocalNode(localNode); joinThreadControl.start(); } - zenPing.start(this); + zenPing.start(); } @Override @@ -286,7 +296,7 @@ protected void doClose() throws IOException { @Override public ClusterState clusterState() { - ClusterState clusterState = state.get(); + ClusterState clusterState = committedState.get(); assert clusterState != null : "accessing cluster state before it is set"; return clusterState; } @@ -297,7 +307,7 @@ public void publish(ClusterChangedEvent clusterChangedEvent, AckListener ackList assert newState.getNodes().isLocalNodeElectedMaster() : "Shouldn't publish state when not master " + clusterChangedEvent.source(); // state got changed locally (maybe because another master published to us) - if (clusterChangedEvent.previousState() != this.state.get()) { + if (clusterChangedEvent.previousState() != this.committedState.get()) { throw new FailedToCommitClusterStateException("state was mutated while calculating new CS update"); } @@ -345,7 +355,7 @@ public void onNewClusterStateFailed(Exception e) { }); synchronized (stateMutex) { - if (clusterChangedEvent.previousState() != this.state.get()) { + if (clusterChangedEvent.previousState() != this.committedState.get()) { throw new FailedToCommitClusterStateException("local state was mutated while CS update was published to other nodes"); } @@ -371,22 +381,6 @@ public void onNewClusterStateFailed(Exception e) { } } - @Override - public synchronized ClusterState getInitialClusterState() { - if (initialState == null) { - assert state.get() == null; - DiscoveryNode localNode = transportService.getLocalNode(); - assert localNode != null; - initialState = ClusterState.builder(clusterName) - .blocks(ClusterBlocks.builder() - .addGlobalBlock(STATE_NOT_RECOVERED_BLOCK) - .addGlobalBlock(discoverySettings.getNoMasterBlock())) - .nodes(DiscoveryNodes.builder().add(localNode).localNodeId(localNode.getId())) - .build(); - } - return initialState; - } - /** * Gets the current set of nodes involved in the node fault detection. * NB: for testing purposes @@ -405,11 +399,6 @@ public DiscoverySettings getDiscoverySettings() { return discoverySettings; } - @Override - public int getMinimumMasterNodes() { - return electMaster.minimumMasterNodes(); - } - /** * returns true if zen discovery is started and there is a currently a background thread active for (re)joining * the cluster used for testing. @@ -548,9 +537,9 @@ private void submitRejoin(String source) { } // visible for testing - void setState(ClusterState clusterState) { + void setCommittedState(ClusterState clusterState) { synchronized (stateMutex) { - state.set(clusterState); + committedState.set(clusterState); } } @@ -693,7 +682,7 @@ private void handleMinimumMasterNodesChanged(final int minimumMasterNodes) { } synchronized (stateMutex) { // check if we have enough master nodes, if not, we need to move into joining the cluster again - if (!electMaster.hasEnoughMasterNodes(state.get().nodes())) { + if (!electMaster.hasEnoughMasterNodes(committedState.get().nodes())) { rejoin("not enough master nodes on change of minimum_master_nodes from [" + prevMinimumMasterNode + "] to [" + minimumMasterNodes + "]"); } } @@ -712,7 +701,7 @@ private void handleMasterGone(final DiscoveryNode masterNode, final Throwable ca logger.info((Supplier) () -> new ParameterizedMessage("master_left [{}], reason [{}]", masterNode, reason), cause); synchronized (stateMutex) { - if (localNodeMaster() == false && masterNode.equals(state.get().nodes().getMasterNode())) { + if (localNodeMaster() == false && masterNode.equals(committedState.get().nodes().getMasterNode())) { // flush any pending cluster states from old master, so it will not be set as master again publishClusterState.pendingStatesQueue().failAllStatesAndClear(new ElasticsearchException("master left [{}]", reason)); rejoin("master left (reason = " + reason + ")"); @@ -725,7 +714,7 @@ boolean processNextCommittedClusterState(String reason) { assert Thread.holdsLock(stateMutex); final ClusterState newClusterState = publishClusterState.pendingStatesQueue().getNextClusterStateToProcess(); - final ClusterState currentState = state.get(); + final ClusterState currentState = committedState.get(); final ClusterState adaptedNewClusterState; // all pending states have been processed if (newClusterState == null) { @@ -801,7 +790,7 @@ boolean processNextCommittedClusterState(String reason) { return false; } - state.set(adaptedNewClusterState); + committedState.set(adaptedNewClusterState); // update failure detection only after the state has been updated to prevent race condition with handleLeaveRequest // and handleNodeFailure as those check the current state to determine whether the failure is to be handled by this node @@ -997,7 +986,7 @@ static List filterPingResponses(List protected void rejoin(String reason) { assert Thread.holdsLock(stateMutex); - ClusterState clusterState = state.get(); + ClusterState clusterState = committedState.get(); logger.warn("{}, current nodes: {}", reason, clusterState.nodes()); nodesFD.stop(); @@ -1021,7 +1010,7 @@ protected void rejoin(String reason) { .nodes(discoveryNodes) .build(); - state.set(clusterState); + committedState.set(clusterState); clusterApplier.onNewClusterState(reason, this::clusterState, (source, e) -> {}); // don't wait for state to be applied } } @@ -1123,7 +1112,7 @@ public void onPingReceived(final NodesFaultDetection.PingRequest pingRequest) { } logger.debug("got a ping from another master {}. resolving who should rejoin. current ping count: [{}]", pingRequest.masterNode(), pingsWhileMaster.get()); synchronized (stateMutex) { - ClusterState currentState = state.get(); + ClusterState currentState = committedState.get(); if (currentState.nodes().isLocalNodeElectedMaster()) { pingsWhileMaster.set(0); handleAnotherMaster(currentState, pingRequest.masterNode(), pingRequest.clusterStateVersion(), "node fd ping"); diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/ZenPing.java b/core/src/main/java/org/elasticsearch/discovery/zen/ZenPing.java index 016d2a5423ce1..d91dad8aee94f 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/ZenPing.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/ZenPing.java @@ -40,7 +40,7 @@ public interface ZenPing extends Releasable { - void start(PingContextProvider contextProvider); + void start(); void ping(Consumer resultsConsumer, TimeValue timeout); diff --git a/core/src/main/java/org/elasticsearch/gateway/Gateway.java b/core/src/main/java/org/elasticsearch/gateway/Gateway.java index 0d562fa4b2b63..4407e97d5a148 100644 --- a/core/src/main/java/org/elasticsearch/gateway/Gateway.java +++ b/core/src/main/java/org/elasticsearch/gateway/Gateway.java @@ -32,13 +32,12 @@ import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.discovery.Discovery; +import org.elasticsearch.discovery.zen.ElectMasterService; import org.elasticsearch.index.Index; import org.elasticsearch.indices.IndicesService; import java.util.Arrays; import java.util.Map; -import java.util.function.Supplier; public class Gateway extends AbstractComponent implements ClusterStateApplier { @@ -48,18 +47,18 @@ public class Gateway extends AbstractComponent implements ClusterStateApplier { private final TransportNodesListGatewayMetaState listGatewayMetaState; - private final Supplier minimumMasterNodesProvider; + private final int minimumMasterNodes; private final IndicesService indicesService; public Gateway(Settings settings, ClusterService clusterService, GatewayMetaState metaState, - TransportNodesListGatewayMetaState listGatewayMetaState, Discovery discovery, + TransportNodesListGatewayMetaState listGatewayMetaState, IndicesService indicesService) { super(settings); this.indicesService = indicesService; this.clusterService = clusterService; this.metaState = metaState; this.listGatewayMetaState = listGatewayMetaState; - this.minimumMasterNodesProvider = discovery::getMinimumMasterNodes; + this.minimumMasterNodes = ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING.get(settings); clusterService.addLowPriorityApplier(this); } @@ -69,7 +68,7 @@ public void performStateRecovery(final GatewayStateRecoveredListener listener) t TransportNodesListGatewayMetaState.NodesGatewayMetaState nodesState = listGatewayMetaState.list(nodesIds, null).actionGet(); - int requiredAllocation = Math.max(1, minimumMasterNodesProvider.get()); + int requiredAllocation = Math.max(1, minimumMasterNodes); if (nodesState.hasFailures()) { diff --git a/core/src/main/java/org/elasticsearch/gateway/GatewayService.java b/core/src/main/java/org/elasticsearch/gateway/GatewayService.java index 0353deab6e5f1..6e884af3b8948 100644 --- a/core/src/main/java/org/elasticsearch/gateway/GatewayService.java +++ b/core/src/main/java/org/elasticsearch/gateway/GatewayService.java @@ -42,7 +42,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.AbstractRunnable; -import org.elasticsearch.discovery.Discovery; import org.elasticsearch.indices.IndicesService; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.threadpool.ThreadPool; @@ -93,10 +92,10 @@ public class GatewayService extends AbstractLifecycleComponent implements Cluste @Inject public GatewayService(Settings settings, AllocationService allocationService, ClusterService clusterService, ThreadPool threadPool, GatewayMetaState metaState, - TransportNodesListGatewayMetaState listGatewayMetaState, Discovery discovery, + TransportNodesListGatewayMetaState listGatewayMetaState, IndicesService indicesService) { super(settings); - this.gateway = new Gateway(settings, clusterService, metaState, listGatewayMetaState, discovery, + this.gateway = new Gateway(settings, clusterService, metaState, listGatewayMetaState, indicesService); this.allocationService = allocationService; this.clusterService = clusterService; @@ -227,10 +226,6 @@ protected void doRun() throws Exception { } } - public Gateway getGateway() { - return gateway; - } - class GatewayRecoveryListener implements Gateway.GatewayStateRecoveredListener { @Override diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index 0643c6b96a170..08f9856c76647 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -684,11 +684,9 @@ public Node start() throws NodeValidationException { .flatMap(p -> p.getBootstrapChecks().stream()).collect(Collectors.toList())); clusterService.addStateApplier(transportService.getTaskManager()); - clusterService.getMasterService().setClusterStateSupplier(discovery::clusterState); - clusterService.getClusterApplierService().setInitialState(discovery.getInitialClusterState()); // start after transport service so the local disco is known + discovery.start(); // start before cluster service so that it can set initial state on ClusterApplierService clusterService.start(); - discovery.start(); assert clusterService.localNode().equals(localNodeFactory.getNode()) : "clusterService has a different local node than the factory provided"; transportService.acceptIncomingRequests(); diff --git a/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java b/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java index d2a520c32846e..d1b86aeaa1608 100644 --- a/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java +++ b/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java @@ -85,14 +85,6 @@ public void testDoesNotRespondToZenPings() throws Exception { final UnicastHostsProvider provider = () -> Collections.singletonList(nodeTransport.getLocalNode()); final CountDownLatch latch = new CountDownLatch(1); - final UnicastZenPing unicastZenPing = - new UnicastZenPing(settings, threadPool, pingTransport, provider) { - @Override - protected void finishPingingRound(PingingRound pingingRound) { - latch.countDown(); - super.finishPingingRound(pingingRound); - } - }; final DiscoveryNodes nodes = DiscoveryNodes.builder() .add(nodeTransport.getLocalNode()) .add(pingTransport.getLocalNode()) @@ -100,7 +92,15 @@ protected void finishPingingRound(PingingRound pingingRound) { .build(); final ClusterName clusterName = new ClusterName(internalCluster().getClusterName()); final ClusterState state = ClusterState.builder(clusterName).nodes(nodes).build(); - unicastZenPing.start(() -> state); + final UnicastZenPing unicastZenPing = + new UnicastZenPing(settings, threadPool, pingTransport, provider, () -> state) { + @Override + protected void finishPingingRound(PingingRound pingingRound) { + latch.countDown(); + super.finishPingingRound(pingingRound); + } + }; + unicastZenPing.start(); closeables.push(unicastZenPing); final CompletableFuture responses = new CompletableFuture<>(); unicastZenPing.ping(responses::complete, TimeValue.timeValueSeconds(3)); diff --git a/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryTests.java b/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryTests.java index 917894b60d74e..135692fd645f5 100644 --- a/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryTests.java @@ -21,9 +21,12 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.Version; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateTaskListener; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.cluster.service.ClusterApplier; +import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.transport.MockTransportService; @@ -32,8 +35,10 @@ import java.io.Closeable; import java.util.Stack; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; -import static org.elasticsearch.test.ClusterServiceUtils.createClusterService; +import static org.elasticsearch.test.ClusterServiceUtils.createMasterService; import static org.hamcrest.Matchers.equalTo; public class SingleNodeDiscoveryTests extends ESTestCase { @@ -49,13 +54,26 @@ public void testInitialJoin() throws Exception { stack.push(transportService); transportService.start(); final DiscoveryNode node = transportService.getLocalNode(); - final ClusterService clusterService = createClusterService(threadPool, node); - stack.push(clusterService); + final MasterService masterService = createMasterService(threadPool, node); + AtomicReference clusterState = new AtomicReference<>(); final SingleNodeDiscovery discovery = new SingleNodeDiscovery(Settings.EMPTY, transportService, - clusterService.getClusterApplierService()); + masterService, new ClusterApplier() { + @Override + public void setInitialState(ClusterState initialState) { + clusterState.set(initialState); + } + + @Override + public void onNewClusterState(String source, Supplier clusterStateSupplier, + ClusterStateTaskListener listener) { + clusterState.set(clusterStateSupplier.get()); + listener.clusterStateProcessed(source, clusterState.get(), clusterState.get()); + } + }); + discovery.start(); discovery.startInitialJoin(); - final DiscoveryNodes nodes = discovery.getInitialClusterState().nodes(); + final DiscoveryNodes nodes = clusterState.get().nodes(); assertThat(nodes.getSize(), equalTo(1)); assertThat(nodes.getMasterNode().getId(), equalTo(node.getId())); } finally { diff --git a/core/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java b/core/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java index fa53f94f42cab..2ed44586f73a4 100644 --- a/core/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java @@ -188,40 +188,40 @@ public void connectToNode(DiscoveryNode node, ConnectionProfile connectionProfil .build(); Settings hostsSettingsMismatch = Settings.builder().put(hostsSettings).put(settingsMismatch).build(); - TestUnicastZenPing zenPingA = new TestUnicastZenPing(hostsSettings, threadPool, handleA, EMPTY_HOSTS_PROVIDER); ClusterState stateA = ClusterState.builder(state) .blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)) .nodes(DiscoveryNodes.builder().add(handleA.node).localNodeId("UZP_A")) .build(); - zenPingA.start(() -> stateA); + TestUnicastZenPing zenPingA = new TestUnicastZenPing(hostsSettings, threadPool, handleA, EMPTY_HOSTS_PROVIDER, () -> stateA); + zenPingA.start(); closeables.push(zenPingA); - TestUnicastZenPing zenPingB = new TestUnicastZenPing(hostsSettings, threadPool, handleB, EMPTY_HOSTS_PROVIDER); ClusterState stateB = ClusterState.builder(state) .nodes(DiscoveryNodes.builder().add(handleB.node).localNodeId("UZP_B")) .build(); - zenPingB.start(() -> stateB); + TestUnicastZenPing zenPingB = new TestUnicastZenPing(hostsSettings, threadPool, handleB, EMPTY_HOSTS_PROVIDER, () -> stateB); + zenPingB.start(); closeables.push(zenPingB); + ClusterState stateC = ClusterState.builder(stateMismatch) + .nodes(DiscoveryNodes.builder().add(handleC.node).localNodeId("UZP_C")) + .build(); TestUnicastZenPing zenPingC = new TestUnicastZenPing(hostsSettingsMismatch, threadPool, handleC, - EMPTY_HOSTS_PROVIDER) { + EMPTY_HOSTS_PROVIDER, () -> stateC) { @Override protected Version getVersion() { return versionD; } }; - ClusterState stateC = ClusterState.builder(stateMismatch) - .nodes(DiscoveryNodes.builder().add(handleC.node).localNodeId("UZP_C")) - .build(); - zenPingC.start(() -> stateC); + zenPingC.start(); closeables.push(zenPingC); - TestUnicastZenPing zenPingD = new TestUnicastZenPing(hostsSettingsMismatch, threadPool, handleD, - EMPTY_HOSTS_PROVIDER); ClusterState stateD = ClusterState.builder(stateMismatch) .nodes(DiscoveryNodes.builder().add(handleD.node).localNodeId("UZP_D")) .build(); - zenPingD.start(() -> stateD); + TestUnicastZenPing zenPingD = new TestUnicastZenPing(hostsSettingsMismatch, threadPool, handleD, + EMPTY_HOSTS_PROVIDER, () -> stateD); + zenPingD.start(); closeables.push(zenPingD); logger.info("ping from UZP_A"); @@ -311,26 +311,26 @@ public TransportAddress[] addressesFromString(String address, int perAddressLimi final ClusterState state = ClusterState.builder(new ClusterName("test")).version(randomNonNegativeLong()).build(); - final TestUnicastZenPing zenPingA = new TestUnicastZenPing(hostsSettings, threadPool, handleA, EMPTY_HOSTS_PROVIDER); ClusterState stateA = ClusterState.builder(state) .blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)) .nodes(DiscoveryNodes.builder().add(handleA.node).localNodeId("UZP_A")) .build(); - zenPingA.start(() -> stateA); + final TestUnicastZenPing zenPingA = new TestUnicastZenPing(hostsSettings, threadPool, handleA, EMPTY_HOSTS_PROVIDER, () -> stateA); + zenPingA.start(); closeables.push(zenPingA); - TestUnicastZenPing zenPingB = new TestUnicastZenPing(hostsSettings, threadPool, handleB, EMPTY_HOSTS_PROVIDER); ClusterState stateB = ClusterState.builder(state) .nodes(DiscoveryNodes.builder().add(handleB.node).localNodeId("UZP_B")) .build(); - zenPingB.start(() -> stateB); + TestUnicastZenPing zenPingB = new TestUnicastZenPing(hostsSettings, threadPool, handleB, EMPTY_HOSTS_PROVIDER, () -> stateB); + zenPingB.start(); closeables.push(zenPingB); - TestUnicastZenPing zenPingC = new TestUnicastZenPing(hostsSettings, threadPool, handleC, EMPTY_HOSTS_PROVIDER); ClusterState stateC = ClusterState.builder(state) .nodes(DiscoveryNodes.builder().add(handleC.node).localNodeId("UZP_C")) .build(); - zenPingC.start(() -> stateC); + TestUnicastZenPing zenPingC = new TestUnicastZenPing(hostsSettings, threadPool, handleC, EMPTY_HOSTS_PROVIDER, () -> stateC); + zenPingC.start(); closeables.push(zenPingC); // the presence of an unresolvable host should not prevent resolvable hosts from being pinged @@ -609,19 +609,19 @@ public void onConnectionOpened(DiscoveryNode node) { } }); - final TestUnicastZenPing zenPingA = new TestUnicastZenPing(hostsSettings, threadPool, handleA, EMPTY_HOSTS_PROVIDER); final ClusterState stateA = ClusterState.builder(state) .blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)) .nodes(DiscoveryNodes.builder().add(handleA.node).add(handleB.node).localNodeId("UZP_A")) .build(); - zenPingA.start(() -> stateA); + final TestUnicastZenPing zenPingA = new TestUnicastZenPing(hostsSettings, threadPool, handleA, EMPTY_HOSTS_PROVIDER, () -> stateA); + zenPingA.start(); closeables.push(zenPingA); - TestUnicastZenPing zenPingB = new TestUnicastZenPing(hostsSettings, threadPool, handleB, EMPTY_HOSTS_PROVIDER); final ClusterState stateB = ClusterState.builder(state) .nodes(DiscoveryNodes.builder().add(handleB.node).localNodeId("UZP_B")) .build(); - zenPingB.start(() -> stateB); + TestUnicastZenPing zenPingB = new TestUnicastZenPing(hostsSettings, threadPool, handleB, EMPTY_HOSTS_PROVIDER, () -> stateB); + zenPingB.start(); closeables.push(zenPingB); Collection pingResponses = zenPingA.pingAndWait().toList(); @@ -660,15 +660,15 @@ public void testPingingTemporalPings() throws ExecutionException, InterruptedExc .blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)) .nodes(DiscoveryNodes.builder().add(handleA.node).add(handleB.node).localNodeId("UZP_A")).build(); - final TestUnicastZenPing zenPingA = new TestUnicastZenPing(hostsSettings, threadPool, handleA, EMPTY_HOSTS_PROVIDER); - zenPingA.start(() -> stateA); + final TestUnicastZenPing zenPingA = new TestUnicastZenPing(hostsSettings, threadPool, handleA, EMPTY_HOSTS_PROVIDER, () -> stateA); + zenPingA.start(); closeables.push(zenPingA); // Node B doesn't know about A! final ClusterState stateB = ClusterState.builder(state).nodes( DiscoveryNodes.builder().add(handleB.node).localNodeId("UZP_B")).build(); - TestUnicastZenPing zenPingB = new TestUnicastZenPing(hostsSettings, threadPool, handleB, EMPTY_HOSTS_PROVIDER); - zenPingB.start(() -> stateB); + TestUnicastZenPing zenPingB = new TestUnicastZenPing(hostsSettings, threadPool, handleB, EMPTY_HOSTS_PROVIDER, () -> stateB); + zenPingB.start(); closeables.push(zenPingB); { @@ -796,9 +796,9 @@ private static class NetworkHandle { private static class TestUnicastZenPing extends UnicastZenPing { TestUnicastZenPing(Settings settings, ThreadPool threadPool, NetworkHandle networkHandle, - UnicastHostsProvider unicastHostsProvider) { + UnicastHostsProvider unicastHostsProvider, PingContextProvider contextProvider) { super(Settings.builder().put("node.name", networkHandle.node.getName()).put(settings).build(), - threadPool, networkHandle.transportService, unicastHostsProvider); + threadPool, networkHandle.transportService, unicastHostsProvider, contextProvider); } volatile CountDownLatch allTasksCompleted; diff --git a/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java b/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java index d0c138954ab82..b961b6d6fbdbe 100644 --- a/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java @@ -26,6 +26,8 @@ import org.elasticsearch.cluster.ClusterModule; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateTaskListener; +import org.elasticsearch.cluster.ESAllocationTestCase; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -37,6 +39,7 @@ import org.elasticsearch.cluster.routing.ShardRoutingState; import org.elasticsearch.cluster.routing.TestShardRouting; import org.elasticsearch.cluster.routing.UnassignedInfo; +import org.elasticsearch.cluster.service.ClusterApplier; import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; @@ -67,7 +70,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; import java.util.stream.Collectors; import static java.util.Collections.emptyMap; @@ -195,7 +198,7 @@ public void testNodesUpdatedAfterClusterStatePublished() throws Exception { ZenDiscovery masterZen = buildZenDiscovery( settingsWithClusterName, masterTransport, masterMasterService, threadPool); - masterZen.setState(state); + masterZen.setCommittedState(state); toClose.addFirst(masterZen); masterTransport.acceptIncomingRequests(); @@ -209,7 +212,7 @@ public void testNodesUpdatedAfterClusterStatePublished() throws Exception { MasterService otherMasterService = ClusterServiceUtils.createMasterService(threadPool, otherNode); toClose.addFirst(otherMasterService); ZenDiscovery otherZen = buildZenDiscovery(settingsWithClusterName, otherTransport, otherMasterService, threadPool); - otherZen.setState(otherState); + otherZen.setCommittedState(otherState); toClose.addFirst(otherZen); otherTransport.acceptIncomingRequests(); @@ -262,7 +265,7 @@ public void testPendingCSQueueIsClearedWhenClusterStatePublished() throws Except toClose.addFirst(masterMasterService); state = ClusterState.builder(discoveryState(masterMasterService).getClusterName()).nodes(state.nodes()).build(); ZenDiscovery masterZen = buildZenDiscovery(settings, masterTransport, masterMasterService, threadPool); - masterZen.setState(state); + masterZen.setCommittedState(state); toClose.addFirst(masterZen); masterTransport.acceptIncomingRequests(); @@ -297,9 +300,19 @@ public void testPendingCSQueueIsClearedWhenClusterStatePublished() throws Except private ZenDiscovery buildZenDiscovery(Settings settings, TransportService service, MasterService masterService, ThreadPool threadPool) { ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); + ClusterApplier clusterApplier = new ClusterApplier() { + @Override + public void setInitialState(ClusterState initialState) { + + } + + @Override + public void onNewClusterState(String source, Supplier clusterStateSupplier, ClusterStateTaskListener listener) { + listener.clusterStateProcessed(source, clusterStateSupplier.get(), clusterStateSupplier.get()); + } + }; ZenDiscovery zenDiscovery = new ZenDiscovery(settings, threadPool, service, new NamedWriteableRegistry(ClusterModule.getNamedWriteables()), - masterService, (source, clusterStateSupplier, listener) -> listener.clusterStateProcessed(source, clusterStateSupplier.get(), clusterStateSupplier.get()), - clusterSettings, Collections::emptyList, null); + masterService, clusterApplier, clusterSettings, Collections::emptyList, ESAllocationTestCase.createAllocationService()); zenDiscovery.start(); return zenDiscovery; } diff --git a/core/src/test/java/org/elasticsearch/gateway/GatewayServiceTests.java b/core/src/test/java/org/elasticsearch/gateway/GatewayServiceTests.java index 2bec3d5eded89..3e4a3dce09153 100644 --- a/core/src/test/java/org/elasticsearch/gateway/GatewayServiceTests.java +++ b/core/src/test/java/org/elasticsearch/gateway/GatewayServiceTests.java @@ -24,7 +24,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.NoopDiscovery; import org.hamcrest.Matchers; import java.io.IOException; @@ -36,7 +35,7 @@ private GatewayService createService(Settings.Builder settings) { new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), null); return new GatewayService(settings.build(), - null, clusterService, null, null, null, new NoopDiscovery(), null); + null, clusterService, null, null, null, null); } public void testDefaultRecoverAfterTime() throws IOException { diff --git a/core/src/test/java/org/elasticsearch/test/NoopDiscovery.java b/core/src/test/java/org/elasticsearch/test/NoopDiscovery.java index 5b33fb1c8deb3..bcb9efd5b5e91 100644 --- a/core/src/test/java/org/elasticsearch/test/NoopDiscovery.java +++ b/core/src/test/java/org/elasticsearch/test/NoopDiscovery.java @@ -19,8 +19,6 @@ package org.elasticsearch.test; import org.elasticsearch.cluster.ClusterChangedEvent; -import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.common.component.Lifecycle; import org.elasticsearch.common.component.LifecycleListener; import org.elasticsearch.discovery.Discovery; @@ -33,16 +31,6 @@ public void publish(ClusterChangedEvent clusterChangedEvent, AckListener ackList } - @Override - public ClusterState getInitialClusterState() { - return null; - } - - @Override - public ClusterState clusterState() { - return null; - } - @Override public DiscoveryStats stats() { return null; @@ -53,11 +41,6 @@ public void startInitialJoin() { } - @Override - public int getMinimumMasterNodes() { - return -1; - } - @Override public Lifecycle.State lifecycleState() { return null; diff --git a/test/framework/src/main/java/org/elasticsearch/test/discovery/MockZenPing.java b/test/framework/src/main/java/org/elasticsearch/test/discovery/MockZenPing.java index b03e157b01cdb..4f2a3c5b1c99e 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/discovery/MockZenPing.java +++ b/test/framework/src/main/java/org/elasticsearch/test/discovery/MockZenPing.java @@ -43,16 +43,15 @@ public final class MockZenPing extends AbstractComponent implements ZenPing { /** a set of the last discovered pings. used to throttle busy spinning where MockZenPing will keep returning the same results */ private Set lastDiscoveredPings = null; - private volatile PingContextProvider contextProvider; + private final PingContextProvider contextProvider; - public MockZenPing(Settings settings) { + public MockZenPing(Settings settings, PingContextProvider contextProvider) { super(settings); + this.contextProvider = contextProvider; } @Override - public void start(PingContextProvider contextProvider) { - this.contextProvider = contextProvider; - assert contextProvider != null; + public void start() { synchronized (activeNodesPerCluster) { boolean added = getActiveNodesForCurrentCluster().add(this); assert added; diff --git a/test/framework/src/main/java/org/elasticsearch/test/discovery/TestZenDiscovery.java b/test/framework/src/main/java/org/elasticsearch/test/discovery/TestZenDiscovery.java index af1bbc94d27ab..be6792d463d97 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/discovery/TestZenDiscovery.java +++ b/test/framework/src/main/java/org/elasticsearch/test/discovery/TestZenDiscovery.java @@ -90,7 +90,7 @@ private TestZenDiscovery(Settings settings, ThreadPool threadPool, TransportServ protected ZenPing newZenPing(Settings settings, ThreadPool threadPool, TransportService transportService, UnicastHostsProvider hostsProvider) { if (USE_MOCK_PINGS.get(settings)) { - return new MockZenPing(settings); + return new MockZenPing(settings, this); } else { return super.newZenPing(settings, threadPool, transportService, hostsProvider); } From be2a6ce80b2282779159bc017352aa6f216349e2 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Fri, 12 May 2017 15:40:40 +0200 Subject: [PATCH 328/619] Notify onConnectionClosed rather than onNodeDisconnect to prune transport handlers (#24639) Today we prune transport handlers in TransportService when a node is disconnected. This can cause connections to starve in the TransportService if the connection is opened as a short living connection ie. without sharing the connection to a node via registering in the transport itself. This change now moves to pruning based on the connections cache key to ensure we notify handlers as soon as the connection is closed for all connections not just for registered connections. Relates to #24632 Relates to #24575 Relates to #24557 --- .../elasticsearch/transport/TcpTransport.java | 11 +++- .../elasticsearch/transport/Transport.java | 8 +++ .../TransportConnectionListener.java | 8 ++- .../transport/TransportService.java | 39 ++++++++----- .../discovery/zen/UnicastZenPingTests.java | 4 +- .../transport/TCPTransportTests.java | 4 +- .../transport/netty4/Netty4Transport.java | 2 +- .../test/transport/MockTransportService.java | 5 ++ .../AbstractSimpleTransportTestCase.java | 58 ++++++++++++++++++- .../transport/MockTcpTransport.java | 3 +- 10 files changed, 114 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/transport/TcpTransport.java b/core/src/main/java/org/elasticsearch/transport/TcpTransport.java index 30a18e05611bf..11afec1bf1e5e 100644 --- a/core/src/main/java/org/elasticsearch/transport/TcpTransport.java +++ b/core/src/main/java/org/elasticsearch/transport/TcpTransport.java @@ -101,6 +101,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -357,8 +358,9 @@ public final class NodeChannels implements Connection { private final DiscoveryNode node; private final AtomicBoolean closed = new AtomicBoolean(false); private final Version version; + private final Consumer onClose; - public NodeChannels(DiscoveryNode node, Channel[] channels, ConnectionProfile connectionProfile) { + public NodeChannels(DiscoveryNode node, Channel[] channels, ConnectionProfile connectionProfile, Consumer onClose) { this.node = node; this.channels = channels; assert channels.length == connectionProfile.getNumConnections() : "expected channels size to be == " @@ -369,6 +371,7 @@ public NodeChannels(DiscoveryNode node, Channel[] channels, ConnectionProfile co typeMapping.put(type, handle); } version = node.getVersion(); + this.onClose = onClose; } NodeChannels(NodeChannels channels, Version handshakeVersion) { @@ -376,6 +379,7 @@ public NodeChannels(DiscoveryNode node, Channel[] channels, ConnectionProfile co this.channels = channels.channels; this.typeMapping = channels.typeMapping; this.version = handshakeVersion; + this.onClose = channels.onClose; } @Override @@ -408,6 +412,7 @@ public Channel channel(TransportRequestOptions.Type type) { public synchronized void close() throws IOException { if (closed.compareAndSet(false, true)) { closeChannels(Arrays.stream(channels).filter(Objects::nonNull).collect(Collectors.toList())); + onClose.accept(this); } } @@ -519,8 +524,8 @@ public final NodeChannels openConnection(DiscoveryNode node, ConnectionProfile c final TimeValue handshakeTimeout = connectionProfile.getHandshakeTimeout() == null ? connectTimeout : connectionProfile.getHandshakeTimeout(); final Version version = executeHandshake(node, channel, handshakeTimeout); - transportServiceAdapter.onConnectionOpened(node); - nodeChannels = new NodeChannels(nodeChannels, version);// clone the channels - we now have the correct version + transportServiceAdapter.onConnectionOpened(nodeChannels); + nodeChannels = new NodeChannels(nodeChannels, version); // clone the channels - we now have the correct version success = true; return nodeChannels; } catch (ConnectTransportException e) { diff --git a/core/src/main/java/org/elasticsearch/transport/Transport.java b/core/src/main/java/org/elasticsearch/transport/Transport.java index d3dcd8bb5c197..a32289332ead5 100644 --- a/core/src/main/java/org/elasticsearch/transport/Transport.java +++ b/core/src/main/java/org/elasticsearch/transport/Transport.java @@ -132,5 +132,13 @@ void sendRequest(long requestId, String action, TransportRequest request, Transp default Version getVersion() { return getNode().getVersion(); } + + /** + * Returns a key that this connection can be cached on. Delegating subclasses must delegate method call to + * the original connection. + */ + default Object getCacheKey() { + return this; + } } } diff --git a/core/src/main/java/org/elasticsearch/transport/TransportConnectionListener.java b/core/src/main/java/org/elasticsearch/transport/TransportConnectionListener.java index 3f277a0ee11b7..de767986b9f09 100644 --- a/core/src/main/java/org/elasticsearch/transport/TransportConnectionListener.java +++ b/core/src/main/java/org/elasticsearch/transport/TransportConnectionListener.java @@ -33,8 +33,14 @@ default void onNodeConnected(DiscoveryNode node) {} */ default void onNodeDisconnected(DiscoveryNode node) {} + /** + * Called once a node connection is closed. The connection might not have been registered in the + * transport as a shared connection to a specific node + */ + default void onConnectionClosed(Transport.Connection connection) {} + /** * Called once a node connection is opened. */ - default void onConnectionOpened(DiscoveryNode node) {} + default void onConnectionOpened(Transport.Connection connection) {} } diff --git a/core/src/main/java/org/elasticsearch/transport/TransportService.java b/core/src/main/java/org/elasticsearch/transport/TransportService.java index 7de9606361555..e5382e4e2617b 100644 --- a/core/src/main/java/org/elasticsearch/transport/TransportService.java +++ b/core/src/main/java/org/elasticsearch/transport/TransportService.java @@ -569,7 +569,7 @@ private void sendRequestInternal(final Transport.C } Supplier storedContextSupplier = threadPool.getThreadContext().newRestorableContext(true); TransportResponseHandler responseHandler = new ContextRestoreResponseHandler<>(storedContextSupplier, handler); - clientHandlers.put(requestId, new RequestHolder<>(responseHandler, connection.getNode(), action, timeoutHandler)); + clientHandlers.put(requestId, new RequestHolder<>(responseHandler, connection, action, timeoutHandler)); if (lifecycle.stoppedOrClosed()) { // if we are not started the exception handling will remove the RequestHolder again and calls the handler to notify // the caller. It will only notify if the toStop code hasn't done the work yet. @@ -810,7 +810,7 @@ public TransportResponseHandler onResponseReceived(final long requestId) { } holder.cancelTimeout(); if (traceEnabled() && shouldTraceAction(holder.action())) { - traceReceivedResponse(requestId, holder.node(), holder.action()); + traceReceivedResponse(requestId, holder.connection().getNode(), holder.action()); } return holder.handler(); } @@ -855,12 +855,12 @@ public void onNodeConnected(final DiscoveryNode node) { } @Override - public void onConnectionOpened(DiscoveryNode node) { + public void onConnectionOpened(Transport.Connection connection) { // capture listeners before spawning the background callback so the following pattern won't trigger a call // connectToNode(); connection is completed successfully // addConnectionListener(); this listener shouldn't be called final Stream listenersToNotify = TransportService.this.connectionListeners.stream(); - threadPool.generic().execute(() -> listenersToNotify.forEach(listener -> listener.onConnectionOpened(node))); + threadPool.generic().execute(() -> listenersToNotify.forEach(listener -> listener.onConnectionOpened(connection))); } @Override @@ -871,20 +871,28 @@ public void onNodeDisconnected(final DiscoveryNode node) { connectionListener.onNodeDisconnected(node); } }); + } catch (EsRejectedExecutionException ex) { + logger.debug("Rejected execution on NodeDisconnected", ex); + } + } + + @Override + public void onConnectionClosed(Transport.Connection connection) { + try { for (Map.Entry entry : clientHandlers.entrySet()) { RequestHolder holder = entry.getValue(); - if (holder.node().equals(node)) { + if (holder.connection().getCacheKey().equals(connection.getCacheKey())) { final RequestHolder holderToNotify = clientHandlers.remove(entry.getKey()); if (holderToNotify != null) { // callback that an exception happened, but on a different thread since we don't // want handlers to worry about stack overflows - threadPool.generic().execute(() -> holderToNotify.handler().handleException(new NodeDisconnectedException(node, - holderToNotify.action()))); + threadPool.generic().execute(() -> holderToNotify.handler().handleException(new NodeDisconnectedException( + connection.getNode(), holderToNotify.action()))); } } } } catch (EsRejectedExecutionException ex) { - logger.debug("Rejected execution on NodeDisconnected", ex); + logger.debug("Rejected execution on onConnectionClosed", ex); } } @@ -929,13 +937,14 @@ public void run() { if (holder != null) { // add it to the timeout information holder, in case we are going to get a response later long timeoutTime = System.currentTimeMillis(); - timeoutInfoHandlers.put(requestId, new TimeoutInfoHolder(holder.node(), holder.action(), sentTime, timeoutTime)); + timeoutInfoHandlers.put(requestId, new TimeoutInfoHolder(holder.connection().getNode(), holder.action(), sentTime, + timeoutTime)); // now that we have the information visible via timeoutInfoHandlers, we try to remove the request id final RequestHolder removedHolder = clientHandlers.remove(requestId); if (removedHolder != null) { assert removedHolder == holder : "two different holder instances for request [" + requestId + "]"; removedHolder.handler().handleException( - new ReceiveTimeoutTransportException(holder.node(), holder.action(), + new ReceiveTimeoutTransportException(holder.connection().getNode(), holder.action(), "request_id [" + requestId + "] timed out after [" + (timeoutTime - sentTime) + "ms]")); } else { // response was processed, remove timeout info. @@ -990,15 +999,15 @@ static class RequestHolder { private final TransportResponseHandler handler; - private final DiscoveryNode node; + private final Transport.Connection connection; private final String action; private final TimeoutHandler timeoutHandler; - RequestHolder(TransportResponseHandler handler, DiscoveryNode node, String action, TimeoutHandler timeoutHandler) { + RequestHolder(TransportResponseHandler handler, Transport.Connection connection, String action, TimeoutHandler timeoutHandler) { this.handler = handler; - this.node = node; + this.connection = connection; this.action = action; this.timeoutHandler = timeoutHandler; } @@ -1007,8 +1016,8 @@ public TransportResponseHandler handler() { return handler; } - public DiscoveryNode node() { - return this.node; + public Transport.Connection connection() { + return this.connection; } public String action() { diff --git a/core/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java b/core/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java index 2ed44586f73a4..6aa47d27bbd53 100644 --- a/core/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java @@ -604,8 +604,8 @@ public void testResolveReuseExistingNodeConnections() throws ExecutionException, // install a listener to check that no new connections are made handleA.transportService.addConnectionListener(new TransportConnectionListener() { @Override - public void onConnectionOpened(DiscoveryNode node) { - fail("should not open any connections. got [" + node + "]"); + public void onConnectionOpened(Transport.Connection connection) { + fail("should not open any connections. got [" + connection.getNode() + "]"); } }); diff --git a/core/src/test/java/org/elasticsearch/transport/TCPTransportTests.java b/core/src/test/java/org/elasticsearch/transport/TCPTransportTests.java index c14d6ec9e05fe..eb9e6496521c0 100644 --- a/core/src/test/java/org/elasticsearch/transport/TCPTransportTests.java +++ b/core/src/test/java/org/elasticsearch/transport/TCPTransportTests.java @@ -204,7 +204,7 @@ protected void sendMessage(Object o, BytesReference reference, ActionListener li @Override protected NodeChannels connectToChannels(DiscoveryNode node, ConnectionProfile profile) throws IOException { - return new NodeChannels(node, new Object[profile.getNumConnections()], profile); + return new NodeChannels(node, new Object[profile.getNumConnections()], profile, c -> {}); } @Override @@ -220,7 +220,7 @@ public long serverOpen() { @Override public NodeChannels getConnection(DiscoveryNode node) { return new NodeChannels(node, new Object[MockTcpTransport.LIGHT_PROFILE.getNumConnections()], - MockTcpTransport.LIGHT_PROFILE); + MockTcpTransport.LIGHT_PROFILE, c -> {}); } }; DiscoveryNode node = new DiscoveryNode("foo", buildNewFakeTransportAddress(), Version.CURRENT); diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Transport.java b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Transport.java index 8adeb665e0454..a86bbfbe2b6bd 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Transport.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Transport.java @@ -320,7 +320,7 @@ public long serverOpen() { @Override protected NodeChannels connectToChannels(DiscoveryNode node, ConnectionProfile profile) { final Channel[] channels = new Channel[profile.getNumConnections()]; - final NodeChannels nodeChannels = new NodeChannels(node, channels, profile); + final NodeChannels nodeChannels = new NodeChannels(node, channels, profile, transportServiceAdapter::onConnectionClosed); boolean success = false; try { final TimeValue connectTimeout; diff --git a/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransportService.java b/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransportService.java index a25f435af2c96..210190940d229 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransportService.java +++ b/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransportService.java @@ -777,6 +777,11 @@ public void sendRequest(long requestId, String action, TransportRequest request, public void close() throws IOException { connection.close(); } + + @Override + public Object getCacheKey() { + return connection.getCacheKey(); + } } public Transport getOriginalTransport() { diff --git a/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java b/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java index afbacf6f63f75..48d90e3ec630d 100644 --- a/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java @@ -2099,9 +2099,6 @@ public void handleException(TransportException exp) { @Override public String executor() { - if (1 == 1) - return "same"; - return randomFrom(executors); } }; @@ -2111,4 +2108,59 @@ public String executor() { latch.await(); } + public void testHandlerIsInvokedOnConnectionClose() throws IOException, InterruptedException { + List executors = new ArrayList<>(ThreadPool.THREAD_POOL_TYPES.keySet()); + CollectionUtil.timSort(executors); // makes sure it's reproducible + TransportService serviceC = build(Settings.builder().put("name", "TS_TEST").build(), version0, null, true); + serviceC.registerRequestHandler("action", TestRequest::new, ThreadPool.Names.SAME, + (request, channel) -> { + // do nothing + }); + serviceC.start(); + serviceC.acceptIncomingRequests(); + CountDownLatch latch = new CountDownLatch(1); + TransportResponseHandler transportResponseHandler = new TransportResponseHandler() { + @Override + public TransportResponse newInstance() { + return TransportResponse.Empty.INSTANCE; + } + + @Override + public void handleResponse(TransportResponse response) { + try { + fail("no response expected"); + } finally { + latch.countDown(); + } + } + + @Override + public void handleException(TransportException exp) { + try { + assertTrue(exp.getClass().toString(), exp instanceof NodeDisconnectedException); + } finally { + latch.countDown(); + } + } + + @Override + public String executor() { + return randomFrom(executors); + } + }; + ConnectionProfile.Builder builder = new ConnectionProfile.Builder(); + builder.addConnections(1, + TransportRequestOptions.Type.BULK, + TransportRequestOptions.Type.PING, + TransportRequestOptions.Type.RECOVERY, + TransportRequestOptions.Type.REG, + TransportRequestOptions.Type.STATE); + Transport.Connection connection = serviceB.openConnection(serviceC.getLocalNode(), builder.build()); + serviceB.sendRequest(connection, "action", new TestRequest(randomFrom("fail", "pass")), TransportRequestOptions.EMPTY, + transportResponseHandler); + connection.close(); + latch.await(); + serviceC.close(); + } + } diff --git a/test/framework/src/main/java/org/elasticsearch/transport/MockTcpTransport.java b/test/framework/src/main/java/org/elasticsearch/transport/MockTcpTransport.java index f9e5ff8981e31..765d675f2da2f 100644 --- a/test/framework/src/main/java/org/elasticsearch/transport/MockTcpTransport.java +++ b/test/framework/src/main/java/org/elasticsearch/transport/MockTcpTransport.java @@ -180,7 +180,8 @@ private void readMessage(MockChannel mockChannel, StreamInput input) throws IOEx @Override protected NodeChannels connectToChannels(DiscoveryNode node, ConnectionProfile profile) throws IOException { final MockChannel[] mockChannels = new MockChannel[1]; - final NodeChannels nodeChannels = new NodeChannels(node, mockChannels, LIGHT_PROFILE); // we always use light here + final NodeChannels nodeChannels = new NodeChannels(node, mockChannels, LIGHT_PROFILE, + transportServiceAdapter::onConnectionClosed); // we always use light here boolean success = false; final MockSocket socket = new MockSocket(); try { From 29a5694bb7ad9f5520be7299aec44f2d85269956 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Fri, 12 May 2017 15:44:39 +0200 Subject: [PATCH 329/619] Add parsing method to GeoHashGrid aggregation (#24589) --- .../bucket/geogrid/ParsedGeoHashGrid.java | 78 +++++++++++++++++++ .../aggregations/AggregationsTests.java | 2 + .../geogrid/InternalGeoHashGridTests.java | 25 ++++-- .../test/InternalAggregationTestCase.java | 4 + 4 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java new file mode 100644 index 0000000000000..4551523e0fc8b --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java @@ -0,0 +1,78 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.geogrid; + +import org.elasticsearch.common.geo.GeoPoint; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; + +import java.io.IOException; +import java.util.List; + +public class ParsedGeoHashGrid extends ParsedMultiBucketAggregation implements GeoHashGrid { + + @Override + public String getType() { + return GeoGridAggregationBuilder.NAME; + } + + @Override + public List getBuckets() { + return buckets; + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedGeoHashGrid.class.getSimpleName(), true, ParsedGeoHashGrid::new); + static { + declareMultiBucketAggregationFields(PARSER, ParsedBucket::fromXContent, ParsedBucket::fromXContent); + } + + public static ParsedGeoHashGrid fromXContent(XContentParser parser, String name) throws IOException { + ParsedGeoHashGrid aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } + + public static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements GeoHashGrid.Bucket { + + private String geohashAsString; + + @Override + public GeoPoint getKey() { + return GeoPoint.fromGeohash(geohashAsString); + } + + @Override + public String getKeyAsString() { + return geohashAsString; + } + + @Override + protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException { + return builder.field(CommonFields.KEY.getPreferredName(), geohashAsString); + } + + static ParsedBucket fromXContent(XContentParser parser) throws IOException { + return parseXContent(parser, false, ParsedBucket::new, (p, bucket) -> bucket.geohashAsString = p.textOrNull()); + } + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java index a338c8ec9ac54..d741240e15804 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java @@ -30,6 +30,7 @@ import org.elasticsearch.search.aggregations.bucket.children.InternalChildrenTests; import org.elasticsearch.search.aggregations.bucket.filter.InternalFilterTests; import org.elasticsearch.search.aggregations.bucket.global.InternalGlobalTests; +import org.elasticsearch.search.aggregations.bucket.geogrid.InternalGeoHashGridTests; import org.elasticsearch.search.aggregations.bucket.histogram.InternalDateHistogramTests; import org.elasticsearch.search.aggregations.bucket.histogram.InternalHistogramTests; import org.elasticsearch.search.aggregations.bucket.missing.InternalMissingTests; @@ -117,6 +118,7 @@ private static List getAggsTests() { aggsTests.add(new InternalGlobalTests()); aggsTests.add(new InternalFilterTests()); aggsTests.add(new InternalSamplerTests()); + aggsTests.add(new InternalGeoHashGridTests()); return Collections.unmodifiableList(aggsTests); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/InternalGeoHashGridTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/InternalGeoHashGridTests.java index a27b47946f9b9..afa9defc78a60 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/InternalGeoHashGridTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/InternalGeoHashGridTests.java @@ -22,24 +22,30 @@ import org.elasticsearch.common.geo.GeoHashUtils; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.InternalMultiBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import org.elasticsearch.test.InternalAggregationTestCase; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -public class InternalGeoHashGridTests extends InternalAggregationTestCase { +public class InternalGeoHashGridTests extends InternalMultiBucketAggregationTestCase { @Override - protected InternalGeoHashGrid createTestInstance(String name, List pipelineAggregators, - Map metaData) { - int size = randomIntBetween(1, 100); + protected InternalGeoHashGrid createTestInstance(String name, + List pipelineAggregators, + Map metaData, + InternalAggregations aggregations) { + int size = randomIntBetween(1, 3); List buckets = new ArrayList<>(size); for (int i = 0; i < size; i++) { - long geoHashAsLong = GeoHashUtils.longEncode(randomInt(90), randomInt(90), 4); - buckets.add(new InternalGeoHashGrid.Bucket(geoHashAsLong, randomInt(IndexWriter.MAX_DOCS), InternalAggregations.EMPTY)); + double latitude = randomDoubleBetween(-90.0, 90.0, false); + double longitude = randomDoubleBetween(-180.0, 180.0, false); + + long geoHashAsLong = GeoHashUtils.longEncode(longitude, latitude, 4); + buckets.add(new InternalGeoHashGrid.Bucket(geoHashAsLong, randomInt(IndexWriter.MAX_DOCS), aggregations)); } return new InternalGeoHashGrid(name, size, buckets, pipelineAggregators, metaData); } @@ -87,4 +93,9 @@ protected void assertReduced(InternalGeoHashGrid reduced, List implementationClass() { + return ParsedGeoHashGrid.class; + } } diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java index 99cf71f65e4e3..da7ac647c5c54 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java @@ -19,6 +19,7 @@ package org.elasticsearch.test; +import com.carrotsearch.randomizedtesting.annotations.Repeat; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -44,6 +45,8 @@ import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter; import org.elasticsearch.search.aggregations.bucket.global.GlobalAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.global.ParsedGlobal; +import org.elasticsearch.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.geogrid.ParsedGeoHashGrid; import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram; @@ -160,6 +163,7 @@ public static List getNamedXContents() { namedXContents.put(GlobalAggregationBuilder.NAME, (p, c) -> ParsedGlobal.fromXContent(p, (String) c)); namedXContents.put(FilterAggregationBuilder.NAME, (p, c) -> ParsedFilter.fromXContent(p, (String) c)); namedXContents.put(InternalSampler.NAME, (p, c) -> ParsedSampler.fromXContent(p, (String) c)); + namedXContents.put(GeoGridAggregationBuilder.NAME, (p, c) -> ParsedGeoHashGrid.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) From 279a18a527b9c23b06c8b75c2aa9321aefca9728 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Fri, 12 May 2017 15:58:06 +0200 Subject: [PATCH 330/619] Add parent-join module (#24638) * Add parent-join module This change adds a new module named `parent-join`. The goal of this module is to provide a replacement for the `_parent` field but as a first step this change only moves the `has_child`, `has_parent` queries and the `children` aggregation to this module. These queries and aggregations are no longer in core but they are deployed by default as a module. Relates #20257 --- build.gradle | 1 + .../resources/checkstyle_suppressions.xml | 3 - .../QueryDSLDocumentationTests.java | 20 - client/transport/build.gradle | 1 + .../index/query/InnerHitBuilder.java | 6 +- .../index/query/NestedQueryBuilder.java | 30 +- .../index/query/QueryBuilders.java | 24 - .../elasticsearch/search/SearchModule.java | 12 - .../aggregations/AggregationBuilders.java | 17 +- .../subphase/highlight/CustomQueryScorer.java | 11 +- .../action/bulk/BulkWithUpdatesIT.java | 149 ----- .../elasticsearch/aliases/IndexAliasesIT.java | 23 - .../index/query/InnerHitBuilderTests.java | 74 --- .../index/query/NestedQueryBuilderTests.java | 44 +- .../search/SearchModuleTests.java | 2 - .../bucket/filter/InternalFilterTests.java | 2 +- .../bucket/global/InternalGlobalTests.java | 2 +- .../bucket/missing/InternalMissingTests.java | 2 +- .../bucket/nested/InternalNestedTests.java | 2 +- .../nested/InternalReverseNestedTests.java | 2 +- .../bucket/sampler/InternalSamplerTests.java | 4 +- .../search/fetch/subphase/InnerHitsIT.java | 458 -------------- .../bucket/children-aggregation.asciidoc | 2 +- modules/parent-join/build.gradle | 24 + .../elasticsearch/join/ParentJoinPlugin.java | 54 ++ .../join/aggregations}/Children.java | 2 +- .../ChildrenAggregationBuilder.java | 2 +- .../ChildrenAggregatorFactory.java | 2 +- .../join/aggregations}/InternalChildren.java | 2 +- .../aggregations/JoinAggregationBuilders.java | 29 + .../ParentToChildrenAggregator.java | 2 +- .../join}/query/HasChildQueryBuilder.java | 38 +- .../join}/query/HasParentQueryBuilder.java | 9 +- .../join/query/JoinQueryBuilders.java | 50 ++ .../ParentChildClientYamlTestSuiteIT.java | 37 ++ .../join/aggregations}/ChildrenIT.java | 73 ++- .../join/aggregations}/ChildrenTests.java | 13 +- .../aggregations}/InternalChildrenTests.java | 4 +- .../ParentToChildrenAggregatorTests.java | 2 +- .../join/query}/ChildQuerySearchIT.java | 348 +++++++++-- .../query/HasChildQueryBuilderTests.java | 81 +-- .../query/HasParentQueryBuilderTests.java | 30 +- .../elasticsearch/join/query/InnerHitsIT.java | 568 ++++++++++++++++++ .../rest-api-spec/test/10_basic.yaml | 48 ++ modules/percolator/build.gradle | 4 + .../percolator/PercolatorFieldMapper.java | 7 +- .../PercolatorFieldMapperTests.java | 8 +- modules/reindex/build.gradle | 2 + .../reindex/ReindexParentChildTests.java | 26 +- .../rest-api-spec/test/reindex/90_remote.yaml | 79 --- .../build.gradle | 4 +- ...ndexWithPainlessClientYamlTestSuiteIT.java | 0 .../rest-api-spec/test/reindex/10_script.yaml | 0 .../rest-api-spec/test/reindex/20_broken.yaml | 0 .../test/reindex/30_timeout.yaml | 0 .../test/reindex/40_search_failures.yaml | 0 .../reindex/50_reindex_with_parentchild.yaml | 79 +++ .../test/update_by_query/10_script.yaml | 0 .../test/update_by_query/20_broken.yaml | 0 .../test/update_by_query/30_timeout.yaml | 0 .../update_by_query/40_search_failure.yaml | 0 .../test/search.inner_hits/10_basic.yaml | 39 -- settings.gradle | 3 +- .../aggregations/AggregatorTestCase.java | 0 .../aggregations/BaseAggregationTestCase.java | 12 +- ...ternalSingleBucketAggregationTestCase.java | 3 +- 66 files changed, 1469 insertions(+), 1106 deletions(-) create mode 100644 modules/parent-join/build.gradle create mode 100644 modules/parent-join/src/main/java/org/elasticsearch/join/ParentJoinPlugin.java rename {core/src/main/java/org/elasticsearch/search/aggregations/bucket/children => modules/parent-join/src/main/java/org/elasticsearch/join/aggregations}/Children.java (94%) rename {core/src/main/java/org/elasticsearch/search/aggregations/bucket/children => modules/parent-join/src/main/java/org/elasticsearch/join/aggregations}/ChildrenAggregationBuilder.java (99%) rename {core/src/main/java/org/elasticsearch/search/aggregations/bucket/children => modules/parent-join/src/main/java/org/elasticsearch/join/aggregations}/ChildrenAggregatorFactory.java (98%) rename {core/src/main/java/org/elasticsearch/search/aggregations/bucket/children => modules/parent-join/src/main/java/org/elasticsearch/join/aggregations}/InternalChildren.java (97%) create mode 100644 modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/JoinAggregationBuilders.java rename {core/src/main/java/org/elasticsearch/search/aggregations/bucket/children => modules/parent-join/src/main/java/org/elasticsearch/join/aggregations}/ParentToChildrenAggregator.java (99%) rename {core/src/main/java/org/elasticsearch/index => modules/parent-join/src/main/java/org/elasticsearch/join}/query/HasChildQueryBuilder.java (94%) rename {core/src/main/java/org/elasticsearch/index => modules/parent-join/src/main/java/org/elasticsearch/join}/query/HasParentQueryBuilder.java (96%) create mode 100644 modules/parent-join/src/main/java/org/elasticsearch/join/query/JoinQueryBuilders.java create mode 100644 modules/parent-join/src/test/java/org/elasticsearch/join/ParentChildClientYamlTestSuiteIT.java rename {core/src/test/java/org/elasticsearch/search/aggregations/bucket => modules/parent-join/src/test/java/org/elasticsearch/join/aggregations}/ChildrenIT.java (92%) rename {core/src/test/java/org/elasticsearch/search/aggregations/bucket => modules/parent-join/src/test/java/org/elasticsearch/join/aggregations}/ChildrenTests.java (78%) rename {core/src/test/java/org/elasticsearch/search/aggregations/bucket/children => modules/parent-join/src/test/java/org/elasticsearch/join/aggregations}/InternalChildrenTests.java (91%) rename {core/src/test/java/org/elasticsearch/search/aggregations/bucket/children => modules/parent-join/src/test/java/org/elasticsearch/join/aggregations}/ParentToChildrenAggregatorTests.java (99%) rename {core/src/test/java/org/elasticsearch/search/child => modules/parent-join/src/test/java/org/elasticsearch/join/query}/ChildQuerySearchIT.java (87%) rename {core/src/test/java/org/elasticsearch/index => modules/parent-join/src/test/java/org/elasticsearch/join}/query/HasChildQueryBuilderTests.java (83%) rename {core/src/test/java/org/elasticsearch/index => modules/parent-join/src/test/java/org/elasticsearch/join}/query/HasParentQueryBuilderTests.java (90%) create mode 100644 modules/parent-join/src/test/java/org/elasticsearch/join/query/InnerHitsIT.java create mode 100644 modules/parent-join/src/test/resources/rest-api-spec/test/10_basic.yaml rename qa/{smoke-test-reindex-with-painless => smoke-test-reindex-with-all-modules}/build.gradle (89%) rename qa/{smoke-test-reindex-with-painless => smoke-test-reindex-with-all-modules}/src/test/java/org/elasticsearch/smoketest/SmokeTestReindexWithPainlessClientYamlTestSuiteIT.java (100%) rename qa/{smoke-test-reindex-with-painless => smoke-test-reindex-with-all-modules}/src/test/resources/rest-api-spec/test/reindex/10_script.yaml (100%) rename qa/{smoke-test-reindex-with-painless => smoke-test-reindex-with-all-modules}/src/test/resources/rest-api-spec/test/reindex/20_broken.yaml (100%) rename qa/{smoke-test-reindex-with-painless => smoke-test-reindex-with-all-modules}/src/test/resources/rest-api-spec/test/reindex/30_timeout.yaml (100%) rename qa/{smoke-test-reindex-with-painless => smoke-test-reindex-with-all-modules}/src/test/resources/rest-api-spec/test/reindex/40_search_failures.yaml (100%) create mode 100644 qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/50_reindex_with_parentchild.yaml rename qa/{smoke-test-reindex-with-painless => smoke-test-reindex-with-all-modules}/src/test/resources/rest-api-spec/test/update_by_query/10_script.yaml (100%) rename qa/{smoke-test-reindex-with-painless => smoke-test-reindex-with-all-modules}/src/test/resources/rest-api-spec/test/update_by_query/20_broken.yaml (100%) rename qa/{smoke-test-reindex-with-painless => smoke-test-reindex-with-all-modules}/src/test/resources/rest-api-spec/test/update_by_query/30_timeout.yaml (100%) rename qa/{smoke-test-reindex-with-painless => smoke-test-reindex-with-all-modules}/src/test/resources/rest-api-spec/test/update_by_query/40_search_failure.yaml (100%) rename {core/src/test => test/framework/src/main}/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java (100%) rename {core/src/test => test/framework/src/main}/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java (95%) rename {core/src/test/java/org/elasticsearch/search/aggregations/bucket => test/framework/src/main/java/org/elasticsearch/search/aggregations}/InternalSingleBucketAggregationTestCase.java (96%) diff --git a/build.gradle b/build.gradle index 09748ea1e8afb..31784a1798a2e 100644 --- a/build.gradle +++ b/build.gradle @@ -138,6 +138,7 @@ subprojects { "org.elasticsearch.plugin:transport-netty4-client:${version}": ':modules:transport-netty4', "org.elasticsearch.plugin:reindex-client:${version}": ':modules:reindex', "org.elasticsearch.plugin:lang-mustache-client:${version}": ':modules:lang-mustache', + "org.elasticsearch.plugin:parent-join-client:${version}": ':modules:parent-join', "org.elasticsearch.plugin:percolator-client:${version}": ':modules:percolator', ] project.afterEvaluate { diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 9a550740fde36..4acd927fff1d9 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -620,7 +620,6 @@ - @@ -689,7 +688,6 @@ - @@ -716,7 +714,6 @@ - diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java index f01e4824b3fa3..f48572b047382 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java @@ -51,8 +51,6 @@ import static org.elasticsearch.index.query.QueryBuilders.geoDistanceQuery; import static org.elasticsearch.index.query.QueryBuilders.geoPolygonQuery; import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasChildQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; import static org.elasticsearch.index.query.QueryBuilders.idsQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.index.query.QueryBuilders.matchQuery; @@ -216,24 +214,6 @@ public void testGeoShape() throws IOException { } } - public void testHasChild() { - // tag::has_child - hasChildQuery( - "blog_tag", // <1> - termQuery("tag","something"), // <2> - ScoreMode.None); // <3> - // end::has_child - } - - public void testHasParent() { - // tag::has_parent - hasParentQuery( - "blog", // <1> - termQuery("tag","something"), // <2> - false); // <3> - // end::has_parent - } - public void testIds() { // tag::ids idsQuery("my_type", "type2") diff --git a/client/transport/build.gradle b/client/transport/build.gradle index 77833c1f2672d..b2edc9c8fcd8f 100644 --- a/client/transport/build.gradle +++ b/client/transport/build.gradle @@ -31,6 +31,7 @@ dependencies { compile "org.elasticsearch.plugin:reindex-client:${version}" compile "org.elasticsearch.plugin:lang-mustache-client:${version}" compile "org.elasticsearch.plugin:percolator-client:${version}" + compile "org.elasticsearch.plugin:parent-join-client:${version}" testCompile "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}" testCompile "junit:junit:${versions.junit}" testCompile "org.hamcrest:hamcrest-all:${versions.hamcrest}" diff --git a/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java b/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java index dd23429dbdb72..b6eb84b03b7f2 100644 --- a/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java @@ -195,7 +195,8 @@ private InnerHitBuilder(InnerHitBuilder other) { } } - InnerHitBuilder(InnerHitBuilder other, QueryBuilder query, String parentChildType, boolean ignoreUnmapped) { + // NORELEASE Do not use this ctr, it is public for hasChild and hasParent query but this is temporary + public InnerHitBuilder(InnerHitBuilder other, QueryBuilder query, String parentChildType, boolean ignoreUnmapped) { this(other); this.query = query; this.parentChildType = parentChildType; @@ -751,7 +752,8 @@ public static void extractInnerHits(QueryBuilder query, Map plugins) { GeoCentroidAggregationBuilder::parse).addResultReader(InternalGeoCentroid::new)); registerAggregation(new AggregationSpec(ScriptedMetricAggregationBuilder.NAME, ScriptedMetricAggregationBuilder::new, ScriptedMetricAggregationBuilder::parse).addResultReader(InternalScriptedMetric::new)); - registerAggregation(new AggregationSpec(ChildrenAggregationBuilder.NAME, ChildrenAggregationBuilder::new, - ChildrenAggregationBuilder::parse).addResultReader(InternalChildren::new)); - registerFromPlugin(plugins, SearchPlugin::getAggregations, this::registerAggregation); } @@ -706,8 +696,6 @@ private void registerQueryParsers(List plugins) { MatchPhrasePrefixQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(MultiMatchQueryBuilder.NAME, MultiMatchQueryBuilder::new, MultiMatchQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(NestedQueryBuilder.NAME, NestedQueryBuilder::new, NestedQueryBuilder::fromXContent)); - registerQuery(new QuerySpec<>(HasChildQueryBuilder.NAME, HasChildQueryBuilder::new, HasChildQueryBuilder::fromXContent)); - registerQuery(new QuerySpec<>(HasParentQueryBuilder.NAME, HasParentQueryBuilder::new, HasParentQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(DisMaxQueryBuilder.NAME, DisMaxQueryBuilder::new, DisMaxQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(IdsQueryBuilder.NAME, IdsQueryBuilder::new, IdsQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(MatchAllQueryBuilder.NAME, MatchAllQueryBuilder::new, MatchAllQueryBuilder::fromXContent)); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilders.java b/core/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilders.java index f43c2670abdb4..8b704ee8a69a2 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilders.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilders.java @@ -23,8 +23,6 @@ import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.aggregations.bucket.adjacency.AdjacencyMatrix; import org.elasticsearch.search.aggregations.bucket.adjacency.AdjacencyMatrixAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.children.Children; -import org.elasticsearch.search.aggregations.bucket.children.ChildrenAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.filter.Filter; import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.filters.Filters; @@ -163,20 +161,20 @@ public static FiltersAggregationBuilder filters(String name, KeyedFilter... filt public static FiltersAggregationBuilder filters(String name, QueryBuilder... filters) { return new FiltersAggregationBuilder(name, filters); } - + /** * Create a new {@link AdjacencyMatrix} aggregation with the given name. */ public static AdjacencyMatrixAggregationBuilder adjacencyMatrix(String name, Map filters) { return new AdjacencyMatrixAggregationBuilder(name, filters); - } - + } + /** * Create a new {@link AdjacencyMatrix} aggregation with the given name and separator */ public static AdjacencyMatrixAggregationBuilder adjacencyMatrix(String name, String separator, Map filters) { return new AdjacencyMatrixAggregationBuilder(name, separator, filters); - } + } /** * Create a new {@link Sampler} aggregation with the given name. @@ -220,13 +218,6 @@ public static ReverseNestedAggregationBuilder reverseNested(String name) { return new ReverseNestedAggregationBuilder(name); } - /** - * Create a new {@link Children} aggregation with the given name. - */ - public static ChildrenAggregationBuilder children(String name, String childType) { - return new ChildrenAggregationBuilder(name, childType); - } - /** * Create a new {@link GeoDistance} aggregation with the given name. */ diff --git a/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/CustomQueryScorer.java b/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/CustomQueryScorer.java index e25e7b74941d7..2f728e21b6f34 100644 --- a/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/CustomQueryScorer.java +++ b/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/CustomQueryScorer.java @@ -26,7 +26,6 @@ import org.apache.lucene.search.highlight.WeightedSpanTermExtractor; import org.elasticsearch.common.lucene.search.function.FiltersFunctionScoreQuery; import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery; -import org.elasticsearch.index.query.HasChildQueryBuilder; import java.io.IOException; import java.util.Map; @@ -83,7 +82,7 @@ protected void extractUnknownQuery(Query query, } protected void extract(Query query, float boost, Map terms) throws IOException { - if (query instanceof HasChildQueryBuilder.LateParsingQuery) { + if (isChildOrParentQuery(query.getClass())) { // skip has_child or has_parent queries, see: https://github.com/elastic/elasticsearch/issues/14999 return; } else if (query instanceof FunctionScoreQuery) { @@ -94,5 +93,13 @@ protected void extract(Query query, float boost, Map t super.extract(query, boost, terms); } } + + /** + * Workaround to detect parent/child query + */ + private static final String PARENT_CHILD_QUERY_NAME = "HasChildQueryBuilder$LateParsingQuery"; + private static boolean isChildOrParentQuery(Class clazz) { + return clazz.getName().endsWith(PARENT_CHILD_QUERY_NAME); + } } } diff --git a/core/src/test/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java b/core/src/test/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java index 21930b3763b6a..cf41042ab8c78 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java @@ -450,156 +450,7 @@ public void testBulkIndexingWhileInitializing() throws Exception { assertHitCount(countResponse, numDocs); } - /* - Test for https://github.com/elastic/elasticsearch/issues/3444 - */ - public void testBulkUpdateDocAsUpsertWithParent() throws Exception { - client().admin().indices().prepareCreate("test") - .setSettings("index.mapping.single_type", false) - .addMapping("parent", "{\"parent\":{}}", XContentType.JSON) - .addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}", XContentType.JSON) - .execute().actionGet(); - ensureGreen(); - - BulkRequestBuilder builder = client().prepareBulk(); - - // It's important to use JSON parsing here and request objects: issue 3444 is related to incomplete option parsing - byte[] addParent = new BytesArray( - "{" + - " \"index\" : {" + - " \"_index\" : \"test\"," + - " \"_type\" : \"parent\"," + - " \"_id\" : \"parent1\"" + - " }" + - "}" + - "\n" + - "{" + - " \"field1\" : \"value1\"" + - "}" + - "\n").array(); - - byte[] addChild = new BytesArray( - "{" + - " \"update\" : {" + - " \"_index\" : \"test\"," + - " \"_type\" : \"child\"," + - " \"_id\" : \"child1\"," + - " \"parent\" : \"parent1\"" + - " }" + - "}" + - "\n" + - "{" + - " \"doc\" : {" + - " \"field1\" : \"value1\"" + - " }," + - " \"doc_as_upsert\" : \"true\"" + - "}" + - "\n").array(); - - builder.add(addParent, 0, addParent.length, XContentType.JSON); - builder.add(addChild, 0, addChild.length, XContentType.JSON); - - BulkResponse bulkResponse = builder.get(); - assertThat(bulkResponse.getItems().length, equalTo(2)); - assertThat(bulkResponse.getItems()[0].isFailed(), equalTo(false)); - assertThat(bulkResponse.getItems()[1].isFailed(), equalTo(false)); - - client().admin().indices().prepareRefresh("test").get(); - //we check that the _parent field was set on the child document by using the has parent query - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.hasParentQuery("parent", QueryBuilders.matchAllQuery(), false)) - .get(); - - assertNoFailures(searchResponse); - assertSearchHits(searchResponse, "child1"); - } - - /* - Test for https://github.com/elastic/elasticsearch/issues/3444 - */ - public void testBulkUpdateUpsertWithParent() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) - .addMapping("parent", "{\"parent\":{}}", XContentType.JSON) - .addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}", XContentType.JSON)); - ensureGreen(); - - BulkRequestBuilder builder = client().prepareBulk(); - - byte[] addParent = new BytesArray( - "{" + - " \"index\" : {" + - " \"_index\" : \"test\"," + - " \"_type\" : \"parent\"," + - " \"_id\" : \"parent1\"" + - " }" + - "}" + - "\n" + - "{" + - " \"field1\" : \"value1\"" + - "}" + - "\n").array(); - - byte[] addChild1 = new BytesArray( - "{" + - " \"update\" : {" + - " \"_index\" : \"test\"," + - " \"_type\" : \"child\"," + - " \"_id\" : \"child1\"," + - " \"parent\" : \"parent1\"" + - " }" + - "}" + - "\n" + - "{" + - " \"script\" : {" + - " \"inline\" : \"ctx._source.field2 = 'value2'\"" + - " }," + - " \"lang\" : \"" + CustomScriptPlugin.NAME + "\"," + - " \"upsert\" : {" + - " \"field1\" : \"value1'\"" + - " }" + - "}" + - "\n").array(); - - byte[] addChild2 = new BytesArray( - "{" + - " \"update\" : {" + - " \"_index\" : \"test\"," + - " \"_type\" : \"child\"," + - " \"_id\" : \"child1\"," + - " \"parent\" : \"parent1\"" + - " }" + - "}" + - "\n" + - "{" + - " \"script\" : \"ctx._source.field2 = 'value2'\"," + - " \"upsert\" : {" + - " \"field1\" : \"value1'\"" + - " }" + - "}" + - "\n").array(); - - builder.add(addParent, 0, addParent.length, XContentType.JSON); - builder.add(addChild1, 0, addChild1.length, XContentType.JSON); - builder.add(addChild2, 0, addChild2.length, XContentType.JSON); - - BulkResponse bulkResponse = builder.get(); - assertThat(bulkResponse.getItems().length, equalTo(3)); - assertThat(bulkResponse.getItems()[0].isFailed(), equalTo(false)); - assertThat(bulkResponse.getItems()[1].isFailed(), equalTo(false)); - assertThat(bulkResponse.getItems()[2].isFailed(), equalTo(true)); - assertThat(bulkResponse.getItems()[2].getFailure().getCause().getCause().getMessage(), - equalTo("script_lang not supported [painless]")); - - client().admin().indices().prepareRefresh("test").get(); - - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.hasParentQuery("parent", QueryBuilders.matchAllQuery(), false)) - .get(); - - assertSearchHits(searchResponse, "child1"); - } /* * Test for https://github.com/elastic/elasticsearch/issues/8365 diff --git a/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java b/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java index 711804153cf1e..e711117fb6ec7 100644 --- a/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java +++ b/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java @@ -63,8 +63,6 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; -import static org.elasticsearch.index.query.QueryBuilders.hasChildQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.index.query.QueryBuilders.rangeQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery; @@ -824,27 +822,6 @@ public void testAliasFilterWithNowInRangeFilterAndQuery() throws Exception { } } - public void testAliasesFilterWithHasChildQuery() throws Exception { - assertAcked(prepareCreate("my-index") - .setSettings("index.mapping.single_type", false) - .addMapping("parent") - .addMapping("child", "_parent", "type=parent") - ); - client().prepareIndex("my-index", "parent", "1").setSource("{}", XContentType.JSON).get(); - client().prepareIndex("my-index", "child", "2").setSource("{}", XContentType.JSON).setParent("1").get(); - refresh(); - - assertAcked(admin().indices().prepareAliases().addAlias("my-index", "filter1", hasChildQuery("child", matchAllQuery(), ScoreMode.None))); - assertAcked(admin().indices().prepareAliases().addAlias("my-index", "filter2", hasParentQuery("parent", matchAllQuery(), false))); - - SearchResponse response = client().prepareSearch("filter1").get(); - assertHitCount(response, 1); - assertThat(response.getHits().getAt(0).getId(), equalTo("1")); - response = client().prepareSearch("filter2").get(); - assertHitCount(response, 1); - assertThat(response.getHits().getAt(0).getId(), equalTo("2")); - } - public void testAliasesWithBlocks() { createIndex("test"); ensureGreen(); diff --git a/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java index cd6622368c931..9e199f71cea6d 100644 --- a/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java @@ -153,24 +153,6 @@ public void testInlineLeafInnerHitsNestedQuery() throws Exception { assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); } - public void testInlineLeafInnerHitsHasChildQuery() throws Exception { - InnerHitBuilder leafInnerHits = randomInnerHits(); - HasChildQueryBuilder hasChildQueryBuilder = new HasChildQueryBuilder("type", new MatchAllQueryBuilder(), ScoreMode.None) - .innerHit(leafInnerHits, false); - Map innerHitBuilders = new HashMap<>(); - hasChildQueryBuilder.extractInnerHitBuilders(innerHitBuilders); - assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); - } - - public void testInlineLeafInnerHitsHasParentQuery() throws Exception { - InnerHitBuilder leafInnerHits = randomInnerHits(); - HasParentQueryBuilder hasParentQueryBuilder = new HasParentQueryBuilder("type", new MatchAllQueryBuilder(), false) - .innerHit(leafInnerHits, false); - Map innerHitBuilders = new HashMap<>(); - hasParentQueryBuilder.extractInnerHitBuilders(innerHitBuilders); - assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); - } - public void testInlineLeafInnerHitsNestedQueryViaBoolQuery() { InnerHitBuilder leafInnerHits = randomInnerHits(); NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("path", new MatchAllQueryBuilder(), ScoreMode.None) @@ -181,25 +163,6 @@ public void testInlineLeafInnerHitsNestedQueryViaBoolQuery() { assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); } - public void testInlineLeafInnerHitsNestedQueryViaDisMaxQuery() { - InnerHitBuilder leafInnerHits1 = randomInnerHits(); - NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("path", new MatchAllQueryBuilder(), ScoreMode.None) - .innerHit(leafInnerHits1, false); - - InnerHitBuilder leafInnerHits2 = randomInnerHits(); - HasChildQueryBuilder hasChildQueryBuilder = new HasChildQueryBuilder("type", new MatchAllQueryBuilder(), ScoreMode.None) - .innerHit(leafInnerHits2, false); - - DisMaxQueryBuilder disMaxQueryBuilder = new DisMaxQueryBuilder(); - disMaxQueryBuilder.add(nestedQueryBuilder); - disMaxQueryBuilder.add(hasChildQueryBuilder); - Map innerHitBuilders = new HashMap<>(); - disMaxQueryBuilder.extractInnerHitBuilders(innerHitBuilders); - assertThat(innerHitBuilders.size(), equalTo(2)); - assertThat(innerHitBuilders.get(leafInnerHits1.getName()), notNullValue()); - assertThat(innerHitBuilders.get(leafInnerHits2.getName()), notNullValue()); - } - public void testInlineLeafInnerHitsNestedQueryViaConstantScoreQuery() { InnerHitBuilder leafInnerHits = randomInnerHits(); NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("path", new MatchAllQueryBuilder(), ScoreMode.None) @@ -252,43 +215,6 @@ public void testBuildIgnoreUnmappedNestQuery() throws Exception { assertThat(innerHitsContext.getInnerHits().size(), equalTo(0)); } - public void testBuild_ignoreUnmappedHasChildQuery() throws Exception { - QueryShardContext queryShardContext = mock(QueryShardContext.class); - when(queryShardContext.documentMapper("type")).thenReturn(null); - SearchContext searchContext = mock(SearchContext.class); - when(searchContext.getQueryShardContext()).thenReturn(queryShardContext); - - InnerHitBuilder leafInnerHits = randomInnerHits(); - HasChildQueryBuilder query1 = new HasChildQueryBuilder("type", new MatchAllQueryBuilder(), ScoreMode.None) - .innerHit(leafInnerHits, false); - expectThrows(IllegalStateException.class, () -> query1.innerHit().build(searchContext, new InnerHitsContext())); - - HasChildQueryBuilder query2 = new HasChildQueryBuilder("type", new MatchAllQueryBuilder(), ScoreMode.None) - .innerHit(leafInnerHits, true); - InnerHitsContext innerHitsContext = new InnerHitsContext(); - query2.innerHit().build(searchContext, innerHitsContext); - assertThat(innerHitsContext.getInnerHits().size(), equalTo(0)); - } - - public void testBuild_ingoreUnmappedHasParentQuery() throws Exception { - QueryShardContext queryShardContext = mock(QueryShardContext.class); - when(queryShardContext.documentMapper("type")).thenReturn(null); - SearchContext searchContext = mock(SearchContext.class); - when(searchContext.getQueryShardContext()).thenReturn(queryShardContext); - - InnerHitBuilder leafInnerHits = randomInnerHits(); - HasParentQueryBuilder query1 = new HasParentQueryBuilder("type", new MatchAllQueryBuilder(), false) - .innerHit(leafInnerHits, false); - expectThrows(IllegalStateException.class, () -> query1.innerHit().build(searchContext, new InnerHitsContext())); - - HasParentQueryBuilder query2 = new HasParentQueryBuilder("type", new MatchAllQueryBuilder(), false) - .innerHit(leafInnerHits, true); - InnerHitsContext innerHitsContext = new InnerHitsContext(); - query2.innerHit().build(searchContext, innerHitsContext); - assertThat(innerHitsContext.getInnerHits().size(), equalTo(0)); - } - - public static InnerHitBuilder randomInnerHits() { return randomInnerHits(true, true); } diff --git a/core/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java index 267963878ea16..ed4fbcd53cdc6 100644 --- a/core/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java @@ -61,7 +61,7 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws } /** - * @return a {@link HasChildQueryBuilder} with random values all over the place + * @return a {@link NestedQueryBuilder} with random values all over the place */ @Override protected NestedQueryBuilder doCreateTestQueryBuilder() { @@ -203,4 +203,46 @@ public void testIgnoreUnmappedWithRewrite() throws IOException { assertThat(query, notNullValue()); assertThat(query, instanceOf(MatchNoDocsQuery.class)); } + + public void testMinFromString() { + assertThat("fromString(min) != MIN", ScoreMode.Min, equalTo(NestedQueryBuilder.parseScoreMode("min"))); + assertThat("min", equalTo(NestedQueryBuilder.scoreModeAsString(ScoreMode.Min))); + } + + public void testMaxFromString() { + assertThat("fromString(max) != MAX", ScoreMode.Max, equalTo(NestedQueryBuilder.parseScoreMode("max"))); + assertThat("max", equalTo(NestedQueryBuilder.scoreModeAsString(ScoreMode.Max))); + } + + public void testAvgFromString() { + assertThat("fromString(avg) != AVG", ScoreMode.Avg, equalTo(NestedQueryBuilder.parseScoreMode("avg"))); + assertThat("avg", equalTo(NestedQueryBuilder.scoreModeAsString(ScoreMode.Avg))); + } + + public void testSumFromString() { + assertThat("fromString(total) != SUM", ScoreMode.Total, equalTo(NestedQueryBuilder.parseScoreMode("sum"))); + assertThat("sum", equalTo(NestedQueryBuilder.scoreModeAsString(ScoreMode.Total))); + } + + public void testNoneFromString() { + assertThat("fromString(none) != NONE", ScoreMode.None, equalTo(NestedQueryBuilder.parseScoreMode("none"))); + assertThat("none", equalTo(NestedQueryBuilder.scoreModeAsString(ScoreMode.None))); + } + + /** + * Should throw {@link IllegalArgumentException} instead of NPE. + */ + public void testThatNullFromStringThrowsException() { + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> NestedQueryBuilder.parseScoreMode(null)); + assertEquals("No score mode for child query [null] found", e.getMessage()); + } + + /** + * Failure should not change (and the value should never match anything...). + */ + public void testThatUnrecognizedFromStringThrowsException() { + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> NestedQueryBuilder.parseScoreMode("unrecognized value")); + assertEquals("No score mode for child query [unrecognized value] found", e.getMessage()); + } } diff --git a/core/src/test/java/org/elasticsearch/search/SearchModuleTests.java b/core/src/test/java/org/elasticsearch/search/SearchModuleTests.java index 8514096b83763..96767c99b9daa 100644 --- a/core/src/test/java/org/elasticsearch/search/SearchModuleTests.java +++ b/core/src/test/java/org/elasticsearch/search/SearchModuleTests.java @@ -270,8 +270,6 @@ public List getPipelineAggregations() { "geo_distance", "geo_polygon", "geo_shape", - "has_child", - "has_parent", "ids", "match", "match_all", diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filter/InternalFilterTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filter/InternalFilterTests.java index 3e74b9c21877e..464f081f6c984 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filter/InternalFilterTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filter/InternalFilterTests.java @@ -21,7 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; -import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.InternalSingleBucketAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/global/InternalGlobalTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/global/InternalGlobalTests.java index 9092c3e028079..2a284746bf612 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/global/InternalGlobalTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/global/InternalGlobalTests.java @@ -21,7 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; -import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.InternalSingleBucketAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/missing/InternalMissingTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/missing/InternalMissingTests.java index f3e151721bf30..1a702e94024cc 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/missing/InternalMissingTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/missing/InternalMissingTests.java @@ -21,7 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; -import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.InternalSingleBucketAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalNestedTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalNestedTests.java index 7b410723666d4..a330c8a146e34 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalNestedTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalNestedTests.java @@ -21,7 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; -import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.InternalSingleBucketAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalReverseNestedTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalReverseNestedTests.java index f918024733e3a..069ac03829552 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalReverseNestedTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/InternalReverseNestedTests.java @@ -21,7 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; -import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.InternalSingleBucketAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSamplerTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSamplerTests.java index 1c4fb6d2a65f7..23facaf8fd25e 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSamplerTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSamplerTests.java @@ -20,7 +20,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.aggregations.InternalAggregations; -import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.InternalSingleBucketAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -42,4 +42,4 @@ protected void extraAssertReduced(InternalSampler reduced, List protected Writeable.Reader instanceReader() { return InternalSampler::new; } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java b/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java index 6fbda92ba2699..8eca50556460c 100644 --- a/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java +++ b/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java @@ -55,8 +55,6 @@ import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue; import static org.elasticsearch.index.query.QueryBuilders.boolQuery; import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasChildQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.index.query.QueryBuilders.matchQuery; import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; @@ -246,268 +244,6 @@ public void testRandomNested() throws Exception { } } - public void testSimpleParentChild() throws Exception { - assertAcked(prepareCreate("articles") - .setSettings("index.mapping.single_type", false) - .addMapping("article", "title", "type=text") - .addMapping("comment", "_parent", "type=article", "message", "type=text,fielddata=true") - ); - - List requests = new ArrayList<>(); - requests.add(client().prepareIndex("articles", "article", "1").setSource("title", "quick brown fox")); - requests.add(client().prepareIndex("articles", "comment", "1").setParent("1").setSource("message", "fox eat quick")); - requests.add(client().prepareIndex("articles", "comment", "2").setParent("1").setSource("message", "fox ate rabbit x y z")); - requests.add(client().prepareIndex("articles", "comment", "3").setParent("1").setSource("message", "rabbit got away")); - requests.add(client().prepareIndex("articles", "article", "2").setSource("title", "big gray elephant")); - requests.add(client().prepareIndex("articles", "comment", "4").setParent("2").setSource("message", "elephant captured")); - requests.add(client().prepareIndex("articles", "comment", "5").setParent("2").setSource("message", "mice squashed by elephant x")); - requests.add(client().prepareIndex("articles", "comment", "6").setParent("2").setSource("message", "elephant scared by mice x y")); - indexRandom(true, requests); - - SearchResponse response = client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None) - .innerHit(new InnerHitBuilder(), false)) - .get(); - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("1")); - assertThat(response.getHits().getAt(0).getShard(), notNullValue()); - - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.getTotalHits(), equalTo(2L)); - - assertThat(innerHits.getAt(0).getId(), equalTo("1")); - assertThat(innerHits.getAt(0).getType(), equalTo("comment")); - assertThat(innerHits.getAt(1).getId(), equalTo("2")); - assertThat(innerHits.getAt(1).getType(), equalTo("comment")); - - response = client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None) - .innerHit(new InnerHitBuilder(), false)) - .get(); - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("2")); - - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.getTotalHits(), equalTo(3L)); - - assertThat(innerHits.getAt(0).getId(), equalTo("4")); - assertThat(innerHits.getAt(0).getType(), equalTo("comment")); - assertThat(innerHits.getAt(1).getId(), equalTo("5")); - assertThat(innerHits.getAt(1).getType(), equalTo("comment")); - assertThat(innerHits.getAt(2).getId(), equalTo("6")); - assertThat(innerHits.getAt(2).getType(), equalTo("comment")); - - response = client().prepareSearch("articles") - .setQuery( - hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( - new InnerHitBuilder() - .addDocValueField("message") - .setHighlightBuilder(new HighlightBuilder().field("message")) - .setExplain(true).setSize(1) - .addScriptField("script", new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5", - Collections.emptyMap())), - false) - ).get(); - assertNoFailures(response); - innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.getHits().length, equalTo(1)); - assertThat(innerHits.getAt(0).getHighlightFields().get("message").getFragments()[0].string(), equalTo("fox eat quick")); - assertThat(innerHits.getAt(0).getExplanation().toString(), containsString("weight(message:fox")); - assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("eat")); - assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5")); - } - - public void testRandomParentChild() throws Exception { - assertAcked(prepareCreate("idx") - .setSettings("index.mapping.single_type", false) - .addMapping("parent") - .addMapping("child1", "_parent", "type=parent") - .addMapping("child2", "_parent", "type=parent") - ); - int numDocs = scaledRandomIntBetween(5, 50); - List requestBuilders = new ArrayList<>(); - - int child1 = 0; - int child2 = 0; - int[] child1InnerObjects = new int[numDocs]; - int[] child2InnerObjects = new int[numDocs]; - for (int parent = 0; parent < numDocs; parent++) { - String parentId = String.format(Locale.ENGLISH, "%03d", parent); - requestBuilders.add(client().prepareIndex("idx", "parent", parentId).setSource("{}", XContentType.JSON)); - - int numChildDocs = child1InnerObjects[parent] = scaledRandomIntBetween(1, numDocs); - int limit = child1 + numChildDocs; - for (; child1 < limit; child1++) { - requestBuilders.add(client().prepareIndex("idx", "child1", - String.format(Locale.ENGLISH, "%04d", child1)).setParent(parentId).setSource("{}", XContentType.JSON)); - } - numChildDocs = child2InnerObjects[parent] = scaledRandomIntBetween(1, numDocs); - limit = child2 + numChildDocs; - for (; child2 < limit; child2++) { - requestBuilders.add(client().prepareIndex("idx", "child2", - String.format(Locale.ENGLISH, "%04d", child2)).setParent(parentId).setSource("{}", XContentType.JSON)); - } - } - indexRandom(true, requestBuilders); - - int size = randomIntBetween(0, numDocs); - BoolQueryBuilder boolQuery = new BoolQueryBuilder(); - boolQuery.should(constantScoreQuery(hasChildQuery("child1", matchAllQuery(), ScoreMode.None) - .innerHit(new InnerHitBuilder().setName("a") - .addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size), false))); - boolQuery.should(constantScoreQuery(hasChildQuery("child2", matchAllQuery(), ScoreMode.None) - .innerHit(new InnerHitBuilder().setName("b") - .addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size), false))); - SearchResponse searchResponse = client().prepareSearch("idx") - .setSize(numDocs) - .setTypes("parent") - .addSort("_uid", SortOrder.ASC) - .setQuery(boolQuery) - .get(); - - assertNoFailures(searchResponse); - assertHitCount(searchResponse, numDocs); - assertThat(searchResponse.getHits().getHits().length, equalTo(numDocs)); - - int offset1 = 0; - int offset2 = 0; - for (int parent = 0; parent < numDocs; parent++) { - SearchHit searchHit = searchResponse.getHits().getAt(parent); - assertThat(searchHit.getType(), equalTo("parent")); - assertThat(searchHit.getId(), equalTo(String.format(Locale.ENGLISH, "%03d", parent))); - assertThat(searchHit.getShard(), notNullValue()); - - SearchHits inner = searchHit.getInnerHits().get("a"); - assertThat(inner.getTotalHits(), equalTo((long) child1InnerObjects[parent])); - for (int child = 0; child < child1InnerObjects[parent] && child < size; child++) { - SearchHit innerHit = inner.getAt(child); - assertThat(innerHit.getType(), equalTo("child1")); - String childId = String.format(Locale.ENGLISH, "%04d", offset1 + child); - assertThat(innerHit.getId(), equalTo(childId)); - assertThat(innerHit.getNestedIdentity(), nullValue()); - } - offset1 += child1InnerObjects[parent]; - - inner = searchHit.getInnerHits().get("b"); - assertThat(inner.getTotalHits(), equalTo((long) child2InnerObjects[parent])); - for (int child = 0; child < child2InnerObjects[parent] && child < size; child++) { - SearchHit innerHit = inner.getAt(child); - assertThat(innerHit.getType(), equalTo("child2")); - String childId = String.format(Locale.ENGLISH, "%04d", offset2 + child); - assertThat(innerHit.getId(), equalTo(childId)); - assertThat(innerHit.getNestedIdentity(), nullValue()); - } - offset2 += child2InnerObjects[parent]; - } - } - - public void testInnerHitsOnHasParent() throws Exception { - assertAcked(prepareCreate("stack") - .setSettings("index.mapping.single_type", false) - .addMapping("question", "body", "type=text") - .addMapping("answer", "_parent", "type=question", "body", "type=text") - ); - List requests = new ArrayList<>(); - requests.add(client().prepareIndex("stack", "question", "1").setSource("body", "I'm using HTTPS + Basic authentication " - + "to protect a resource. How can I throttle authentication attempts to protect against brute force attacks?")); - requests.add(client().prepareIndex("stack", "answer", "1").setParent("1").setSource("body", - "install fail2ban and enable rules for apache")); - requests.add(client().prepareIndex("stack", "question", "2").setSource("body", - "I have firewall rules set up and also denyhosts installed.\\ndo I also need to install fail2ban?")); - requests.add(client().prepareIndex("stack", "answer", "2").setParent("2").setSource("body", - "Denyhosts protects only ssh; Fail2Ban protects all daemons.")); - indexRandom(true, requests); - - SearchResponse response = client().prepareSearch("stack") - .setTypes("answer") - .addSort("_uid", SortOrder.ASC) - .setQuery( - boolQuery() - .must(matchQuery("body", "fail2ban")) - .must(hasParentQuery("question", matchAllQuery(), false).innerHit(new InnerHitBuilder(), false)) - ).get(); - assertNoFailures(response); - assertHitCount(response, 2); - - SearchHit searchHit = response.getHits().getAt(0); - assertThat(searchHit.getId(), equalTo("1")); - assertThat(searchHit.getType(), equalTo("answer")); - assertThat(searchHit.getInnerHits().get("question").getTotalHits(), equalTo(1L)); - assertThat(searchHit.getInnerHits().get("question").getAt(0).getType(), equalTo("question")); - assertThat(searchHit.getInnerHits().get("question").getAt(0).getId(), equalTo("1")); - - searchHit = response.getHits().getAt(1); - assertThat(searchHit.getId(), equalTo("2")); - assertThat(searchHit.getType(), equalTo("answer")); - assertThat(searchHit.getInnerHits().get("question").getTotalHits(), equalTo(1L)); - assertThat(searchHit.getInnerHits().get("question").getAt(0).getType(), equalTo("question")); - assertThat(searchHit.getInnerHits().get("question").getAt(0).getId(), equalTo("2")); - } - - public void testParentChildMultipleLayers() throws Exception { - assertAcked(prepareCreate("articles") - .setSettings("index.mapping.single_type", false) - .addMapping("article", "title", "type=text") - .addMapping("comment", "_parent", "type=article", "message", "type=text") - .addMapping("remark", "_parent", "type=comment", "message", "type=text") - ); - - List requests = new ArrayList<>(); - requests.add(client().prepareIndex("articles", "article", "1").setSource("title", "quick brown fox")); - requests.add(client().prepareIndex("articles", "comment", "1").setParent("1").setSource("message", "fox eat quick")); - requests.add(client().prepareIndex("articles", "remark", "1").setParent("1").setRouting("1").setSource("message", "good")); - requests.add(client().prepareIndex("articles", "article", "2").setSource("title", "big gray elephant")); - requests.add(client().prepareIndex("articles", "comment", "2").setParent("2").setSource("message", "elephant captured")); - requests.add(client().prepareIndex("articles", "remark", "2").setParent("2").setRouting("2").setSource("message", "bad")); - indexRandom(true, requests); - - SearchResponse response = client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", - hasChildQuery("remark", matchQuery("message", "good"), ScoreMode.None).innerHit(new InnerHitBuilder(), false), - ScoreMode.None).innerHit(new InnerHitBuilder(), false)) - .get(); - - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("1")); - - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.getTotalHits(), equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("1")); - assertThat(innerHits.getAt(0).getType(), equalTo("comment")); - - innerHits = innerHits.getAt(0).getInnerHits().get("remark"); - assertThat(innerHits.getTotalHits(), equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("1")); - assertThat(innerHits.getAt(0).getType(), equalTo("remark")); - - response = client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", - hasChildQuery("remark", matchQuery("message", "bad"), ScoreMode.None).innerHit(new InnerHitBuilder(), false), - ScoreMode.None).innerHit(new InnerHitBuilder(), false)) - .get(); - - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("2")); - - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.getTotalHits(), equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("2")); - assertThat(innerHits.getAt(0).getType(), equalTo("comment")); - - innerHits = innerHits.getAt(0).getInnerHits().get("remark"); - assertThat(innerHits.getTotalHits(), equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("2")); - assertThat(innerHits.getAt(0).getType(), equalTo("remark")); - } - public void testNestedMultipleLayers() throws Exception { assertAcked(prepareCreate("articles").addMapping("article", jsonBuilder().startObject() .startObject("article").startObject("properties") @@ -724,92 +460,6 @@ public void testInnerHitsWithObjectFieldThatHasANestedField() throws Exception { assertThat(messages.getAt(0).getNestedIdentity().getChild(), nullValue()); } - public void testRoyals() throws Exception { - assertAcked( - prepareCreate("royals") - .setSettings("index.mapping.single_type", false) - .addMapping("king") - .addMapping("prince", "_parent", "type=king") - .addMapping("duke", "_parent", "type=prince") - .addMapping("earl", "_parent", "type=duke") - .addMapping("baron", "_parent", "type=earl") - ); - - List requests = new ArrayList<>(); - requests.add(client().prepareIndex("royals", "king", "king").setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "prince", "prince").setParent("king").setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "duke", "duke").setParent("prince").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "earl", "earl1").setParent("duke").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "earl", "earl2").setParent("duke").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "earl", "earl3").setParent("duke").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "earl", "earl4").setParent("duke").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "baron", "baron1").setParent("earl1").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "baron", "baron2").setParent("earl2").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "baron", "baron3").setParent("earl3").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "baron", "baron4").setParent("earl4").setRouting("king") - .setSource("{}", XContentType.JSON)); - indexRandom(true, requests); - - SearchResponse response = client().prepareSearch("royals") - .setTypes("duke") - .setQuery(boolQuery() - .filter(hasParentQuery("prince", - hasParentQuery("king", matchAllQuery(), false).innerHit(new InnerHitBuilder().setName("kings"), false), - false).innerHit(new InnerHitBuilder().setName("princes"), false) - ) - .filter(hasChildQuery("earl", - hasChildQuery("baron", matchAllQuery(), ScoreMode.None) - .innerHit(new InnerHitBuilder().setName("barons"), false), - ScoreMode.None).innerHit(new InnerHitBuilder() - .addSort(SortBuilders.fieldSort("_uid").order(SortOrder.ASC)) - .setName("earls") - .setSize(4), false) - ) - ) - .get(); - assertHitCount(response, 1); - assertThat(response.getHits().getAt(0).getId(), equalTo("duke")); - - SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("earls"); - assertThat(innerHits.getTotalHits(), equalTo(4L)); - assertThat(innerHits.getAt(0).getId(), equalTo("earl1")); - assertThat(innerHits.getAt(1).getId(), equalTo("earl2")); - assertThat(innerHits.getAt(2).getId(), equalTo("earl3")); - assertThat(innerHits.getAt(3).getId(), equalTo("earl4")); - - SearchHits innerInnerHits = innerHits.getAt(0).getInnerHits().get("barons"); - assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron1")); - - innerInnerHits = innerHits.getAt(1).getInnerHits().get("barons"); - assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron2")); - - innerInnerHits = innerHits.getAt(2).getInnerHits().get("barons"); - assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron3")); - - innerInnerHits = innerHits.getAt(3).getInnerHits().get("barons"); - assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron4")); - - innerHits = response.getHits().getAt(0).getInnerHits().get("princes"); - assertThat(innerHits.getTotalHits(), equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("prince")); - - innerInnerHits = innerHits.getAt(0).getInnerHits().get("kings"); - assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("king")); - } - public void testMatchesQueriesNestedInnerHits() throws Exception { XContentBuilder builder = jsonBuilder().startObject() .startObject("type1") @@ -914,84 +564,6 @@ public void testMatchesQueriesNestedInnerHits() throws Exception { } } - public void testMatchesQueriesParentChildInnerHits() throws Exception { - assertAcked(prepareCreate("index") - .setSettings("index.mapping.single_type", false) - .addMapping("child", "_parent", "type=parent")); - List requests = new ArrayList<>(); - requests.add(client().prepareIndex("index", "parent", "1").setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("index", "child", "1").setParent("1").setSource("field", "value1")); - requests.add(client().prepareIndex("index", "child", "2").setParent("1").setSource("field", "value2")); - requests.add(client().prepareIndex("index", "parent", "2").setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("index", "child", "3").setParent("2").setSource("field", "value1")); - indexRandom(true, requests); - - SearchResponse response = client().prepareSearch("index") - .setQuery(hasChildQuery("child", matchQuery("field", "value1").queryName("_name1"), ScoreMode.None) - .innerHit(new InnerHitBuilder(), false)) - .addSort("_uid", SortOrder.ASC) - .get(); - assertHitCount(response, 2); - assertThat(response.getHits().getAt(0).getId(), equalTo("1")); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits(), equalTo(1L)); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); - - assertThat(response.getHits().getAt(1).getId(), equalTo("2")); - assertThat(response.getHits().getAt(1).getInnerHits().get("child").getTotalHits(), equalTo(1L)); - assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); - - QueryBuilder query = hasChildQuery("child", matchQuery("field", "value2").queryName("_name2"), ScoreMode.None) - .innerHit(new InnerHitBuilder(), false); - response = client().prepareSearch("index") - .setQuery(query) - .addSort("_uid", SortOrder.ASC) - .get(); - assertHitCount(response, 1); - assertThat(response.getHits().getAt(0).getId(), equalTo("1")); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits(), equalTo(1L)); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name2")); - } - - public void testDontExplode() throws Exception { - assertAcked(prepareCreate("index1") - .setSettings("index.mapping.single_type", false) - .addMapping("child", "_parent", "type=parent")); - List requests = new ArrayList<>(); - requests.add(client().prepareIndex("index1", "parent", "1").setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("index1", "child", "1").setParent("1").setSource("field", "value1")); - indexRandom(true, requests); - - QueryBuilder query = hasChildQuery("child", matchQuery("field", "value1"), ScoreMode.None) - .innerHit(new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1), false); - SearchResponse response = client().prepareSearch("index1") - .setQuery(query) - .get(); - assertNoFailures(response); - assertHitCount(response, 1); - - assertAcked(prepareCreate("index2").addMapping("type", "nested", "type=nested")); - client().prepareIndex("index2", "type", "1").setSource(jsonBuilder().startObject() - .startArray("nested") - .startObject() - .field("field", "value1") - .endObject() - .endArray() - .endObject()) - .setRefreshPolicy(IMMEDIATE) - .get(); - - query = nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg) - .innerHit(new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1), false); - response = client().prepareSearch("index2") - .setQuery(query) - .get(); - assertNoFailures(response); - assertHitCount(response, 1); - } - public void testNestedSourceFiltering() throws Exception { assertAcked(prepareCreate("index1").addMapping("message", "comments", "type=nested")); client().prepareIndex("index1", "message", "1").setSource(jsonBuilder().startObject() @@ -1021,25 +593,6 @@ public void testNestedSourceFiltering() throws Exception { equalTo("fox ate rabbit x y z")); } - public void testNestedInnerHitWrappedInParentChildInnerhit() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) - .addMapping("child_type", "_parent", "type=parent_type", "nested_type", "type=nested")); - client().prepareIndex("test", "parent_type", "1").setSource("key", "value").get(); - client().prepareIndex("test", "child_type", "2").setParent("1").setSource("nested_type", Collections.singletonMap("key", "value")) - .get(); - refresh(); - SearchResponse response = client().prepareSearch("test") - .setQuery(boolQuery().must(matchQuery("key", "value")) - .should(hasChildQuery("child_type", nestedQuery("nested_type", matchAllQuery(), ScoreMode.None) - .innerHit(new InnerHitBuilder(), false), ScoreMode.None).innerHit(new InnerHitBuilder(), false))) - .get(); - assertHitCount(response, 1); - SearchHit hit = response.getHits().getAt(0); - assertThat(hit.getInnerHits().get("child_type").getAt(0).field("_parent").getValue(), equalTo("1")); - assertThat(hit.getInnerHits().get("child_type").getAt(0).getInnerHits().get("nested_type").getAt(0).field("_parent"), nullValue()); - } - public void testInnerHitsWithIgnoreUnmapped() throws Exception { assertAcked(prepareCreate("index1") .setSettings("index.mapping.single_type", false) @@ -1062,17 +615,6 @@ public void testInnerHitsWithIgnoreUnmapped() throws Exception { assertNoFailures(response); assertHitCount(response, 2); assertSearchHits(response, "1", "3"); - - response = client().prepareSearch("index1", "index2") - .setQuery(boolQuery() - .should(hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) - .innerHit(new InnerHitBuilder(), true)) - .should(termQuery("key", "value")) - ) - .get(); - assertNoFailures(response); - assertHitCount(response, 2); - assertSearchHits(response, "1", "3"); } } diff --git a/docs/java-api/aggregations/bucket/children-aggregation.asciidoc b/docs/java-api/aggregations/bucket/children-aggregation.asciidoc index 1bf8a5b26e69e..f6a23fdafe976 100644 --- a/docs/java-api/aggregations/bucket/children-aggregation.asciidoc +++ b/docs/java-api/aggregations/bucket/children-aggregation.asciidoc @@ -24,7 +24,7 @@ Import Aggregation definition classes: [source,java] -------------------------------------------------- -import org.elasticsearch.search.aggregations.bucket.children.Children; +import org.elasticsearch.join.aggregations.Children; -------------------------------------------------- [source,java] diff --git a/modules/parent-join/build.gradle b/modules/parent-join/build.gradle new file mode 100644 index 0000000000000..67bcc9d54e8e7 --- /dev/null +++ b/modules/parent-join/build.gradle @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +esplugin { + description 'This module adds the support parent-child queries and aggregations' + classname 'org.elasticsearch.join.ParentJoinPlugin' + hasClientJar = true +} diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/ParentJoinPlugin.java b/modules/parent-join/src/main/java/org/elasticsearch/join/ParentJoinPlugin.java new file mode 100644 index 0000000000000..dec3950836aab --- /dev/null +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/ParentJoinPlugin.java @@ -0,0 +1,54 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.join; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.join.aggregations.ChildrenAggregationBuilder; +import org.elasticsearch.join.aggregations.InternalChildren; +import org.elasticsearch.join.query.HasChildQueryBuilder; +import org.elasticsearch.join.query.HasParentQueryBuilder; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.plugins.SearchPlugin; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class ParentJoinPlugin extends Plugin implements SearchPlugin { + public ParentJoinPlugin(Settings settings) {} + + @Override + public List> getQueries() { + return Arrays.asList( + new QuerySpec<>(HasChildQueryBuilder.NAME, HasChildQueryBuilder::new, HasChildQueryBuilder::fromXContent), + new QuerySpec<>(HasParentQueryBuilder.NAME, HasParentQueryBuilder::new, HasParentQueryBuilder::fromXContent) + ); + } + + @Override + public List getAggregations() { + return Collections.singletonList( + new AggregationSpec(ChildrenAggregationBuilder.NAME, ChildrenAggregationBuilder::new, ChildrenAggregationBuilder::parse) + .addResultReader(InternalChildren::new) + ); + } + + +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/Children.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/Children.java similarity index 94% rename from core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/Children.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/Children.java index b1e4b2877a3a0..394c690709d8a 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/Children.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/Children.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket.children; +package org.elasticsearch.join.aggregations; import org.elasticsearch.search.aggregations.bucket.SingleBucketAggregation; diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregationBuilder.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java similarity index 99% rename from core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregationBuilder.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java index 3a0d2fff982d8..d04b1f0a66052 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregationBuilder.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket.children; +package org.elasticsearch.join.aggregations; import org.apache.lucene.search.Query; import org.elasticsearch.common.ParsingException; diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregatorFactory.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregatorFactory.java similarity index 98% rename from core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregatorFactory.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregatorFactory.java index b0a4c64305aa1..800be74ba6fe6 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregatorFactory.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregatorFactory.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket.children; +package org.elasticsearch.join.aggregations; import org.apache.lucene.search.Query; import org.elasticsearch.search.aggregations.Aggregator; diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/InternalChildren.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/InternalChildren.java similarity index 97% rename from core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/InternalChildren.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/InternalChildren.java index 05a38c8cd59b5..05cd40e3d3366 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/InternalChildren.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/InternalChildren.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket.children; +package org.elasticsearch.join.aggregations; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.search.aggregations.InternalAggregations; diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/JoinAggregationBuilders.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/JoinAggregationBuilders.java new file mode 100644 index 0000000000000..73522a68b4595 --- /dev/null +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/JoinAggregationBuilders.java @@ -0,0 +1,29 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.join.aggregations; + +public abstract class JoinAggregationBuilders { + /** + * Create a new {@link Children} aggregation with the given name. + */ + public static ChildrenAggregationBuilder children(String name, String childType) { + return new ChildrenAggregationBuilder(name, childType); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregator.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregator.java similarity index 99% rename from core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregator.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregator.java index 37a443e9bab34..c1ffb097abc39 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregator.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.search.aggregations.bucket.children; +package org.elasticsearch.join.aggregations; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.LeafReaderContext; diff --git a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java b/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasChildQueryBuilder.java similarity index 94% rename from core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/query/HasChildQueryBuilder.java index 18ad7f9f310b5..494c5e498e13b 100644 --- a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasChildQueryBuilder.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.index.query; +package org.elasticsearch.join.query; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; @@ -38,6 +38,14 @@ import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.ParentFieldMapper; +import org.elasticsearch.index.query.AbstractQueryBuilder; +import org.elasticsearch.index.query.InnerHitBuilder; +import org.elasticsearch.index.query.NestedQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.index.query.QueryRewriteContext; +import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.QueryShardException; import java.io.IOException; import java.util.Locale; @@ -210,7 +218,7 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep builder.field(QUERY_FIELD.getPreferredName()); query.toXContent(builder, params); builder.field(TYPE_FIELD.getPreferredName(), type); - builder.field(SCORE_MODE_FIELD.getPreferredName(), scoreModeAsString(scoreMode)); + builder.field(SCORE_MODE_FIELD.getPreferredName(), NestedQueryBuilder.scoreModeAsString(scoreMode)); builder.field(MIN_CHILDREN_FIELD.getPreferredName(), minChildren); builder.field(MAX_CHILDREN_FIELD.getPreferredName(), maxChildren); builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped); @@ -251,7 +259,7 @@ public static HasChildQueryBuilder fromXContent(QueryParseContext parseContext) if (TYPE_FIELD.match(currentFieldName)) { childType = parser.text(); } else if (SCORE_MODE_FIELD.match(currentFieldName)) { - scoreMode = parseScoreMode(parser.text()); + scoreMode = NestedQueryBuilder.parseScoreMode(parser.text()); } else if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName)) { boost = parser.floatValue(); } else if (MIN_CHILDREN_FIELD.match(currentFieldName)) { @@ -278,30 +286,6 @@ public static HasChildQueryBuilder fromXContent(QueryParseContext parseContext) return hasChildQueryBuilder; } - public static ScoreMode parseScoreMode(String scoreModeString) { - if ("none".equals(scoreModeString)) { - return ScoreMode.None; - } else if ("min".equals(scoreModeString)) { - return ScoreMode.Min; - } else if ("max".equals(scoreModeString)) { - return ScoreMode.Max; - } else if ("avg".equals(scoreModeString)) { - return ScoreMode.Avg; - } else if ("sum".equals(scoreModeString)) { - return ScoreMode.Total; - } - throw new IllegalArgumentException("No score mode for child query [" + scoreModeString + "] found"); - } - - public static String scoreModeAsString(ScoreMode scoreMode) { - if (scoreMode == ScoreMode.Total) { - // Lucene uses 'total' but 'sum' is more consistent with other elasticsearch APIs - return "sum"; - } else { - return scoreMode.name().toLowerCase(Locale.ROOT); - } - } - @Override public String getWriteableName() { return NAME; diff --git a/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java b/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasParentQueryBuilder.java similarity index 96% rename from core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/query/HasParentQueryBuilder.java index 63c9484691766..ca0bfd623d51b 100644 --- a/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasParentQueryBuilder.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.index.query; +package org.elasticsearch.join.query; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; @@ -33,6 +33,13 @@ import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.ParentFieldMapper; +import org.elasticsearch.index.query.AbstractQueryBuilder; +import org.elasticsearch.index.query.InnerHitBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.index.query.QueryRewriteContext; +import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.QueryShardException; import java.io.IOException; import java.util.HashSet; diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/query/JoinQueryBuilders.java b/modules/parent-join/src/main/java/org/elasticsearch/join/query/JoinQueryBuilders.java new file mode 100644 index 0000000000000..af778f400f7b3 --- /dev/null +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/query/JoinQueryBuilders.java @@ -0,0 +1,50 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.join.query; + +import org.apache.lucene.search.join.ScoreMode; +import org.elasticsearch.index.query.QueryBuilder; + +public abstract class JoinQueryBuilders { + /** + * Constructs a new has_child query, with the child type and the query to run on the child documents. The + * results of this query are the parent docs that those child docs matched. + * + * @param type The child type. + * @param query The query. + * @param scoreMode How the scores from the children hits should be aggregated into the parent hit. + */ + public static HasChildQueryBuilder hasChildQuery(String type, QueryBuilder query, ScoreMode scoreMode) { + return new HasChildQueryBuilder(type, query, scoreMode); + } + + /** + * Constructs a new parent query, with the parent type and the query to run on the parent documents. The + * results of this query are the children docs that those parent docs matched. + * + * @param type The parent type. + * @param query The query. + * @param score Whether the score from the parent hit should propagate to the child hit + */ + public static HasParentQueryBuilder hasParentQuery(String type, QueryBuilder query, boolean score) { + return new HasParentQueryBuilder(type, query, score); + } + +} diff --git a/modules/parent-join/src/test/java/org/elasticsearch/join/ParentChildClientYamlTestSuiteIT.java b/modules/parent-join/src/test/java/org/elasticsearch/join/ParentChildClientYamlTestSuiteIT.java new file mode 100644 index 0000000000000..666fa736d45de --- /dev/null +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/ParentChildClientYamlTestSuiteIT.java @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.join; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate; +import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase; + +public class ParentChildClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase { + public ParentChildClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) { + super(testCandidate); + } + + @ParametersFactory + public static Iterable parameters() throws Exception { + return createParameters(); + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenIT.java b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenIT.java similarity index 92% rename from core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenIT.java rename to modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenIT.java index bfe483ca89050..8da6dbcdf6cde 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenIT.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenIT.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.search.aggregations.bucket; +package org.elasticsearch.join.aggregations; import org.apache.lucene.search.join.ScoreMode; import org.elasticsearch.action.index.IndexRequestBuilder; @@ -26,27 +26,33 @@ import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.InternalAggregation; -import org.elasticsearch.search.aggregations.bucket.children.Children; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.search.aggregations.metrics.tophits.TopHits; import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESIntegTestCase.ClusterScope; +import org.elasticsearch.test.ESIntegTestCase.Scope; +import org.junit.Before; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import static org.elasticsearch.index.query.QueryBuilders.hasChildQuery; import static org.elasticsearch.index.query.QueryBuilders.matchQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery; -import static org.elasticsearch.search.aggregations.AggregationBuilders.children; +import static org.elasticsearch.join.aggregations.JoinAggregationBuilders.children; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery; import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.search.aggregations.AggregationBuilders.topHits; @@ -59,13 +65,28 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; -@ESIntegTestCase.SuiteScopeTestCase +@ClusterScope(scope = Scope.SUITE) public class ChildrenIT extends ESIntegTestCase { - private static final Map categoryToControl = new HashMap<>(); @Override - public void setupSuiteScopeCluster() throws Exception { + protected boolean ignoreExternalCluster() { + return true; + } + + @Override + protected Collection> nodePlugins() { + return Collections.singleton(ParentJoinPlugin.class); + } + + @Override + protected Collection> transportClientPlugins() { + return nodePlugins(); + } + + @Before + public void setupCluster() throws Exception { + categoryToControl.clear(); assertAcked( prepareCreate("test") .setSettings("index.mapping.single_type", false) @@ -95,7 +116,8 @@ public void setupSuiteScopeCluster() throws Exception { control.articleIds.add(id); } - requests.add(client().prepareIndex("test", "article", id).setCreate(true).setSource("category", categories, "randomized", true)); + requests.add(client() + .prepareIndex("test", "article", id).setCreate(true).setSource("category", categories, "randomized", true)); } String[] commenters = new String[randomIntBetween(5, 50)]; @@ -116,17 +138,24 @@ public void setupSuiteScopeCluster() throws Exception { control.commenterToCommentId.put(commenter, ids = new HashSet<>()); } ids.add(idValue); - requests.add(client().prepareIndex("test", "comment", idValue).setCreate(true).setParent(articleId).setSource("commenter", commenter)); + requests.add(client().prepareIndex("test", "comment", idValue) + .setCreate(true).setParent(articleId).setSource("commenter", commenter)); } } } - requests.add(client().prepareIndex("test", "article", "a").setSource("category", new String[]{"a"}, "randomized", false)); - requests.add(client().prepareIndex("test", "article", "b").setSource("category", new String[]{"a", "b"}, "randomized", false)); - requests.add(client().prepareIndex("test", "article", "c").setSource("category", new String[]{"a", "b", "c"}, "randomized", false)); - requests.add(client().prepareIndex("test", "article", "d").setSource("category", new String[]{"c"}, "randomized", false)); - requests.add(client().prepareIndex("test", "comment", "a").setParent("a").setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("test", "comment", "c").setParent("c").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("test", "article", "a") + .setSource("category", new String[]{"a"}, "randomized", false)); + requests.add(client().prepareIndex("test", "article", "b") + .setSource("category", new String[]{"a", "b"}, "randomized", false)); + requests.add(client().prepareIndex("test", "article", "c") + .setSource("category", new String[]{"a", "b", "c"}, "randomized", false)); + requests.add(client().prepareIndex("test", "article", "d") + .setSource("category", new String[]{"c"}, "randomized", false)); + requests.add(client().prepareIndex("test", "comment", "a") + .setParent("a").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("test", "comment", "c") + .setParent("c").setSource("{}", XContentType.JSON)); indexRandom(true, requests); ensureSearchable("test"); @@ -155,7 +184,8 @@ public void testChildrenAggs() throws Exception { Children childrenBucket = categoryBucket.getAggregations().get("to_comment"); assertThat(childrenBucket.getName(), equalTo("to_comment")); assertThat(childrenBucket.getDocCount(), equalTo((long) entry1.getValue().commentIds.size())); - assertThat((long) ((InternalAggregation)childrenBucket).getProperty("_count"), equalTo((long) entry1.getValue().commentIds.size())); + assertThat((long) ((InternalAggregation)childrenBucket).getProperty("_count"), + equalTo((long) entry1.getValue().commentIds.size())); Terms commentersTerms = childrenBucket.getAggregations().get("commenters"); assertThat((Terms) ((InternalAggregation)childrenBucket).getProperty("commenters"), sameInstance(commentersTerms)); @@ -283,7 +313,7 @@ public void testWithDeletes() throws Exception { public void testNonExistingChildType() throws Exception { SearchResponse searchResponse = client().prepareSearch("test") .addAggregation( -children("non-existing", "xyz") + children("non-existing", "xyz") ).get(); assertSearchResponse(searchResponse); @@ -304,7 +334,8 @@ public void testPostCollection() throws Exception { ); List requests = new ArrayList<>(); - requests.add(client().prepareIndex(indexName, masterType, "1").setSource("brand", "Levis", "name", "Style 501", "material", "Denim")); + requests.add(client().prepareIndex(indexName, masterType, "1") + .setSource("brand", "Levis", "name", "Style 501", "material", "Denim")); requests.add(client().prepareIndex(indexName, childType, "0").setParent("1").setSource("color", "blue", "size", "32")); requests.add(client().prepareIndex(indexName, childType, "1").setParent("1").setSource("color", "blue", "size", "34")); requests.add(client().prepareIndex(indexName, childType, "2").setParent("1").setSource("color", "blue", "size", "36")); @@ -312,7 +343,8 @@ public void testPostCollection() throws Exception { requests.add(client().prepareIndex(indexName, childType, "4").setParent("1").setSource("color", "black", "size", "40")); requests.add(client().prepareIndex(indexName, childType, "5").setParent("1").setSource("color", "gray", "size", "36")); - requests.add(client().prepareIndex(indexName, masterType, "2").setSource("brand", "Wrangler", "name", "Regular Cut", "material", "Leather")); + requests.add(client().prepareIndex(indexName, masterType, "2") + .setSource("brand", "Wrangler", "name", "Regular Cut", "material", "Leather")); requests.add(client().prepareIndex(indexName, childType, "6").setParent("2").setSource("color", "blue", "size", "32")); requests.add(client().prepareIndex(indexName, childType, "7").setParent("2").setSource("color", "blue", "size", "34")); requests.add(client().prepareIndex(indexName, childType, "8").setParent("2").setSource("color", "black", "size", "36")); @@ -425,7 +457,7 @@ public void testPostCollectAllLeafReaders() throws Exception { .setSize(0) .addAggregation(AggregationBuilders.terms("towns").field("town") .subAggregation(AggregationBuilders.terms("parent_names").field("name") -.subAggregation(AggregationBuilders.children("child_docs", "childType")) +.subAggregation(children("child_docs", "childType")) ) ) .get(); @@ -468,5 +500,4 @@ private Control(String category) { this.category = category; } } - } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenTests.java similarity index 78% rename from core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenTests.java rename to modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenTests.java index 4098e85c62eda..85a97c4b9b413 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenTests.java @@ -17,13 +17,22 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket; +package org.elasticsearch.join.aggregations; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.aggregations.BaseAggregationTestCase; -import org.elasticsearch.search.aggregations.bucket.children.ChildrenAggregationBuilder; + +import java.util.Collection; +import java.util.Collections; public class ChildrenTests extends BaseAggregationTestCase { + @Override + protected Collection> getPlugins() { + return Collections.singleton(ParentJoinPlugin.class); + } + @Override protected ChildrenAggregationBuilder createTestAggregatorBuilder() { String name = randomAlphaOfLengthBetween(3, 20); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/InternalChildrenTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/InternalChildrenTests.java similarity index 91% rename from core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/InternalChildrenTests.java rename to modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/InternalChildrenTests.java index b248d5ed98117..afcbc953ebb66 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/InternalChildrenTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/InternalChildrenTests.java @@ -17,11 +17,11 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket.children; +package org.elasticsearch.join.aggregations; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; -import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.InternalSingleBucketAggregationTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregatorTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregatorTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregatorTests.java rename to modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregatorTests.java index 17152bc450a22..0a00b2d1c26d8 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregatorTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregatorTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket.children; +package org.elasticsearch.join.aggregations; import org.apache.lucene.document.Field; import org.apache.lucene.document.SortedDocValuesField; diff --git a/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java b/modules/parent-join/src/test/java/org/elasticsearch/join/query/ChildQuerySearchIT.java similarity index 87% rename from core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java rename to modules/parent-join/src/test/java/org/elasticsearch/join/query/ChildQuerySearchIT.java index 697352c5edce7..ed910ac89e639 100644 --- a/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/query/ChildQuerySearchIT.java @@ -16,17 +16,20 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.search.child; +package org.elasticsearch.join.query; import org.apache.lucene.search.join.ScoreMode; import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse; +import org.elasticsearch.action.bulk.BulkRequestBuilder; +import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.explain.ExplainResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.support.WriteRequest.RefreshPolicy; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.lucene.search.function.CombineFunction; import org.elasticsearch.common.lucene.search.function.FiltersFunctionScoreQuery; import org.elasticsearch.common.settings.Settings; @@ -34,8 +37,6 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.query.BoolQueryBuilder; -import org.elasticsearch.index.query.HasChildQueryBuilder; -import org.elasticsearch.index.query.HasParentQueryBuilder; import org.elasticsearch.index.query.IdsQueryBuilder; import org.elasticsearch.index.query.InnerHitBuilder; import org.elasticsearch.index.query.MatchAllQueryBuilder; @@ -43,6 +44,8 @@ import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.aggregations.AggregationBuilders; @@ -62,6 +65,8 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -72,8 +77,6 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.index.query.QueryBuilders.boolQuery; import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasChildQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; import static org.elasticsearch.index.query.QueryBuilders.idsQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.index.query.QueryBuilders.matchQuery; @@ -83,6 +86,8 @@ import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.index.query.QueryBuilders.termsQuery; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasParentQuery; import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.fieldValueFactorFunction; import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.weightFactorFunction; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; @@ -100,6 +105,21 @@ @ClusterScope(scope = Scope.SUITE) public class ChildQuerySearchIT extends ESIntegTestCase { + + @Override + protected boolean ignoreExternalCluster() { + return true; + } + + @Override + protected Collection> nodePlugins() { + return Collections.singleton(ParentJoinPlugin.class); + } + + @Override + protected Collection> transportClientPlugins() { + return nodePlugins(); + } @Override public Settings indexSettings() { @@ -145,26 +165,30 @@ public void testMultiLevelChild() throws Exception { assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", termQuery("p_field", "p_value1"), false))).execute() + .setQuery(boolQuery().must(matchAllQuery()) + .filter(hasParentQuery("parent", termQuery("p_field", "p_value1"), false))).execute() .actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c1")); searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasParentQuery("child", termQuery("c_field", "c_value1"), false))).execute() + .setQuery(boolQuery().must(matchAllQuery()) + .filter(hasParentQuery("child", termQuery("c_field", "c_value1"), false))).execute() .actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("gc1")); - searchResponse = client().prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "p_value1"), false)).execute() + searchResponse = client().prepareSearch("test") + .setQuery(hasParentQuery("parent", termQuery("p_field", "p_value1"), false)).execute() .actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c1")); - searchResponse = client().prepareSearch("test").setQuery(hasParentQuery("child", termQuery("c_field", "c_value1"), false)).execute() + searchResponse = client().prepareSearch("test") + .setQuery(hasParentQuery("child", termQuery("c_field", "c_value1"), false)).execute() .actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); @@ -209,7 +233,8 @@ public void testSimpleChildQuery() throws Exception { refresh(); // TEST FETCHING _parent from child - SearchResponse searchResponse = client().prepareSearch("test").setQuery(idsQuery("child").addIds("c1")).storedFields("_parent").execute() + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(idsQuery("child").addIds("c1")).storedFields("_parent").execute() .actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); @@ -284,7 +309,8 @@ public void testCachingBugWithFqueryFilter() throws Exception { builders.add(client().prepareIndex("test", "child", Integer.toString(i)).setSource("c_field", i).setParent("" + 0)); } for (int i = 0; i < 10; i++) { - builders.add(client().prepareIndex("test", "child", Integer.toString(i + 10)).setSource("c_field", i + 10).setParent(Integer.toString(i))); + builders.add(client().prepareIndex("test", "child", Integer.toString(i + 10)) + .setSource("c_field", i + 10).setParent(Integer.toString(i))); } if (randomBoolean()) { @@ -445,9 +471,11 @@ public void testScopedFacet() throws Exception { SearchResponse searchResponse = client() .prepareSearch("test") - .setQuery(hasChildQuery("child", boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow")), ScoreMode.None)) + .setQuery(hasChildQuery("child", + boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow")), ScoreMode.None)) .addAggregation(AggregationBuilders.global("global").subAggregation( - AggregationBuilders.filter("filter", boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow"))).subAggregation( + AggregationBuilders.filter("filter", + boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow"))).subAggregation( AggregationBuilders.terms("facet1").field("c_field")))).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); @@ -523,7 +551,8 @@ public void testDfsSearchType() throws Exception { assertNoFailures(searchResponse); searchResponse = client().prepareSearch("test").setSearchType(SearchType.DFS_QUERY_THEN_FETCH) - .setQuery(boolQuery().mustNot(hasParentQuery("parent", boolQuery().should(queryStringQuery("p_field:*")), false))).execute() + .setQuery(boolQuery().mustNot(hasParentQuery("parent", + boolQuery().should(queryStringQuery("p_field:*")), false))).execute() .actionGet(); assertNoFailures(searchResponse); } @@ -570,7 +599,8 @@ public void testCountApiUsage() throws Exception { .get(); assertHitCount(countResponse, 1L); - countResponse = client().prepareSearch("test").setSize(0).setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true)) + countResponse = client().prepareSearch("test").setSize(0) + .setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true)) .get(); assertHitCount(countResponse, 1L); @@ -579,7 +609,8 @@ public void testCountApiUsage() throws Exception { .get(); assertHitCount(countResponse, 1L); - countResponse = client().prepareSearch("test").setSize(0).setQuery(constantScoreQuery(hasParentQuery("parent", termQuery("p_field", "1"), false))) + countResponse = client().prepareSearch("test").setSize(0) + .setQuery(constantScoreQuery(hasParentQuery("parent", termQuery("p_field", "1"), false))) .get(); assertHitCount(countResponse, 1L); } @@ -685,11 +716,11 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { SearchResponse response = client() .prepareSearch("test") .setQuery( - QueryBuilders.hasChildQuery( - "child", - QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), - fieldValueFactorFunction("c_field1")) - .boostMode(CombineFunction.REPLACE), ScoreMode.Total)).get(); + hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), + fieldValueFactorFunction("c_field1")) + .boostMode(CombineFunction.REPLACE), ScoreMode.Total)).get(); assertThat(response.getHits().getTotalHits(), equalTo(3L)); assertThat(response.getHits().getHits()[0].getId(), equalTo("1")); @@ -702,11 +733,11 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { response = client() .prepareSearch("test") .setQuery( - QueryBuilders.hasChildQuery( - "child", - QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), - fieldValueFactorFunction("c_field1")) - .boostMode(CombineFunction.REPLACE), ScoreMode.Max)).get(); + hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), + fieldValueFactorFunction("c_field1")) + .boostMode(CombineFunction.REPLACE), ScoreMode.Max)).get(); assertThat(response.getHits().getTotalHits(), equalTo(3L)); assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); @@ -719,11 +750,11 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { response = client() .prepareSearch("test") .setQuery( - QueryBuilders.hasChildQuery( - "child", - QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), - fieldValueFactorFunction("c_field1")) - .boostMode(CombineFunction.REPLACE), ScoreMode.Avg)).get(); + hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), + fieldValueFactorFunction("c_field1")) + .boostMode(CombineFunction.REPLACE), ScoreMode.Avg)).get(); assertThat(response.getHits().getTotalHits(), equalTo(3L)); assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); @@ -736,11 +767,11 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { response = client() .prepareSearch("test") .setQuery( - QueryBuilders.hasParentQuery( - "parent", - QueryBuilders.functionScoreQuery(matchQuery("p_field1", "p_value3"), - fieldValueFactorFunction("p_field2")) - .boostMode(CombineFunction.REPLACE), true)) + hasParentQuery( + "parent", + QueryBuilders.functionScoreQuery(matchQuery("p_field1", "p_value3"), + fieldValueFactorFunction("p_field2")) + .boostMode(CombineFunction.REPLACE), true)) .addSort(SortBuilders.fieldSort("c_field3")).addSort(SortBuilders.scoreSort()).get(); assertThat(response.getHits().getTotalHits(), equalTo(7L)); @@ -769,7 +800,7 @@ public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Excep ensureGreen(); SearchResponse response = client().prepareSearch("test") - .setQuery(QueryBuilders.hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); + .setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits(), equalTo(0L)); @@ -777,20 +808,21 @@ public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Excep .setRefreshPolicy(RefreshPolicy.IMMEDIATE).get(); response = client().prepareSearch("test") - .setQuery(QueryBuilders.hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); + .setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits(), equalTo(0L)); - response = client().prepareSearch("test").setQuery(QueryBuilders.hasChildQuery("child", matchQuery("text", "value"), ScoreMode.Max)) + response = client().prepareSearch("test").setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.Max)) .get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits(), equalTo(0L)); - response = client().prepareSearch("test").setQuery(QueryBuilders.hasParentQuery("parent", matchQuery("text", "value"), false)).get(); + response = client().prepareSearch("test") + .setQuery(hasParentQuery("parent", matchQuery("text", "value"), false)).get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits(), equalTo(0L)); - response = client().prepareSearch("test").setQuery(QueryBuilders.hasParentQuery("parent", matchQuery("text", "value"), true)) + response = client().prepareSearch("test").setQuery(hasParentQuery("parent", matchQuery("text", "value"), true)) .get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits(), equalTo(0L)); @@ -818,7 +850,8 @@ public void testHasChildAndHasParentFilter_withFilter() throws Exception { assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1")); searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", termQuery("p_field", 1), false))).get(); + .setQuery(boolQuery().must(matchAllQuery()) + .filter(hasParentQuery("parent", termQuery("p_field", 1), false))).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("2")); @@ -873,12 +906,14 @@ public void testHasChildAndHasParentWrappedInAQueryFilter() throws Exception { assertSearchHit(searchResponse, 1, hasId("2")); searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", 1), ScoreMode.None)))) + .setQuery(boolQuery().must(matchAllQuery()) + .filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", 1), ScoreMode.None)))) .get(); assertSearchHit(searchResponse, 1, hasId("1")); searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(boolQuery().must(hasParentQuery("parent", matchQuery("p_field", 1), false)))).get(); + .setQuery(boolQuery().must(matchAllQuery()) + .filter(boolQuery().must(hasParentQuery("parent", matchQuery("p_field", 1), false)))).get(); assertSearchHit(searchResponse, 1, hasId("2")); } @@ -974,7 +1009,8 @@ public void testReIndexingParentAndChildDocuments() throws Exception { client().admin().indices().prepareRefresh("test").get(); } - searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.Total)) + searchResponse = client().prepareSearch("test") + .setQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.Total)) .get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); @@ -1009,7 +1045,8 @@ public void testHasChildQueryWithMinimumScore() throws Exception { client().prepareIndex("test", "child", "c5").setSource("c_field", "x").setParent("p2").get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.Total)) + SearchResponse searchResponse = client() + .prepareSearch("test").setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.Total)) .setMinScore(3) // Score needs to be 3 or above! .get(); assertNoFailures(searchResponse); @@ -1219,7 +1256,8 @@ public void testAddingParentToExistingMapping() throws IOException { createIndex("test"); ensureGreen(); - PutMappingResponse putMappingResponse = client().admin().indices().preparePutMapping("test").setType("child").setSource("number", "type=integer") + PutMappingResponse putMappingResponse = client().admin().indices() + .preparePutMapping("test").setType("child").setSource("number", "type=integer") .get(); assertThat(putMappingResponse.isAcknowledged(), equalTo(true)); @@ -1269,13 +1307,15 @@ public void testHasChildQueryWithNestedInnerObjects() throws Exception { ScoreMode scoreMode = randomFrom(ScoreMode.values()); SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(QueryBuilders.hasChildQuery("child", termQuery("c_field", "blue"), scoreMode)).filter(boolQuery().mustNot(termQuery("p_field", "3")))) + .setQuery(boolQuery().must(hasChildQuery("child", termQuery("c_field", "blue"), scoreMode)) + .filter(boolQuery().mustNot(termQuery("p_field", "3")))) .get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(QueryBuilders.hasChildQuery("child", termQuery("c_field", "red"), scoreMode)).filter(boolQuery().mustNot(termQuery("p_field", "3")))) + .setQuery(boolQuery().must(hasChildQuery("child", termQuery("c_field", "red"), scoreMode)) + .filter(boolQuery().mustNot(termQuery("p_field", "3")))) .get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); @@ -1293,25 +1333,29 @@ public void testNamedFilters() throws Exception { client().prepareIndex("test", "child", "c1").setSource("c_field", "1").setParent(parentId).get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max).queryName("test")) + SearchResponse searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", + termQuery("c_field", "1"), ScoreMode.Max).queryName("test")) .get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); - searchResponse = client().prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true).queryName("test")) + searchResponse = client().prepareSearch("test").setQuery(hasParentQuery("parent", + termQuery("p_field", "1"), true).queryName("test")) .get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.None).queryName("test"))) + searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(hasChildQuery("child", + termQuery("c_field", "1"), ScoreMode.None).queryName("test"))) .get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(hasParentQuery("parent", termQuery("p_field", "1"), false).queryName("test"))) + searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(hasParentQuery("parent", + termQuery("p_field", "1"), false).queryName("test"))) .get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); @@ -1400,7 +1444,7 @@ public void testParentChildCaching() throws Exception { for (int i = 0; i < 2; i++) { SearchResponse searchResponse = client().prepareSearch() .setQuery(boolQuery().must(matchAllQuery()).filter(boolQuery() - .must(QueryBuilders.hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)) + .must(hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)) .must(matchAllQuery()))) .get(); assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); @@ -1412,7 +1456,7 @@ public void testParentChildCaching() throws Exception { SearchResponse searchResponse = client().prepareSearch() .setQuery(boolQuery().must(matchAllQuery()).filter(boolQuery() - .must(QueryBuilders.hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)) + .must(hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)) .must(matchAllQuery()))) .get(); @@ -1478,7 +1522,8 @@ public void testQueryBeforeChildType() throws Exception { SearchResponse resp; resp = client().prepareSearch("test") - .setSource(new SearchSourceBuilder().query(QueryBuilders.hasChildQuery("posts", QueryBuilders.matchQuery("field", "bar"), ScoreMode.None))) + .setSource(new SearchSourceBuilder().query(hasChildQuery("posts", + QueryBuilders.matchQuery("field", "bar"), ScoreMode.None))) .get(); assertHitCount(resp, 1L); } @@ -1580,8 +1625,10 @@ private SearchResponse minMaxQuery(ScoreMode scoreMode, int minChildren, Integer QueryBuilders.functionScoreQuery(constantScoreQuery(QueryBuilders.termQuery("foo", "two")), new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{ new FunctionScoreQueryBuilder.FilterFunctionBuilder(weightFactorFunction(1)), - new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("foo", "three"), weightFactorFunction(1)), - new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("foo", "four"), weightFactorFunction(1)) + new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("foo", "three"), + weightFactorFunction(1)), + new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("foo", "four"), + weightFactorFunction(1)) }).boostMode(CombineFunction.REPLACE).scoreMode(FiltersFunctionScoreQuery.ScoreMode.SUM), scoreMode) .minMaxChildren(minChildren, maxChildren != null ? maxChildren : HasChildQueryBuilder.DEFAULT_MAX_CHILDREN); @@ -1916,7 +1963,7 @@ public void testParentFieldToNonExistingType() { try { client().prepareSearch("test") - .setQuery(QueryBuilders.hasChildQuery("child", matchAllQuery(), ScoreMode.None)) + .setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.None)) .get(); fail(); } catch (SearchPhaseExecutionException e) { @@ -1932,7 +1979,7 @@ public void testHasParentInnerQueryType() { refresh(); //make sure that when we explicitly set a type, the inner query is executed in the context of the parent type instead SearchResponse searchResponse = client().prepareSearch("test").setTypes("child-type").setQuery( - QueryBuilders.hasParentQuery("parent-type", new IdsQueryBuilder().addIds("parent-id"), false)).get(); + hasParentQuery("parent-type", new IdsQueryBuilder().addIds("parent-id"), false)).get(); assertSearchHits(searchResponse, "child-id"); } @@ -1945,7 +1992,7 @@ public void testHasChildInnerQueryType() { refresh(); //make sure that when we explicitly set a type, the inner query is executed in the context of the child type instead SearchResponse searchResponse = client().prepareSearch("test").setTypes("parent-type").setQuery( - QueryBuilders.hasChildQuery("child-type", new IdsQueryBuilder().addIds("child-id"), ScoreMode.None)).get(); + hasChildQuery("child-type", new IdsQueryBuilder().addIds("child-id"), ScoreMode.None)).get(); assertSearchHits(searchResponse, "parent-id"); } @@ -1955,8 +2002,10 @@ public void testHighlightersIgnoreParentChild() { .addMapping("parent-type", "searchText", "type=text,term_vector=with_positions_offsets,index_options=offsets") .addMapping("child-type", "_parent", "type=parent-type", "searchText", "type=text,term_vector=with_positions_offsets,index_options=offsets")); - client().prepareIndex("test", "parent-type", "parent-id").setSource("searchText", "quick brown fox").get(); - client().prepareIndex("test", "child-type", "child-id").setParent("parent-id").setSource("searchText", "quick brown fox").get(); + client().prepareIndex("test", "parent-type", "parent-id") + .setSource("searchText", "quick brown fox").get(); + client().prepareIndex("test", "child-type", "child-id") + .setParent("parent-id").setSource("searchText", "quick brown fox").get(); refresh(); String[] highlightTypes = new String[] {"plain", "fvh", "postings"}; @@ -1988,4 +2037,177 @@ public void testHighlightersIgnoreParentChild() { } } + public void testAliasesFilterWithHasChildQuery() throws Exception { + assertAcked(prepareCreate("my-index") + .setSettings("index.mapping.single_type", false) + .addMapping("parent") + .addMapping("child", "_parent", "type=parent") + ); + client().prepareIndex("my-index", "parent", "1").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("my-index", "child", "2").setSource("{}", XContentType.JSON).setParent("1").get(); + refresh(); + + assertAcked(admin().indices().prepareAliases().addAlias("my-index", "filter1", + hasChildQuery("child", matchAllQuery(), ScoreMode.None))); + assertAcked(admin().indices().prepareAliases().addAlias("my-index", "filter2", + hasParentQuery("parent", matchAllQuery(), false))); + + SearchResponse response = client().prepareSearch("filter1").get(); + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + response = client().prepareSearch("filter2").get(); + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).getId(), equalTo("2")); + } + + /* + Test for https://github.com/elastic/elasticsearch/issues/3444 + */ + public void testBulkUpdateDocAsUpsertWithParent() throws Exception { + client().admin().indices().prepareCreate("test") + .setSettings("index.mapping.single_type", false) + .addMapping("parent", "{\"parent\":{}}", XContentType.JSON) + .addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}", XContentType.JSON) + .execute().actionGet(); + ensureGreen(); + + BulkRequestBuilder builder = client().prepareBulk(); + + // It's important to use JSON parsing here and request objects: issue 3444 is related to incomplete option parsing + byte[] addParent = new BytesArray( + "{" + + " \"index\" : {" + + " \"_index\" : \"test\"," + + " \"_type\" : \"parent\"," + + " \"_id\" : \"parent1\"" + + " }" + + "}" + + "\n" + + "{" + + " \"field1\" : \"value1\"" + + "}" + + "\n").array(); + + byte[] addChild = new BytesArray( + "{" + + " \"update\" : {" + + " \"_index\" : \"test\"," + + " \"_type\" : \"child\"," + + " \"_id\" : \"child1\"," + + " \"parent\" : \"parent1\"" + + " }" + + "}" + + "\n" + + "{" + + " \"doc\" : {" + + " \"field1\" : \"value1\"" + + " }," + + " \"doc_as_upsert\" : \"true\"" + + "}" + + "\n").array(); + + builder.add(addParent, 0, addParent.length, XContentType.JSON); + builder.add(addChild, 0, addChild.length, XContentType.JSON); + + BulkResponse bulkResponse = builder.get(); + assertThat(bulkResponse.getItems().length, equalTo(2)); + assertThat(bulkResponse.getItems()[0].isFailed(), equalTo(false)); + assertThat(bulkResponse.getItems()[1].isFailed(), equalTo(false)); + + client().admin().indices().prepareRefresh("test").get(); + + //we check that the _parent field was set on the child document by using the has parent query + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(hasParentQuery("parent", QueryBuilders.matchAllQuery(), false)) + .get(); + + assertNoFailures(searchResponse); + assertSearchHits(searchResponse, "child1"); + } + + /* + Test for https://github.com/elastic/elasticsearch/issues/3444 + */ + public void testBulkUpdateUpsertWithParent() throws Exception { + assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) + .addMapping("parent", "{\"parent\":{}}", XContentType.JSON) + .addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}", XContentType.JSON)); + ensureGreen(); + + BulkRequestBuilder builder = client().prepareBulk(); + + byte[] addParent = new BytesArray( + "{" + + " \"index\" : {" + + " \"_index\" : \"test\"," + + " \"_type\" : \"parent\"," + + " \"_id\" : \"parent1\"" + + " }" + + "}" + + "\n" + + "{" + + " \"field1\" : \"value1\"" + + "}" + + "\n").array(); + + byte[] addChild1 = new BytesArray( + "{" + + " \"update\" : {" + + " \"_index\" : \"test\"," + + " \"_type\" : \"child\"," + + " \"_id\" : \"child1\"," + + " \"parent\" : \"parent1\"" + + " }" + + "}" + + "\n" + + "{" + + " \"script\" : {" + + " \"inline\" : \"ctx._source.field2 = 'value2'\"" + + " }," + + " \"lang\" : \"" + InnerHitsIT.CustomScriptPlugin.NAME + "\"," + + " \"upsert\" : {" + + " \"field1\" : \"value1'\"" + + " }" + + "}" + + "\n").array(); + + byte[] addChild2 = new BytesArray( + "{" + + " \"update\" : {" + + " \"_index\" : \"test\"," + + " \"_type\" : \"child\"," + + " \"_id\" : \"child1\"," + + " \"parent\" : \"parent1\"" + + " }" + + "}" + + "\n" + + "{" + + " \"script\" : \"ctx._source.field2 = 'value2'\"," + + " \"upsert\" : {" + + " \"field1\" : \"value1'\"" + + " }" + + "}" + + "\n").array(); + + builder.add(addParent, 0, addParent.length, XContentType.JSON); + builder.add(addChild1, 0, addChild1.length, XContentType.JSON); + builder.add(addChild2, 0, addChild2.length, XContentType.JSON); + + BulkResponse bulkResponse = builder.get(); + assertThat(bulkResponse.getItems().length, equalTo(3)); + assertThat(bulkResponse.getItems()[0].isFailed(), equalTo(false)); + assertThat(bulkResponse.getItems()[1].isFailed(), equalTo(false)); + assertThat(bulkResponse.getItems()[2].isFailed(), equalTo(true)); + assertThat(bulkResponse.getItems()[2].getFailure().getCause().getCause().getMessage(), + equalTo("script_lang not supported [painless]")); + + client().admin().indices().prepareRefresh("test").get(); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(hasParentQuery("parent", QueryBuilders.matchAllQuery(), false)) + .get(); + + assertSearchHits(searchResponse, "child1"); + } } diff --git a/core/src/test/java/org/elasticsearch/index/query/HasChildQueryBuilderTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasChildQueryBuilderTests.java similarity index 83% rename from core/src/test/java/org/elasticsearch/index/query/HasChildQueryBuilderTests.java rename to modules/parent-join/src/test/java/org/elasticsearch/join/query/HasChildQueryBuilderTests.java index 49523fe923d7b..8f4fc9d0c34a4 100644 --- a/core/src/test/java/org/elasticsearch/index/query/HasChildQueryBuilderTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasChildQueryBuilderTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.query; +package org.elasticsearch.join.query; import com.carrotsearch.randomizedtesting.generators.RandomPicks; import org.apache.lucene.search.TermInSetQuery; @@ -40,7 +40,17 @@ import org.elasticsearch.index.mapper.TypeFieldMapper; import org.elasticsearch.index.mapper.Uid; import org.elasticsearch.index.mapper.UidFieldMapper; +import org.elasticsearch.index.query.IdsQueryBuilder; +import org.elasticsearch.index.query.InnerHitBuilder; +import org.elasticsearch.index.query.MatchAllQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.QueryShardException; +import org.elasticsearch.index.query.TermQueryBuilder; +import org.elasticsearch.index.query.WrapperQueryBuilder; import org.elasticsearch.index.similarity.SimilarityService; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.fetch.subphase.InnerHitsContext; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.sort.FieldSortBuilder; @@ -48,10 +58,12 @@ import org.elasticsearch.test.AbstractQueryTestCase; import java.io.IOException; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; @@ -65,6 +77,11 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase> getPlugins() { + return Collections.singletonList(ParentJoinPlugin.class); + } + @Override protected void initializeAdditionalMappings(MapperService mapperService) throws IOException { similarity = randomFrom("classic", "BM25"); @@ -97,7 +114,7 @@ protected HasChildQueryBuilder doCreateTestQueryBuilder() { int min = randomIntBetween(0, Integer.MAX_VALUE / 2); int max = randomIntBetween(min, Integer.MAX_VALUE); - QueryBuilder innerQueryBuilder = RandomQueryBuilder.createQuery(random()); + QueryBuilder innerQueryBuilder = new MatchAllQueryBuilder(); if (randomBoolean()) { requiresRewrite = true; innerQueryBuilder = new WrapperQueryBuilder(innerQueryBuilder.toString()); @@ -144,19 +161,19 @@ protected void doAssertLuceneQuery(HasChildQueryBuilder queryBuilder, Query quer } public void testIllegalValues() { - QueryBuilder query = RandomQueryBuilder.createQuery(random()); + QueryBuilder query = new MatchAllQueryBuilder(); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> QueryBuilders.hasChildQuery(null, query, ScoreMode.None)); + () -> hasChildQuery(null, query, ScoreMode.None)); assertEquals("[has_child] requires 'type' field", e.getMessage()); - e = expectThrows(IllegalArgumentException.class, () -> QueryBuilders.hasChildQuery("foo", null, ScoreMode.None)); + e = expectThrows(IllegalArgumentException.class, () -> hasChildQuery("foo", null, ScoreMode.None)); assertEquals("[has_child] requires 'query' field", e.getMessage()); - e = expectThrows(IllegalArgumentException.class, () -> QueryBuilders.hasChildQuery("foo", query, null)); + e = expectThrows(IllegalArgumentException.class, () -> hasChildQuery("foo", query, null)); assertEquals("[has_child] requires 'score_mode' field", e.getMessage()); int positiveValue = randomIntBetween(0, Integer.MAX_VALUE); - HasChildQueryBuilder foo = QueryBuilders.hasChildQuery("foo", query, ScoreMode.None); // all good + HasChildQueryBuilder foo = hasChildQuery("foo", query, ScoreMode.None); // all good e = expectThrows(IllegalArgumentException.class, () -> foo.minMaxChildren(randomIntBetween(Integer.MIN_VALUE, -1), positiveValue)); assertEquals("[has_child] requires non-negative 'min_children' field", e.getMessage()); @@ -225,7 +242,7 @@ public void testToQueryInnerQueryType() throws IOException { String[] searchTypes = new String[]{PARENT_TYPE}; QueryShardContext shardContext = createShardContext(); shardContext.setTypes(searchTypes); - HasChildQueryBuilder hasChildQueryBuilder = QueryBuilders.hasChildQuery(CHILD_TYPE, new IdsQueryBuilder().addIds("id"), ScoreMode.None); + HasChildQueryBuilder hasChildQueryBuilder = hasChildQuery(CHILD_TYPE, new IdsQueryBuilder().addIds("id"), ScoreMode.None); Query query = hasChildQueryBuilder.toQuery(shardContext); //verify that the context types are still the same as the ones we previously set assertThat(shardContext.getTypes(), equalTo(searchTypes)); @@ -252,7 +269,8 @@ static void assertLateParsingQuery(Query query, String type, String id) throws I assertThat(booleanTermsQuery.clauses().get(0).getQuery(), instanceOf(TermQuery.class)); TermQuery termQuery = (TermQuery) booleanTermsQuery.clauses().get(0).getQuery(); assertThat(termQuery.getTerm().field(), equalTo(UidFieldMapper.NAME)); - //we want to make sure that the inner ids query gets executed against the child type rather than the main type we initially set to the context + //we want to make sure that the inner ids query gets executed against the child type rather + // than the main type we initially set to the context BytesRef[] ids = Uid.createUidsForTypesAndIds(Collections.singletonList(type), Collections.singletonList(id)); assertThat(termQuery.getTerm().bytes(), equalTo(ids[0])); //check the type filter @@ -273,7 +291,8 @@ public void testMustRewrite() throws IOException { public void testNonDefaultSimilarity() throws Exception { QueryShardContext shardContext = createShardContext(); - HasChildQueryBuilder hasChildQueryBuilder = QueryBuilders.hasChildQuery(CHILD_TYPE, new TermQueryBuilder("custom_string", "value"), ScoreMode.None); + HasChildQueryBuilder hasChildQueryBuilder = + hasChildQuery(CHILD_TYPE, new TermQueryBuilder("custom_string", "value"), ScoreMode.None); HasChildQueryBuilder.LateParsingQuery query = (HasChildQueryBuilder.LateParsingQuery) hasChildQueryBuilder.toQuery(shardContext); Similarity expected = SimilarityService.BUILT_IN.get(similarity) .apply(similarity, Settings.EMPTY, Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build()) @@ -281,48 +300,6 @@ public void testNonDefaultSimilarity() throws Exception { assertThat(((PerFieldSimilarityWrapper) query.getSimilarity()).get("custom_string"), instanceOf(expected.getClass())); } - public void testMinFromString() { - assertThat("fromString(min) != MIN", ScoreMode.Min, equalTo(HasChildQueryBuilder.parseScoreMode("min"))); - assertThat("min", equalTo(HasChildQueryBuilder.scoreModeAsString(ScoreMode.Min))); - } - - public void testMaxFromString() { - assertThat("fromString(max) != MAX", ScoreMode.Max, equalTo(HasChildQueryBuilder.parseScoreMode("max"))); - assertThat("max", equalTo(HasChildQueryBuilder.scoreModeAsString(ScoreMode.Max))); - } - - public void testAvgFromString() { - assertThat("fromString(avg) != AVG", ScoreMode.Avg, equalTo(HasChildQueryBuilder.parseScoreMode("avg"))); - assertThat("avg", equalTo(HasChildQueryBuilder.scoreModeAsString(ScoreMode.Avg))); - } - - public void testSumFromString() { - assertThat("fromString(total) != SUM", ScoreMode.Total, equalTo(HasChildQueryBuilder.parseScoreMode("sum"))); - assertThat("sum", equalTo(HasChildQueryBuilder.scoreModeAsString(ScoreMode.Total))); - } - - public void testNoneFromString() { - assertThat("fromString(none) != NONE", ScoreMode.None, equalTo(HasChildQueryBuilder.parseScoreMode("none"))); - assertThat("none", equalTo(HasChildQueryBuilder.scoreModeAsString(ScoreMode.None))); - } - - /** - * Should throw {@link IllegalArgumentException} instead of NPE. - */ - public void testThatNullFromStringThrowsException() { - IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> HasChildQueryBuilder.parseScoreMode(null)); - assertEquals("No score mode for child query [null] found", e.getMessage()); - } - - /** - * Failure should not change (and the value should never match anything...). - */ - public void testThatUnrecognizedFromStringThrowsException() { - IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> HasChildQueryBuilder.parseScoreMode("unrecognized value")); - assertEquals("No score mode for child query [unrecognized value] found", e.getMessage()); - } - public void testIgnoreUnmapped() throws IOException { final HasChildQueryBuilder queryBuilder = new HasChildQueryBuilder("unmapped", new MatchAllQueryBuilder(), ScoreMode.None); queryBuilder.ignoreUnmapped(true); diff --git a/core/src/test/java/org/elasticsearch/index/query/HasParentQueryBuilderTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasParentQueryBuilderTests.java similarity index 90% rename from core/src/test/java/org/elasticsearch/index/query/HasParentQueryBuilderTests.java rename to modules/parent-join/src/test/java/org/elasticsearch/join/query/HasParentQueryBuilderTests.java index 22ea2cc9c4442..825dfede61e90 100644 --- a/core/src/test/java/org/elasticsearch/index/query/HasParentQueryBuilderTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasParentQueryBuilderTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.query; +package org.elasticsearch.join.query; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; @@ -28,6 +28,16 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.query.IdsQueryBuilder; +import org.elasticsearch.index.query.InnerHitBuilder; +import org.elasticsearch.index.query.MatchAllQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.QueryShardException; +import org.elasticsearch.index.query.TermQueryBuilder; +import org.elasticsearch.index.query.WrapperQueryBuilder; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.fetch.subphase.InnerHitsContext; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.sort.FieldSortBuilder; @@ -35,9 +45,12 @@ import org.elasticsearch.test.AbstractQueryTestCase; import java.io.IOException; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasParentQuery; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; @@ -49,6 +62,11 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase> getPlugins() { + return Collections.singletonList(ParentJoinPlugin.class); + } + @Override protected void initializeAdditionalMappings(MapperService mapperService) throws IOException { mapperService.merge(PARENT_TYPE, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(PARENT_TYPE, @@ -79,7 +97,7 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws */ @Override protected HasParentQueryBuilder doCreateTestQueryBuilder() { - QueryBuilder innerQueryBuilder = RandomQueryBuilder.createQuery(random()); + QueryBuilder innerQueryBuilder = new MatchAllQueryBuilder(); if (randomBoolean()) { requiresRewrite = true; innerQueryBuilder = new WrapperQueryBuilder(innerQueryBuilder.toString()); @@ -124,17 +142,17 @@ protected void doAssertLuceneQuery(HasParentQueryBuilder queryBuilder, Query que } public void testIllegalValues() throws IOException { - QueryBuilder query = RandomQueryBuilder.createQuery(random()); + QueryBuilder query = new MatchAllQueryBuilder(); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> QueryBuilders.hasParentQuery(null, query, false)); + () -> hasParentQuery(null, query, false)); assertThat(e.getMessage(), equalTo("[has_parent] requires 'type' field")); e = expectThrows(IllegalArgumentException.class, - () -> QueryBuilders.hasParentQuery("foo", null, false)); + () -> hasParentQuery("foo", null, false)); assertThat(e.getMessage(), equalTo("[has_parent] requires 'query' field")); QueryShardContext context = createShardContext(); - HasParentQueryBuilder qb = QueryBuilders.hasParentQuery("just_a_type", new MatchAllQueryBuilder(), false); + HasParentQueryBuilder qb = hasParentQuery("just_a_type", new MatchAllQueryBuilder(), false); QueryShardException qse = expectThrows(QueryShardException.class, () -> qb.doToQuery(context)); assertThat(qse.getMessage(), equalTo("[has_parent] no child types found for type [just_a_type]")); } diff --git a/modules/parent-join/src/test/java/org/elasticsearch/join/query/InnerHitsIT.java b/modules/parent-join/src/test/java/org/elasticsearch/join/query/InnerHitsIT.java new file mode 100644 index 0000000000000..ad8e49e9f5d31 --- /dev/null +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/query/InnerHitsIT.java @@ -0,0 +1,568 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.join.query; + +import org.apache.lucene.search.join.ScoreMode; +import org.apache.lucene.util.ArrayUtil; +import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.InnerHitBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.script.MockScriptEngine; +import org.elasticsearch.script.MockScriptPlugin; +import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptType; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; +import org.elasticsearch.search.sort.FieldSortBuilder; +import org.elasticsearch.search.sort.SortBuilders; +import org.elasticsearch.search.sort.SortOrder; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESIntegTestCase.ClusterScope; +import org.elasticsearch.test.ESIntegTestCase.Scope; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; + +import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.index.query.QueryBuilders.boolQuery; +import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchQuery; +import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; +import static org.elasticsearch.index.query.QueryBuilders.termQuery; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasParentQuery; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHit; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +@ClusterScope(scope = Scope.SUITE) +public class InnerHitsIT extends ESIntegTestCase { + @Override + protected boolean ignoreExternalCluster() { + return true; + } + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(ParentJoinPlugin.class, CustomScriptPlugin.class); + } + + @Override + protected Collection> transportClientPlugins() { + return nodePlugins(); + } + + public static class CustomScriptPlugin extends MockScriptPlugin { + @Override + protected Map, Object>> pluginScripts() { + return Collections.singletonMap("5", script -> "5"); + } + } + + public void testSimpleParentChild() throws Exception { + assertAcked(prepareCreate("articles") + .setSettings("index.mapping.single_type", false) + .addMapping("article", "title", "type=text") + .addMapping("comment", "_parent", "type=article", "message", "type=text,fielddata=true") + ); + + List requests = new ArrayList<>(); + requests.add(client().prepareIndex("articles", "article", "1").setSource("title", "quick brown fox")); + requests.add(client().prepareIndex("articles", "comment", "1").setParent("1").setSource("message", "fox eat quick")); + requests.add(client().prepareIndex("articles", "comment", "2").setParent("1").setSource("message", "fox ate rabbit x y z")); + requests.add(client().prepareIndex("articles", "comment", "3").setParent("1").setSource("message", "rabbit got away")); + requests.add(client().prepareIndex("articles", "article", "2").setSource("title", "big gray elephant")); + requests.add(client().prepareIndex("articles", "comment", "4").setParent("2").setSource("message", "elephant captured")); + requests.add(client().prepareIndex("articles", "comment", "5").setParent("2").setSource("message", "mice squashed by elephant x")); + requests.add(client().prepareIndex("articles", "comment", "6").setParent("2").setSource("message", "elephant scared by mice x y")); + indexRandom(true, requests); + + SearchResponse response = client().prepareSearch("articles") + .setQuery(hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None) + .innerHit(new InnerHitBuilder(), false)) + .get(); + assertNoFailures(response); + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("1")); + assertThat(response.getHits().getAt(0).getShard(), notNullValue()); + + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getTotalHits(), equalTo(2L)); + + assertThat(innerHits.getAt(0).getId(), equalTo("1")); + assertThat(innerHits.getAt(0).getType(), equalTo("comment")); + assertThat(innerHits.getAt(1).getId(), equalTo("2")); + assertThat(innerHits.getAt(1).getType(), equalTo("comment")); + + response = client().prepareSearch("articles") + .setQuery(hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None) + .innerHit(new InnerHitBuilder(), false)) + .get(); + assertNoFailures(response); + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("2")); + + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getTotalHits(), equalTo(3L)); + + assertThat(innerHits.getAt(0).getId(), equalTo("4")); + assertThat(innerHits.getAt(0).getType(), equalTo("comment")); + assertThat(innerHits.getAt(1).getId(), equalTo("5")); + assertThat(innerHits.getAt(1).getType(), equalTo("comment")); + assertThat(innerHits.getAt(2).getId(), equalTo("6")); + assertThat(innerHits.getAt(2).getType(), equalTo("comment")); + + response = client().prepareSearch("articles") + .setQuery( + hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( + new InnerHitBuilder() + .addDocValueField("message") + .setHighlightBuilder(new HighlightBuilder().field("message")) + .setExplain(true).setSize(1) + .addScriptField("script", new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5", + Collections.emptyMap())), + false) + ).get(); + assertNoFailures(response); + innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getHits().length, equalTo(1)); + assertThat(innerHits.getAt(0).getHighlightFields().get("message").getFragments()[0].string(), equalTo("fox eat quick")); + assertThat(innerHits.getAt(0).getExplanation().toString(), containsString("weight(message:fox")); + assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("eat")); + assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5")); + } + + public void testRandomParentChild() throws Exception { + assertAcked(prepareCreate("idx") + .setSettings("index.mapping.single_type", false) + .addMapping("parent") + .addMapping("child1", "_parent", "type=parent") + .addMapping("child2", "_parent", "type=parent") + ); + int numDocs = scaledRandomIntBetween(5, 50); + List requestBuilders = new ArrayList<>(); + + int child1 = 0; + int child2 = 0; + int[] child1InnerObjects = new int[numDocs]; + int[] child2InnerObjects = new int[numDocs]; + for (int parent = 0; parent < numDocs; parent++) { + String parentId = String.format(Locale.ENGLISH, "%03d", parent); + requestBuilders.add(client().prepareIndex("idx", "parent", parentId).setSource("{}", XContentType.JSON)); + + int numChildDocs = child1InnerObjects[parent] = scaledRandomIntBetween(1, numDocs); + int limit = child1 + numChildDocs; + for (; child1 < limit; child1++) { + requestBuilders.add(client().prepareIndex("idx", "child1", + String.format(Locale.ENGLISH, "%04d", child1)).setParent(parentId).setSource("{}", XContentType.JSON)); + } + numChildDocs = child2InnerObjects[parent] = scaledRandomIntBetween(1, numDocs); + limit = child2 + numChildDocs; + for (; child2 < limit; child2++) { + requestBuilders.add(client().prepareIndex("idx", "child2", + String.format(Locale.ENGLISH, "%04d", child2)).setParent(parentId).setSource("{}", XContentType.JSON)); + } + } + indexRandom(true, requestBuilders); + + int size = randomIntBetween(0, numDocs); + BoolQueryBuilder boolQuery = new BoolQueryBuilder(); + boolQuery.should(constantScoreQuery(hasChildQuery("child1", matchAllQuery(), ScoreMode.None) + .innerHit(new InnerHitBuilder().setName("a") + .addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size), false))); + boolQuery.should(constantScoreQuery(hasChildQuery("child2", matchAllQuery(), ScoreMode.None) + .innerHit(new InnerHitBuilder().setName("b") + .addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size), false))); + SearchResponse searchResponse = client().prepareSearch("idx") + .setSize(numDocs) + .setTypes("parent") + .addSort("_uid", SortOrder.ASC) + .setQuery(boolQuery) + .get(); + + assertNoFailures(searchResponse); + assertHitCount(searchResponse, numDocs); + assertThat(searchResponse.getHits().getHits().length, equalTo(numDocs)); + + int offset1 = 0; + int offset2 = 0; + for (int parent = 0; parent < numDocs; parent++) { + SearchHit searchHit = searchResponse.getHits().getAt(parent); + assertThat(searchHit.getType(), equalTo("parent")); + assertThat(searchHit.getId(), equalTo(String.format(Locale.ENGLISH, "%03d", parent))); + assertThat(searchHit.getShard(), notNullValue()); + + SearchHits inner = searchHit.getInnerHits().get("a"); + assertThat(inner.getTotalHits(), equalTo((long) child1InnerObjects[parent])); + for (int child = 0; child < child1InnerObjects[parent] && child < size; child++) { + SearchHit innerHit = inner.getAt(child); + assertThat(innerHit.getType(), equalTo("child1")); + String childId = String.format(Locale.ENGLISH, "%04d", offset1 + child); + assertThat(innerHit.getId(), equalTo(childId)); + assertThat(innerHit.getNestedIdentity(), nullValue()); + } + offset1 += child1InnerObjects[parent]; + + inner = searchHit.getInnerHits().get("b"); + assertThat(inner.getTotalHits(), equalTo((long) child2InnerObjects[parent])); + for (int child = 0; child < child2InnerObjects[parent] && child < size; child++) { + SearchHit innerHit = inner.getAt(child); + assertThat(innerHit.getType(), equalTo("child2")); + String childId = String.format(Locale.ENGLISH, "%04d", offset2 + child); + assertThat(innerHit.getId(), equalTo(childId)); + assertThat(innerHit.getNestedIdentity(), nullValue()); + } + offset2 += child2InnerObjects[parent]; + } + } + + public void testInnerHitsOnHasParent() throws Exception { + assertAcked(prepareCreate("stack") + .setSettings("index.mapping.single_type", false) + .addMapping("question", "body", "type=text") + .addMapping("answer", "_parent", "type=question", "body", "type=text") + ); + List requests = new ArrayList<>(); + requests.add(client().prepareIndex("stack", "question", "1").setSource("body", "I'm using HTTPS + Basic authentication " + + "to protect a resource. How can I throttle authentication attempts to protect against brute force attacks?")); + requests.add(client().prepareIndex("stack", "answer", "1").setParent("1").setSource("body", + "install fail2ban and enable rules for apache")); + requests.add(client().prepareIndex("stack", "question", "2").setSource("body", + "I have firewall rules set up and also denyhosts installed.\\ndo I also need to install fail2ban?")); + requests.add(client().prepareIndex("stack", "answer", "2").setParent("2").setSource("body", + "Denyhosts protects only ssh; Fail2Ban protects all daemons.")); + indexRandom(true, requests); + + SearchResponse response = client().prepareSearch("stack") + .setTypes("answer") + .addSort("_uid", SortOrder.ASC) + .setQuery( + boolQuery() + .must(matchQuery("body", "fail2ban")) + .must(hasParentQuery("question", matchAllQuery(), false).innerHit(new InnerHitBuilder(), false)) + ).get(); + assertNoFailures(response); + assertHitCount(response, 2); + + SearchHit searchHit = response.getHits().getAt(0); + assertThat(searchHit.getId(), equalTo("1")); + assertThat(searchHit.getType(), equalTo("answer")); + assertThat(searchHit.getInnerHits().get("question").getTotalHits(), equalTo(1L)); + assertThat(searchHit.getInnerHits().get("question").getAt(0).getType(), equalTo("question")); + assertThat(searchHit.getInnerHits().get("question").getAt(0).getId(), equalTo("1")); + + searchHit = response.getHits().getAt(1); + assertThat(searchHit.getId(), equalTo("2")); + assertThat(searchHit.getType(), equalTo("answer")); + assertThat(searchHit.getInnerHits().get("question").getTotalHits(), equalTo(1L)); + assertThat(searchHit.getInnerHits().get("question").getAt(0).getType(), equalTo("question")); + assertThat(searchHit.getInnerHits().get("question").getAt(0).getId(), equalTo("2")); + } + + public void testParentChildMultipleLayers() throws Exception { + assertAcked(prepareCreate("articles") + .setSettings("index.mapping.single_type", false) + .addMapping("article", "title", "type=text") + .addMapping("comment", "_parent", "type=article", "message", "type=text") + .addMapping("remark", "_parent", "type=comment", "message", "type=text") + ); + + List requests = new ArrayList<>(); + requests.add(client().prepareIndex("articles", "article", "1").setSource("title", "quick brown fox")); + requests.add(client().prepareIndex("articles", "comment", "1").setParent("1").setSource("message", "fox eat quick")); + requests.add(client().prepareIndex("articles", "remark", "1").setParent("1").setRouting("1").setSource("message", "good")); + requests.add(client().prepareIndex("articles", "article", "2").setSource("title", "big gray elephant")); + requests.add(client().prepareIndex("articles", "comment", "2").setParent("2").setSource("message", "elephant captured")); + requests.add(client().prepareIndex("articles", "remark", "2").setParent("2").setRouting("2").setSource("message", "bad")); + indexRandom(true, requests); + + SearchResponse response = client().prepareSearch("articles") + .setQuery(hasChildQuery("comment", + hasChildQuery("remark", matchQuery("message", "good"), ScoreMode.None).innerHit(new InnerHitBuilder(), false), + ScoreMode.None).innerHit(new InnerHitBuilder(), false)) + .get(); + + assertNoFailures(response); + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("1")); + + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getTotalHits(), equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("1")); + assertThat(innerHits.getAt(0).getType(), equalTo("comment")); + + innerHits = innerHits.getAt(0).getInnerHits().get("remark"); + assertThat(innerHits.getTotalHits(), equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("1")); + assertThat(innerHits.getAt(0).getType(), equalTo("remark")); + + response = client().prepareSearch("articles") + .setQuery(hasChildQuery("comment", + hasChildQuery("remark", matchQuery("message", "bad"), ScoreMode.None).innerHit(new InnerHitBuilder(), false), + ScoreMode.None).innerHit(new InnerHitBuilder(), false)) + .get(); + + assertNoFailures(response); + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("2")); + + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getTotalHits(), equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("2")); + assertThat(innerHits.getAt(0).getType(), equalTo("comment")); + + innerHits = innerHits.getAt(0).getInnerHits().get("remark"); + assertThat(innerHits.getTotalHits(), equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("2")); + assertThat(innerHits.getAt(0).getType(), equalTo("remark")); + } + + public void testRoyals() throws Exception { + assertAcked( + prepareCreate("royals") + .setSettings("index.mapping.single_type", false) + .addMapping("king") + .addMapping("prince", "_parent", "type=king") + .addMapping("duke", "_parent", "type=prince") + .addMapping("earl", "_parent", "type=duke") + .addMapping("baron", "_parent", "type=earl") + ); + + List requests = new ArrayList<>(); + requests.add(client().prepareIndex("royals", "king", "king").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "prince", "prince").setParent("king").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "duke", "duke").setParent("prince").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "earl", "earl1").setParent("duke").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "earl", "earl2").setParent("duke").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "earl", "earl3").setParent("duke").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "earl", "earl4").setParent("duke").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "baron", "baron1").setParent("earl1").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "baron", "baron2").setParent("earl2").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "baron", "baron3").setParent("earl3").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "baron", "baron4").setParent("earl4").setRouting("king") + .setSource("{}", XContentType.JSON)); + indexRandom(true, requests); + + SearchResponse response = client().prepareSearch("royals") + .setTypes("duke") + .setQuery(boolQuery() + .filter(hasParentQuery("prince", + hasParentQuery("king", matchAllQuery(), false).innerHit(new InnerHitBuilder().setName("kings"), false), + false).innerHit(new InnerHitBuilder().setName("princes"), false) + ) + .filter(hasChildQuery("earl", + hasChildQuery("baron", matchAllQuery(), ScoreMode.None) + .innerHit(new InnerHitBuilder().setName("barons"), false), + ScoreMode.None).innerHit(new InnerHitBuilder() + .addSort(SortBuilders.fieldSort("_uid").order(SortOrder.ASC)) + .setName("earls") + .setSize(4), false) + ) + ) + .get(); + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).getId(), equalTo("duke")); + + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("earls"); + assertThat(innerHits.getTotalHits(), equalTo(4L)); + assertThat(innerHits.getAt(0).getId(), equalTo("earl1")); + assertThat(innerHits.getAt(1).getId(), equalTo("earl2")); + assertThat(innerHits.getAt(2).getId(), equalTo("earl3")); + assertThat(innerHits.getAt(3).getId(), equalTo("earl4")); + + SearchHits innerInnerHits = innerHits.getAt(0).getInnerHits().get("barons"); + assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron1")); + + innerInnerHits = innerHits.getAt(1).getInnerHits().get("barons"); + assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron2")); + + innerInnerHits = innerHits.getAt(2).getInnerHits().get("barons"); + assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron3")); + + innerInnerHits = innerHits.getAt(3).getInnerHits().get("barons"); + assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron4")); + + innerHits = response.getHits().getAt(0).getInnerHits().get("princes"); + assertThat(innerHits.getTotalHits(), equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("prince")); + + innerInnerHits = innerHits.getAt(0).getInnerHits().get("kings"); + assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("king")); + } + + public void testMatchesQueriesParentChildInnerHits() throws Exception { + assertAcked(prepareCreate("index") + .setSettings("index.mapping.single_type", false) + .addMapping("child", "_parent", "type=parent")); + List requests = new ArrayList<>(); + requests.add(client().prepareIndex("index", "parent", "1").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("index", "child", "1").setParent("1").setSource("field", "value1")); + requests.add(client().prepareIndex("index", "child", "2").setParent("1").setSource("field", "value2")); + requests.add(client().prepareIndex("index", "parent", "2").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("index", "child", "3").setParent("2").setSource("field", "value1")); + indexRandom(true, requests); + + SearchResponse response = client().prepareSearch("index") + .setQuery(hasChildQuery("child", matchQuery("field", "value1").queryName("_name1"), ScoreMode.None) + .innerHit(new InnerHitBuilder(), false)) + .addSort("_uid", SortOrder.ASC) + .get(); + assertHitCount(response, 2); + assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits(), equalTo(1L)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); + + assertThat(response.getHits().getAt(1).getId(), equalTo("2")); + assertThat(response.getHits().getAt(1).getInnerHits().get("child").getTotalHits(), equalTo(1L)); + assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); + + QueryBuilder query = hasChildQuery("child", matchQuery("field", "value2").queryName("_name2"), ScoreMode.None) + .innerHit(new InnerHitBuilder(), false); + response = client().prepareSearch("index") + .setQuery(query) + .addSort("_uid", SortOrder.ASC) + .get(); + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits(), equalTo(1L)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name2")); + } + + public void testDontExplode() throws Exception { + assertAcked(prepareCreate("index1") + .setSettings("index.mapping.single_type", false) + .addMapping("child", "_parent", "type=parent")); + List requests = new ArrayList<>(); + requests.add(client().prepareIndex("index1", "parent", "1").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("index1", "child", "1").setParent("1").setSource("field", "value1")); + indexRandom(true, requests); + + QueryBuilder query = hasChildQuery("child", matchQuery("field", "value1"), ScoreMode.None) + .innerHit(new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1), false); + SearchResponse response = client().prepareSearch("index1") + .setQuery(query) + .get(); + assertNoFailures(response); + assertHitCount(response, 1); + + assertAcked(prepareCreate("index2").addMapping("type", "nested", "type=nested")); + client().prepareIndex("index2", "type", "1").setSource(jsonBuilder().startObject() + .startArray("nested") + .startObject() + .field("field", "value1") + .endObject() + .endArray() + .endObject()) + .setRefreshPolicy(IMMEDIATE) + .get(); + + query = nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg) + .innerHit(new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1), false); + response = client().prepareSearch("index2") + .setQuery(query) + .get(); + assertNoFailures(response); + assertHitCount(response, 1); + } + + public void testNestedInnerHitWrappedInParentChildInnerhit() throws Exception { + assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) + .addMapping("child_type", "_parent", "type=parent_type", "nested_type", "type=nested")); + client().prepareIndex("test", "parent_type", "1").setSource("key", "value").get(); + client().prepareIndex("test", "child_type", "2").setParent("1").setSource("nested_type", Collections.singletonMap("key", "value")) + .get(); + refresh(); + SearchResponse response = client().prepareSearch("test") + .setQuery(boolQuery().must(matchQuery("key", "value")) + .should(hasChildQuery("child_type", nestedQuery("nested_type", matchAllQuery(), ScoreMode.None) + .innerHit(new InnerHitBuilder(), false), ScoreMode.None).innerHit(new InnerHitBuilder(), false))) + .get(); + assertHitCount(response, 1); + SearchHit hit = response.getHits().getAt(0); + assertThat(hit.getInnerHits().get("child_type").getAt(0).field("_parent").getValue(), equalTo("1")); + assertThat(hit.getInnerHits().get("child_type").getAt(0).getInnerHits().get("nested_type").getAt(0).field("_parent"), nullValue()); + } + + public void testInnerHitsWithIgnoreUnmapped() throws Exception { + assertAcked(prepareCreate("index1") + .setSettings("index.mapping.single_type", false) + .addMapping("parent_type", "nested_type", "type=nested") + .addMapping("child_type", "_parent", "type=parent_type") + ); + createIndex("index2"); + client().prepareIndex("index1", "parent_type", "1").setSource("nested_type", Collections.singletonMap("key", "value")).get(); + client().prepareIndex("index1", "child_type", "2").setParent("1").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("index2", "type", "3").setSource("key", "value").get(); + refresh(); + + SearchResponse response = client().prepareSearch("index1", "index2") + .setQuery(boolQuery() + .should(hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) + .innerHit(new InnerHitBuilder(), true)) + .should(termQuery("key", "value")) + ) + .get(); + assertNoFailures(response); + assertHitCount(response, 2); + assertSearchHits(response, "1", "3"); + } +} diff --git a/modules/parent-join/src/test/resources/rest-api-spec/test/10_basic.yaml b/modules/parent-join/src/test/resources/rest-api-spec/test/10_basic.yaml new file mode 100644 index 0000000000000..f5a5808012914 --- /dev/null +++ b/modules/parent-join/src/test/resources/rest-api-spec/test/10_basic.yaml @@ -0,0 +1,48 @@ +setup: + - do: + indices.create: + index: test + body: + settings: + mapping.single_type: false + mappings: + type_2: {} + type_3: + _parent: + type: type_2 + +--- +"Parent/child inner hits": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + + - do: + index: + index: test + type: type_2 + id: 1 + body: {"foo": "bar"} + + - do: + index: + index: test + type: type_3 + id: 1 + parent: 1 + body: {"bar": "baz"} + + - do: + indices.refresh: {} + + - do: + search: + body: { "query" : { "has_child" : { "type" : "type_3", "query" : { "match_all" : {} }, "inner_hits" : {} } } } + - match: { hits.total: 1 } + - match: { hits.hits.0._index: "test" } + - match: { hits.hits.0._type: "type_2" } + - match: { hits.hits.0._id: "1" } + - is_false: hits.hits.0.inner_hits.type_3.hits.hits.0._index + - match: { hits.hits.0.inner_hits.type_3.hits.hits.0._type: "type_3" } + - match: { hits.hits.0.inner_hits.type_3.hits.hits.0._id: "1" } + - is_false: hits.hits.0.inner_hits.type_3.hits.hits.0._nested diff --git a/modules/percolator/build.gradle b/modules/percolator/build.gradle index 60fb82bdf4e32..cf55368861aef 100644 --- a/modules/percolator/build.gradle +++ b/modules/percolator/build.gradle @@ -23,5 +23,9 @@ esplugin { hasClientJar = true } +dependencies { + // for testing hasChild and hasParent rejections + testCompile project(path: ':modules:parent-join', configuration: 'runtime') +} compileJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes" compileTestJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes" diff --git a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java index f33aca55bc145..1865f68158eb4 100644 --- a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java +++ b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java @@ -57,8 +57,6 @@ import org.elasticsearch.index.query.BoostingQueryBuilder; import org.elasticsearch.index.query.ConstantScoreQueryBuilder; import org.elasticsearch.index.query.DisMaxQueryBuilder; -import org.elasticsearch.index.query.HasChildQueryBuilder; -import org.elasticsearch.index.query.HasParentQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryShardContext; @@ -372,15 +370,16 @@ protected String contentType() { return CONTENT_TYPE; } + /** * Fails if a percolator contains an unsupported query. The following queries are not supported: * 1) a has_child query * 2) a has_parent query */ static void verifyQuery(QueryBuilder queryBuilder) { - if (queryBuilder instanceof HasChildQueryBuilder) { + if (queryBuilder.getName().equals("has_child")) { throw new IllegalArgumentException("the [has_child] query is unsupported inside a percolator query"); - } else if (queryBuilder instanceof HasParentQueryBuilder) { + } else if (queryBuilder.getName().equals("has_parent")) { throw new IllegalArgumentException("the [has_parent] query is unsupported inside a percolator query"); } else if (queryBuilder instanceof BoolQueryBuilder) { BoolQueryBuilder boolQueryBuilder = (BoolQueryBuilder) queryBuilder; diff --git a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java index ae585dc9dc703..5a150349ed254 100644 --- a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java +++ b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java @@ -52,13 +52,10 @@ import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.SourceToParse; -import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.BoostingQueryBuilder; import org.elasticsearch.index.query.ConstantScoreQueryBuilder; import org.elasticsearch.index.query.DisMaxQueryBuilder; -import org.elasticsearch.index.query.HasChildQueryBuilder; -import org.elasticsearch.index.query.HasParentQueryBuilder; import org.elasticsearch.index.query.MatchAllQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryParseContext; @@ -67,6 +64,9 @@ import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; import org.elasticsearch.index.query.functionscore.RandomScoreFunctionBuilder; import org.elasticsearch.indices.TermsLookup; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.join.query.HasChildQueryBuilder; +import org.elasticsearch.join.query.HasParentQueryBuilder; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.script.MockScriptPlugin; import org.elasticsearch.script.Script; @@ -109,7 +109,7 @@ public class PercolatorFieldMapperTests extends ESSingleNodeTestCase { @Override protected Collection> getPlugins() { - return pluginList(InternalSettingsPlugin.class, PercolatorPlugin.class, FoolMeScriptPlugin.class); + return pluginList(InternalSettingsPlugin.class, PercolatorPlugin.class, FoolMeScriptPlugin.class, ParentJoinPlugin.class); } @Before diff --git a/modules/reindex/build.gradle b/modules/reindex/build.gradle index eba8b9646177d..b636c46d3afc9 100644 --- a/modules/reindex/build.gradle +++ b/modules/reindex/build.gradle @@ -39,6 +39,8 @@ dependencies { compile "org.elasticsearch.client:rest:${version}" // for http - testing reindex from remote testCompile project(path: ':modules:transport-netty4', configuration: 'runtime') + // for parent/child testing + testCompile project(path: ':modules:parent-join', configuration: 'runtime') } dependencyLicenses { diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java index d0eb1dcd75ef2..8c4135f1f261c 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java @@ -22,9 +22,16 @@ import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; -import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; import static org.elasticsearch.index.query.QueryBuilders.idsQuery; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasParentQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; import static org.hamcrest.Matchers.containsString; @@ -40,6 +47,23 @@ public class ReindexParentChildTests extends ReindexTestCase { QueryBuilder findsCity; QueryBuilder findsNeighborhood; + @Override + protected boolean ignoreExternalCluster() { + return true; + } + + @Override + protected Collection> nodePlugins() { + final List> plugins = new ArrayList<>(super.nodePlugins()); + plugins.add(ParentJoinPlugin.class); + return Collections.unmodifiableList(plugins); + } + + @Override + protected Collection> transportClientPlugins() { + return nodePlugins(); + } + public void testParentChild() throws Exception { createParentChildIndex("source"); createParentChildIndex("dest"); diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml index b30f263e869eb..32de51d022a54 100644 --- a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml +++ b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml @@ -158,85 +158,6 @@ metric: search - match: {indices.source.total.search.open_contexts: 0} ---- -"Reindex from remote with parent/child": - - do: - indices.create: - index: source - body: - settings: - mapping.single_type: false - mappings: - foo: {} - bar: - _parent: - type: foo - - do: - indices.create: - index: dest - body: - settings: - mapping.single_type: false - mappings: - foo: {} - bar: - _parent: - type: foo - - do: - index: - index: source - type: foo - id: 1 - body: { "text": "test" } - - do: - index: - index: source - type: bar - id: 1 - parent: 1 - body: { "text": "test2" } - - do: - indices.refresh: {} - - # Fetch the http host. We use the host of the master because we know there will always be a master. - - do: - cluster.state: {} - - set: { master_node: master } - - do: - nodes.info: - metric: [ http ] - - is_true: nodes.$master.http.publish_address - - set: {nodes.$master.http.publish_address: host} - - do: - reindex: - refresh: true - body: - source: - remote: - host: http://${host} - index: source - dest: - index: dest - - match: {created: 2} - - - do: - search: - index: dest - body: - query: - has_parent: - parent_type: foo - query: - match: - text: test - - match: {hits.total: 1} - - # Make sure reindex closed all the scroll contexts - - do: - indices.stats: - index: source - metric: search - - match: {indices.source.total.search.open_contexts: 0} --- "Reindex from remote with timeouts": diff --git a/qa/smoke-test-reindex-with-painless/build.gradle b/qa/smoke-test-reindex-with-all-modules/build.gradle similarity index 89% rename from qa/smoke-test-reindex-with-painless/build.gradle rename to qa/smoke-test-reindex-with-all-modules/build.gradle index b32f4ee80bef9..cab01cb9412ec 100644 --- a/qa/smoke-test-reindex-with-painless/build.gradle +++ b/qa/smoke-test-reindex-with-all-modules/build.gradle @@ -22,4 +22,6 @@ apply plugin: 'elasticsearch.rest-test' integTestCluster { setting 'script.max_compilations_per_minute', '1000' -} + // Whitelist reindexing from the local node so we can test it. + setting 'reindex.remote.whitelist', '127.0.0.1:*' +} \ No newline at end of file diff --git a/qa/smoke-test-reindex-with-painless/src/test/java/org/elasticsearch/smoketest/SmokeTestReindexWithPainlessClientYamlTestSuiteIT.java b/qa/smoke-test-reindex-with-all-modules/src/test/java/org/elasticsearch/smoketest/SmokeTestReindexWithPainlessClientYamlTestSuiteIT.java similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/java/org/elasticsearch/smoketest/SmokeTestReindexWithPainlessClientYamlTestSuiteIT.java rename to qa/smoke-test-reindex-with-all-modules/src/test/java/org/elasticsearch/smoketest/SmokeTestReindexWithPainlessClientYamlTestSuiteIT.java diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/10_script.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/10_script.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/10_script.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/10_script.yaml diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/20_broken.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/20_broken.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/20_broken.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/20_broken.yaml diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/30_timeout.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/30_timeout.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/30_timeout.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/30_timeout.yaml diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/40_search_failures.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/40_search_failures.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/40_search_failures.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/40_search_failures.yaml diff --git a/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/50_reindex_with_parentchild.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/50_reindex_with_parentchild.yaml new file mode 100644 index 0000000000000..81e142c9195c1 --- /dev/null +++ b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/50_reindex_with_parentchild.yaml @@ -0,0 +1,79 @@ +--- +"Reindex from remote with parent/child": + - do: + indices.create: + index: source + body: + settings: + mapping.single_type: false + mappings: + foo: {} + bar: + _parent: + type: foo + - do: + indices.create: + index: dest + body: + settings: + mapping.single_type: false + mappings: + foo: {} + bar: + _parent: + type: foo + - do: + index: + index: source + type: foo + id: 1 + body: { "text": "test" } + - do: + index: + index: source + type: bar + id: 1 + parent: 1 + body: { "text": "test2" } + - do: + indices.refresh: {} + + # Fetch the http host. We use the host of the master because we know there will always be a master. + - do: + cluster.state: {} + - set: { master_node: master } + - do: + nodes.info: + metric: [ http ] + - is_true: nodes.$master.http.publish_address + - set: {nodes.$master.http.publish_address: host} + - do: + reindex: + refresh: true + body: + source: + remote: + host: http://${host} + index: source + dest: + index: dest + - match: {created: 2} + + - do: + search: + index: dest + body: + query: + has_parent: + parent_type: foo + query: + match: + text: test + - match: {hits.total: 1} + + # Make sure reindex closed all the scroll contexts + - do: + indices.stats: + index: source + metric: search + - match: {indices.source.total.search.open_contexts: 0} diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/10_script.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/10_script.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/10_script.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/10_script.yaml diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/20_broken.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/20_broken.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/20_broken.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/20_broken.yaml diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/30_timeout.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/30_timeout.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/30_timeout.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/30_timeout.yaml diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/40_search_failure.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/40_search_failure.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/40_search_failure.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/40_search_failure.yaml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml index d50c3dcb574fa..455243663420b 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml @@ -11,10 +11,6 @@ setup: properties: nested_field: type: nested - type_2: {} - type_3: - _parent: - type: type_2 --- "Nested inner hits": @@ -47,38 +43,3 @@ setup: - match: { hits.hits.0.inner_hits.nested_field.hits.hits.0._nested.offset: 0 } - is_false: hits.hits.0.inner_hits.nested_field.hits.hits.0._nested.child ---- -"Parent/child inner hits": - - skip: - version: " - 5.99.99" - reason: mapping.single_type was added in 6.0 - - - do: - index: - index: test - type: type_2 - id: 1 - body: {"foo": "bar"} - - - do: - index: - index: test - type: type_3 - id: 1 - parent: 1 - body: {"bar": "baz"} - - - do: - indices.refresh: {} - - - do: - search: - body: { "query" : { "has_child" : { "type" : "type_3", "query" : { "match_all" : {} }, "inner_hits" : {} } } } - - match: { hits.total: 1 } - - match: { hits.hits.0._index: "test" } - - match: { hits.hits.0._type: "type_2" } - - match: { hits.hits.0._id: "1" } - - is_false: hits.hits.0.inner_hits.type_3.hits.hits.0._index - - match: { hits.hits.0.inner_hits.type_3.hits.hits.0._type: "type_3" } - - match: { hits.hits.0.inner_hits.type_3.hits.hits.0._id: "1" } - - is_false: hits.hits.0.inner_hits.type_3.hits.hits.0._nested diff --git a/settings.gradle b/settings.gradle index 7acedd9c98bc0..b2ef30b99ddbe 100644 --- a/settings.gradle +++ b/settings.gradle @@ -34,6 +34,7 @@ List projects = [ 'modules:lang-expression', 'modules:lang-mustache', 'modules:lang-painless', + 'modules:parent-join', 'modules:percolator', 'modules:reindex', 'modules:repository-url', @@ -72,7 +73,7 @@ List projects = [ 'qa:smoke-test-ingest-disabled', 'qa:smoke-test-multinode', 'qa:smoke-test-plugins', - 'qa:smoke-test-reindex-with-painless', + 'qa:smoke-test-reindex-with-all-modules', 'qa:smoke-test-tribe-node', 'qa:vagrant', 'qa:wildfly' diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java similarity index 100% rename from core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java rename to test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java similarity index 95% rename from core/src/test/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java rename to test/framework/src/main/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java index c76d1a5f0ddc5..e27199918f412 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java @@ -33,6 +33,9 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.indices.IndicesModule; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.plugins.PluginsService; +import org.elasticsearch.plugins.SearchPlugin; import org.elasticsearch.search.SearchModule; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder; import org.elasticsearch.test.AbstractQueryTestCase; @@ -40,6 +43,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -66,6 +70,10 @@ protected String[] getCurrentTypes() { private NamedXContentRegistry xContentRegistry; protected abstract AB createTestAggregatorBuilder(); + protected Collection> getPlugins() { + return Collections.emptyList(); + } + /** * Setup for the whole base test class. */ @@ -77,7 +85,8 @@ public void setUp() throws Exception { .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) .build(); IndicesModule indicesModule = new IndicesModule(Collections.emptyList()); - SearchModule searchModule = new SearchModule(settings, false, emptyList()); + PluginsService pluginsService = new PluginsService(settings, null, null, getPlugins()); + SearchModule searchModule = new SearchModule(settings, false, pluginsService.filterPlugins(SearchPlugin.class)); List entries = new ArrayList<>(); entries.addAll(indicesModule.getNamedWriteables()); entries.addAll(searchModule.getNamedWriteables()); @@ -145,7 +154,6 @@ public void testSerialization() throws IOException { } } - public void testEqualsAndHashcode() throws IOException { // TODO we only change name and boost, we should extend by any sub-test supplying a "mutate" method that randomly changes one // aspect of the object under test diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/InternalSingleBucketAggregationTestCase.java similarity index 96% rename from core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java rename to test/framework/src/main/java/org/elasticsearch/search/aggregations/InternalSingleBucketAggregationTestCase.java index 8da1f0a90e95d..48c62b050c01c 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/InternalSingleBucketAggregationTestCase.java @@ -17,10 +17,11 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket; +package org.elasticsearch.search.aggregations; import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.bucket.InternalSingleBucketAggregation; import org.elasticsearch.search.aggregations.metrics.max.InternalMax; import org.elasticsearch.search.aggregations.metrics.min.InternalMin; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; From e8e2ccdcf54c186e1e8198d929116655ef071f7b Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Fri, 12 May 2017 16:32:29 +0200 Subject: [PATCH 331/619] Call onClose listener in a finally block --- .../java/org/elasticsearch/transport/TcpTransport.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/transport/TcpTransport.java b/core/src/main/java/org/elasticsearch/transport/TcpTransport.java index 11afec1bf1e5e..ba355e41477b9 100644 --- a/core/src/main/java/org/elasticsearch/transport/TcpTransport.java +++ b/core/src/main/java/org/elasticsearch/transport/TcpTransport.java @@ -411,8 +411,11 @@ public Channel channel(TransportRequestOptions.Type type) { @Override public synchronized void close() throws IOException { if (closed.compareAndSet(false, true)) { - closeChannels(Arrays.stream(channels).filter(Objects::nonNull).collect(Collectors.toList())); - onClose.accept(this); + try { + closeChannels(Arrays.stream(channels).filter(Objects::nonNull).collect(Collectors.toList())); + } finally { + onClose.accept(this); + } } } From 878ae8eb3c3b93fe0cb8b25daeb0ceb16da37801 Mon Sep 17 00:00:00 2001 From: Koen De Groote Date: Fri, 12 May 2017 16:36:13 +0200 Subject: [PATCH 332/619] Size lists in advance when known When constructing an array list, if we know the size of the list in advance (because we are adding objects to it derived from another list), we should size the array list to the appropriate capacity in advance (to avoid resizing allocations). This commit does this in various places. Relates #24439 --- .../cluster/repositories/get/GetRepositoriesResponse.java | 2 +- .../admin/cluster/snapshots/get/GetSnapshotsResponse.java | 2 +- .../action/admin/cluster/stats/ClusterStatsNodes.java | 4 ++-- .../action/admin/indices/get/GetIndexResponse.java | 2 +- .../action/support/tasks/BaseTasksResponse.java | 4 ++-- .../org/elasticsearch/cluster/block/ClusterBlock.java | 2 +- .../common/inject/assistedinject/AssistedConstructor.java | 2 +- .../index/analysis/CustomAnalyzerProvider.java | 4 ++-- .../index/analysis/CustomNormalizerProvider.java | 4 ++-- .../org/elasticsearch/index/mapper/DynamicTemplate.java | 2 +- .../elasticsearch/index/query/AbstractQueryBuilder.java | 2 +- .../elasticsearch/index/query/GeoPolygonQueryBuilder.java | 2 +- .../snapshots/blobstore/BlobStoreIndexShardSnapshots.java | 2 +- .../indices/breaker/HierarchyCircuitBreakerService.java | 2 +- .../search/aggregations/AggregatorFactories.java | 2 +- .../search/aggregations/BucketCollector.java | 2 +- .../adjacency/AdjacencyMatrixAggregationBuilder.java | 2 +- .../bucket/filters/FiltersAggregationBuilder.java | 2 +- .../search/aggregations/bucket/terms/DoubleTerms.java | 2 +- .../cumulativesum/CumulativeSumPipelineAggregator.java | 2 +- .../org/elasticsearch/ingest/common/ConvertProcessor.java | 2 +- .../org/elasticsearch/ingest/common/DateProcessor.java | 2 +- .../src/main/java/org/elasticsearch/painless/Def.java | 2 +- .../java/org/elasticsearch/painless/node/ELambda.java | 8 ++++---- .../discovery/ec2/AwsEc2UnicastHostsProvider.java | 4 ++-- .../repositories/gcs/GoogleCloudStorageBlobStore.java | 2 +- .../org/elasticsearch/bootstrap/BootstrapForTesting.java | 2 +- .../test/rest/yaml/ClientYamlTestExecutionContext.java | 2 +- 28 files changed, 36 insertions(+), 36 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/repositories/get/GetRepositoriesResponse.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/repositories/get/GetRepositoriesResponse.java index c933156fcb077..6d4cb83934548 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/repositories/get/GetRepositoriesResponse.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/repositories/get/GetRepositoriesResponse.java @@ -60,7 +60,7 @@ public List repositories() { public void readFrom(StreamInput in) throws IOException { super.readFrom(in); int size = in.readVInt(); - List repositoryListBuilder = new ArrayList<>(); + List repositoryListBuilder = new ArrayList<>(size); for (int j = 0; j < size; j++) { repositoryListBuilder.add(new RepositoryMetaData( in.readString(), diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsResponse.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsResponse.java index 308a846c9058b..0d1e5eda7f2d2 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsResponse.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsResponse.java @@ -59,7 +59,7 @@ public List getSnapshots() { public void readFrom(StreamInput in) throws IOException { super.readFrom(in); int size = in.readVInt(); - List builder = new ArrayList<>(); + List builder = new ArrayList<>(size); for (int i = 0; i < size; i++) { builder.add(new SnapshotInfo(in)); } diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodes.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodes.java index 27276b27dd99f..41cacf2a8515c 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodes.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodes.java @@ -65,8 +65,8 @@ public class ClusterStatsNodes implements ToXContent { this.plugins = new HashSet<>(); Set seenAddresses = new HashSet<>(nodeResponses.size()); - List nodeInfos = new ArrayList<>(); - List nodeStats = new ArrayList<>(); + List nodeInfos = new ArrayList<>(nodeResponses.size()); + List nodeStats = new ArrayList<>(nodeResponses.size()); for (ClusterStatsNodeResponse nodeResponse : nodeResponses) { nodeInfos.add(nodeResponse.nodeInfo()); nodeStats.add(nodeResponse.nodeStats()); diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/get/GetIndexResponse.java b/core/src/main/java/org/elasticsearch/action/admin/indices/get/GetIndexResponse.java index 6c2e4627523e7..36bfa81a33416 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/get/GetIndexResponse.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/get/GetIndexResponse.java @@ -114,7 +114,7 @@ public void readFrom(StreamInput in) throws IOException { for (int i = 0; i < aliasesSize; i++) { String key = in.readString(); int valueSize = in.readVInt(); - List aliasEntryBuilder = new ArrayList<>(); + List aliasEntryBuilder = new ArrayList<>(valueSize); for (int j = 0; j < valueSize; j++) { aliasEntryBuilder.add(new AliasMetaData(in)); } diff --git a/core/src/main/java/org/elasticsearch/action/support/tasks/BaseTasksResponse.java b/core/src/main/java/org/elasticsearch/action/support/tasks/BaseTasksResponse.java index b62cfd714bbf2..4ddbe541993e6 100644 --- a/core/src/main/java/org/elasticsearch/action/support/tasks/BaseTasksResponse.java +++ b/core/src/main/java/org/elasticsearch/action/support/tasks/BaseTasksResponse.java @@ -81,13 +81,13 @@ public void rethrowFailures(String operationName) { public void readFrom(StreamInput in) throws IOException { super.readFrom(in); int size = in.readVInt(); - List taskFailures = new ArrayList<>(); + List taskFailures = new ArrayList<>(size); for (int i = 0; i < size; i++) { taskFailures.add(new TaskOperationFailure(in)); } size = in.readVInt(); this.taskFailures = Collections.unmodifiableList(taskFailures); - List nodeFailures = new ArrayList<>(); + List nodeFailures = new ArrayList<>(size); for (int i = 0; i < size; i++) { nodeFailures.add(new FailedNodeException(in)); } diff --git a/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlock.java b/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlock.java index 253206222b433..f09e1dd9cda3c 100644 --- a/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlock.java +++ b/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlock.java @@ -125,7 +125,7 @@ public void readFrom(StreamInput in) throws IOException { id = in.readVInt(); description = in.readString(); final int len = in.readVInt(); - ArrayList levels = new ArrayList<>(); + ArrayList levels = new ArrayList<>(len); for (int i = 0; i < len; i++) { levels.add(ClusterBlockLevel.fromId(in.readVInt())); } diff --git a/core/src/main/java/org/elasticsearch/common/inject/assistedinject/AssistedConstructor.java b/core/src/main/java/org/elasticsearch/common/inject/assistedinject/AssistedConstructor.java index edd45c290a54b..cb434a90369d3 100644 --- a/core/src/main/java/org/elasticsearch/common/inject/assistedinject/AssistedConstructor.java +++ b/core/src/main/java/org/elasticsearch/common/inject/assistedinject/AssistedConstructor.java @@ -49,7 +49,7 @@ class AssistedConstructor { Annotation[][] annotations = constructor.getParameterAnnotations(); List typeList = new ArrayList<>(); - allParameters = new ArrayList<>(); + allParameters = new ArrayList<>(parameterTypes.size()); // categorize params as @Assisted or @Injected for (int i = 0; i < parameterTypes.size(); i++) { diff --git a/core/src/main/java/org/elasticsearch/index/analysis/CustomAnalyzerProvider.java b/core/src/main/java/org/elasticsearch/index/analysis/CustomAnalyzerProvider.java index 282edaeaf73fa..f112304562283 100644 --- a/core/src/main/java/org/elasticsearch/index/analysis/CustomAnalyzerProvider.java +++ b/core/src/main/java/org/elasticsearch/index/analysis/CustomAnalyzerProvider.java @@ -55,8 +55,8 @@ public void build(final Map tokenizers, final Map charFiltersList = new ArrayList<>(); String[] charFilterNames = analyzerSettings.getAsArray("char_filter"); + List charFiltersList = new ArrayList<>(charFilterNames.length); for (String charFilterName : charFilterNames) { CharFilterFactory charFilter = charFilters.get(charFilterName); if (charFilter == null) { @@ -65,8 +65,8 @@ public void build(final Map tokenizers, final Map tokenFilterList = new ArrayList<>(); String[] tokenFilterNames = analyzerSettings.getAsArray("filter"); + List tokenFilterList = new ArrayList<>(tokenFilterNames.length); for (String tokenFilterName : tokenFilterNames) { TokenFilterFactory tokenFilter = tokenFilters.get(tokenFilterName); if (tokenFilter == null) { diff --git a/core/src/main/java/org/elasticsearch/index/analysis/CustomNormalizerProvider.java b/core/src/main/java/org/elasticsearch/index/analysis/CustomNormalizerProvider.java index 4f50a34dd9e4a..2fcc987df6aa3 100644 --- a/core/src/main/java/org/elasticsearch/index/analysis/CustomNormalizerProvider.java +++ b/core/src/main/java/org/elasticsearch/index/analysis/CustomNormalizerProvider.java @@ -50,8 +50,8 @@ public void build(final Map charFilters, final Map charFiltersList = new ArrayList<>(); String[] charFilterNames = analyzerSettings.getAsArray("char_filter"); + List charFiltersList = new ArrayList<>(charFilterNames.length); for (String charFilterName : charFilterNames) { CharFilterFactory charFilter = charFilters.get(charFilterName); if (charFilter == null) { @@ -66,8 +66,8 @@ public void build(final Map charFilters, final Map tokenFilterList = new ArrayList<>(); String[] tokenFilterNames = analyzerSettings.getAsArray("filter"); + List tokenFilterList = new ArrayList<>(tokenFilterNames.length); for (String tokenFilterName : tokenFilterNames) { TokenFilterFactory tokenFilter = tokenFilters.get(tokenFilterName); if (tokenFilter == null) { diff --git a/core/src/main/java/org/elasticsearch/index/mapper/DynamicTemplate.java b/core/src/main/java/org/elasticsearch/index/mapper/DynamicTemplate.java index b07492740ae7d..ca8fcd1ffd8ba 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/DynamicTemplate.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/DynamicTemplate.java @@ -318,7 +318,7 @@ private Map processMap(Map map, String name, Str } private List processList(List list, String name, String dynamicType) { - List processedList = new ArrayList(); + List processedList = new ArrayList(list.size()); for (Object value : list) { if (value instanceof Map) { value = processMap((Map) value, name, dynamicType); diff --git a/core/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java index 6a3cd2f1ed576..9d18e42138dc8 100644 --- a/core/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java @@ -250,8 +250,8 @@ protected static final void writeQueries(StreamOutput out, List readQueries(StreamInput in) throws IOException { - List queries = new ArrayList<>(); int size = in.readVInt(); + List queries = new ArrayList<>(size); for (int i = 0; i < size; i++) { queries.add(in.readNamedWriteable(QueryBuilder.class)); } diff --git a/core/src/main/java/org/elasticsearch/index/query/GeoPolygonQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/GeoPolygonQueryBuilder.java index cc25ac4772a84..1f9e81cecc161 100644 --- a/core/src/main/java/org/elasticsearch/index/query/GeoPolygonQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/GeoPolygonQueryBuilder.java @@ -163,7 +163,7 @@ protected Query doToQuery(QueryShardContext context) throws IOException { throw new QueryShardException(context, "field [" + fieldName + "] is not a geo_point field"); } - List shell = new ArrayList(); + List shell = new ArrayList<>(this.shell.size()); for (GeoPoint geoPoint : this.shell) { shell.add(new GeoPoint(geoPoint)); } diff --git a/core/src/main/java/org/elasticsearch/index/snapshots/blobstore/BlobStoreIndexShardSnapshots.java b/core/src/main/java/org/elasticsearch/index/snapshots/blobstore/BlobStoreIndexShardSnapshots.java index 359c3165f535a..56c343a5ae9b5 100644 --- a/core/src/main/java/org/elasticsearch/index/snapshots/blobstore/BlobStoreIndexShardSnapshots.java +++ b/core/src/main/java/org/elasticsearch/index/snapshots/blobstore/BlobStoreIndexShardSnapshots.java @@ -284,7 +284,7 @@ public static BlobStoreIndexShardSnapshots fromXContent(XContentParser parser) t } } - List snapshots = new ArrayList<>(); + List snapshots = new ArrayList<>(snapshotsMap.size()); for (Map.Entry> entry : snapshotsMap.entrySet()) { List fileInfosBuilder = new ArrayList<>(); for (String file : entry.getValue()) { diff --git a/core/src/main/java/org/elasticsearch/indices/breaker/HierarchyCircuitBreakerService.java b/core/src/main/java/org/elasticsearch/indices/breaker/HierarchyCircuitBreakerService.java index 88cff7f559a09..b8ec92ba15374 100644 --- a/core/src/main/java/org/elasticsearch/indices/breaker/HierarchyCircuitBreakerService.java +++ b/core/src/main/java/org/elasticsearch/indices/breaker/HierarchyCircuitBreakerService.java @@ -177,7 +177,7 @@ public CircuitBreaker getBreaker(String name) { @Override public AllCircuitBreakerStats stats() { long parentEstimated = 0; - List allStats = new ArrayList<>(); + List allStats = new ArrayList<>(this.breakers.size()); // Gather the "estimated" count for the parent breaker by adding the // estimations for each individual breaker for (CircuitBreaker breaker : this.breakers.values()) { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactories.java b/core/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactories.java index 106335380ccbe..d635952ec150f 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactories.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactories.java @@ -183,7 +183,7 @@ private AggregatorFactories(AggregatorFactory parent, AggregatorFactory[] } public List createPipelineAggregators() throws IOException { - List pipelineAggregators = new ArrayList<>(); + List pipelineAggregators = new ArrayList<>(this.pipelineAggregatorFactories.size()); for (PipelineAggregationBuilder factory : this.pipelineAggregatorFactories) { pipelineAggregators.add(factory.create()); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/BucketCollector.java b/core/src/main/java/org/elasticsearch/search/aggregations/BucketCollector.java index 2de6ae0cf933f..40e66bd964539 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/BucketCollector.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/BucketCollector.java @@ -70,7 +70,7 @@ public static BucketCollector wrap(Iterable collector @Override public LeafBucketCollector getLeafCollector(LeafReaderContext ctx) throws IOException { - List leafCollectors = new ArrayList<>(); + List leafCollectors = new ArrayList<>(collectors.length); for (BucketCollector c : collectors) { leafCollectors.add(c.getLeafCollector(ctx)); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/AdjacencyMatrixAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/AdjacencyMatrixAggregationBuilder.java index 526db6a79b7a1..eeb60d393e5c8 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/AdjacencyMatrixAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/AdjacencyMatrixAggregationBuilder.java @@ -197,7 +197,7 @@ protected AggregatorFactory doBuild(SearchContext context, AggregatorFactory< + "] index level setting."); } - List rewrittenFilters = new ArrayList<>(); + List rewrittenFilters = new ArrayList<>(filters.size()); for (KeyedFilter kf : filters) { rewrittenFilters.add(new KeyedFilter(kf.key(), QueryBuilder.rewriteQuery(kf.filter(), context.getQueryShardContext()))); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filters/FiltersAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filters/FiltersAggregationBuilder.java index f34df7368acae..b80ce74c3da2b 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filters/FiltersAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filters/FiltersAggregationBuilder.java @@ -169,7 +169,7 @@ public String otherBucketKey() { @Override protected AggregatorFactory doBuild(SearchContext context, AggregatorFactory parent, Builder subFactoriesBuilder) throws IOException { - List rewrittenFilters = new ArrayList<>(); + List rewrittenFilters = new ArrayList<>(filters.size()); for(KeyedFilter kf : filters) { rewrittenFilters.add(new KeyedFilter(kf.key(), QueryBuilder.rewriteQuery(kf.filter(), context.getQueryShardContext()))); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java index e4885bc0539d5..b87ce51c4bd25 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java @@ -164,7 +164,7 @@ public InternalAggregation doReduce(List aggregations, Redu if (promoteToDouble == false) { return super.doReduce(aggregations, reduceContext); } - List newAggs = new ArrayList<>(); + List newAggs = new ArrayList<>(aggregations.size()); for (InternalAggregation agg : aggregations) { if (agg instanceof LongTerms) { DoubleTerms dTerms = LongTerms.convertLongTermsToDouble((LongTerms) agg, format); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/cumulativesum/CumulativeSumPipelineAggregator.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/cumulativesum/CumulativeSumPipelineAggregator.java index e79ba1047e29c..8a1b70fdd145b 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/cumulativesum/CumulativeSumPipelineAggregator.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/cumulativesum/CumulativeSumPipelineAggregator.java @@ -75,7 +75,7 @@ public InternalAggregation reduce(InternalAggregation aggregation, ReduceContext InternalMultiBucketAggregation.InternalBucket>) aggregation; List buckets = histo.getBuckets(); HistogramFactory factory = (HistogramFactory) histo; - List newBuckets = new ArrayList<>(); + List newBuckets = new ArrayList<>(buckets.size()); double sum = 0; for (InternalMultiBucketAggregation.InternalBucket bucket : buckets) { Double thisBucketValue = resolveBucketValue(histo, bucket, bucketsPaths()[0], GapPolicy.INSERT_ZEROS); diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/ConvertProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/ConvertProcessor.java index 3cf35c76117ee..8ecd4d99eb2bf 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/ConvertProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/ConvertProcessor.java @@ -153,7 +153,7 @@ public void execute(IngestDocument document) { if (oldValue instanceof List) { List list = (List) oldValue; - List newList = new ArrayList<>(); + List newList = new ArrayList<>(list.size()); for (Object value : list) { newList.add(convertType.convert(value)); } diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateProcessor.java index 676cf2e7419c2..e6034138873aa 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateProcessor.java @@ -54,7 +54,7 @@ public final class DateProcessor extends AbstractProcessor { this.field = field; this.targetField = targetField; this.formats = formats; - this.dateParsers = new ArrayList<>(); + this.dateParsers = new ArrayList<>(this.formats.size()); for (String format : formats) { DateFormat dateFormat = DateFormat.fromString(format); dateParsers.add(dateFormat.getFunction(format, timezone, locale)); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index dd1b51a5151bb..ebd25032220fe 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java @@ -240,7 +240,7 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp } // convert recipe string to a bitset for convenience (the code below should be refactored...) - BitSet lambdaArgs = new BitSet(); + BitSet lambdaArgs = new BitSet(recipeString.length()); for (int i = 0; i < recipeString.length(); i++) { lambdaArgs.set(recipeString.charAt(i)); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java index ad843f86af200..748ff67ddd4b8 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java @@ -113,7 +113,7 @@ void analyze(Locals locals) { // we don't know anything: treat as def returnType = Definition.DEF_TYPE; // don't infer any types, replace any null types with def - actualParamTypeStrs = new ArrayList<>(); + actualParamTypeStrs = new ArrayList<>(paramTypeStrs.size()); for (String type : paramTypeStrs) { if (type == null) { actualParamTypeStrs.add("def"); @@ -139,7 +139,7 @@ void analyze(Locals locals) { returnType = interfaceMethod.rtn; } // replace any null types with the actual type - actualParamTypeStrs = new ArrayList<>(); + actualParamTypeStrs = new ArrayList<>(paramTypeStrs.size()); for (int i = 0; i < paramTypeStrs.size(); i++) { String paramType = paramTypeStrs.get(i); if (paramType == null) { @@ -162,8 +162,8 @@ void analyze(Locals locals) { } } // prepend capture list to lambda's arguments - List paramTypes = new ArrayList<>(); - List paramNames = new ArrayList<>(); + List paramTypes = new ArrayList<>(captures.size() + actualParamTypeStrs.size()); + List paramNames = new ArrayList<>(captures.size() + paramNameStrs.size()); for (Variable var : captures) { paramTypes.add(var.type.name); paramNames.add(var.name); diff --git a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2UnicastHostsProvider.java b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2UnicastHostsProvider.java index d428b953af1f5..91713ce2177fd 100644 --- a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2UnicastHostsProvider.java +++ b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2UnicastHostsProvider.java @@ -125,8 +125,8 @@ protected List fetchDynamicNodes() { // lets see if we can filter based on groups if (!groups.isEmpty()) { List instanceSecurityGroups = instance.getSecurityGroups(); - ArrayList securityGroupNames = new ArrayList(); - ArrayList securityGroupIds = new ArrayList(); + List securityGroupNames = new ArrayList<>(instanceSecurityGroups.size()); + List securityGroupIds = new ArrayList<>(instanceSecurityGroups.size()); for (GroupIdentifier sg : instanceSecurityGroups) { securityGroupNames.add(sg.getGroupName()); securityGroupIds.add(sg.getGroupId()); diff --git a/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStore.java b/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStore.java index 3c50c3a2a2daa..225411f86dc49 100644 --- a/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStore.java +++ b/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStore.java @@ -249,7 +249,7 @@ void deleteBlobs(Collection blobNames) throws IOException { deleteBlob(blobNames.iterator().next()); return; } - final List deletions = new ArrayList<>(); + final List deletions = new ArrayList<>(Math.min(MAX_BATCHING_REQUESTS, blobNames.size())); final Iterator blobs = blobNames.iterator(); SocketAccess.doPrivilegedVoidIOException(() -> { diff --git a/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java b/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java index c88b56abdd074..fbdab0b126b4a 100644 --- a/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java +++ b/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java @@ -201,7 +201,7 @@ static Map getPluginPermissions() throws Exception { codebases.removeAll(excluded); // parse each policy file, with codebase substitution from the classpath - final List policies = new ArrayList<>(); + final List policies = new ArrayList<>(pluginPolicies.size()); for (URL policyFile : pluginPolicies) { policies.add(Security.readPolicy(policyFile, codebases)); } diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ClientYamlTestExecutionContext.java b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ClientYamlTestExecutionContext.java index c2c0f57c942ce..bea9aab3ff784 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ClientYamlTestExecutionContext.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ClientYamlTestExecutionContext.java @@ -110,7 +110,7 @@ private HttpEntity createEntity(List> bodies, Map bytesRefList = new ArrayList<>(); + List bytesRefList = new ArrayList<>(bodies.size()); int totalBytesLength = 0; for (Map body : bodies) { BytesRef bytesRef = bodyAsBytesRef(body, xContentType); From f8df2a22e9171e07125e17f8dd95da2eeb89fe3f Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Fri, 12 May 2017 16:38:50 +0200 Subject: [PATCH 333/619] SniffNodesSampler should close connection after handling responses (#24632) With the current implementation, SniffNodesSampler might close the current connection right after a request is sent but before the response is correctly handled. This causes to timeouts in the transport client when the sniffing is activated. closes #24575 closes #24557 --- .../TransportClientNodesService.java | 18 +- .../TransportClientNodesServiceTests.java | 186 ++++++++++++++++-- 2 files changed, 185 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/client/transport/TransportClientNodesService.java b/core/src/main/java/org/elasticsearch/client/transport/TransportClientNodesService.java index 145208baf0db8..3e50b4d74c916 100644 --- a/core/src/main/java/org/elasticsearch/client/transport/TransportClientNodesService.java +++ b/core/src/main/java/org/elasticsearch/client/transport/TransportClientNodesService.java @@ -469,14 +469,17 @@ protected void doSample() { */ Transport.Connection connectionToClose = null; - @Override - public void onAfter() { - IOUtils.closeWhileHandlingException(connectionToClose); + void onDone() { + try { + IOUtils.closeWhileHandlingException(connectionToClose); + } finally { + latch.countDown(); + } } @Override public void onFailure(Exception e) { - latch.countDown(); + onDone(); if (e instanceof ConnectTransportException) { logger.debug((Supplier) () -> new ParameterizedMessage("failed to connect to node [{}], ignoring...", nodeToPing), e); @@ -522,7 +525,7 @@ public String executor() { @Override public void handleResponse(ClusterStateResponse response) { clusterStateResponses.put(nodeToPing, response); - latch.countDown(); + onDone(); } @Override @@ -532,9 +535,8 @@ public void handleException(TransportException e) { "failed to get local cluster state for {}, disconnecting...", nodeToPing), e); try { hostFailureListener.onNodeDisconnected(nodeToPing, e); - } - finally { - latch.countDown(); + } finally { + onDone(); } } }); diff --git a/core/src/test/java/org/elasticsearch/client/transport/TransportClientNodesServiceTests.java b/core/src/test/java/org/elasticsearch/client/transport/TransportClientNodesServiceTests.java index a6e2431a15f0c..ed15660c85ec8 100644 --- a/core/src/test/java/org/elasticsearch/client/transport/TransportClientNodesServiceTests.java +++ b/core/src/test/java/org/elasticsearch/client/transport/TransportClientNodesServiceTests.java @@ -19,21 +19,13 @@ package org.elasticsearch.client.transport; -import java.io.Closeable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.cluster.node.liveness.LivenessResponse; import org.elasticsearch.action.admin.cluster.node.liveness.TransportLivenessAction; +import org.elasticsearch.action.admin.cluster.state.ClusterStateAction; +import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; +import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -41,19 +33,40 @@ import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.node.Node; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.ConnectionProfile; import org.elasticsearch.transport.Transport; +import org.elasticsearch.transport.TransportChannel; import org.elasticsearch.transport.TransportException; import org.elasticsearch.transport.TransportInterceptor; import org.elasticsearch.transport.TransportRequest; +import org.elasticsearch.transport.TransportRequestHandler; import org.elasticsearch.transport.TransportRequestOptions; import org.elasticsearch.transport.TransportResponse; import org.elasticsearch.transport.TransportResponseHandler; import org.elasticsearch.transport.TransportService; import org.hamcrest.CustomMatcher; +import java.io.Closeable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static org.elasticsearch.test.transport.MockTransportService.createNewService; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.everyItem; import static org.hamcrest.CoreMatchers.hasItem; @@ -322,6 +335,157 @@ public boolean matches(Object item) { } } + public void testSniffNodesSamplerClosesConnections() throws Exception { + final TestThreadPool threadPool = new TestThreadPool("testSniffNodesSamplerClosesConnections"); + + Settings remoteSettings = Settings.builder().put(Node.NODE_NAME_SETTING.getKey(), "remote").build(); + try (MockTransportService remoteService = createNewService(remoteSettings, Version.CURRENT, threadPool, null)) { + final MockHandler handler = new MockHandler(remoteService); + remoteService.registerRequestHandler(ClusterStateAction.NAME, ClusterStateRequest::new, ThreadPool.Names.SAME, handler); + remoteService.start(); + remoteService.acceptIncomingRequests(); + + Settings clientSettings = Settings.builder() + .put(TransportClient.CLIENT_TRANSPORT_SNIFF.getKey(), true) + .put(TransportClient.CLIENT_TRANSPORT_PING_TIMEOUT.getKey(), TimeValue.timeValueSeconds(1)) + .put(TransportClient.CLIENT_TRANSPORT_NODES_SAMPLER_INTERVAL.getKey(), TimeValue.timeValueSeconds(30)) + .build(); + + try (MockTransportService clientService = createNewService(clientSettings, Version.CURRENT, threadPool, null)) { + final List establishedConnections = new CopyOnWriteArrayList<>(); + final List reusedConnections = new CopyOnWriteArrayList<>(); + + clientService.addDelegate(remoteService, new MockTransportService.DelegateTransport(clientService.original()) { + @Override + public Connection openConnection(DiscoveryNode node, ConnectionProfile profile) throws IOException { + MockConnection connection = new MockConnection(super.openConnection(node, profile)); + establishedConnections.add(connection); + return connection; + } + + @Override + public Connection getConnection(DiscoveryNode node) { + MockConnection connection = new MockConnection(super.getConnection(node)); + reusedConnections.add(connection); + return connection; + } + }); + + clientService.start(); + clientService.acceptIncomingRequests(); + + try (TransportClientNodesService transportClientNodesService = + new TransportClientNodesService(clientSettings, clientService, threadPool, (a, b) -> {})) { + assertEquals(0, transportClientNodesService.connectedNodes().size()); + assertEquals(0, establishedConnections.size()); + assertEquals(0, reusedConnections.size()); + + transportClientNodesService.addTransportAddresses(remoteService.getLocalDiscoNode().getAddress()); + assertEquals(1, transportClientNodesService.connectedNodes().size()); + assertClosedConnections(establishedConnections, 1); + + transportClientNodesService.doSample(); + assertClosedConnections(establishedConnections, 2); + assertOpenConnections(reusedConnections, 1); + + handler.blockRequest(); + Thread thread = new Thread(transportClientNodesService::doSample); + thread.start(); + + assertBusy(() -> assertEquals(3, establishedConnections.size())); + assertFalse("Temporary ping connection must be opened", establishedConnections.get(2).isClosed()); + + handler.releaseRequest(); + thread.join(); + + assertClosedConnections(establishedConnections, 3); + } + } + } finally { + terminate(threadPool); + } + } + + private void assertClosedConnections(final List connections, final int size) { + assertEquals("Expecting " + size + " closed connections but got " + connections.size(), size, connections.size()); + connections.forEach(c -> assertConnection(c, true)); + } + + private void assertOpenConnections(final List connections, final int size) { + assertEquals("Expecting " + size + " open connections but got " + connections.size(), size, connections.size()); + connections.forEach(c -> assertConnection(c, false)); + } + + private static void assertConnection(final MockConnection connection, final boolean closed) { + assertEquals("Connection [" + connection + "] must be " + (closed ? "closed" : "open"), closed, connection.isClosed()); + } + + class MockConnection implements Transport.Connection { + private final AtomicBoolean closed = new AtomicBoolean(false); + private final Transport.Connection connection; + + private MockConnection(Transport.Connection connection) { + this.connection = connection; + } + + @Override + public DiscoveryNode getNode() { + return connection.getNode(); + } + + @Override + public Version getVersion() { + return connection.getVersion(); + } + + @Override + public void sendRequest(long requestId, String action, TransportRequest request, TransportRequestOptions options) + throws IOException, TransportException { + connection.sendRequest(requestId, action, request, options); + } + + @Override + public void close() throws IOException { + if (closed.compareAndSet(false, true)) { + connection.close(); + } + } + + boolean isClosed() { + return closed.get(); + } + } + + class MockHandler implements TransportRequestHandler { + private final AtomicBoolean block = new AtomicBoolean(false); + private final CountDownLatch release = new CountDownLatch(1); + private final MockTransportService transportService; + + MockHandler(MockTransportService transportService) { + this.transportService = transportService; + } + + @Override + public void messageReceived(ClusterStateRequest request, TransportChannel channel) throws Exception { + if (block.get()) { + release.await(); + return; + } + DiscoveryNodes discoveryNodes = DiscoveryNodes.builder().add(transportService.getLocalDiscoNode()).build(); + ClusterState build = ClusterState.builder(ClusterName.DEFAULT).nodes(discoveryNodes).build(); + channel.sendResponse(new ClusterStateResponse(ClusterName.DEFAULT, build, 0L)); + } + + void blockRequest() { + if (block.compareAndSet(false, true) == false) { + throw new AssertionError("Request handler is already marked as blocking"); + } + } + void releaseRequest() { + release.countDown(); + } + } + public static class TestRequest extends TransportRequest { } From b9d2ecc3eab93a2cf909e0c2a2540b668e3d207e Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Fri, 12 May 2017 16:52:47 +0200 Subject: [PATCH 334/619] Add parsing methods to Range aggregations (#24583) --- .../ParsedMultiBucketAggregation.java | 4 + .../bucket/range/ParsedRange.java | 193 ++++++++++++++++++ .../bucket/range/date/ParsedDateRange.java | 74 +++++++ .../range/geodistance/ParsedGeoDistance.java | 55 +++++ .../aggregations/AggregationsTests.java | 6 + ...nternalMultiBucketAggregationTestCase.java | 2 +- .../bucket/range/InternalRangeTestCase.java | 30 ++- .../bucket/range/InternalRangeTests.java | 26 ++- .../range/date/InternalDateRangeTests.java | 9 +- .../geodistance/InternalGeoDistanceTests.java | 10 +- .../test/InternalAggregationTestCase.java | 9 + 11 files changed, 407 insertions(+), 11 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ParsedRange.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/ParsedDateRange.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/ParsedGeoDistance.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java index 52b5fad53798e..df80ada8ddd1a 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedMultiBucketAggregation.java @@ -107,6 +107,10 @@ public void setKeyed(boolean keyed) { this.keyed = keyed; } + protected boolean isKeyed() { + return keyed; + } + protected void setAggregations(Aggregations aggregations) { this.aggregations = aggregations; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ParsedRange.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ParsedRange.java new file mode 100644 index 0000000000000..85066296d5fdb --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ParsedRange.java @@ -0,0 +1,193 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.range; + +import org.elasticsearch.common.CheckedFunction; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParserUtils; +import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.aggregations.Aggregations; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; + +public class ParsedRange extends ParsedMultiBucketAggregation implements Range { + + @Override + public String getType() { + return RangeAggregationBuilder.NAME; + } + + @Override + public List getBuckets() { + return buckets; + } + + protected static void declareParsedRangeFields(final ObjectParser objectParser, + final CheckedFunction bucketParser, + final CheckedFunction keyedBucketParser) { + declareMultiBucketAggregationFields(objectParser, bucketParser::apply, keyedBucketParser::apply); + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedRange.class.getSimpleName(), true, ParsedRange::new); + static { + declareParsedRangeFields(PARSER, + parser -> ParsedBucket.fromXContent(parser, false), + parser -> ParsedBucket.fromXContent(parser, true)); + } + + public static ParsedRange fromXContent(XContentParser parser, String name) throws IOException { + ParsedRange aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } + + public static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements Range.Bucket { + + protected String key; + protected double from = Double.NEGATIVE_INFINITY; + protected String fromAsString; + protected double to = Double.POSITIVE_INFINITY; + protected String toAsString; + + @Override + public String getKey() { + return getKeyAsString(); + } + + @Override + public String getKeyAsString() { + String keyAsString = super.getKeyAsString(); + if (keyAsString != null) { + return keyAsString; + } + return key; + } + + @Override + public Object getFrom() { + return from; + } + + @Override + public String getFromAsString() { + if (fromAsString != null) { + return fromAsString; + } + return doubleAsString(from); + } + + @Override + public Object getTo() { + return to; + } + + @Override + public String getToAsString() { + if (toAsString != null) { + return toAsString; + } + return doubleAsString(to); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + if (isKeyed()) { + builder.startObject(key); + } else { + builder.startObject(); + builder.field(CommonFields.KEY.getPreferredName(), key); + } + if (Double.isInfinite(from) == false) { + builder.field(CommonFields.FROM.getPreferredName(), from); + if (fromAsString != null) { + builder.field(CommonFields.FROM_AS_STRING.getPreferredName(), fromAsString); + } + } + if (Double.isInfinite(to) == false) { + builder.field(CommonFields.TO.getPreferredName(), to); + if (toAsString != null) { + builder.field(CommonFields.TO_AS_STRING.getPreferredName(), toAsString); + } + } + builder.field(CommonFields.DOC_COUNT.getPreferredName(), getDocCount()); + getAggregations().toXContentInternal(builder, params); + builder.endObject(); + return builder; + } + + private static String doubleAsString(double d) { + return Double.isInfinite(d) ? null : Double.toString(d); + } + + protected static B parseRangeBucketXContent(final XContentParser parser, + final Supplier bucketSupplier, + final boolean keyed) throws IOException { + final B bucket = bucketSupplier.get(); + bucket.setKeyed(keyed); + XContentParser.Token token = parser.currentToken(); + String currentFieldName = parser.currentName(); + if (keyed) { + ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation); + bucket.key = currentFieldName; + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + } + + List aggregations = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (CommonFields.KEY_AS_STRING.getPreferredName().equals(currentFieldName)) { + bucket.setKeyAsString(parser.text()); + } else if (CommonFields.KEY.getPreferredName().equals(currentFieldName)) { + bucket.key = parser.text(); + } else if (CommonFields.DOC_COUNT.getPreferredName().equals(currentFieldName)) { + bucket.setDocCount(parser.longValue()); + } else if (CommonFields.FROM.getPreferredName().equals(currentFieldName)) { + bucket.from = parser.doubleValue(); + } else if (CommonFields.FROM_AS_STRING.getPreferredName().equals(currentFieldName)) { + bucket.fromAsString = parser.text(); + } else if (CommonFields.TO.getPreferredName().equals(currentFieldName)) { + bucket.to = parser.doubleValue(); + } else if (CommonFields.TO_AS_STRING.getPreferredName().equals(currentFieldName)) { + bucket.toAsString = parser.text(); + } + } else if (token == XContentParser.Token.START_OBJECT) { + aggregations.add(XContentParserUtils.parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Aggregation.class)); + } + } + bucket.setAggregations(new Aggregations(aggregations)); + return bucket; + } + + static ParsedBucket fromXContent(final XContentParser parser, final boolean keyed) throws IOException { + return parseRangeBucketXContent(parser, ParsedBucket::new, keyed); + } + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/ParsedDateRange.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/ParsedDateRange.java new file mode 100644 index 0000000000000..b8f2f008cec65 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/ParsedDateRange.java @@ -0,0 +1,74 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.range.date; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.bucket.range.ParsedRange; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; + +import java.io.IOException; + +public class ParsedDateRange extends ParsedRange { + + @Override + public String getType() { + return DateRangeAggregationBuilder.NAME; + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedDateRange.class.getSimpleName(), true, ParsedDateRange::new); + static { + declareParsedRangeFields(PARSER, + parser -> ParsedBucket.fromXContent(parser, false), + parser -> ParsedBucket.fromXContent(parser, true)); + } + + public static ParsedDateRange fromXContent(XContentParser parser, String name) throws IOException { + ParsedDateRange aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } + + public static class ParsedBucket extends ParsedRange.ParsedBucket { + + @Override + public Object getFrom() { + return doubleAsDateTime(from); + } + + @Override + public Object getTo() { + return doubleAsDateTime(to); + } + + private static DateTime doubleAsDateTime(Double d) { + if (d == null || Double.isInfinite(d)) { + return null; + } + return new DateTime(d.longValue(), DateTimeZone.UTC); + } + + static ParsedBucket fromXContent(final XContentParser parser, final boolean keyed) throws IOException { + return parseRangeBucketXContent(parser, ParsedBucket::new, keyed); + } + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/ParsedGeoDistance.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/ParsedGeoDistance.java new file mode 100644 index 0000000000000..a926499e9249a --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/ParsedGeoDistance.java @@ -0,0 +1,55 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.range.geodistance; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.bucket.range.ParsedRange; + +import java.io.IOException; + +public class ParsedGeoDistance extends ParsedRange { + + @Override + public String getType() { + return GeoDistanceAggregationBuilder.NAME; + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedGeoDistance.class.getSimpleName(), true, ParsedGeoDistance::new); + static { + declareParsedRangeFields(PARSER, + parser -> ParsedBucket.fromXContent(parser, false), + parser -> ParsedBucket.fromXContent(parser, true)); + } + + public static ParsedGeoDistance fromXContent(XContentParser parser, String name) throws IOException { + ParsedGeoDistance aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } + + public static class ParsedBucket extends ParsedRange.ParsedBucket { + + static ParsedBucket fromXContent(final XContentParser parser, final boolean keyed) throws IOException { + return parseRangeBucketXContent(parser, ParsedBucket::new, keyed); + } + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java index d741240e15804..4cfc87c171c5a 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java @@ -37,6 +37,9 @@ import org.elasticsearch.search.aggregations.bucket.nested.InternalNestedTests; import org.elasticsearch.search.aggregations.bucket.nested.InternalReverseNestedTests; import org.elasticsearch.search.aggregations.bucket.sampler.InternalSamplerTests; +import org.elasticsearch.search.aggregations.bucket.range.InternalRangeTests; +import org.elasticsearch.search.aggregations.bucket.range.date.InternalDateRangeTests; +import org.elasticsearch.search.aggregations.bucket.range.geodistance.InternalGeoDistanceTests; import org.elasticsearch.search.aggregations.bucket.terms.DoubleTermsTests; import org.elasticsearch.search.aggregations.bucket.terms.LongTermsTests; import org.elasticsearch.search.aggregations.bucket.terms.StringTermsTests; @@ -119,6 +122,9 @@ private static List getAggsTests() { aggsTests.add(new InternalFilterTests()); aggsTests.add(new InternalSamplerTests()); aggsTests.add(new InternalGeoHashGridTests()); + aggsTests.add(new InternalRangeTests()); + aggsTests.add(new InternalDateRangeTests()); + aggsTests.add(new InternalGeoDistanceTests()); return Collections.unmodifiableList(aggsTests); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java index b16b7f49d7e02..df977abbe8118 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java @@ -117,7 +117,7 @@ private void assertMultiBucketsAggregation(Aggregation expected, Aggregation act } } - private void assertBucket(MultiBucketsAggregation.Bucket expected, MultiBucketsAggregation.Bucket actual, boolean checkOrder) { + protected void assertBucket(MultiBucketsAggregation.Bucket expected, MultiBucketsAggregation.Bucket actual, boolean checkOrder) { assertTrue(expected instanceof InternalMultiBucketAggregation.InternalBucket); assertTrue(actual instanceof ParsedMultiBucketAggregation.ParsedBucket); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTestCase.java index 1edaea2f8ecf7..1901f4800e0df 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTestCase.java @@ -20,15 +20,17 @@ package org.elasticsearch.search.aggregations.bucket.range; import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.InternalMultiBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import org.elasticsearch.test.InternalAggregationTestCase; import org.junit.Before; import java.util.List; import java.util.Map; import java.util.TreeMap; -public abstract class InternalRangeTestCase extends InternalAggregationTestCase { +public abstract class InternalRangeTestCase extends InternalMultiBucketAggregationTestCase { private boolean keyed; @@ -40,13 +42,17 @@ public void setUp() throws Exception { } @Override - protected T createTestInstance(String name, List pipelineAggregators, Map metaData) { - return createTestInstance(name, pipelineAggregators, metaData, keyed); + protected T createTestInstance(String name, + List pipelineAggregators, + Map metaData, + InternalAggregations aggregations) { + return createTestInstance(name, pipelineAggregators, metaData, aggregations, keyed); } protected abstract T createTestInstance(String name, List pipelineAggregators, Map metaData, + InternalAggregations aggregations, boolean keyed); @Override protected void assertReduced(T reduced, List inputs) { @@ -65,4 +71,20 @@ protected void assertReduced(T reduced, List inputs) { } assertEquals(expectedCounts, actualCounts); } + + @Override + protected void assertBucket(MultiBucketsAggregation.Bucket expected, MultiBucketsAggregation.Bucket actual, boolean checkOrder) { + super.assertBucket(expected, actual, checkOrder); + + assertTrue(expected instanceof InternalRange.Bucket); + assertTrue(actual instanceof ParsedRange.ParsedBucket); + + Range.Bucket expectedRange = (Range.Bucket) expected; + Range.Bucket actualRange = (Range.Bucket) actual; + + assertEquals(expectedRange.getFrom(), actualRange.getFrom()); + assertEquals(expectedRange.getFromAsString(), actualRange.getFromAsString()); + assertEquals(expectedRange.getTo(), actualRange.getTo()); + assertEquals(expectedRange.getToAsString(), actualRange.getToAsString()); + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTests.java index 9264028d0734a..a235471852608 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/InternalRangeTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.junit.Before; @@ -43,7 +44,7 @@ public void setUp() throws Exception { format = randomNumericDocValueFormat(); final int interval = randomFrom(1, 5, 10, 25, 50, 100); - final int numRanges = 1;//randomIntBetween(1, 10); + final int numRanges = randomIntBetween(1, 10); List> listOfRanges = new ArrayList<>(numRanges); for (int i = 0; i < numRanges; i++) { @@ -58,11 +59,23 @@ public void setUp() throws Exception { listOfRanges.add(Tuple.tuple(0.0, max / 2)); listOfRanges.add(Tuple.tuple(max / 3, max / 3 * 2)); } + if (rarely()) { + listOfRanges.add(Tuple.tuple(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)); + } + if (rarely()) { + listOfRanges.add(Tuple.tuple(Double.NEGATIVE_INFINITY, randomDouble())); + } + if (rarely()) { + listOfRanges.add(Tuple.tuple(randomDouble(), Double.POSITIVE_INFINITY)); + } ranges = Collections.unmodifiableList(listOfRanges); } @Override - protected InternalRange createTestInstance(String name, List pipelineAggregators, Map metaData, + protected InternalRange createTestInstance(String name, + List pipelineAggregators, + Map metaData, + InternalAggregations aggregations, boolean keyed) { final List buckets = new ArrayList<>(); for (int i = 0; i < ranges.size(); ++i) { @@ -70,13 +83,18 @@ protected InternalRange createTestInstance(String name, List int docCount = randomIntBetween(0, 1000); double from = range.v1(); double to = range.v2(); - buckets.add( new InternalRange.Bucket("range_" + i, from, to, docCount, InternalAggregations.EMPTY, keyed, format)); + buckets.add(new InternalRange.Bucket("range_" + i, from, to, docCount, aggregations, keyed, format)); } - return new InternalRange<>(name, buckets, format, keyed, pipelineAggregators, Collections.emptyMap()); + return new InternalRange<>(name, buckets, format, keyed, pipelineAggregators, metaData); } @Override protected Writeable.Reader instanceReader() { return InternalRange::new; } + + @Override + protected Class implementationClass() { + return ParsedRange.class; + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/date/InternalDateRangeTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/date/InternalDateRangeTests.java index bbfcdf7463aae..318e5f6b5ad6f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/date/InternalDateRangeTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/date/InternalDateRangeTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.bucket.range.InternalRangeTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.joda.time.DateTime; @@ -78,6 +79,7 @@ public void setUp() throws Exception { protected InternalDateRange createTestInstance(String name, List pipelineAggregators, Map metaData, + InternalAggregations aggregations, boolean keyed) { final List buckets = new ArrayList<>(); for (int i = 0; i < dateRanges.size(); ++i) { @@ -85,7 +87,7 @@ protected InternalDateRange createTestInstance(String name, int docCount = randomIntBetween(0, 1000); double from = range.v1(); double to = range.v2(); - buckets.add( new InternalDateRange.Bucket("range_" + i, from, to, docCount, InternalAggregations.EMPTY, keyed, format)); + buckets.add(new InternalDateRange.Bucket("range_" + i, from, to, docCount, aggregations, keyed, format)); } return new InternalDateRange(name, buckets, format, keyed, pipelineAggregators, metaData); } @@ -94,4 +96,9 @@ protected InternalDateRange createTestInstance(String name, protected Writeable.Reader instanceReader() { return InternalDateRange::new; } + + @Override + protected Class implementationClass() { + return ParsedDateRange.class; + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/InternalGeoDistanceTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/InternalGeoDistanceTests.java index 9dd2a7a67c75b..25c0a9ae3e103 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/InternalGeoDistanceTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/InternalGeoDistanceTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.bucket.range.InternalRangeTestCase; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.junit.Before; @@ -58,6 +59,7 @@ public void setUp() throws Exception { } geoDistanceRanges = Collections.unmodifiableList(listOfRanges); } + @Override protected Writeable.Reader instanceReader() { return InternalGeoDistance::new; @@ -67,6 +69,7 @@ protected Writeable.Reader instanceReader() { protected InternalGeoDistance createTestInstance(String name, List pipelineAggregators, Map metaData, + InternalAggregations aggregations, boolean keyed) { final List buckets = new ArrayList<>(); for (int i = 0; i < geoDistanceRanges.size(); ++i) { @@ -74,8 +77,13 @@ protected InternalGeoDistance createTestInstance(String name, int docCount = randomIntBetween(0, 1000); double from = range.v1(); double to = range.v2(); - buckets.add(new InternalGeoDistance.Bucket("range_" + i, from, to, docCount, InternalAggregations.EMPTY, keyed)); + buckets.add(new InternalGeoDistance.Bucket("range_" + i, from, to, docCount, aggregations, keyed)); } return new InternalGeoDistance(name, buckets, keyed, pipelineAggregators, metaData); } + + @Override + protected Class implementationClass() { + return ParsedGeoDistance.class; + } } diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java index da7ac647c5c54..66dd194e01863 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java @@ -59,6 +59,12 @@ import org.elasticsearch.search.aggregations.bucket.nested.ReverseNestedAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.sampler.InternalSampler; import org.elasticsearch.search.aggregations.bucket.sampler.ParsedSampler; +import org.elasticsearch.search.aggregations.bucket.range.ParsedRange; +import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.range.date.DateRangeAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.range.date.ParsedDateRange; +import org.elasticsearch.search.aggregations.bucket.range.geodistance.GeoDistanceAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.range.geodistance.ParsedGeoDistance; import org.elasticsearch.search.aggregations.bucket.terms.DoubleTerms; import org.elasticsearch.search.aggregations.bucket.terms.LongTerms; import org.elasticsearch.search.aggregations.bucket.terms.ParsedDoubleTerms; @@ -164,6 +170,9 @@ public static List getNamedXContents() { namedXContents.put(FilterAggregationBuilder.NAME, (p, c) -> ParsedFilter.fromXContent(p, (String) c)); namedXContents.put(InternalSampler.NAME, (p, c) -> ParsedSampler.fromXContent(p, (String) c)); namedXContents.put(GeoGridAggregationBuilder.NAME, (p, c) -> ParsedGeoHashGrid.fromXContent(p, (String) c)); + namedXContents.put(RangeAggregationBuilder.NAME, (p, c) -> ParsedRange.fromXContent(p, (String) c)); + namedXContents.put(DateRangeAggregationBuilder.NAME, (p, c) -> ParsedDateRange.fromXContent(p, (String) c)); + namedXContents.put(GeoDistanceAggregationBuilder.NAME, (p, c) -> ParsedGeoDistance.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) From 4291880a297663e20f5ea92ab06d5897a705ecb4 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Fri, 12 May 2017 17:03:26 +0200 Subject: [PATCH 335/619] [Tests] #24638 Fix rest high level client docs build by restoring hasChild and hasParent in the tests --- client/rest-high-level/build.gradle | 2 ++ .../QueryDSLDocumentationTests.java | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/client/rest-high-level/build.gradle b/client/rest-high-level/build.gradle index 44ac53d9d56ac..7c5df5760a2f7 100644 --- a/client/rest-high-level/build.gradle +++ b/client/rest-high-level/build.gradle @@ -29,6 +29,8 @@ dependencies { testCompile "org.elasticsearch.client:test:${version}" testCompile "org.elasticsearch.test:framework:${version}" + // for parent/child testing + testCompile "org.elasticsearch.plugin:parent-join-client:${version}" testCompile "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}" testCompile "junit:junit:${versions.junit}" testCompile "org.hamcrest:hamcrest-all:${versions.hamcrest}" diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java index f48572b047382..269606c9cc201 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java @@ -77,6 +77,8 @@ import static org.elasticsearch.index.query.QueryBuilders.wildcardQuery; import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.exponentialDecayFunction; import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.randomFunction; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasParentQuery; /** * Examples of using the transport client that are imported into the transport client documentation. @@ -214,6 +216,24 @@ public void testGeoShape() throws IOException { } } + public void testHasChild() { + // tag::has_child + hasChildQuery( + "blog_tag", // <1> + termQuery("tag","something"), // <2> + ScoreMode.None); // <3> + // end::has_child + } + + public void testHasParent() { + // tag::has_parent + hasParentQuery( + "blog", // <1> + termQuery("tag","something"), // <2> + false); // <3> + // end::has_parent + } + public void testIds() { // tag::ids idsQuery("my_type", "type2") From e804817ce4ed5c969b38ff428c5ca9d373c0b855 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Fri, 12 May 2017 17:25:34 +0200 Subject: [PATCH 336/619] #24638 Add the parent-join plugin in the prebuilt transport client by default --- .../transport/client/PreBuiltTransportClient.java | 7 +++++-- .../transport/client/PreBuiltTransportClientTests.java | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/client/transport/src/main/java/org/elasticsearch/transport/client/PreBuiltTransportClient.java b/client/transport/src/main/java/org/elasticsearch/transport/client/PreBuiltTransportClient.java index 3233470a253a4..1669373829d21 100644 --- a/client/transport/src/main/java/org/elasticsearch/transport/client/PreBuiltTransportClient.java +++ b/client/transport/src/main/java/org/elasticsearch/transport/client/PreBuiltTransportClient.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.reindex.ReindexPlugin; +import org.elasticsearch.join.ParentJoinPlugin; import org.elasticsearch.percolator.PercolatorPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.script.mustache.MustachePlugin; @@ -41,7 +42,8 @@ * {@link Netty4Plugin}, * {@link ReindexPlugin}, * {@link PercolatorPlugin}, - * and {@link MustachePlugin} + * {@link MustachePlugin}, + * {@link ParentJoinPlugin} * plugins for the client. These plugins are all the required modules for Elasticsearch. */ @SuppressWarnings({"unchecked","varargs"}) @@ -83,7 +85,8 @@ private static void initializeNetty() { Netty4Plugin.class, ReindexPlugin.class, PercolatorPlugin.class, - MustachePlugin.class)); + MustachePlugin.class, + ParentJoinPlugin.class)); /** * Creates a new transport client with pre-installed plugins. diff --git a/client/transport/src/test/java/org/elasticsearch/transport/client/PreBuiltTransportClientTests.java b/client/transport/src/test/java/org/elasticsearch/transport/client/PreBuiltTransportClientTests.java index 161b1d7e89ce4..dbcf3571125de 100644 --- a/client/transport/src/test/java/org/elasticsearch/transport/client/PreBuiltTransportClientTests.java +++ b/client/transport/src/test/java/org/elasticsearch/transport/client/PreBuiltTransportClientTests.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.reindex.ReindexPlugin; +import org.elasticsearch.join.ParentJoinPlugin; import org.elasticsearch.percolator.PercolatorPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.script.mustache.MustachePlugin; @@ -50,7 +51,8 @@ public void testPluginInstalled() { @Test public void testInstallPluginTwice() { - for (Class plugin : Arrays.asList(ReindexPlugin.class, PercolatorPlugin.class, MustachePlugin.class)) { + for (Class plugin : + Arrays.asList(ParentJoinPlugin.class, ReindexPlugin.class, PercolatorPlugin.class, MustachePlugin.class)) { try { new PreBuiltTransportClient(Settings.EMPTY, plugin); fail("exception expected"); From 322a4c18dce4b1438990fe7f92d2d17b165188af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Fri, 12 May 2017 21:11:21 +0200 Subject: [PATCH 337/619] Fix msearch rest test using typed_keys --- .../resources/rest-api-spec/test/msearch/20_typed_keys.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/20_typed_keys.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/20_typed_keys.yaml index 360405fd31780..51a17b7c1e083 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/20_typed_keys.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/20_typed_keys.yaml @@ -107,8 +107,8 @@ setup: - {query: {match_all: {} }, size: 0, aggs: {test_sterms: {terms: {field: name}, aggs: {test_umsignificant_terms: {significant_terms: {field: surname} } } } } } - match: { responses.0.hits.total: 5 } - - match: { responses.0.aggregations.sampler#test_sampler.doc_count : 5 } - - match: { responses.0.aggregations.sampler#test_sampler.sigsterms#test_significant_terms.doc_count : 5 } + - match: { responses.0.aggregations.mapped_sampler#test_sampler.doc_count : 5 } + - match: { responses.0.aggregations.mapped_sampler#test_sampler.sigsterms#test_significant_terms.doc_count : 5 } - match: { responses.1.hits.total: 5 } - match: { responses.1.aggregations.sterms#test_umterms.doc_count_error_upper_bound : 0 } - match: { responses.2.hits.total: 5 } From 458129a85aafb201857e11afc8fa728eaae47e7d Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Fri, 12 May 2017 15:53:51 -0400 Subject: [PATCH 338/619] Upgrade to Netty 4.1.11.Final This commit upgrades the Netty dependency from 4.1.10.Final to 4.1.11.Final. Relates #24652 --- modules/transport-netty4/build.gradle | 14 +++++++------- .../licenses/netty-buffer-4.1.10.Final.jar.sha1 | 1 - .../licenses/netty-buffer-4.1.11.Final.jar.sha1 | 1 + .../licenses/netty-codec-4.1.10.Final.jar.sha1 | 1 - .../licenses/netty-codec-4.1.11.Final.jar.sha1 | 1 + .../netty-codec-http-4.1.10.Final.jar.sha1 | 1 - .../netty-codec-http-4.1.11.Final.jar.sha1 | 1 + .../licenses/netty-common-4.1.10.Final.jar.sha1 | 1 - .../licenses/netty-common-4.1.11.Final.jar.sha1 | 1 + .../licenses/netty-handler-4.1.10.Final.jar.sha1 | 1 - .../licenses/netty-handler-4.1.11.Final.jar.sha1 | 1 + .../licenses/netty-resolver-4.1.10.Final.jar.sha1 | 1 - .../licenses/netty-resolver-4.1.11.Final.jar.sha1 | 1 + .../licenses/netty-transport-4.1.10.Final.jar.sha1 | 1 - .../licenses/netty-transport-4.1.11.Final.jar.sha1 | 1 + .../main/plugin-metadata/plugin-security.policy | 4 ++-- 16 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 modules/transport-netty4/licenses/netty-buffer-4.1.10.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-buffer-4.1.11.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-4.1.10.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-4.1.11.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-http-4.1.10.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-http-4.1.11.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-common-4.1.10.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-common-4.1.11.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-handler-4.1.10.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-handler-4.1.11.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-resolver-4.1.10.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-resolver-4.1.11.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-transport-4.1.10.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-transport-4.1.11.Final.jar.sha1 diff --git a/modules/transport-netty4/build.gradle b/modules/transport-netty4/build.gradle index c35dbe8ff293f..87db7e9307531 100644 --- a/modules/transport-netty4/build.gradle +++ b/modules/transport-netty4/build.gradle @@ -34,13 +34,13 @@ compileTestJava.options.compilerArgs << "-Xlint:-cast,-deprecation,-rawtypes,-tr dependencies { // network stack - compile "io.netty:netty-buffer:4.1.10.Final" - compile "io.netty:netty-codec:4.1.10.Final" - compile "io.netty:netty-codec-http:4.1.10.Final" - compile "io.netty:netty-common:4.1.10.Final" - compile "io.netty:netty-handler:4.1.10.Final" - compile "io.netty:netty-resolver:4.1.10.Final" - compile "io.netty:netty-transport:4.1.10.Final" + compile "io.netty:netty-buffer:4.1.11.Final" + compile "io.netty:netty-codec:4.1.11.Final" + compile "io.netty:netty-codec-http:4.1.11.Final" + compile "io.netty:netty-common:4.1.11.Final" + compile "io.netty:netty-handler:4.1.11.Final" + compile "io.netty:netty-resolver:4.1.11.Final" + compile "io.netty:netty-transport:4.1.11.Final" } dependencyLicenses { diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.10.Final.jar.sha1 deleted file mode 100644 index 147710bd3b438..0000000000000 --- a/modules/transport-netty4/licenses/netty-buffer-4.1.10.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -62b25bb39c22f5a4c267b9ff1d9915f8c0191c3a \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.11.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.11.Final.jar.sha1 new file mode 100644 index 0000000000000..5c7fd45c71b73 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-buffer-4.1.11.Final.jar.sha1 @@ -0,0 +1 @@ +84da342824017dcbeefda0becfef11ce2b5836da \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.10.Final.jar.sha1 deleted file mode 100644 index 81bdd2f885483..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-4.1.10.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1fd4a9d3a8d1ded1e1620ce16ae4afe0b3746c41 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.11.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.11.Final.jar.sha1 new file mode 100644 index 0000000000000..0d5093c08a24f --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-4.1.11.Final.jar.sha1 @@ -0,0 +1 @@ +d9ffe2192b567a4df052f6a36e7b7090b510e0cf \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.10.Final.jar.sha1 deleted file mode 100644 index e25988ddfa3f6..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-http-4.1.10.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -38cbc4d4a0d40dd6f94ff722d76f6f3ea07a6ca6 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.11.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.11.Final.jar.sha1 new file mode 100644 index 0000000000000..ba7787f052d3b --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-http-4.1.11.Final.jar.sha1 @@ -0,0 +1 @@ +3edeb0f08e455e570a55eb56bf64595fcb1a6b15 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.10.Final.jar.sha1 deleted file mode 100644 index 25de3864866cd..0000000000000 --- a/modules/transport-netty4/licenses/netty-common-4.1.10.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -93eba0a663a990b8350c0949870b0db29d4d4f38 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.11.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.11.Final.jar.sha1 new file mode 100644 index 0000000000000..786f535a32255 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-common-4.1.11.Final.jar.sha1 @@ -0,0 +1 @@ +f79a702bc5f275832ae18e33ba3d2a264a4aa728 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.10.Final.jar.sha1 deleted file mode 100644 index 22316debbe78b..0000000000000 --- a/modules/transport-netty4/licenses/netty-handler-4.1.10.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -c984b5d37563f15f10a0de0f4927d4a0dcb675de \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.11.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.11.Final.jar.sha1 new file mode 100644 index 0000000000000..5a27bb52a82b7 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-handler-4.1.11.Final.jar.sha1 @@ -0,0 +1 @@ +6f43aae489b2e4fd7446cd347b077bb058a225d8 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.10.Final.jar.sha1 deleted file mode 100644 index 8c8a275ce0540..0000000000000 --- a/modules/transport-netty4/licenses/netty-resolver-4.1.10.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -dd05aa176779768dde2562283b0df3c39b92767b \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.11.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.11.Final.jar.sha1 new file mode 100644 index 0000000000000..5fdf253a11078 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-resolver-4.1.11.Final.jar.sha1 @@ -0,0 +1 @@ +3310d435f97ef9769dd5659dae3ef762ee3f0f57 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.10.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.10.Final.jar.sha1 deleted file mode 100644 index fca9fad19622e..0000000000000 --- a/modules/transport-netty4/licenses/netty-transport-4.1.10.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -7a2be072e9962c751f90307379c2edb86d0b61a7 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.11.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.11.Final.jar.sha1 new file mode 100644 index 0000000000000..4c2de1a7a1a50 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-transport-4.1.11.Final.jar.sha1 @@ -0,0 +1 @@ +6244fb27cbc24a8d006e9aaaead6b25dcf3aa2e1 \ No newline at end of file diff --git a/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy b/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy index 490e6254cd25b..2fd74d6c762b4 100644 --- a/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy +++ b/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy @@ -17,7 +17,7 @@ * under the License. */ -grant codeBase "${codebase.netty-common-4.1.10.Final.jar}" { +grant codeBase "${codebase.netty-common-4.1.11.Final.jar}" { // for reading the system-wide configuration for the backlog of established sockets permission java.io.FilePermission "/proc/sys/net/core/somaxconn", "read"; @@ -25,7 +25,7 @@ grant codeBase "${codebase.netty-common-4.1.10.Final.jar}" { permission java.net.SocketPermission "*", "accept,connect"; }; -grant codeBase "${codebase.netty-transport-4.1.10.Final.jar}" { +grant codeBase "${codebase.netty-transport-4.1.11.Final.jar}" { // Netty NioEventLoop wants to change this, because of https://bugs.openjdk.java.net/browse/JDK-6427854 // the bug says it only happened rarely, and that its fixed, but apparently it still happens rarely! permission java.util.PropertyPermission "sun.nio.ch.bugLevel", "write"; From 5da940532dd48f12fecc814be290631b552a658c Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Fri, 12 May 2017 16:05:13 -0400 Subject: [PATCH 339/619] Remove Netty logging hack (#24653) Netty removed a logging guarded we added to prevent a scary logging message. We added a hack to work around this. They've added the guard back, so we can remove the hack now. --- .../transport/netty4/Netty4InternalESLogger.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java index 3d509db65617b..91bbe1c1a9b3c 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java @@ -101,11 +101,7 @@ public boolean isInfoEnabled() { @Override public void info(String msg) { - if (!("Your platform does not provide complete low-level API for accessing direct buffers reliably. " + - "Unless explicitly requested, heap buffer will always be preferred to avoid potential system " + - "instability.").equals(msg)) { - logger.info(msg); - } + logger.info(msg); } @Override From 4e21a33689cc494b3040607788cc041f30b2b7de Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Fri, 12 May 2017 16:09:52 -0400 Subject: [PATCH 340/619] Clarify disabling swap in docs Our strong recommendation is disabling swap over any other alternative to avoid the JVM from landing on disk. This commit clarifies the docs in this regard. --- docs/reference/setup/sysconfig/swap.asciidoc | 94 +++++++++++--------- 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/docs/reference/setup/sysconfig/swap.asciidoc b/docs/reference/setup/sysconfig/swap.asciidoc index 78ca7d40beeee..df9ad7babaf10 100644 --- a/docs/reference/setup/sysconfig/swap.asciidoc +++ b/docs/reference/setup/sysconfig/swap.asciidoc @@ -2,32 +2,64 @@ === Disable swapping Most operating systems try to use as much memory as possible for file system -caches and eagerly swap out unused application memory. This can result in -parts of the JVM heap being swapped out to disk. +caches and eagerly swap out unused application memory. This can result in parts +of the JVM heap or even its executable pages being swapped out to disk. -Swapping is very bad for performance and for node stability and should be -avoided at all costs. It can cause garbage collections to last for **minutes** -instead of milliseconds and can cause nodes to respond slowly or even to -disconnect from the cluster. +Swapping is very bad for performance, for node stability, and should be avoided +at all costs. It can cause garbage collections to last for **minutes** instead +of milliseconds and can cause nodes to respond slowly or even to disconnect +from the cluster. In a resilient distributed system, it's more effective to let +the operating system kill the node. -There are three approaches to disabling swapping: +There are three approaches to disabling swapping. The preferred option is to +completely disable swap. If this is not an option, whether or not to prefer +minimizing swappiness versus memory locking is dependent on your environment. + +[[disable-swap-files]] +==== Disable all swap files + +Usually Elasticsearch is the only service running on a box, and its memory usage +is controlled by the JVM options. There should be no need to have swap enabled. + +On Linux systems, you can disable swap temporarily by running: + +[source,sh] +-------------- +sudo swapoff -a +-------------- + +To disable it permanently, you will need to edit the `/etc/fstab` file and +comment out any lines that contain the word `swap`. + +On Windows, the equivalent can be achieved by disabling the paging file entirely +via `System Properties → Advanced → Performance → Advanced → Virtual memory`. + +[[swappiness]] +==== Configure `swappiness` + +Another option available on Linux systems is to ensure that the sysctl value +`vm.swappiness` is set to `1`. This reduces the kernel's tendency to swap and +should not lead to swapping under normal circumstances, while still allowing the +whole system to swap in emergency conditions. [[mlockall]] ==== Enable `bootstrap.memory_lock` -The first option is to use -http://opengroup.org/onlinepubs/007908799/xsh/mlockall.html[mlockall] on Linux/Unix systems, or https://msdn.microsoft.com/en-us/library/windows/desktop/aa366895%28v=vs.85%29.aspx[VirtualLock] on Windows, to -try to lock the process address space into RAM, preventing any Elasticsearch -memory from being swapped out. This can be done, by adding this line -to the `config/elasticsearch.yml` file: +Another option is to use +http://opengroup.org/onlinepubs/007908799/xsh/mlockall.html[mlockall] on +Linux/Unix systems, or +https://msdn.microsoft.com/en-us/library/windows/desktop/aa366895%28v=vs.85%29.aspx[VirtualLock] +on Windows, to try to lock the process address space into RAM, preventing any +Elasticsearch memory from being swapped out. This can be done, by adding this +line to the `config/elasticsearch.yml` file: [source,yaml] -------------- bootstrap.memory_lock: true -------------- -WARNING: `mlockall` might cause the JVM or shell session to exit if it tries -to allocate more memory than is available! +WARNING: `mlockall` might cause the JVM or shell session to exit if it tries to +allocate more memory than is available! After starting Elasticsearch, you can see whether this setting was applied successfully by checking the value of `mlockall` in the output from this @@ -40,11 +72,12 @@ GET _nodes?filter_path=**.mlockall // CONSOLE If you see that `mlockall` is `false`, then it means that the `mlockall` -request has failed. You will also see a line with more information in the -logs with the words `Unable to lock JVM Memory`. +request has failed. You will also see a line with more information in the logs +with the words `Unable to lock JVM Memory`. The most probable reason, on Linux/Unix systems, is that the user running -Elasticsearch doesn't have permission to lock memory. This can be granted as follows: +Elasticsearch doesn't have permission to lock memory. This can be granted as +follows: `.zip` and `.tar.gz`:: @@ -55,13 +88,13 @@ Elasticsearch doesn't have permission to lock memory. This can be granted as fo RPM and Debian:: Set `MAX_LOCKED_MEMORY` to `unlimited` in the - <> (or see below for systems using `systemd`). + <> (or see below for systems using + `systemd`). Systems using `systemd`:: Set `LimitMEMLOCK` to `infinity` in the <>. - Another possible reason why `mlockall` can fail is that the temporary directory (usually `/tmp`) is mounted with the `noexec` option. This can be solved by specifying a new temp directory using the `ES_JAVA_OPTS` environment variable: @@ -73,26 +106,3 @@ export ES_JAVA_OPTS="$ES_JAVA_OPTS -Djava.io.tmpdir=/path/to/temp/dir" -------------- or setting this JVM flag in the jvm.options configuration file. - -[[disable-swap-files]] -==== Disable all swap files - -The second option is to completely disable swap. Usually Elasticsearch -is the only service running on a box, and its memory usage is controlled -by the JVM options. There should be no need to have swap enabled. - -On Linux systems, you can disable swap temporarily -by running: `sudo swapoff -a`. To disable it permanently, you will need -to edit the `/etc/fstab` file and comment out any lines that contain the -word `swap`. - -On Windows, the equivalent can be achieved by disabling the paging file entirely -via `System Properties → Advanced → Performance → Advanced → Virtual memory`. - -[[swappiness]] -==== Configure `swappiness` - -Another option available on Linux systems is to ensure that the sysctl value -`vm.swappiness` is set to `1`. This reduces the kernel's tendency to swap and -should not lead to swapping under normal circumstances, while still allowing -the whole system to swap in emergency conditions. From e6535bc7710df2df8122c8f22c4b5b588ef34d78 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sun, 14 May 2017 17:40:50 -0700 Subject: [PATCH 341/619] Settings: Update settings deprecation from yml to yaml (#24663) This converts the deprecation added for 5.5 from .yml to .yaml. Note that this deprecation has not been released yet. relates #19391 --- .../main/java/org/elasticsearch/bootstrap/Bootstrap.java | 4 ++-- .../java/org/elasticsearch/bootstrap/BootstrapTests.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index d0462efc391bc..1fa2e6ed6cf62 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -416,9 +416,9 @@ private static void checkUnsetAndMaybeExit(String confFileSetting, String settin // pkg private for tests static void checkConfigExtension(String extension) { - if (".yml".equals(extension) || ".json".equals(extension)) { + if (".yaml".equals(extension) || ".json".equals(extension)) { final DeprecationLogger deprecationLogger = new DeprecationLogger(Loggers.getLogger(Bootstrap.class)); - deprecationLogger.deprecated("elasticsearch{} is deprecated; rename your configuration file to elasticsearch.yaml", extension); + deprecationLogger.deprecated("elasticsearch{} is deprecated; rename your configuration file to elasticsearch.yml", extension); } } diff --git a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapTests.java b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapTests.java index b2fab7746fbf7..6506ed8d05689 100644 --- a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapTests.java +++ b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapTests.java @@ -25,9 +25,9 @@ public class BootstrapTests extends ESTestCase { public void testConfigDeprecation() { Bootstrap.checkConfigExtension(".json"); - assertWarnings("elasticsearch.json is deprecated; rename your configuration file to elasticsearch.yaml"); - Bootstrap.checkConfigExtension(".yml"); - assertWarnings("elasticsearch.yml is deprecated; rename your configuration file to elasticsearch.yaml"); - Bootstrap.checkConfigExtension(".yaml"); // no warnings, will be checked in @After + assertWarnings("elasticsearch.json is deprecated; rename your configuration file to elasticsearch.yml"); + Bootstrap.checkConfigExtension(".yaml"); + assertWarnings("elasticsearch.yaml is deprecated; rename your configuration file to elasticsearch.yml"); + Bootstrap.checkConfigExtension(".yml"); // no warnings, will be checked in @After } } From 17f8d2debdf926461f8d669771a2a2c4fcd3c3bc Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sun, 14 May 2017 17:45:04 -0700 Subject: [PATCH 342/619] Settings: Remove support for yaml and json config files (#24664) This commit removes the deprecated support for .yaml and .json files. If the files still exist, the node will fail to start, indicating the file must be converted or renamed. closes #19391 --- .../elasticsearch/bootstrap/Bootstrap.java | 9 ----- .../org/elasticsearch/env/Environment.java | 13 ------- .../node/InternalSettingsPreparer.java | 35 ++++++++----------- .../bootstrap/BootstrapTests.java | 33 ----------------- .../node/InternalSettingsPreparerTests.java | 30 +++++----------- .../resources/config/elasticsearch.properties | 2 -- .../{elasticsearch.yaml => elasticsearch.yml} | 0 7 files changed, 23 insertions(+), 99 deletions(-) delete mode 100644 core/src/test/java/org/elasticsearch/bootstrap/BootstrapTests.java delete mode 100644 core/src/test/resources/config/elasticsearch.properties rename core/src/test/resources/config/{elasticsearch.yaml => elasticsearch.yml} (100%) diff --git a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index 1fa2e6ed6cf62..74fc600d627c5 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -298,7 +298,6 @@ static void init( throw new BootstrapException(e); } checkForCustomConfFile(); - checkConfigExtension(environment.configExtension()); if (environment.pidFile() != null) { try { @@ -414,14 +413,6 @@ private static void checkUnsetAndMaybeExit(String confFileSetting, String settin } } - // pkg private for tests - static void checkConfigExtension(String extension) { - if (".yaml".equals(extension) || ".json".equals(extension)) { - final DeprecationLogger deprecationLogger = new DeprecationLogger(Loggers.getLogger(Bootstrap.class)); - deprecationLogger.deprecated("elasticsearch{} is deprecated; rename your configuration file to elasticsearch.yml", extension); - } - } - @SuppressForbidden(reason = "Allowed to exit explicitly in bootstrap phase") private static void exit(int status) { System.exit(status); diff --git a/core/src/main/java/org/elasticsearch/env/Environment.java b/core/src/main/java/org/elasticsearch/env/Environment.java index df859ab7deb1f..7ac442d716549 100644 --- a/core/src/main/java/org/elasticsearch/env/Environment.java +++ b/core/src/main/java/org/elasticsearch/env/Environment.java @@ -71,8 +71,6 @@ public class Environment { private final Settings settings; - private final String configExtension; - private final Path[] dataFiles; private final Path[] dataWithClusterFiles; @@ -104,12 +102,6 @@ public class Environment { private final Path tmpFile = PathUtils.get(System.getProperty("java.io.tmpdir")); public Environment(Settings settings) { - this(settings, null); - } - - // Note: Do not use this ctor, it is for correct deprecation logging in 5.5 and will be removed - public Environment(Settings settings, String configExtension) { - this.configExtension = configExtension; final Path homeFile; if (PATH_HOME_SETTING.exists(settings)) { homeFile = PathUtils.get(cleanPath(PATH_HOME_SETTING.get(settings))); @@ -281,11 +273,6 @@ public URL resolveRepoURL(URL url) { } } - /** Return then extension of the config file that was loaded, or*/ - public String configExtension() { - return configExtension; - } - // TODO: rename all these "file" methods to "dir" /** * The config directory. diff --git a/core/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java b/core/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java index b8862fb20e570..32ad3e933293f 100644 --- a/core/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java +++ b/core/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java @@ -86,27 +86,23 @@ public static Environment prepareEnvironment(Settings input, Terminal terminal, initializeSettings(output, input, properties); Environment environment = new Environment(output.build()); + if (Files.exists(environment.configFile().resolve("elasticsearch.yaml"))) { + throw new SettingsException("elasticsearch.yaml was deprecated in 5.5.0 and must be renamed to elasticsearch.yml"); + } + + if (Files.exists(environment.configFile().resolve("elasticsearch.json"))) { + throw new SettingsException("elasticsearch.json was deprecated in 5.5.0 and must be converted to elasticsearch.yml"); + } + output = Settings.builder(); // start with a fresh output - boolean settingsFileFound = false; - Set foundSuffixes = new HashSet<>(); - for (String allowedSuffix : ALLOWED_SUFFIXES) { - Path path = environment.configFile().resolve("elasticsearch" + allowedSuffix); - if (Files.exists(path)) { - if (!settingsFileFound) { - try { - output.loadFromPath(path); - } catch (IOException e) { - throw new SettingsException("Failed to load settings from " + path.toString(), e); - } - } - settingsFileFound = true; - foundSuffixes.add(allowedSuffix); + Path path = environment.configFile().resolve("elasticsearch.yml"); + if (Files.exists(path)) { + try { + output.loadFromPath(path); + } catch (IOException e) { + throw new SettingsException("Failed to load settings from " + path.toString(), e); } } - if (foundSuffixes.size() > 1) { - throw new SettingsException("multiple settings files found with suffixes: " - + Strings.collectionToDelimitedString(foundSuffixes, ",")); - } // re-initialize settings now that the config file has been loaded initializeSettings(output, input, properties); @@ -116,8 +112,7 @@ public static Environment prepareEnvironment(Settings input, Terminal terminal, // we put back the path.logs so we can use it in the logging configuration file output.put(Environment.PATH_LOGS_SETTING.getKey(), cleanPath(environment.logsFile().toAbsolutePath().toString())); - String configExtension = foundSuffixes.isEmpty() ? null : foundSuffixes.iterator().next(); - return new Environment(output.build(), configExtension); + return new Environment(output.build()); } /** diff --git a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapTests.java b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapTests.java deleted file mode 100644 index 6506ed8d05689..0000000000000 --- a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapTests.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.bootstrap; - -import org.elasticsearch.test.ESTestCase; - -public class BootstrapTests extends ESTestCase { - - public void testConfigDeprecation() { - Bootstrap.checkConfigExtension(".json"); - assertWarnings("elasticsearch.json is deprecated; rename your configuration file to elasticsearch.yml"); - Bootstrap.checkConfigExtension(".yaml"); - assertWarnings("elasticsearch.yaml is deprecated; rename your configuration file to elasticsearch.yml"); - Bootstrap.checkConfigExtension(".yml"); // no warnings, will be checked in @After - } -} diff --git a/core/src/test/java/org/elasticsearch/node/InternalSettingsPreparerTests.java b/core/src/test/java/org/elasticsearch/node/InternalSettingsPreparerTests.java index 5db397ab16d93..bf23da1868db7 100644 --- a/core/src/test/java/org/elasticsearch/node/InternalSettingsPreparerTests.java +++ b/core/src/test/java/org/elasticsearch/node/InternalSettingsPreparerTests.java @@ -150,38 +150,24 @@ public void testGarbageIsNotSwallowed() throws IOException { } } - public void testMultipleSettingsFileNotAllowed() throws IOException { - InputStream yaml = getClass().getResourceAsStream("/config/elasticsearch.yaml"); - InputStream json = getClass().getResourceAsStream("/config/elasticsearch.json"); + public void testYamlNotAllowed() throws IOException { + InputStream yaml = getClass().getResourceAsStream("/config/elasticsearch.yml"); Path config = homeDir.resolve("config"); Files.createDirectory(config); Files.copy(yaml, config.resolve("elasticsearch.yaml")); - Files.copy(json, config.resolve("elasticsearch.json")); - SettingsException e = expectThrows(SettingsException.class, () -> - InternalSettingsPreparer.prepareEnvironment(Settings.builder().put(baseEnvSettings).build(), null) - ); - assertTrue(e.getMessage(), e.getMessage().contains("multiple settings files found with suffixes")); - assertTrue(e.getMessage(), e.getMessage().contains(".yaml")); - assertTrue(e.getMessage(), e.getMessage().contains(".json")); - } - - public void testYmlExtension() throws IOException { - InputStream yaml = getClass().getResourceAsStream("/config/elasticsearch.yaml"); - Path config = homeDir.resolve("config"); - Files.createDirectory(config); - Files.copy(yaml, config.resolve("elasticsearch.yml")); - Environment env = InternalSettingsPreparer.prepareEnvironment(Settings.builder().put(baseEnvSettings).build(), null); - assertEquals(".yml", env.configExtension()); + InternalSettingsPreparer.prepareEnvironment(Settings.builder().put(baseEnvSettings).build(), null)); + assertEquals("elasticsearch.yaml was deprecated in 5.5.0 and must be renamed to elasticsearch.yml", e.getMessage()); } - public void testJsonExtension() throws IOException { + public void testJsonNotAllowed() throws IOException { InputStream yaml = getClass().getResourceAsStream("/config/elasticsearch.json"); Path config = homeDir.resolve("config"); Files.createDirectory(config); Files.copy(yaml, config.resolve("elasticsearch.json")); - Environment env = InternalSettingsPreparer.prepareEnvironment(Settings.builder().put(baseEnvSettings).build(), null); - assertEquals(".json", env.configExtension()); + SettingsException e = expectThrows(SettingsException.class, () -> + InternalSettingsPreparer.prepareEnvironment(Settings.builder().put(baseEnvSettings).build(), null)); + assertEquals("elasticsearch.json was deprecated in 5.5.0 and must be converted to elasticsearch.yml", e.getMessage()); } public void testSecureSettings() { diff --git a/core/src/test/resources/config/elasticsearch.properties b/core/src/test/resources/config/elasticsearch.properties deleted file mode 100644 index d3f822cafb555..0000000000000 --- a/core/src/test/resources/config/elasticsearch.properties +++ /dev/null @@ -1,2 +0,0 @@ - -properties.config.exists: true diff --git a/core/src/test/resources/config/elasticsearch.yaml b/core/src/test/resources/config/elasticsearch.yml similarity index 100% rename from core/src/test/resources/config/elasticsearch.yaml rename to core/src/test/resources/config/elasticsearch.yml From f185b69e0407bea336b4964d2a649658eb78e7eb Mon Sep 17 00:00:00 2001 From: Koen De Groote Date: Mon, 15 May 2017 09:35:48 +0200 Subject: [PATCH 343/619] Replace manual copying an array or collection with static methods calls (#24657) --- .../org/elasticsearch/common/inject/ModulesBuilder.java | 5 ++--- .../common/lucene/search/MultiPhrasePrefixQuery.java | 4 +--- .../main/java/org/elasticsearch/index/IndexWarmer.java | 5 ++--- .../indices/fielddata/cache/IndicesFieldDataCache.java | 9 +++------ .../search/suggest/phrase/DirectCandidateGenerator.java | 5 ++--- .../org/elasticsearch/painless/node/PSubDefCall.java | 6 ++---- 6 files changed, 12 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/common/inject/ModulesBuilder.java b/core/src/main/java/org/elasticsearch/common/inject/ModulesBuilder.java index 3321b75f4e50b..6928033c69a1f 100644 --- a/core/src/main/java/org/elasticsearch/common/inject/ModulesBuilder.java +++ b/core/src/main/java/org/elasticsearch/common/inject/ModulesBuilder.java @@ -20,6 +20,7 @@ package org.elasticsearch.common.inject; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -28,9 +29,7 @@ public class ModulesBuilder implements Iterable { private final List modules = new ArrayList<>(); public ModulesBuilder add(Module... newModules) { - for (Module module : newModules) { - modules.add(module); - } + Collections.addAll(modules, newModules); return this; } diff --git a/core/src/main/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQuery.java b/core/src/main/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQuery.java index a76428e829afd..dbfc1f0af11b6 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQuery.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQuery.java @@ -124,9 +124,7 @@ public Term[][] getTerms() { Term[][] terms = new Term[termArrays.size()][]; for (int i = 0; i < termArrays.size(); i++) { terms[i] = new Term[termArrays.get(i).length]; - for (int j = 0; j < termArrays.get(i).length; j++) { - terms[i][j] = termArrays.get(i)[j]; - } + System.arraycopy(termArrays.get(i), 0, terms[i], 0, termArrays.get(i).length); } return terms; } diff --git a/core/src/main/java/org/elasticsearch/index/IndexWarmer.java b/core/src/main/java/org/elasticsearch/index/IndexWarmer.java index fdaad19e528aa..e177ca668f472 100644 --- a/core/src/main/java/org/elasticsearch/index/IndexWarmer.java +++ b/core/src/main/java/org/elasticsearch/index/IndexWarmer.java @@ -54,9 +54,8 @@ public final class IndexWarmer extends AbstractComponent { ArrayList list = new ArrayList<>(); final Executor executor = threadPool.executor(ThreadPool.Names.WARMER); list.add(new FieldDataWarmer(executor)); - for (Listener listener : listeners) { - list.add(listener); - } + + Collections.addAll(list, listeners); this.listeners = Collections.unmodifiableList(list); } diff --git a/core/src/main/java/org/elasticsearch/indices/fielddata/cache/IndicesFieldDataCache.java b/core/src/main/java/org/elasticsearch/indices/fielddata/cache/IndicesFieldDataCache.java index fcd925c2585b2..ff7c3009dcf4e 100644 --- a/core/src/main/java/org/elasticsearch/indices/fielddata/cache/IndicesFieldDataCache.java +++ b/core/src/main/java/org/elasticsearch/indices/fielddata/cache/IndicesFieldDataCache.java @@ -19,6 +19,7 @@ package org.elasticsearch.indices.fielddata.cache; +import java.util.Collections; import org.apache.logging.log4j.Logger; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; @@ -134,9 +135,7 @@ public > FD load(fina //noinspection unchecked final Accountable accountable = cache.computeIfAbsent(key, k -> { cacheHelper.addClosedListener(IndexFieldCache.this); - for (Listener listener : this.listeners) { - k.listeners.add(listener); - } + Collections.addAll(k.listeners, this.listeners); final AtomicFieldData fieldData = indexFieldData.loadDirect(context); for (Listener listener : k.listeners) { try { @@ -162,9 +161,7 @@ public > IFD l //noinspection unchecked final Accountable accountable = cache.computeIfAbsent(key, k -> { ElasticsearchDirectoryReader.addReaderCloseListener(indexReader, IndexFieldCache.this); - for (Listener listener : this.listeners) { - k.listeners.add(listener); - } + Collections.addAll(k.listeners, this.listeners); final Accountable ifd = (Accountable) indexFieldData.localGlobalDirect(indexReader); for (Listener listener : k.listeners) { try { diff --git a/core/src/main/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGenerator.java b/core/src/main/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGenerator.java index 24f2647167e22..b874c3aeca311 100644 --- a/core/src/main/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGenerator.java +++ b/core/src/main/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGenerator.java @@ -229,9 +229,8 @@ public void addCandidates(List candidates) { // Merge new candidates into existing ones, // deduping: final Set set = new HashSet<>(candidates); - for (int i = 0; i < this.candidates.length; i++) { - set.add(this.candidates[i]); - } + Collections.addAll(set, this.candidates); + this.candidates = set.toArray(new Candidate[set.size()]); // Sort strongest to weakest: Arrays.sort(this.candidates, Collections.reverseOrder()); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefCall.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefCall.java index d159834bd26bc..2d0734915af8b 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefCall.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefCall.java @@ -19,6 +19,7 @@ package org.elasticsearch.painless.node; +import java.util.Collections; import org.elasticsearch.painless.DefBootstrap; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Globals; @@ -97,10 +98,7 @@ void write(MethodWriter writer, Globals globals) { if (argument instanceof ILambda) { ILambda lambda = (ILambda) argument; - - for (Type capture : lambda.getCaptures()) { - parameterTypes.add(capture); - } + Collections.addAll(parameterTypes, lambda.getCaptures()); } argument.write(writer, globals); From bd364c56a8a5b673c7fc4dfdf98df4b9c545b790 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Mon, 15 May 2017 11:01:35 +0200 Subject: [PATCH 344/619] Mute packagingTest on CentOS 6 Relates #24645 --- qa/vagrant/build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qa/vagrant/build.gradle b/qa/vagrant/build.gradle index b30ea329f1028..aa118f1075b04 100644 --- a/qa/vagrant/build.gradle +++ b/qa/vagrant/build.gradle @@ -27,3 +27,5 @@ dependencies { } } } + +tasks."vagrantCentos6#packagingTest".onlyIf { false } // fails, see https://github.com/elastic/elasticsearch/issues/24645 From fdb6cd8088cb00ff09552c60f42d44c623490fd4 Mon Sep 17 00:00:00 2001 From: Kunal Kapoor Date: Mon, 15 May 2017 14:56:51 +0530 Subject: [PATCH 345/619] Deprecate use of + in index names (#24585) Use of '+' in index names is implicit. There is no need to support it. This commit deprecates support for it. Closes #24515 --- .../metadata/IndexNameExpressionResolver.java | 10 ++++++- .../IndexNameExpressionResolverTests.java | 27 +++++++++++++++---- .../WildcardExpressionResolverTests.java | 25 +++++++++++++---- 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java b/core/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java index d4c6ec587db78..711d685c1d668 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java @@ -29,6 +29,8 @@ import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.joda.DateMathParser; import org.elasticsearch.common.joda.FormatDateTimeFormatter; +import org.elasticsearch.common.logging.DeprecationLogger; +import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.Index; @@ -55,6 +57,8 @@ public class IndexNameExpressionResolver extends AbstractComponent { private final List expressionResolvers; private final DateMathExpressionResolver dateMathExpressionResolver; + private static final DeprecationLogger DEPRECATION_LOGGER = + new DeprecationLogger(Loggers.getLogger(IndexNameExpressionResolver.class)); public IndexNameExpressionResolver(Settings settings) { super(settings); @@ -159,7 +163,6 @@ Index[] concreteIndices(Context context, String... indexExpressions) { if (indexExpressions.length == 1) { failNoIndices = options.allowNoIndices() == false; } - List expressions = Arrays.asList(indexExpressions); for (ExpressionResolver expressionResolver : expressionResolvers) { expressions = expressionResolver.resolve(context, expressions); @@ -588,6 +591,7 @@ public List resolve(Context context, List expressions) { private Set innerResolve(Context context, List expressions, IndicesOptions options, MetaData metaData) { Set result = null; boolean wildcardSeen = false; + boolean plusSeen = false; for (int i = 0; i < expressions.size(); i++) { String expression = expressions.get(i); if (aliasOrIndexExists(metaData, expression)) { @@ -602,6 +606,7 @@ private Set innerResolve(Context context, List expressions, Indi boolean add = true; if (expression.charAt(0) == '+') { // if its the first, add empty result set + plusSeen = true; if (i == 0) { result = new HashSet<>(); } @@ -649,6 +654,9 @@ private Set innerResolve(Context context, List expressions, Indi wildcardSeen = true; } } + if (plusSeen) { + DEPRECATION_LOGGER.deprecated("support for '+' as part of index expressions is deprecated"); + } return result; } diff --git a/core/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java b/core/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java index 7d3ca04e5a849..2143e5e67d47c 100644 --- a/core/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java @@ -33,7 +33,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; -import java.util.function.Predicate; import static org.elasticsearch.common.util.set.Sets.newHashSet; import static org.hamcrest.Matchers.arrayContaining; @@ -621,7 +620,7 @@ public void testConcreteIndicesWildcardWithNegation() { assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, "test*", "-testXXX")), equalTo(newHashSet("testYYX", "testXYY", "testYYY", "testXXY"))); - assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, "+testXXX", "+testXXY", "+testYYY", "-testYYY")), + assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, "testXXX", "testXXY", "testYYY", "-testYYY")), equalTo(newHashSet("testXXX", "testXXY", "testYYY", "-testYYY"))); assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, "testYYY", "testYYX", "testX*", "-testXXX")), @@ -637,7 +636,7 @@ public void testConcreteIndicesWildcardWithNegation() { equalTo(newHashSet("-testXYZ", "-testXZZ", "-testYYY"))); assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(state, IndicesOptions.lenientExpandOpen(), - "+testXXX", "+testXXY", "+testXYY", "-testXXY")), + "testXXX", "testXXY", "testXYY", "-testXXY")), equalTo(newHashSet("testXXX", "testXYY", "testXXY"))); indexNames = indexNameExpressionResolver.concreteIndexNames(state, IndicesOptions.lenientExpandOpen(), "*", "-*"); @@ -817,7 +816,7 @@ public void testIsPatternMatchingAllIndicesNonMatchingTrailingWildcard() throws } public void testIsPatternMatchingAllIndicesMatchingSingleExclusion() throws Exception { - String[] indicesOrAliases = new String[]{"-index1", "+index1"}; + String[] indicesOrAliases = new String[]{"-index1", "index1"}; String[] concreteIndices = new String[]{"index1", "index2", "index3"}; MetaData metaData = metaDataBuilder(concreteIndices); assertThat(indexNameExpressionResolver.isPatternMatchingAllIndices(metaData, indicesOrAliases, concreteIndices), equalTo(true)); @@ -832,7 +831,7 @@ public void testIsPatternMatchingAllIndicesNonMatchingSingleExclusion() throws E } public void testIsPatternMatchingAllIndicesMatchingTrailingWildcardAndExclusion() throws Exception { - String[] indicesOrAliases = new String[]{"index*", "-index1", "+index1"}; + String[] indicesOrAliases = new String[]{"index*", "-index1", "index1"}; String[] concreteIndices = new String[]{"index1", "index2", "index3"}; MetaData metaData = metaDataBuilder(concreteIndices); assertThat(indexNameExpressionResolver.isPatternMatchingAllIndices(metaData, indicesOrAliases, concreteIndices), equalTo(true)); @@ -970,4 +969,22 @@ public void testIndexAliases() { Arrays.sort(strings); assertArrayEquals(new String[] {"test-alias-0", "test-alias-1", "test-alias-non-filtering"}, strings); } + + public void testConcreteIndicesForDeprecatedPattern() { + MetaData.Builder mdBuilder = MetaData.builder() + .put(indexBuilder("testXXX").state(State.OPEN)) + .put(indexBuilder("testXXY").state(State.OPEN)) + .put(indexBuilder("testYYY").state(State.OPEN)); + ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build(); + + IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, + IndicesOptions.fromOptions(true, true, true, true)); + assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, "+testX*")), + equalTo(newHashSet("testXXX", "testXXY"))); + assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, "+testXXX", "+testXXY", "+testYYY", "-testYYY")), + equalTo(newHashSet("testXXX", "testXXY", "testYYY"))); + assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, "+testXX*", "+testY*")), + equalTo(newHashSet("testXXX", "testXXY", "testYYY"))); + assertWarnings("support for '+' as part of index expressions is deprecated"); + } } diff --git a/core/src/test/java/org/elasticsearch/cluster/metadata/WildcardExpressionResolverTests.java b/core/src/test/java/org/elasticsearch/cluster/metadata/WildcardExpressionResolverTests.java index bac9a68134106..2778525f7dad9 100644 --- a/core/src/test/java/org/elasticsearch/cluster/metadata/WildcardExpressionResolverTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/metadata/WildcardExpressionResolverTests.java @@ -49,9 +49,9 @@ public void testConvertWildcardsJustIndicesTests() { assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testX*", "kuku"))), equalTo(newHashSet("testXXX", "testXYY", "kuku"))); assertThat(newHashSet(resolver.resolve(context, Arrays.asList("*"))), equalTo(newHashSet("testXXX", "testXYY", "testYYY", "kuku"))); assertThat(newHashSet(resolver.resolve(context, Arrays.asList("*", "-kuku"))), equalTo(newHashSet("testXXX", "testXYY", "testYYY"))); - assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testXXX", "+testYYY"))), equalTo(newHashSet("testXXX", "testYYY"))); + assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testXXX", "testYYY"))), equalTo(newHashSet("testXXX", "testYYY"))); assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testXXX", "-testXXX"))), equalTo(newHashSet("testXXX", "-testXXX"))); - assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testXXX", "+testY*"))), equalTo(newHashSet("testXXX", "testYYY"))); + assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testXXX", "testY*"))), equalTo(newHashSet("testXXX", "testYYY"))); assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testXXX", "-testX*"))), equalTo(newHashSet("testXXX"))); } @@ -67,9 +67,9 @@ public void testConvertWildcardsTests() { IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen()); assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testYY*", "alias*"))), equalTo(newHashSet("testXXX", "testXYY", "testYYY"))); assertThat(newHashSet(resolver.resolve(context, Arrays.asList("-kuku"))), equalTo(newHashSet("-kuku"))); - assertThat(newHashSet(resolver.resolve(context, Arrays.asList("+test*", "-testYYY"))), equalTo(newHashSet("testXXX", "testXYY"))); - assertThat(newHashSet(resolver.resolve(context, Arrays.asList("+testX*", "+testYYY"))), equalTo(newHashSet("testXXX", "testXYY", "testYYY"))); - assertThat(newHashSet(resolver.resolve(context, Arrays.asList("+testYYY", "+testX*"))), equalTo(newHashSet("testXXX", "testXYY", "testYYY"))); + assertThat(newHashSet(resolver.resolve(context, Arrays.asList("test*", "-testYYY"))), equalTo(newHashSet("testXXX", "testXYY"))); + assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testX*", "testYYY"))), equalTo(newHashSet("testXXX", "testXYY", "testYYY"))); + assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testYYY", "testX*"))), equalTo(newHashSet("testXXX", "testXYY", "testYYY"))); } public void testConvertWildcardsOpenClosedIndicesTests() { @@ -129,4 +129,19 @@ private IndexMetaData.Builder indexBuilder(String index) { return IndexMetaData.builder(index).settings(settings(Version.CURRENT).put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)); } + public void testForDeprecatedPlusPattern() { + MetaData.Builder mdBuilder = MetaData.builder() + .put(indexBuilder("testXXX").state(IndexMetaData.State.OPEN)) + .put(indexBuilder("testXYY").state(IndexMetaData.State.OPEN)) + .put(indexBuilder("testYYY").state(IndexMetaData.State.OPEN)); + ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build(); + IndexNameExpressionResolver.WildcardExpressionResolver resolver = new IndexNameExpressionResolver.WildcardExpressionResolver(); + + IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, true, true)); + assertThat(newHashSet(resolver.resolve(context, Arrays.asList("+testX*", "-testYYY"))), equalTo(newHashSet("testXXX", "testXYY"))); + assertThat(newHashSet(resolver.resolve(context, Arrays.asList("+testYYY", "+testXY*"))), equalTo(newHashSet("testYYY", "testXYY"))); + assertThat(newHashSet(resolver.resolve(context, Arrays.asList("testYYY", "+testXX*"))), equalTo(newHashSet("testXXX", "testYYY"))); + assertWarnings("support for '+' as part of index expressions is deprecated"); + } + } From bb59ee51b07e61a19d8b359cf365fb386c8c4b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Mon, 15 May 2017 11:49:47 +0200 Subject: [PATCH 346/619] Revert changing the InternalSampler type constant (#24667) --- .../aggregations/bucket/sampler/InternalSampler.java | 4 +++- .../aggregations/bucket/sampler/ParsedSampler.java | 2 +- .../rest-api-spec/test/msearch/20_typed_keys.yaml | 4 ++-- .../test/InternalAggregationTestCase.java | 11 +++++------ 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSampler.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSampler.java index 5d7e19ccad511..1a04133e82bfc 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSampler.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/InternalSampler.java @@ -29,6 +29,8 @@ public class InternalSampler extends InternalSingleBucketAggregation implements Sampler { public static final String NAME = "mapped_sampler"; + // InternalSampler and UnmappedSampler share the same parser name, so we use this when identifying the aggregation type + public static final String PARSER_NAME = "sampler"; InternalSampler(String name, long docCount, InternalAggregations subAggregations, List pipelineAggregators, Map metaData) { @@ -49,7 +51,7 @@ public String getWriteableName() { @Override public String getType() { - return NAME; + return PARSER_NAME; } @Override diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/ParsedSampler.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/ParsedSampler.java index 5e1c4d77b79cc..3d5e946beadfc 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/ParsedSampler.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/ParsedSampler.java @@ -27,7 +27,7 @@ public class ParsedSampler extends ParsedSingleBucketAggregation implements Samp @Override public String getType() { - return InternalSampler.NAME; + return InternalSampler.PARSER_NAME; } public static ParsedSampler fromXContent(XContentParser parser, final String name) throws IOException { diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/20_typed_keys.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/20_typed_keys.yaml index 51a17b7c1e083..360405fd31780 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/20_typed_keys.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/20_typed_keys.yaml @@ -107,8 +107,8 @@ setup: - {query: {match_all: {} }, size: 0, aggs: {test_sterms: {terms: {field: name}, aggs: {test_umsignificant_terms: {significant_terms: {field: surname} } } } } } - match: { responses.0.hits.total: 5 } - - match: { responses.0.aggregations.mapped_sampler#test_sampler.doc_count : 5 } - - match: { responses.0.aggregations.mapped_sampler#test_sampler.sigsterms#test_significant_terms.doc_count : 5 } + - match: { responses.0.aggregations.sampler#test_sampler.doc_count : 5 } + - match: { responses.0.aggregations.sampler#test_sampler.sigsterms#test_significant_terms.doc_count : 5 } - match: { responses.1.hits.total: 5 } - match: { responses.1.aggregations.sterms#test_umterms.doc_count_error_upper_bound : 0 } - match: { responses.2.hits.total: 5 } diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java index 66dd194e01863..7646a84ee1d8f 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java @@ -19,7 +19,6 @@ package org.elasticsearch.test; -import com.carrotsearch.randomizedtesting.annotations.Repeat; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -43,10 +42,10 @@ import org.elasticsearch.search.aggregations.bucket.children.ParsedChildren; import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter; -import org.elasticsearch.search.aggregations.bucket.global.GlobalAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.global.ParsedGlobal; import org.elasticsearch.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.geogrid.ParsedGeoHashGrid; +import org.elasticsearch.search.aggregations.bucket.global.GlobalAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.global.ParsedGlobal; import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram; @@ -57,14 +56,14 @@ import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested; import org.elasticsearch.search.aggregations.bucket.nested.ParsedReverseNested; import org.elasticsearch.search.aggregations.bucket.nested.ReverseNestedAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.sampler.InternalSampler; -import org.elasticsearch.search.aggregations.bucket.sampler.ParsedSampler; import org.elasticsearch.search.aggregations.bucket.range.ParsedRange; import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.range.date.DateRangeAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.range.date.ParsedDateRange; import org.elasticsearch.search.aggregations.bucket.range.geodistance.GeoDistanceAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.range.geodistance.ParsedGeoDistance; +import org.elasticsearch.search.aggregations.bucket.sampler.InternalSampler; +import org.elasticsearch.search.aggregations.bucket.sampler.ParsedSampler; import org.elasticsearch.search.aggregations.bucket.terms.DoubleTerms; import org.elasticsearch.search.aggregations.bucket.terms.LongTerms; import org.elasticsearch.search.aggregations.bucket.terms.ParsedDoubleTerms; @@ -168,7 +167,7 @@ public static List getNamedXContents() { namedXContents.put(ChildrenAggregationBuilder.NAME, (p, c) -> ParsedChildren.fromXContent(p, (String) c)); namedXContents.put(GlobalAggregationBuilder.NAME, (p, c) -> ParsedGlobal.fromXContent(p, (String) c)); namedXContents.put(FilterAggregationBuilder.NAME, (p, c) -> ParsedFilter.fromXContent(p, (String) c)); - namedXContents.put(InternalSampler.NAME, (p, c) -> ParsedSampler.fromXContent(p, (String) c)); + namedXContents.put(InternalSampler.PARSER_NAME, (p, c) -> ParsedSampler.fromXContent(p, (String) c)); namedXContents.put(GeoGridAggregationBuilder.NAME, (p, c) -> ParsedGeoHashGrid.fromXContent(p, (String) c)); namedXContents.put(RangeAggregationBuilder.NAME, (p, c) -> ParsedRange.fromXContent(p, (String) c)); namedXContents.put(DateRangeAggregationBuilder.NAME, (p, c) -> ParsedDateRange.fromXContent(p, (String) c)); From bd5aee8cfa92dbd15980ce004710c7b9222fb8f2 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 15 May 2017 07:47:42 -0400 Subject: [PATCH 347/619] Split disruption test suite The disruption tests sit in a single test suite which causes these tests to be single-threaded. We can split this test suite into multiple suites (logically, of course) enabling them to be run in parallel reducing the total run time of all integration tests in core. This commit splits the discovery with service disruptions test suite into three suites - master disruptions - discovery disruptions - cluster disruptions The last one could probably be better named, it is meant to represent performing actions in the cluster (indexing, failing a shard, etc.) while a disruption is taking place. Relates #24662 --- .../resources/checkstyle_suppressions.xml | 1 - .../discovery/AbstractDisruptionTestCase.java | 300 ++++ .../discovery/ClusterDisruptionIT.java | 452 ++++++ .../discovery/DiscoveryDisruptionIT.java | 320 ++++ .../DiscoveryWithServiceDisruptionsIT.java | 1377 ----------------- .../discovery/MasterDisruptionIT.java | 466 ++++++ .../test/disruption/NetworkDisruption.java | 2 + .../test/disruption/NetworkDisruptionIT.java | 5 +- 8 files changed, 1542 insertions(+), 1381 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/discovery/AbstractDisruptionTestCase.java create mode 100644 core/src/test/java/org/elasticsearch/discovery/ClusterDisruptionIT.java create mode 100644 core/src/test/java/org/elasticsearch/discovery/DiscoveryDisruptionIT.java delete mode 100644 core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java create mode 100644 core/src/test/java/org/elasticsearch/discovery/MasterDisruptionIT.java diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 4acd927fff1d9..491659031e93e 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -554,7 +554,6 @@ - diff --git a/core/src/test/java/org/elasticsearch/discovery/AbstractDisruptionTestCase.java b/core/src/test/java/org/elasticsearch/discovery/AbstractDisruptionTestCase.java new file mode 100644 index 0000000000000..f1b7415c67953 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/discovery/AbstractDisruptionTestCase.java @@ -0,0 +1,300 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.discovery; + +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.block.ClusterBlock; +import org.elasticsearch.cluster.block.ClusterBlockLevel; +import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.discovery.zen.ElectMasterService; +import org.elasticsearch.discovery.zen.FaultDetection; +import org.elasticsearch.discovery.zen.UnicastZenPing; +import org.elasticsearch.discovery.zen.ZenPing; +import org.elasticsearch.env.NodeEnvironment; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.discovery.ClusterDiscoveryConfiguration; +import org.elasticsearch.test.discovery.TestZenDiscovery; +import org.elasticsearch.test.disruption.NetworkDisruption; +import org.elasticsearch.test.disruption.NetworkDisruption.Bridge; +import org.elasticsearch.test.disruption.NetworkDisruption.DisruptedLinks; +import org.elasticsearch.test.disruption.NetworkDisruption.NetworkDisconnect; +import org.elasticsearch.test.disruption.NetworkDisruption.NetworkLinkDisruptionType; +import org.elasticsearch.test.disruption.NetworkDisruption.TwoPartitions; +import org.elasticsearch.test.disruption.ServiceDisruptionScheme; +import org.elasticsearch.test.disruption.SlowClusterStateProcessing; +import org.elasticsearch.test.transport.MockTransportService; +import org.elasticsearch.transport.TcpTransport; +import org.junit.Before; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; + +public abstract class AbstractDisruptionTestCase extends ESIntegTestCase { + + static final TimeValue DISRUPTION_HEALING_OVERHEAD = TimeValue.timeValueSeconds(40); // we use 30s as timeout in many places. + + private ClusterDiscoveryConfiguration discoveryConfig; + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder().put(discoveryConfig.nodeSettings(nodeOrdinal)) + .put(TestZenDiscovery.USE_MOCK_PINGS.getKey(), false).build(); + } + + @Before + public void clearConfig() { + discoveryConfig = null; + } + + @Override + protected int numberOfShards() { + return 3; + } + + @Override + protected int numberOfReplicas() { + return 1; + } + + private boolean disableBeforeIndexDeletion; + + @Before + public void setUp() throws Exception { + super.setUp(); + disableBeforeIndexDeletion = false; + } + + @Override + public void setDisruptionScheme(ServiceDisruptionScheme scheme) { + if (scheme instanceof NetworkDisruption && + ((NetworkDisruption) scheme).getNetworkLinkDisruptionType() instanceof NetworkDisruption.NetworkUnresponsive) { + // the network unresponsive disruption may leave operations in flight + // this is because this disruption scheme swallows requests by design + // as such, these operations will never be marked as finished + disableBeforeIndexDeletion = true; + } + super.setDisruptionScheme(scheme); + } + + @Override + protected void beforeIndexDeletion() throws Exception { + if (disableBeforeIndexDeletion == false) { + super.beforeIndexDeletion(); + } + } + + List startCluster(int numberOfNodes) throws ExecutionException, InterruptedException { + return startCluster(numberOfNodes, -1); + } + + List startCluster(int numberOfNodes, int minimumMasterNode) throws ExecutionException, InterruptedException { + return startCluster(numberOfNodes, minimumMasterNode, null); + } + + List startCluster(int numberOfNodes, int minimumMasterNode, @Nullable int[] unicastHostsOrdinals) throws + ExecutionException, InterruptedException { + configureCluster(numberOfNodes, unicastHostsOrdinals, minimumMasterNode); + List nodes = internalCluster().startNodes(numberOfNodes); + ensureStableCluster(numberOfNodes); + + // TODO: this is a temporary solution so that nodes will not base their reaction to a partition based on previous successful results + ZenPing zenPing = ((TestZenDiscovery) internalCluster().getInstance(Discovery.class)).getZenPing(); + if (zenPing instanceof UnicastZenPing) { + ((UnicastZenPing) zenPing).clearTemporalResponses(); + } + return nodes; + } + + static final Settings DEFAULT_SETTINGS = Settings.builder() + .put(FaultDetection.PING_TIMEOUT_SETTING.getKey(), "1s") // for hitting simulated network failures quickly + .put(FaultDetection.PING_RETRIES_SETTING.getKey(), "1") // for hitting simulated network failures quickly + .put("discovery.zen.join_timeout", "10s") // still long to induce failures but to long so test won't time out + .put(DiscoverySettings.PUBLISH_TIMEOUT_SETTING.getKey(), "1s") // <-- for hitting simulated network failures quickly + .put(TcpTransport.TCP_CONNECT_TIMEOUT.getKey(), "10s") // Network delay disruption waits for the min between this + // value and the time of disruption and does not recover immediately + // when disruption is stop. We should make sure we recover faster + // then the default of 30s, causing ensureGreen and friends to time out + .build(); + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(MockTransportService.TestPlugin.class); + } + + void configureCluster( + int numberOfNodes, + @Nullable int[] unicastHostsOrdinals, + int minimumMasterNode + ) throws ExecutionException, InterruptedException { + configureCluster(DEFAULT_SETTINGS, numberOfNodes, unicastHostsOrdinals, minimumMasterNode); + } + + void configureCluster( + Settings settings, + int numberOfNodes, + @Nullable int[] unicastHostsOrdinals, + int minimumMasterNode + ) throws ExecutionException, InterruptedException { + if (minimumMasterNode < 0) { + minimumMasterNode = numberOfNodes / 2 + 1; + } + logger.info("---> configured unicast"); + // TODO: Rarely use default settings form some of these + Settings nodeSettings = Settings.builder() + .put(settings) + .put(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey(), numberOfNodes) + .put(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING.getKey(), minimumMasterNode) + .build(); + + if (discoveryConfig == null) { + if (unicastHostsOrdinals == null) { + discoveryConfig = new ClusterDiscoveryConfiguration.UnicastZen(numberOfNodes, nodeSettings); + } else { + discoveryConfig = new ClusterDiscoveryConfiguration.UnicastZen(numberOfNodes, nodeSettings, unicastHostsOrdinals); + } + } + } + + ClusterState getNodeClusterState(String node) { + return client(node).admin().cluster().prepareState().setLocal(true).get().getState(); + } + + void assertNoMaster(final String node) throws Exception { + assertNoMaster(node, null, TimeValue.timeValueSeconds(10)); + } + + void assertNoMaster(final String node, TimeValue maxWaitTime) throws Exception { + assertNoMaster(node, null, maxWaitTime); + } + + void assertNoMaster(final String node, @Nullable final ClusterBlock expectedBlocks, TimeValue maxWaitTime) throws Exception { + assertBusy(new Runnable() { + @Override + public void run() { + ClusterState state = getNodeClusterState(node); + final DiscoveryNodes nodes = state.nodes(); + assertNull("node [" + node + "] still has [" + nodes.getMasterNode() + "] as master", nodes.getMasterNode()); + if (expectedBlocks != null) { + for (ClusterBlockLevel level : expectedBlocks.levels()) { + assertTrue("node [" + node + "] does have level [" + level + "] in it's blocks", state.getBlocks().hasGlobalBlock + (level)); + } + } + } + }, maxWaitTime.getMillis(), TimeUnit.MILLISECONDS); + } + + void assertDifferentMaster(final String node, final String oldMasterNode) throws Exception { + assertBusy(new Runnable() { + @Override + public void run() { + ClusterState state = getNodeClusterState(node); + String masterNode = null; + if (state.nodes().getMasterNode() != null) { + masterNode = state.nodes().getMasterNode().getName(); + } + logger.trace("[{}] master is [{}]", node, state.nodes().getMasterNode()); + assertThat("node [" + node + "] still has [" + masterNode + "] as master", + oldMasterNode, not(equalTo(masterNode))); + } + }, 10, TimeUnit.SECONDS); + } + + void assertMaster(String masterNode, List nodes) throws Exception { + assertBusy(() -> { + for (String node : nodes) { + ClusterState state = getNodeClusterState(node); + String failMsgSuffix = "cluster_state:\n" + state; + assertThat("wrong node count on [" + node + "]. " + failMsgSuffix, state.nodes().getSize(), equalTo(nodes.size())); + String otherMasterNodeName = state.nodes().getMasterNode() != null ? state.nodes().getMasterNode().getName() : null; + assertThat("wrong master on node [" + node + "]. " + failMsgSuffix, otherMasterNodeName, equalTo(masterNode)); + } + }); + } + + public ServiceDisruptionScheme addRandomDisruptionScheme() { + // TODO: add partial partitions + NetworkDisruption p; + final DisruptedLinks disruptedLinks; + if (randomBoolean()) { + disruptedLinks = TwoPartitions.random(random(), internalCluster().getNodeNames()); + } else { + disruptedLinks = Bridge.random(random(), internalCluster().getNodeNames()); + } + final NetworkLinkDisruptionType disruptionType; + switch (randomInt(2)) { + case 0: + disruptionType = new NetworkDisruption.NetworkUnresponsive(); + break; + case 1: + disruptionType = new NetworkDisconnect(); + break; + case 2: + disruptionType = NetworkDisruption.NetworkDelay.random(random()); + break; + default: + throw new IllegalArgumentException(); + } + final ServiceDisruptionScheme scheme; + if (rarely()) { + scheme = new SlowClusterStateProcessing(random()); + } else { + scheme = new NetworkDisruption(disruptedLinks, disruptionType); + } + setDisruptionScheme(scheme); + return scheme; + } + + NetworkDisruption addRandomDisruptionType(TwoPartitions partitions) { + final NetworkLinkDisruptionType disruptionType; + if (randomBoolean()) { + disruptionType = new NetworkDisruption.NetworkUnresponsive(); + } else { + disruptionType = new NetworkDisconnect(); + } + NetworkDisruption partition = new NetworkDisruption(partitions, disruptionType); + + setDisruptionScheme(partition); + + return partition; + } + + TwoPartitions isolateNode(String isolatedNode) { + Set side1 = new HashSet<>(); + Set side2 = new HashSet<>(Arrays.asList(internalCluster().getNodeNames())); + side1.add(isolatedNode); + side2.remove(isolatedNode); + + return new TwoPartitions(side1, side2); + } + +} diff --git a/core/src/test/java/org/elasticsearch/discovery/ClusterDisruptionIT.java b/core/src/test/java/org/elasticsearch/discovery/ClusterDisruptionIT.java new file mode 100644 index 0000000000000..38c9bcb72459f --- /dev/null +++ b/core/src/test/java/org/elasticsearch/discovery/ClusterDisruptionIT.java @@ -0,0 +1,452 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.discovery; + +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.logging.log4j.util.Supplier; +import org.apache.lucene.index.CorruptIndexException; +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.DocWriteResponse; +import org.elasticsearch.action.NoShardAvailableActionException; +import org.elasticsearch.action.get.GetResponse; +import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.client.Client; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.action.shard.ShardStateAction; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.cluster.routing.Murmur3HashFunction; +import org.elasticsearch.cluster.routing.ShardRouting; +import org.elasticsearch.cluster.routing.ShardRoutingState; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.indices.store.IndicesStoreIntegrationIT; +import org.elasticsearch.test.ESIntegTestCase.ClusterScope; +import org.elasticsearch.test.ESIntegTestCase.Scope; +import org.elasticsearch.test.InternalTestCluster; +import org.elasticsearch.test.disruption.NetworkDisruption; +import org.elasticsearch.test.disruption.NetworkDisruption.Bridge; +import org.elasticsearch.test.disruption.NetworkDisruption.NetworkDisconnect; +import org.elasticsearch.test.disruption.NetworkDisruption.NetworkLinkDisruptionType; +import org.elasticsearch.test.disruption.NetworkDisruption.TwoPartitions; +import org.elasticsearch.test.disruption.ServiceDisruptionScheme; +import org.elasticsearch.test.junit.annotations.TestLogging; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +/** + * Tests various cluster operations (e.g., indexing) during disruptions. + */ +@ClusterScope(scope = Scope.TEST, numDataNodes = 0, transportClientRatio = 0, autoMinMasterNodes = false) +@TestLogging("_root:DEBUG,org.elasticsearch.cluster.service:TRACE") +public class ClusterDisruptionIT extends AbstractDisruptionTestCase { + + /** + * Test that we do not loose document whose indexing request was successful, under a randomly selected disruption scheme + * We also collect & report the type of indexing failures that occur. + *

+ * This test is a superset of tests run in the Jepsen test suite, with the exception of versioned updates + */ + @TestLogging("_root:DEBUG,org.elasticsearch.action.bulk:TRACE,org.elasticsearch.action.get:TRACE,discovery:TRACE," + + "org.elasticsearch.cluster.service:TRACE,org.elasticsearch.indices.recovery:TRACE," + + "org.elasticsearch.indices.cluster:TRACE,org.elasticsearch.index.shard:TRACE") + public void testAckedIndexing() throws Exception { + + final int seconds = !(TEST_NIGHTLY && rarely()) ? 1 : 5; + final String timeout = seconds + "s"; + + final List nodes = startCluster(rarely() ? 5 : 3); + + assertAcked(prepareCreate("test") + .setSettings(Settings.builder() + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1 + randomInt(2)) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, randomInt(2)) + )); + ensureGreen(); + + ServiceDisruptionScheme disruptionScheme = addRandomDisruptionScheme(); + logger.info("disruption scheme [{}] added", disruptionScheme); + + final ConcurrentHashMap ackedDocs = new ConcurrentHashMap<>(); // id -> node sent. + + final AtomicBoolean stop = new AtomicBoolean(false); + List indexers = new ArrayList<>(nodes.size()); + List semaphores = new ArrayList<>(nodes.size()); + final AtomicInteger idGenerator = new AtomicInteger(0); + final AtomicReference countDownLatchRef = new AtomicReference<>(); + final List exceptedExceptions = Collections.synchronizedList(new ArrayList()); + + logger.info("starting indexers"); + try { + for (final String node : nodes) { + final Semaphore semaphore = new Semaphore(0); + semaphores.add(semaphore); + final Client client = client(node); + final String name = "indexer_" + indexers.size(); + final int numPrimaries = getNumShards("test").numPrimaries; + Thread thread = new Thread(() -> { + while (!stop.get()) { + String id = null; + try { + if (!semaphore.tryAcquire(10, TimeUnit.SECONDS)) { + continue; + } + logger.info("[{}] Acquired semaphore and it has {} permits left", name, semaphore.availablePermits()); + try { + id = Integer.toString(idGenerator.incrementAndGet()); + int shard = Math.floorMod(Murmur3HashFunction.hash(id), numPrimaries); + logger.trace("[{}] indexing id [{}] through node [{}] targeting shard [{}]", name, id, node, shard); + IndexResponse response = + client.prepareIndex("test", "type", id) + .setSource("{}", XContentType.JSON) + .setTimeout(timeout) + .get(timeout); + assertEquals(DocWriteResponse.Result.CREATED, response.getResult()); + ackedDocs.put(id, node); + logger.trace("[{}] indexed id [{}] through node [{}]", name, id, node); + } catch (ElasticsearchException e) { + exceptedExceptions.add(e); + final String docId = id; + logger.trace( + (Supplier) + () -> new ParameterizedMessage("[{}] failed id [{}] through node [{}]", name, docId, node), e); + } finally { + countDownLatchRef.get().countDown(); + logger.trace("[{}] decreased counter : {}", name, countDownLatchRef.get().getCount()); + } + } catch (InterruptedException e) { + // fine - semaphore interrupt + } catch (AssertionError | Exception e) { + logger.info( + (Supplier) () -> new ParameterizedMessage("unexpected exception in background thread of [{}]", node), + e); + } + } + }); + + thread.setName(name); + thread.start(); + indexers.add(thread); + } + + int docsPerIndexer = randomInt(3); + logger.info("indexing {} docs per indexer before partition", docsPerIndexer); + countDownLatchRef.set(new CountDownLatch(docsPerIndexer * indexers.size())); + for (Semaphore semaphore : semaphores) { + semaphore.release(docsPerIndexer); + } + assertTrue(countDownLatchRef.get().await(1, TimeUnit.MINUTES)); + + for (int iter = 1 + randomInt(2); iter > 0; iter--) { + logger.info("starting disruptions & indexing (iteration [{}])", iter); + disruptionScheme.startDisrupting(); + + docsPerIndexer = 1 + randomInt(5); + logger.info("indexing {} docs per indexer during partition", docsPerIndexer); + countDownLatchRef.set(new CountDownLatch(docsPerIndexer * indexers.size())); + Collections.shuffle(semaphores, random()); + for (Semaphore semaphore : semaphores) { + assertThat(semaphore.availablePermits(), equalTo(0)); + semaphore.release(docsPerIndexer); + } + logger.info("waiting for indexing requests to complete"); + assertTrue(countDownLatchRef.get().await(docsPerIndexer * seconds * 1000 + 2000, TimeUnit.MILLISECONDS)); + + logger.info("stopping disruption"); + disruptionScheme.stopDisrupting(); + for (String node : internalCluster().getNodeNames()) { + ensureStableCluster(nodes.size(), TimeValue.timeValueMillis(disruptionScheme.expectedTimeToHeal().millis() + + DISRUPTION_HEALING_OVERHEAD.millis()), true, node); + } + // in case of a bridge partition, shard allocation can fail "index.allocation.max_retries" times if the master + // is the super-connected node and recovery source and target are on opposite sides of the bridge + if (disruptionScheme instanceof NetworkDisruption && + ((NetworkDisruption) disruptionScheme).getDisruptedLinks() instanceof Bridge) { + assertAcked(client().admin().cluster().prepareReroute().setRetryFailed(true)); + } + ensureGreen("test"); + + logger.info("validating successful docs"); + assertBusy(() -> { + for (String node : nodes) { + try { + logger.debug("validating through node [{}] ([{}] acked docs)", node, ackedDocs.size()); + for (String id : ackedDocs.keySet()) { + assertTrue("doc [" + id + "] indexed via node [" + ackedDocs.get(id) + "] not found", + client(node).prepareGet("test", "type", id).setPreference("_local").get().isExists()); + } + } catch (AssertionError | NoShardAvailableActionException e) { + throw new AssertionError(e.getMessage() + " (checked via node [" + node + "]", e); + } + } + }, 30, TimeUnit.SECONDS); + + logger.info("done validating (iteration [{}])", iter); + } + } finally { + if (exceptedExceptions.size() > 0) { + StringBuilder sb = new StringBuilder(); + for (Exception e : exceptedExceptions) { + sb.append("\n").append(e.getMessage()); + } + logger.debug("Indexing exceptions during disruption: {}", sb); + } + logger.info("shutting down indexers"); + stop.set(true); + for (Thread indexer : indexers) { + indexer.interrupt(); + indexer.join(60000); + } + } + } + + /** + * Test that a document which is indexed on the majority side of a partition, is available from the minority side, + * once the partition is healed + */ + public void testRejoinDocumentExistsInAllShardCopies() throws Exception { + List nodes = startCluster(3); + + assertAcked(prepareCreate("test") + .setSettings(Settings.builder() + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 2) + ) + .get()); + ensureGreen("test"); + + nodes = new ArrayList<>(nodes); + Collections.shuffle(nodes, random()); + String isolatedNode = nodes.get(0); + String notIsolatedNode = nodes.get(1); + + TwoPartitions partitions = isolateNode(isolatedNode); + NetworkDisruption scheme = addRandomDisruptionType(partitions); + scheme.startDisrupting(); + ensureStableCluster(2, notIsolatedNode); + assertFalse(client(notIsolatedNode).admin().cluster().prepareHealth("test").setWaitForYellowStatus().get().isTimedOut()); + + + IndexResponse indexResponse = internalCluster().client(notIsolatedNode).prepareIndex("test", "type").setSource("field", "value") + .get(); + assertThat(indexResponse.getVersion(), equalTo(1L)); + + logger.info("Verifying if document exists via node[{}]", notIsolatedNode); + GetResponse getResponse = internalCluster().client(notIsolatedNode).prepareGet("test", "type", indexResponse.getId()) + .setPreference("_local") + .get(); + assertThat(getResponse.isExists(), is(true)); + assertThat(getResponse.getVersion(), equalTo(1L)); + assertThat(getResponse.getId(), equalTo(indexResponse.getId())); + + scheme.stopDisrupting(); + + ensureStableCluster(3); + ensureGreen("test"); + + for (String node : nodes) { + logger.info("Verifying if document exists after isolating node[{}] via node[{}]", isolatedNode, node); + getResponse = internalCluster().client(node).prepareGet("test", "type", indexResponse.getId()) + .setPreference("_local") + .get(); + assertThat(getResponse.isExists(), is(true)); + assertThat(getResponse.getVersion(), equalTo(1L)); + assertThat(getResponse.getId(), equalTo(indexResponse.getId())); + } + } + + // simulate handling of sending shard failure during an isolation + public void testSendingShardFailure() throws Exception { + List nodes = startCluster(3, 2); + String masterNode = internalCluster().getMasterName(); + List nonMasterNodes = nodes.stream().filter(node -> !node.equals(masterNode)).collect(Collectors.toList()); + String nonMasterNode = randomFrom(nonMasterNodes); + assertAcked(prepareCreate("test") + .setSettings(Settings.builder() + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 2) + )); + ensureGreen(); + String nonMasterNodeId = internalCluster().clusterService(nonMasterNode).localNode().getId(); + + // fail a random shard + ShardRouting failedShard = + randomFrom(clusterService().state().getRoutingNodes().node(nonMasterNodeId).shardsWithState(ShardRoutingState.STARTED)); + ShardStateAction service = internalCluster().getInstance(ShardStateAction.class, nonMasterNode); + CountDownLatch latch = new CountDownLatch(1); + AtomicBoolean success = new AtomicBoolean(); + + String isolatedNode = randomBoolean() ? masterNode : nonMasterNode; + TwoPartitions partitions = isolateNode(isolatedNode); + // we cannot use the NetworkUnresponsive disruption type here as it will swallow the "shard failed" request, calling neither + // onSuccess nor onFailure on the provided listener. + NetworkLinkDisruptionType disruptionType = new NetworkDisconnect(); + NetworkDisruption networkDisruption = new NetworkDisruption(partitions, disruptionType); + setDisruptionScheme(networkDisruption); + networkDisruption.startDisrupting(); + + service.localShardFailed(failedShard, "simulated", new CorruptIndexException("simulated", (String) null), new + ShardStateAction.Listener() { + @Override + public void onSuccess() { + success.set(true); + latch.countDown(); + } + + @Override + public void onFailure(Exception e) { + success.set(false); + latch.countDown(); + assert false; + } + }); + + if (isolatedNode.equals(nonMasterNode)) { + assertNoMaster(nonMasterNode); + } else { + ensureStableCluster(2, nonMasterNode); + } + + // heal the partition + networkDisruption.removeAndEnsureHealthy(internalCluster()); + + // the cluster should stabilize + ensureStableCluster(3); + + latch.await(); + + // the listener should be notified + assertTrue(success.get()); + + // the failed shard should be gone + List shards = clusterService().state().getRoutingTable().allShards("test"); + for (ShardRouting shard : shards) { + assertThat(shard.allocationId(), not(equalTo(failedShard.allocationId()))); + } + } + + /** + * This test creates a scenario where a primary shard (0 replicas) relocates and is in POST_RECOVERY on the target + * node but already deleted on the source node. Search request should still work. + */ + public void testSearchWithRelocationAndSlowClusterStateProcessing() throws Exception { + // don't use DEFAULT settings (which can cause node disconnects on a slow CI machine) + configureCluster(Settings.EMPTY, 3, null, 1); + final String masterNode = internalCluster().startMasterOnlyNode(); + final String node_1 = internalCluster().startDataOnlyNode(); + + logger.info("--> creating index [test] with one shard and on replica"); + assertAcked(prepareCreate("test").setSettings( + Settings.builder().put(indexSettings()) + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)) + ); + ensureGreen("test"); + + final String node_2 = internalCluster().startDataOnlyNode(); + List indexRequestBuilderList = new ArrayList<>(); + for (int i = 0; i < 100; i++) { + indexRequestBuilderList.add(client().prepareIndex().setIndex("test").setType("doc") + .setSource("{\"int_field\":1}", XContentType.JSON)); + } + indexRandom(true, indexRequestBuilderList); + + IndicesStoreIntegrationIT.relocateAndBlockCompletion(logger, "test", 0, node_1, node_2); + // now search for the documents and see if we get a reply + assertThat(client().prepareSearch().setSize(0).get().getHits().getTotalHits(), equalTo(100L)); + } + + public void testIndexImportedFromDataOnlyNodesIfMasterLostDataFolder() throws Exception { + // test for https://github.com/elastic/elasticsearch/issues/8823 + configureCluster(2, null, 1); + String masterNode = internalCluster().startMasterOnlyNode(Settings.EMPTY); + internalCluster().startDataOnlyNode(Settings.EMPTY); + + ensureStableCluster(2); + assertAcked(prepareCreate("index").setSettings(Settings.builder().put("index.number_of_replicas", 0))); + index("index", "doc", "1", jsonBuilder().startObject().field("text", "some text").endObject()); + ensureGreen(); + + internalCluster().restartNode(masterNode, new InternalTestCluster.RestartCallback() { + @Override + public boolean clearData(String nodeName) { + return true; + } + }); + + ensureGreen("index"); + assertTrue(client().prepareGet("index", "doc", "1").get().isExists()); + } + + /** + * Tests that indices are properly deleted even if there is a master transition in between. + * Test for https://github.com/elastic/elasticsearch/issues/11665 + */ + public void testIndicesDeleted() throws Exception { + final Settings settings = Settings.builder() + .put(DEFAULT_SETTINGS) + .put(DiscoverySettings.PUBLISH_TIMEOUT_SETTING.getKey(), "0s") // don't wait on isolated data node + .put(DiscoverySettings.COMMIT_TIMEOUT_SETTING.getKey(), "30s") // wait till cluster state is committed + .build(); + final String idxName = "test"; + configureCluster(settings, 3, null, 2); + final List allMasterEligibleNodes = internalCluster().startMasterOnlyNodes(2); + final String dataNode = internalCluster().startDataOnlyNode(); + ensureStableCluster(3); + assertAcked(prepareCreate("test")); + + final String masterNode1 = internalCluster().getMasterName(); + NetworkDisruption networkDisruption = + new NetworkDisruption(new TwoPartitions(masterNode1, dataNode), new NetworkDisruption.NetworkUnresponsive()); + internalCluster().setDisruptionScheme(networkDisruption); + networkDisruption.startDisrupting(); + // We know this will time out due to the partition, we check manually below to not proceed until + // the delete has been applied to the master node and the master eligible node. + internalCluster().client(masterNode1).admin().indices().prepareDelete(idxName).setTimeout("0s").get(); + // Don't restart the master node until we know the index deletion has taken effect on master and the master eligible node. + assertBusy(() -> { + for (String masterNode : allMasterEligibleNodes) { + final ClusterState masterState = internalCluster().clusterService(masterNode).state(); + assertTrue("index not deleted on " + masterNode, masterState.metaData().hasIndex(idxName) == false); + } + }); + internalCluster().restartNode(masterNode1, InternalTestCluster.EMPTY_CALLBACK); + ensureYellow(); + assertFalse(client().admin().indices().prepareExists(idxName).get().isExists()); + } + +} diff --git a/core/src/test/java/org/elasticsearch/discovery/DiscoveryDisruptionIT.java b/core/src/test/java/org/elasticsearch/discovery/DiscoveryDisruptionIT.java new file mode 100644 index 0000000000000..5dbf5a2c97ddc --- /dev/null +++ b/core/src/test/java/org/elasticsearch/discovery/DiscoveryDisruptionIT.java @@ -0,0 +1,320 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.discovery; + +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.discovery.zen.MembershipAction; +import org.elasticsearch.discovery.zen.PublishClusterStateAction; +import org.elasticsearch.discovery.zen.UnicastZenPing; +import org.elasticsearch.discovery.zen.ZenPing; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.discovery.TestZenDiscovery; +import org.elasticsearch.test.disruption.NetworkDisruption; +import org.elasticsearch.test.disruption.NetworkDisruption.NetworkDisconnect; +import org.elasticsearch.test.disruption.NetworkDisruption.TwoPartitions; +import org.elasticsearch.test.disruption.ServiceDisruptionScheme; +import org.elasticsearch.test.disruption.SlowClusterStateProcessing; +import org.elasticsearch.test.junit.annotations.TestLogging; +import org.elasticsearch.test.transport.MockTransportService; +import org.elasticsearch.transport.ConnectionProfile; +import org.elasticsearch.transport.Transport; +import org.elasticsearch.transport.TransportRequest; +import org.elasticsearch.transport.TransportRequestOptions; +import org.elasticsearch.transport.TransportService; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CountDownLatch; + +import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING; +import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_NUMBER_OF_SHARDS_SETTING; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; + +/** + * Tests for discovery during disruptions. + */ +@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0, transportClientRatio = 0, autoMinMasterNodes = false) +@TestLogging("_root:DEBUG,org.elasticsearch.cluster.service:TRACE") +public class DiscoveryDisruptionIT extends AbstractDisruptionTestCase { + + public void testIsolatedUnicastNodes() throws Exception { + List nodes = startCluster(4, -1, new int[]{0}); + // Figure out what is the elected master node + final String unicastTarget = nodes.get(0); + + Set unicastTargetSide = new HashSet<>(); + unicastTargetSide.add(unicastTarget); + + Set restOfClusterSide = new HashSet<>(); + restOfClusterSide.addAll(nodes); + restOfClusterSide.remove(unicastTarget); + + // Forcefully clean temporal response lists on all nodes. Otherwise the node in the unicast host list + // includes all the other nodes that have pinged it and the issue doesn't manifest + ZenPing zenPing = ((TestZenDiscovery) internalCluster().getInstance(Discovery.class)).getZenPing(); + if (zenPing instanceof UnicastZenPing) { + ((UnicastZenPing) zenPing).clearTemporalResponses(); + } + + // Simulate a network issue between the unicast target node and the rest of the cluster + NetworkDisruption networkDisconnect = new NetworkDisruption(new TwoPartitions(unicastTargetSide, restOfClusterSide), + new NetworkDisconnect()); + setDisruptionScheme(networkDisconnect); + networkDisconnect.startDisrupting(); + // Wait until elected master has removed that the unlucky node... + ensureStableCluster(3, nodes.get(1)); + + // The isolate master node must report no master, so it starts with pinging + assertNoMaster(unicastTarget); + networkDisconnect.stopDisrupting(); + // Wait until the master node sees all 3 nodes again. + ensureStableCluster(4); + } + + /** + * A 4 node cluster with m_m_n set to 3 and each node has one unicast endpoint. One node partitions from the master node. + * The temporal unicast responses is empty. When partition is solved the one ping response contains a master node. + * The rejoining node should take this master node and connect. + */ + public void testUnicastSinglePingResponseContainsMaster() throws Exception { + List nodes = startCluster(4, -1, new int[]{0}); + // Figure out what is the elected master node + final String masterNode = internalCluster().getMasterName(); + logger.info("---> legit elected master node={}", masterNode); + List otherNodes = new ArrayList<>(nodes); + otherNodes.remove(masterNode); + otherNodes.remove(nodes.get(0)); // <-- Don't isolate the node that is in the unicast endpoint for all the other nodes. + final String isolatedNode = otherNodes.get(0); + + // Forcefully clean temporal response lists on all nodes. Otherwise the node in the unicast host list + // includes all the other nodes that have pinged it and the issue doesn't manifest + ZenPing zenPing = ((TestZenDiscovery) internalCluster().getInstance(Discovery.class)).getZenPing(); + if (zenPing instanceof UnicastZenPing) { + ((UnicastZenPing) zenPing).clearTemporalResponses(); + } + + // Simulate a network issue between the unlucky node and elected master node in both directions. + NetworkDisruption networkDisconnect = new NetworkDisruption(new TwoPartitions(masterNode, isolatedNode), + new NetworkDisconnect()); + setDisruptionScheme(networkDisconnect); + networkDisconnect.startDisrupting(); + // Wait until elected master has removed that the unlucky node... + ensureStableCluster(3, masterNode); + + // The isolate master node must report no master, so it starts with pinging + assertNoMaster(isolatedNode); + networkDisconnect.stopDisrupting(); + // Wait until the master node sees all 4 nodes again. + ensureStableCluster(4); + // The elected master shouldn't have changed, since the isolated node never could have elected himself as + // master since m_m_n of 3 could never be satisfied. + assertMaster(masterNode, nodes); + } + + /** + * Test cluster join with issues in cluster state publishing * + */ + public void testClusterJoinDespiteOfPublishingIssues() throws Exception { + List nodes = startCluster(2, 1); + + String masterNode = internalCluster().getMasterName(); + String nonMasterNode; + if (masterNode.equals(nodes.get(0))) { + nonMasterNode = nodes.get(1); + } else { + nonMasterNode = nodes.get(0); + } + + DiscoveryNodes discoveryNodes = internalCluster().getInstance(ClusterService.class, nonMasterNode).state().nodes(); + + TransportService masterTranspotService = + internalCluster().getInstance(TransportService.class, discoveryNodes.getMasterNode().getName()); + + logger.info("blocking requests from non master [{}] to master [{}]", nonMasterNode, masterNode); + MockTransportService nonMasterTransportService = (MockTransportService) internalCluster().getInstance(TransportService.class, + nonMasterNode); + nonMasterTransportService.addFailToSendNoConnectRule(masterTranspotService); + + assertNoMaster(nonMasterNode); + + logger.info("blocking cluster state publishing from master [{}] to non master [{}]", masterNode, nonMasterNode); + MockTransportService masterTransportService = + (MockTransportService) internalCluster().getInstance(TransportService.class, masterNode); + TransportService localTransportService = + internalCluster().getInstance(TransportService.class, discoveryNodes.getLocalNode().getName()); + if (randomBoolean()) { + masterTransportService.addFailToSendNoConnectRule(localTransportService, PublishClusterStateAction.SEND_ACTION_NAME); + } else { + masterTransportService.addFailToSendNoConnectRule(localTransportService, PublishClusterStateAction.COMMIT_ACTION_NAME); + } + + logger.info("allowing requests from non master [{}] to master [{}], waiting for two join request", nonMasterNode, masterNode); + final CountDownLatch countDownLatch = new CountDownLatch(2); + nonMasterTransportService.addDelegate(masterTranspotService, new MockTransportService.DelegateTransport(nonMasterTransportService + .original()) { + @Override + protected void sendRequest(Transport.Connection connection, long requestId, String action, TransportRequest request, + TransportRequestOptions options) throws IOException { + if (action.equals(MembershipAction.DISCOVERY_JOIN_ACTION_NAME)) { + countDownLatch.countDown(); + } + super.sendRequest(connection, requestId, action, request, options); + } + + @Override + public Transport.Connection openConnection(DiscoveryNode node, ConnectionProfile profile) throws IOException { + return super.openConnection(node, profile); + } + + }); + + countDownLatch.await(); + + logger.info("waiting for cluster to reform"); + masterTransportService.clearRule(localTransportService); + nonMasterTransportService.clearRule(localTransportService); + + ensureStableCluster(2); + + // shutting down the nodes, to avoid the leakage check tripping + // on the states associated with the commit requests we may have dropped + internalCluster().stopRandomNonMasterNode(); + } + + public void testClusterFormingWithASlowNode() throws Exception { + configureCluster(3, null, 2); + + SlowClusterStateProcessing disruption = new SlowClusterStateProcessing(random(), 0, 0, 1000, 2000); + + // don't wait for initial state, we want to add the disruption while the cluster is forming + internalCluster().startNodes(3, Settings.builder().put(DiscoverySettings.PUBLISH_TIMEOUT_SETTING.getKey(), "3s").build()); + + logger.info("applying disruption while cluster is forming ..."); + + internalCluster().setDisruptionScheme(disruption); + disruption.startDisrupting(); + + ensureStableCluster(3); + } + + public void testElectMasterWithLatestVersion() throws Exception { + configureCluster(3, null, 2); + final Set nodes = new HashSet<>(internalCluster().startNodes(3)); + ensureStableCluster(3); + ServiceDisruptionScheme isolateAllNodes = + new NetworkDisruption(new NetworkDisruption.IsolateAllNodes(nodes), new NetworkDisconnect()); + internalCluster().setDisruptionScheme(isolateAllNodes); + + logger.info("--> forcing a complete election to make sure \"preferred\" master is elected"); + isolateAllNodes.startDisrupting(); + for (String node : nodes) { + assertNoMaster(node); + } + internalCluster().clearDisruptionScheme(); + ensureStableCluster(3); + final String preferredMasterName = internalCluster().getMasterName(); + final DiscoveryNode preferredMaster = internalCluster().clusterService(preferredMasterName).localNode(); + for (String node : nodes) { + DiscoveryNode discoveryNode = internalCluster().clusterService(node).localNode(); + assertThat(discoveryNode.getId(), greaterThanOrEqualTo(preferredMaster.getId())); + } + + logger.info("--> preferred master is {}", preferredMaster); + final Set nonPreferredNodes = new HashSet<>(nodes); + nonPreferredNodes.remove(preferredMasterName); + final ServiceDisruptionScheme isolatePreferredMaster = + new NetworkDisruption( + new NetworkDisruption.TwoPartitions( + Collections.singleton(preferredMasterName), nonPreferredNodes), + new NetworkDisconnect()); + internalCluster().setDisruptionScheme(isolatePreferredMaster); + isolatePreferredMaster.startDisrupting(); + + assertAcked(client(randomFrom(nonPreferredNodes)).admin().indices().prepareCreate("test").setSettings( + INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1, + INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0 + )); + + internalCluster().clearDisruptionScheme(false); + internalCluster().setDisruptionScheme(isolateAllNodes); + + logger.info("--> forcing a complete election again"); + isolateAllNodes.startDisrupting(); + for (String node : nodes) { + assertNoMaster(node); + } + + isolateAllNodes.stopDisrupting(); + + final ClusterState state = client().admin().cluster().prepareState().get().getState(); + if (state.metaData().hasIndex("test") == false) { + fail("index 'test' was lost. current cluster state: " + state); + } + + } + + /** + * Adds an asymmetric break between a master and one of the nodes and makes + * sure that the node is removed form the cluster, that the node start pinging and that + * the cluster reforms when healed. + */ + public void testNodeNotReachableFromMaster() throws Exception { + startCluster(3); + + String masterNode = internalCluster().getMasterName(); + String nonMasterNode = null; + while (nonMasterNode == null) { + nonMasterNode = randomFrom(internalCluster().getNodeNames()); + if (nonMasterNode.equals(masterNode)) { + nonMasterNode = null; + } + } + + logger.info("blocking request from master [{}] to [{}]", masterNode, nonMasterNode); + MockTransportService masterTransportService = (MockTransportService) internalCluster().getInstance(TransportService.class, + masterNode); + if (randomBoolean()) { + masterTransportService.addUnresponsiveRule(internalCluster().getInstance(TransportService.class, nonMasterNode)); + } else { + masterTransportService.addFailToSendNoConnectRule(internalCluster().getInstance(TransportService.class, nonMasterNode)); + } + + logger.info("waiting for [{}] to be removed from cluster", nonMasterNode); + ensureStableCluster(2, masterNode); + + logger.info("waiting for [{}] to have no master", nonMasterNode); + assertNoMaster(nonMasterNode); + + logger.info("healing partition and checking cluster reforms"); + masterTransportService.clearAllRules(); + + ensureStableCluster(3); + } + +} diff --git a/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java b/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java deleted file mode 100644 index ca1f572547db2..0000000000000 --- a/core/src/test/java/org/elasticsearch/discovery/DiscoveryWithServiceDisruptionsIT.java +++ /dev/null @@ -1,1377 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.discovery; - -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.apache.logging.log4j.util.Supplier; -import org.apache.lucene.index.CorruptIndexException; -import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.action.DocWriteResponse; -import org.elasticsearch.action.NoShardAvailableActionException; -import org.elasticsearch.action.get.GetResponse; -import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.client.Client; -import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.ClusterStateUpdateTask; -import org.elasticsearch.cluster.action.shard.ShardStateAction; -import org.elasticsearch.cluster.block.ClusterBlock; -import org.elasticsearch.cluster.block.ClusterBlockLevel; -import org.elasticsearch.cluster.metadata.IndexMetaData; -import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.Murmur3HashFunction; -import org.elasticsearch.cluster.routing.ShardRouting; -import org.elasticsearch.cluster.routing.ShardRoutingState; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.Nullable; -import org.elasticsearch.common.Priority; -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.collect.Tuple; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.xcontent.XContentType; -import org.elasticsearch.discovery.zen.ElectMasterService; -import org.elasticsearch.discovery.zen.FaultDetection; -import org.elasticsearch.discovery.zen.MembershipAction; -import org.elasticsearch.discovery.zen.PublishClusterStateAction; -import org.elasticsearch.discovery.zen.UnicastZenPing; -import org.elasticsearch.discovery.zen.ZenDiscovery; -import org.elasticsearch.discovery.zen.ZenPing; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.index.IndexSettings; -import org.elasticsearch.indices.store.IndicesStoreIntegrationIT; -import org.elasticsearch.monitor.jvm.HotThreads; -import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.test.ESIntegTestCase.ClusterScope; -import org.elasticsearch.test.ESIntegTestCase.Scope; -import org.elasticsearch.test.InternalTestCluster; -import org.elasticsearch.test.discovery.ClusterDiscoveryConfiguration; -import org.elasticsearch.test.discovery.TestZenDiscovery; -import org.elasticsearch.test.disruption.IntermittentLongGCDisruption; -import org.elasticsearch.test.disruption.LongGCDisruption; -import org.elasticsearch.test.disruption.NetworkDisruption; -import org.elasticsearch.test.disruption.NetworkDisruption.Bridge; -import org.elasticsearch.test.disruption.NetworkDisruption.DisruptedLinks; -import org.elasticsearch.test.disruption.NetworkDisruption.NetworkDelay; -import org.elasticsearch.test.disruption.NetworkDisruption.NetworkDisconnect; -import org.elasticsearch.test.disruption.NetworkDisruption.NetworkLinkDisruptionType; -import org.elasticsearch.test.disruption.NetworkDisruption.NetworkUnresponsive; -import org.elasticsearch.test.disruption.NetworkDisruption.TwoPartitions; -import org.elasticsearch.test.disruption.ServiceDisruptionScheme; -import org.elasticsearch.test.disruption.SingleNodeDisruption; -import org.elasticsearch.test.disruption.SlowClusterStateProcessing; -import org.elasticsearch.test.junit.annotations.TestLogging; -import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.ConnectionProfile; -import org.elasticsearch.transport.TcpTransport; -import org.elasticsearch.transport.TransportRequest; -import org.elasticsearch.transport.TransportRequestOptions; -import org.elasticsearch.transport.TransportService; -import org.junit.Before; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING; -import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_NUMBER_OF_SHARDS_SETTING; -import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.nullValue; - -@ClusterScope(scope = Scope.TEST, numDataNodes = 0, transportClientRatio = 0, autoMinMasterNodes = false) -@TestLogging("_root:DEBUG,org.elasticsearch.cluster.service:TRACE") -public class DiscoveryWithServiceDisruptionsIT extends ESIntegTestCase { - - private static final TimeValue DISRUPTION_HEALING_OVERHEAD = TimeValue.timeValueSeconds(40); // we use 30s as timeout in many places. - - private ClusterDiscoveryConfiguration discoveryConfig; - - @Override - protected Settings nodeSettings(int nodeOrdinal) { - return Settings.builder().put(discoveryConfig.nodeSettings(nodeOrdinal)) - .put(TestZenDiscovery.USE_MOCK_PINGS.getKey(), false).build(); - } - - @Before - public void clearConfig() { - discoveryConfig = null; - } - - @Override - protected int numberOfShards() { - return 3; - } - - @Override - protected int numberOfReplicas() { - return 1; - } - - private boolean disableBeforeIndexDeletion; - - @Before - public void setUp() throws Exception { - super.setUp(); - disableBeforeIndexDeletion = false; - } - - @Override - public void setDisruptionScheme(ServiceDisruptionScheme scheme) { - if (scheme instanceof NetworkDisruption && - ((NetworkDisruption) scheme).getNetworkLinkDisruptionType() instanceof NetworkUnresponsive) { - // the network unresponsive disruption may leave operations in flight - // this is because this disruption scheme swallows requests by design - // as such, these operations will never be marked as finished - disableBeforeIndexDeletion = true; - } - super.setDisruptionScheme(scheme); - } - - @Override - protected void beforeIndexDeletion() throws Exception { - if (disableBeforeIndexDeletion == false) { - super.beforeIndexDeletion(); - } - } - - private List startCluster(int numberOfNodes) throws ExecutionException, InterruptedException { - return startCluster(numberOfNodes, -1); - } - - private List startCluster(int numberOfNodes, int minimumMasterNode) throws ExecutionException, InterruptedException { - return startCluster(numberOfNodes, minimumMasterNode, null); - } - - private List startCluster(int numberOfNodes, int minimumMasterNode, @Nullable int[] unicastHostsOrdinals) throws - ExecutionException, InterruptedException { - configureCluster(numberOfNodes, unicastHostsOrdinals, minimumMasterNode); - List nodes = internalCluster().startNodes(numberOfNodes); - ensureStableCluster(numberOfNodes); - - // TODO: this is a temporary solution so that nodes will not base their reaction to a partition based on previous successful results - ZenPing zenPing = ((TestZenDiscovery) internalCluster().getInstance(Discovery.class)).getZenPing(); - if (zenPing instanceof UnicastZenPing) { - ((UnicastZenPing) zenPing).clearTemporalResponses(); - } - return nodes; - } - - static final Settings DEFAULT_SETTINGS = Settings.builder() - .put(FaultDetection.PING_TIMEOUT_SETTING.getKey(), "1s") // for hitting simulated network failures quickly - .put(FaultDetection.PING_RETRIES_SETTING.getKey(), "1") // for hitting simulated network failures quickly - .put("discovery.zen.join_timeout", "10s") // still long to induce failures but to long so test won't time out - .put(DiscoverySettings.PUBLISH_TIMEOUT_SETTING.getKey(), "1s") // <-- for hitting simulated network failures quickly - .put(TcpTransport.TCP_CONNECT_TIMEOUT.getKey(), "10s") // Network delay disruption waits for the min between this - // value and the time of disruption and does not recover immediately - // when disruption is stop. We should make sure we recover faster - // then the default of 30s, causing ensureGreen and friends to time out - - .build(); - - @Override - protected Collection> nodePlugins() { - return Arrays.asList(MockTransportService.TestPlugin.class); - } - - private void configureCluster( - int numberOfNodes, - @Nullable int[] unicastHostsOrdinals, - int minimumMasterNode - ) throws ExecutionException, InterruptedException { - configureCluster(DEFAULT_SETTINGS, numberOfNodes, unicastHostsOrdinals, minimumMasterNode); - } - - private void configureCluster( - Settings settings, - int numberOfNodes, - @Nullable int[] unicastHostsOrdinals, - int minimumMasterNode - ) throws ExecutionException, InterruptedException { - if (minimumMasterNode < 0) { - minimumMasterNode = numberOfNodes / 2 + 1; - } - logger.info("---> configured unicast"); - // TODO: Rarely use default settings form some of these - Settings nodeSettings = Settings.builder() - .put(settings) - .put(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey(), numberOfNodes) - .put(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING.getKey(), minimumMasterNode) - .build(); - - if (discoveryConfig == null) { - if (unicastHostsOrdinals == null) { - discoveryConfig = new ClusterDiscoveryConfiguration.UnicastZen(numberOfNodes, nodeSettings); - } else { - discoveryConfig = new ClusterDiscoveryConfiguration.UnicastZen(numberOfNodes, nodeSettings, unicastHostsOrdinals); - } - } - } - - /** - * Test that no split brain occurs under partial network partition. See https://github.com/elastic/elasticsearch/issues/2488 - */ - public void testFailWithMinimumMasterNodesConfigured() throws Exception { - List nodes = startCluster(3); - - // Figure out what is the elected master node - final String masterNode = internalCluster().getMasterName(); - logger.info("---> legit elected master node={}", masterNode); - - // Pick a node that isn't the elected master. - Set nonMasters = new HashSet<>(nodes); - nonMasters.remove(masterNode); - final String unluckyNode = randomFrom(nonMasters.toArray(Strings.EMPTY_ARRAY)); - - - // Simulate a network issue between the unlucky node and elected master node in both directions. - - NetworkDisruption networkDisconnect = new NetworkDisruption(new TwoPartitions(masterNode, unluckyNode), - new NetworkDisconnect()); - setDisruptionScheme(networkDisconnect); - networkDisconnect.startDisrupting(); - - // Wait until elected master has removed that the unlucky node... - ensureStableCluster(2, masterNode); - - // The unlucky node must report *no* master node, since it can't connect to master and in fact it should - // continuously ping until network failures have been resolved. However - // It may a take a bit before the node detects it has been cut off from the elected master - assertNoMaster(unluckyNode); - - networkDisconnect.stopDisrupting(); - - // Wait until the master node sees all 3 nodes again. - ensureStableCluster(3); - - // The elected master shouldn't have changed, since the unlucky node never could have elected himself as - // master since m_m_n of 2 could never be satisfied. - assertMaster(masterNode, nodes); - } - - - /** - * Verify that nodes fault detection works after master (re) election - */ - public void testNodesFDAfterMasterReelection() throws Exception { - startCluster(4); - - logger.info("--> stopping current master"); - internalCluster().stopCurrentMasterNode(); - - ensureStableCluster(3); - - logger.info("--> reducing min master nodes to 2"); - assertAcked(client().admin().cluster().prepareUpdateSettings() - .setTransientSettings(Settings.builder().put(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING.getKey(), 2)) - .get()); - - String master = internalCluster().getMasterName(); - String nonMaster = null; - for (String node : internalCluster().getNodeNames()) { - if (!node.equals(master)) { - nonMaster = node; - } - } - - logger.info("--> isolating [{}]", nonMaster); - TwoPartitions partitions = isolateNode(nonMaster); - NetworkDisruption networkDisruption = addRandomDisruptionType(partitions); - networkDisruption.startDisrupting(); - - logger.info("--> waiting for master to remove it"); - ensureStableCluster(2, master); - } - - /** - * Verify that the proper block is applied when nodes loose their master - */ - public void testVerifyApiBlocksDuringPartition() throws Exception { - startCluster(3); - - // Makes sure that the get request can be executed on each node locally: - assertAcked(prepareCreate("test").setSettings(Settings.builder() - .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) - .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 2) - )); - - // Everything is stable now, it is now time to simulate evil... - // but first make sure we have no initializing shards and all is green - // (waiting for green here, because indexing / search in a yellow index is fine as long as no other nodes go down) - ensureGreen("test"); - - TwoPartitions partitions = TwoPartitions.random(random(), internalCluster().getNodeNames()); - NetworkDisruption networkDisruption = addRandomDisruptionType(partitions); - - assertEquals(1, partitions.getMinoritySide().size()); - final String isolatedNode = partitions.getMinoritySide().iterator().next(); - assertEquals(2, partitions.getMajoritySide().size()); - final String nonIsolatedNode = partitions.getMajoritySide().iterator().next(); - - // Simulate a network issue between the unlucky node and the rest of the cluster. - networkDisruption.startDisrupting(); - - - // The unlucky node must report *no* master node, since it can't connect to master and in fact it should - // continuously ping until network failures have been resolved. However - // It may a take a bit before the node detects it has been cut off from the elected master - logger.info("waiting for isolated node [{}] to have no master", isolatedNode); - assertNoMaster(isolatedNode, DiscoverySettings.NO_MASTER_BLOCK_WRITES, TimeValue.timeValueSeconds(10)); - - - logger.info("wait until elected master has been removed and a new 2 node cluster was from (via [{}])", isolatedNode); - ensureStableCluster(2, nonIsolatedNode); - - for (String node : partitions.getMajoritySide()) { - ClusterState nodeState = getNodeClusterState(node); - boolean success = true; - if (nodeState.nodes().getMasterNode() == null) { - success = false; - } - if (!nodeState.blocks().global().isEmpty()) { - success = false; - } - if (!success) { - fail("node [" + node + "] has no master or has blocks, despite of being on the right side of the partition. State dump:\n" - + nodeState); - } - } - - - networkDisruption.stopDisrupting(); - - // Wait until the master node sees al 3 nodes again. - ensureStableCluster(3, new TimeValue(DISRUPTION_HEALING_OVERHEAD.millis() + networkDisruption.expectedTimeToHeal().millis())); - - logger.info("Verify no master block with {} set to {}", DiscoverySettings.NO_MASTER_BLOCK_SETTING.getKey(), "all"); - client().admin().cluster().prepareUpdateSettings() - .setTransientSettings(Settings.builder().put(DiscoverySettings.NO_MASTER_BLOCK_SETTING.getKey(), "all")) - .get(); - - networkDisruption.startDisrupting(); - - - // The unlucky node must report *no* master node, since it can't connect to master and in fact it should - // continuously ping until network failures have been resolved. However - // It may a take a bit before the node detects it has been cut off from the elected master - logger.info("waiting for isolated node [{}] to have no master", isolatedNode); - assertNoMaster(isolatedNode, DiscoverySettings.NO_MASTER_BLOCK_ALL, TimeValue.timeValueSeconds(10)); - - // make sure we have stable cluster & cross partition recoveries are canceled by the removal of the missing node - // the unresponsive partition causes recoveries to only time out after 15m (default) and these will cause - // the test to fail due to unfreed resources - ensureStableCluster(2, nonIsolatedNode); - - } - - /** - * This test isolates the master from rest of the cluster, waits for a new master to be elected, restores the partition - * and verifies that all node agree on the new cluster state - */ - @TestLogging("_root:DEBUG,org.elasticsearch.cluster.service:TRACE,org.elasticsearch.gateway:TRACE,org.elasticsearch.indices.store:TRACE") - public void testIsolateMasterAndVerifyClusterStateConsensus() throws Exception { - final List nodes = startCluster(3); - - assertAcked(prepareCreate("test") - .setSettings(Settings.builder() - .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1 + randomInt(2)) - .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, randomInt(2)) - )); - - ensureGreen(); - String isolatedNode = internalCluster().getMasterName(); - TwoPartitions partitions = isolateNode(isolatedNode); - NetworkDisruption networkDisruption = addRandomDisruptionType(partitions); - networkDisruption.startDisrupting(); - - String nonIsolatedNode = partitions.getMajoritySide().iterator().next(); - - // make sure cluster reforms - ensureStableCluster(2, nonIsolatedNode); - - // make sure isolated need picks up on things. - assertNoMaster(isolatedNode, TimeValue.timeValueSeconds(40)); - - // restore isolation - networkDisruption.stopDisrupting(); - - for (String node : nodes) { - ensureStableCluster(3, new TimeValue(DISRUPTION_HEALING_OVERHEAD.millis() + networkDisruption.expectedTimeToHeal().millis()), - true, node); - } - - logger.info("issue a reroute"); - // trigger a reroute now, instead of waiting for the background reroute of RerouteService - assertAcked(client().admin().cluster().prepareReroute()); - // and wait for it to finish and for the cluster to stabilize - ensureGreen("test"); - - // verify all cluster states are the same - // use assert busy to wait for cluster states to be applied (as publish_timeout has low value) - assertBusy(() -> { - ClusterState state = null; - for (String node : nodes) { - ClusterState nodeState = getNodeClusterState(node); - if (state == null) { - state = nodeState; - continue; - } - // assert nodes are identical - try { - assertEquals("unequal versions", state.version(), nodeState.version()); - assertEquals("unequal node count", state.nodes().getSize(), nodeState.nodes().getSize()); - assertEquals("different masters ", state.nodes().getMasterNodeId(), nodeState.nodes().getMasterNodeId()); - assertEquals("different meta data version", state.metaData().version(), nodeState.metaData().version()); - assertEquals("different routing", state.routingTable().toString(), nodeState.routingTable().toString()); - } catch (AssertionError t) { - fail("failed comparing cluster state: " + t.getMessage() + "\n" + - "--- cluster state of node [" + nodes.get(0) + "]: ---\n" + state + - "\n--- cluster state [" + node + "]: ---\n" + nodeState); - } - - } - }); - } - - /** - * Test that we do not loose document whose indexing request was successful, under a randomly selected disruption scheme - * We also collect & report the type of indexing failures that occur. - *

- * This test is a superset of tests run in the Jepsen test suite, with the exception of versioned updates - */ - @TestLogging("_root:DEBUG,org.elasticsearch.action.bulk:TRACE,org.elasticsearch.action.get:TRACE,discovery:TRACE," + - "org.elasticsearch.cluster.service:TRACE,org.elasticsearch.indices.recovery:TRACE," + - "org.elasticsearch.indices.cluster:TRACE,org.elasticsearch.index.shard:TRACE") - public void testAckedIndexing() throws Exception { - - final int seconds = !(TEST_NIGHTLY && rarely()) ? 1 : 5; - final String timeout = seconds + "s"; - - final List nodes = startCluster(rarely() ? 5 : 3); - - assertAcked(prepareCreate("test") - .setSettings(Settings.builder() - .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1 + randomInt(2)) - .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, randomInt(2)) - )); - ensureGreen(); - - ServiceDisruptionScheme disruptionScheme = addRandomDisruptionScheme(); - logger.info("disruption scheme [{}] added", disruptionScheme); - - final ConcurrentHashMap ackedDocs = new ConcurrentHashMap<>(); // id -> node sent. - - final AtomicBoolean stop = new AtomicBoolean(false); - List indexers = new ArrayList<>(nodes.size()); - List semaphores = new ArrayList<>(nodes.size()); - final AtomicInteger idGenerator = new AtomicInteger(0); - final AtomicReference countDownLatchRef = new AtomicReference<>(); - final List exceptedExceptions = Collections.synchronizedList(new ArrayList()); - - logger.info("starting indexers"); - try { - for (final String node : nodes) { - final Semaphore semaphore = new Semaphore(0); - semaphores.add(semaphore); - final Client client = client(node); - final String name = "indexer_" + indexers.size(); - final int numPrimaries = getNumShards("test").numPrimaries; - Thread thread = new Thread(() -> { - while (!stop.get()) { - String id = null; - try { - if (!semaphore.tryAcquire(10, TimeUnit.SECONDS)) { - continue; - } - logger.info("[{}] Acquired semaphore and it has {} permits left", name, semaphore.availablePermits()); - try { - id = Integer.toString(idGenerator.incrementAndGet()); - int shard = Math.floorMod(Murmur3HashFunction.hash(id), numPrimaries); - logger.trace("[{}] indexing id [{}] through node [{}] targeting shard [{}]", name, id, node, shard); - IndexResponse response = - client.prepareIndex("test", "type", id).setSource("{}", XContentType.JSON).setTimeout(timeout).get(timeout); - assertEquals(DocWriteResponse.Result.CREATED, response.getResult()); - ackedDocs.put(id, node); - logger.trace("[{}] indexed id [{}] through node [{}]", name, id, node); - } catch (ElasticsearchException e) { - exceptedExceptions.add(e); - final String docId = id; - logger.trace( - (Supplier) - () -> new ParameterizedMessage("[{}] failed id [{}] through node [{}]", name, docId, node), e); - } finally { - countDownLatchRef.get().countDown(); - logger.trace("[{}] decreased counter : {}", name, countDownLatchRef.get().getCount()); - } - } catch (InterruptedException e) { - // fine - semaphore interrupt - } catch (AssertionError | Exception e) { - logger.info((Supplier) () -> new ParameterizedMessage("unexpected exception in background thread of [{}]", node), e); - } - } - }); - - thread.setName(name); - thread.start(); - indexers.add(thread); - } - - int docsPerIndexer = randomInt(3); - logger.info("indexing {} docs per indexer before partition", docsPerIndexer); - countDownLatchRef.set(new CountDownLatch(docsPerIndexer * indexers.size())); - for (Semaphore semaphore : semaphores) { - semaphore.release(docsPerIndexer); - } - assertTrue(countDownLatchRef.get().await(1, TimeUnit.MINUTES)); - - for (int iter = 1 + randomInt(2); iter > 0; iter--) { - logger.info("starting disruptions & indexing (iteration [{}])", iter); - disruptionScheme.startDisrupting(); - - docsPerIndexer = 1 + randomInt(5); - logger.info("indexing {} docs per indexer during partition", docsPerIndexer); - countDownLatchRef.set(new CountDownLatch(docsPerIndexer * indexers.size())); - Collections.shuffle(semaphores, random()); - for (Semaphore semaphore : semaphores) { - assertThat(semaphore.availablePermits(), equalTo(0)); - semaphore.release(docsPerIndexer); - } - logger.info("waiting for indexing requests to complete"); - assertTrue(countDownLatchRef.get().await(docsPerIndexer * seconds * 1000 + 2000, TimeUnit.MILLISECONDS)); - - logger.info("stopping disruption"); - disruptionScheme.stopDisrupting(); - for (String node : internalCluster().getNodeNames()) { - ensureStableCluster(nodes.size(), TimeValue.timeValueMillis(disruptionScheme.expectedTimeToHeal().millis() + - DISRUPTION_HEALING_OVERHEAD.millis()), true, node); - } - // in case of a bridge partition, shard allocation can fail "index.allocation.max_retries" times if the master - // is the super-connected node and recovery source and target are on opposite sides of the bridge - if (disruptionScheme instanceof NetworkDisruption && - ((NetworkDisruption) disruptionScheme).getDisruptedLinks() instanceof Bridge) { - assertAcked(client().admin().cluster().prepareReroute().setRetryFailed(true)); - } - ensureGreen("test"); - - logger.info("validating successful docs"); - assertBusy(() -> { - for (String node : nodes) { - try { - logger.debug("validating through node [{}] ([{}] acked docs)", node, ackedDocs.size()); - for (String id : ackedDocs.keySet()) { - assertTrue("doc [" + id + "] indexed via node [" + ackedDocs.get(id) + "] not found", - client(node).prepareGet("test", "type", id).setPreference("_local").get().isExists()); - } - } catch (AssertionError | NoShardAvailableActionException e) { - throw new AssertionError(e.getMessage() + " (checked via node [" + node + "]", e); - } - } - }, 30, TimeUnit.SECONDS); - - logger.info("done validating (iteration [{}])", iter); - } - } finally { - if (exceptedExceptions.size() > 0) { - StringBuilder sb = new StringBuilder(); - for (Exception e : exceptedExceptions) { - sb.append("\n").append(e.getMessage()); - } - logger.debug("Indexing exceptions during disruption: {}", sb); - } - logger.info("shutting down indexers"); - stop.set(true); - for (Thread indexer : indexers) { - indexer.interrupt(); - indexer.join(60000); - } - } - } - - /** - * Test that cluster recovers from a long GC on master that causes other nodes to elect a new one - */ - public void testMasterNodeGCs() throws Exception { - List nodes = startCluster(3, -1); - - String oldMasterNode = internalCluster().getMasterName(); - // a very long GC, but it's OK as we remove the disruption when it has had an effect - SingleNodeDisruption masterNodeDisruption = new IntermittentLongGCDisruption(random(), oldMasterNode, 100, 200, 30000, 60000); - internalCluster().setDisruptionScheme(masterNodeDisruption); - masterNodeDisruption.startDisrupting(); - - Set oldNonMasterNodesSet = new HashSet<>(nodes); - oldNonMasterNodesSet.remove(oldMasterNode); - - List oldNonMasterNodes = new ArrayList<>(oldNonMasterNodesSet); - - logger.info("waiting for nodes to de-elect master [{}]", oldMasterNode); - for (String node : oldNonMasterNodesSet) { - assertDifferentMaster(node, oldMasterNode); - } - - logger.info("waiting for nodes to elect a new master"); - ensureStableCluster(2, oldNonMasterNodes.get(0)); - - logger.info("waiting for any pinging to stop"); - assertDiscoveryCompleted(oldNonMasterNodes); - - // restore GC - masterNodeDisruption.stopDisrupting(); - ensureStableCluster(3, new TimeValue(DISRUPTION_HEALING_OVERHEAD.millis() + masterNodeDisruption.expectedTimeToHeal().millis()), false, oldNonMasterNodes.get(0)); - - // make sure all nodes agree on master - String newMaster = internalCluster().getMasterName(); - assertThat(newMaster, not(equalTo(oldMasterNode))); - assertMaster(newMaster, nodes); - } - - /** - * Tests that emulates a frozen elected master node that unfreezes and pushes his cluster state to other nodes - * that already are following another elected master node. These nodes should reject this cluster state and prevent - * them from following the stale master. - */ - @TestLogging("_root:DEBUG,org.elasticsearch.cluster.service:TRACE,org.elasticsearch.test.disruption:TRACE") - public void testStaleMasterNotHijackingMajority() throws Exception { - // 3 node cluster with unicast discovery and minimum_master_nodes set to 2: - final List nodes = startCluster(3, 2); - - // Save the current master node as old master node, because that node will get frozen - final String oldMasterNode = internalCluster().getMasterName(); - for (String node : nodes) { - ensureStableCluster(3, node); - } - assertMaster(oldMasterNode, nodes); - - // Simulating a painful gc by suspending all threads for a long time on the current elected master node. - SingleNodeDisruption masterNodeDisruption = new LongGCDisruption(random(), oldMasterNode); - - // Save the majority side - final List majoritySide = new ArrayList<>(nodes); - majoritySide.remove(oldMasterNode); - - // Keeps track of the previous and current master when a master node transition took place on each node on the majority side: - final Map>> masters = Collections.synchronizedMap(new HashMap>>()); - for (final String node : majoritySide) { - masters.put(node, new ArrayList>()); - internalCluster().getInstance(ClusterService.class, node).addListener(event -> { - DiscoveryNode previousMaster = event.previousState().nodes().getMasterNode(); - DiscoveryNode currentMaster = event.state().nodes().getMasterNode(); - if (!Objects.equals(previousMaster, currentMaster)) { - logger.info("node {} received new cluster state: {} \n and had previous cluster state: {}", node, event.state(), - event.previousState()); - String previousMasterNodeName = previousMaster != null ? previousMaster.getName() : null; - String currentMasterNodeName = currentMaster != null ? currentMaster.getName() : null; - masters.get(node).add(new Tuple<>(previousMasterNodeName, currentMasterNodeName)); - } - }); - } - - final CountDownLatch oldMasterNodeSteppedDown = new CountDownLatch(1); - internalCluster().getInstance(ClusterService.class, oldMasterNode).addListener(event -> { - if (event.state().nodes().getMasterNodeId() == null) { - oldMasterNodeSteppedDown.countDown(); - } - }); - - internalCluster().setDisruptionScheme(masterNodeDisruption); - logger.info("freezing node [{}]", oldMasterNode); - masterNodeDisruption.startDisrupting(); - - // Wait for the majority side to get stable - assertDifferentMaster(majoritySide.get(0), oldMasterNode); - assertDifferentMaster(majoritySide.get(1), oldMasterNode); - - // the test is periodically tripping on the following assertion. To find out which threads are blocking the nodes from making - // progress we print a stack dump - boolean failed = true; - try { - assertDiscoveryCompleted(majoritySide); - failed = false; - } finally { - if (failed) { - logger.error("discovery failed to complete, probably caused by a blocked thread: {}", - new HotThreads().busiestThreads(Integer.MAX_VALUE).ignoreIdleThreads(false).detect()); - } - } - - // The old master node is frozen, but here we submit a cluster state update task that doesn't get executed, - // but will be queued and once the old master node un-freezes it gets executed. - // The old master node will send this update + the cluster state where he is flagged as master to the other - // nodes that follow the new master. These nodes should ignore this update. - internalCluster().getInstance(ClusterService.class, oldMasterNode).submitStateUpdateTask("sneaky-update", new - ClusterStateUpdateTask(Priority.IMMEDIATE) { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - return ClusterState.builder(currentState).build(); - } - - @Override - public void onFailure(String source, Exception e) { - logger.warn((Supplier) () -> new ParameterizedMessage("failure [{}]", source), e); - } - }); - - // Save the new elected master node - final String newMasterNode = internalCluster().getMasterName(majoritySide.get(0)); - logger.info("new detected master node [{}]", newMasterNode); - - // Stop disruption - logger.info("Unfreeze node [{}]", oldMasterNode); - masterNodeDisruption.stopDisrupting(); - - oldMasterNodeSteppedDown.await(30, TimeUnit.SECONDS); - // Make sure that the end state is consistent on all nodes: - assertDiscoveryCompleted(nodes); - assertMaster(newMasterNode, nodes); - - assertThat(masters.size(), equalTo(2)); - for (Map.Entry>> entry : masters.entrySet()) { - String nodeName = entry.getKey(); - List> recordedMasterTransition = entry.getValue(); - assertThat("[" + nodeName + "] Each node should only record two master node transitions", recordedMasterTransition.size(), - equalTo(2)); - assertThat("[" + nodeName + "] First transition's previous master should be [null]", recordedMasterTransition.get(0).v1(), - equalTo(oldMasterNode)); - assertThat("[" + nodeName + "] First transition's current master should be [" + newMasterNode + "]", recordedMasterTransition - .get(0).v2(), nullValue()); - assertThat("[" + nodeName + "] Second transition's previous master should be [null]", recordedMasterTransition.get(1).v1(), - nullValue()); - assertThat("[" + nodeName + "] Second transition's current master should be [" + newMasterNode + "]", - recordedMasterTransition.get(1).v2(), equalTo(newMasterNode)); - } - } - - /** - * Test that a document which is indexed on the majority side of a partition, is available from the minority side, - * once the partition is healed - */ - public void testRejoinDocumentExistsInAllShardCopies() throws Exception { - List nodes = startCluster(3); - - assertAcked(prepareCreate("test") - .setSettings(Settings.builder() - .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) - .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 2) - ) - .get()); - ensureGreen("test"); - - nodes = new ArrayList<>(nodes); - Collections.shuffle(nodes, random()); - String isolatedNode = nodes.get(0); - String notIsolatedNode = nodes.get(1); - - TwoPartitions partitions = isolateNode(isolatedNode); - NetworkDisruption scheme = addRandomDisruptionType(partitions); - scheme.startDisrupting(); - ensureStableCluster(2, notIsolatedNode); - assertFalse(client(notIsolatedNode).admin().cluster().prepareHealth("test").setWaitForYellowStatus().get().isTimedOut()); - - - IndexResponse indexResponse = internalCluster().client(notIsolatedNode).prepareIndex("test", "type").setSource("field", "value") - .get(); - assertThat(indexResponse.getVersion(), equalTo(1L)); - - logger.info("Verifying if document exists via node[{}]", notIsolatedNode); - GetResponse getResponse = internalCluster().client(notIsolatedNode).prepareGet("test", "type", indexResponse.getId()) - .setPreference("_local") - .get(); - assertThat(getResponse.isExists(), is(true)); - assertThat(getResponse.getVersion(), equalTo(1L)); - assertThat(getResponse.getId(), equalTo(indexResponse.getId())); - - scheme.stopDisrupting(); - - ensureStableCluster(3); - ensureGreen("test"); - - for (String node : nodes) { - logger.info("Verifying if document exists after isolating node[{}] via node[{}]", isolatedNode, node); - getResponse = internalCluster().client(node).prepareGet("test", "type", indexResponse.getId()) - .setPreference("_local") - .get(); - assertThat(getResponse.isExists(), is(true)); - assertThat(getResponse.getVersion(), equalTo(1L)); - assertThat(getResponse.getId(), equalTo(indexResponse.getId())); - } - } - - /** - * A 4 node cluster with m_m_n set to 3 and each node has one unicast endpoint. One node partitions from the master node. - * The temporal unicast responses is empty. When partition is solved the one ping response contains a master node. - * The rejoining node should take this master node and connect. - */ - public void testUnicastSinglePingResponseContainsMaster() throws Exception { - List nodes = startCluster(4, -1, new int[]{0}); - // Figure out what is the elected master node - final String masterNode = internalCluster().getMasterName(); - logger.info("---> legit elected master node={}", masterNode); - List otherNodes = new ArrayList<>(nodes); - otherNodes.remove(masterNode); - otherNodes.remove(nodes.get(0)); // <-- Don't isolate the node that is in the unicast endpoint for all the other nodes. - final String isolatedNode = otherNodes.get(0); - - // Forcefully clean temporal response lists on all nodes. Otherwise the node in the unicast host list - // includes all the other nodes that have pinged it and the issue doesn't manifest - ZenPing zenPing = ((TestZenDiscovery) internalCluster().getInstance(Discovery.class)).getZenPing(); - if (zenPing instanceof UnicastZenPing) { - ((UnicastZenPing) zenPing).clearTemporalResponses(); - } - - // Simulate a network issue between the unlucky node and elected master node in both directions. - NetworkDisruption networkDisconnect = new NetworkDisruption(new TwoPartitions(masterNode, isolatedNode), - new NetworkDisconnect()); - setDisruptionScheme(networkDisconnect); - networkDisconnect.startDisrupting(); - // Wait until elected master has removed that the unlucky node... - ensureStableCluster(3, masterNode); - - // The isolate master node must report no master, so it starts with pinging - assertNoMaster(isolatedNode); - networkDisconnect.stopDisrupting(); - // Wait until the master node sees all 4 nodes again. - ensureStableCluster(4); - // The elected master shouldn't have changed, since the isolated node never could have elected himself as - // master since m_m_n of 3 could never be satisfied. - assertMaster(masterNode, nodes); - } - - public void testIsolatedUnicastNodes() throws Exception { - List nodes = startCluster(4, -1, new int[]{0}); - // Figure out what is the elected master node - final String unicastTarget = nodes.get(0); - - Set unicastTargetSide = new HashSet<>(); - unicastTargetSide.add(unicastTarget); - - Set restOfClusterSide = new HashSet<>(); - restOfClusterSide.addAll(nodes); - restOfClusterSide.remove(unicastTarget); - - // Forcefully clean temporal response lists on all nodes. Otherwise the node in the unicast host list - // includes all the other nodes that have pinged it and the issue doesn't manifest - ZenPing zenPing = ((TestZenDiscovery) internalCluster().getInstance(Discovery.class)).getZenPing(); - if (zenPing instanceof UnicastZenPing) { - ((UnicastZenPing) zenPing).clearTemporalResponses(); - } - - // Simulate a network issue between the unicast target node and the rest of the cluster - NetworkDisruption networkDisconnect = new NetworkDisruption(new TwoPartitions(unicastTargetSide, restOfClusterSide), - new NetworkDisconnect()); - setDisruptionScheme(networkDisconnect); - networkDisconnect.startDisrupting(); - // Wait until elected master has removed that the unlucky node... - ensureStableCluster(3, nodes.get(1)); - - // The isolate master node must report no master, so it starts with pinging - assertNoMaster(unicastTarget); - networkDisconnect.stopDisrupting(); - // Wait until the master node sees all 3 nodes again. - ensureStableCluster(4); - } - - - /** - * Test cluster join with issues in cluster state publishing * - */ - public void testClusterJoinDespiteOfPublishingIssues() throws Exception { - List nodes = startCluster(2, 1); - - String masterNode = internalCluster().getMasterName(); - String nonMasterNode; - if (masterNode.equals(nodes.get(0))) { - nonMasterNode = nodes.get(1); - } else { - nonMasterNode = nodes.get(0); - } - - DiscoveryNodes discoveryNodes = internalCluster().getInstance(ClusterService.class, nonMasterNode).state().nodes(); - - TransportService masterTranspotService = - internalCluster().getInstance(TransportService.class, discoveryNodes.getMasterNode().getName()); - - logger.info("blocking requests from non master [{}] to master [{}]", nonMasterNode, masterNode); - MockTransportService nonMasterTransportService = (MockTransportService) internalCluster().getInstance(TransportService.class, - nonMasterNode); - nonMasterTransportService.addFailToSendNoConnectRule(masterTranspotService); - - assertNoMaster(nonMasterNode); - - logger.info("blocking cluster state publishing from master [{}] to non master [{}]", masterNode, nonMasterNode); - MockTransportService masterTransportService = - (MockTransportService) internalCluster().getInstance(TransportService.class, masterNode); - TransportService localTransportService = - internalCluster().getInstance(TransportService.class, discoveryNodes.getLocalNode().getName()); - if (randomBoolean()) { - masterTransportService.addFailToSendNoConnectRule(localTransportService, PublishClusterStateAction.SEND_ACTION_NAME); - } else { - masterTransportService.addFailToSendNoConnectRule(localTransportService, PublishClusterStateAction.COMMIT_ACTION_NAME); - } - - logger.info("allowing requests from non master [{}] to master [{}], waiting for two join request", nonMasterNode, masterNode); - final CountDownLatch countDownLatch = new CountDownLatch(2); - nonMasterTransportService.addDelegate(masterTranspotService, new MockTransportService.DelegateTransport(nonMasterTransportService - .original()) { - @Override - protected void sendRequest(Connection connection, long requestId, String action, TransportRequest request, - TransportRequestOptions options) throws IOException { - if (action.equals(MembershipAction.DISCOVERY_JOIN_ACTION_NAME)) { - countDownLatch.countDown(); - } - super.sendRequest(connection, requestId, action, request, options); - } - - @Override - public Connection openConnection(DiscoveryNode node, ConnectionProfile profile) throws IOException { - return super.openConnection(node, profile); - } - - }); - - countDownLatch.await(); - - logger.info("waiting for cluster to reform"); - masterTransportService.clearRule(localTransportService); - nonMasterTransportService.clearRule(localTransportService); - - ensureStableCluster(2); - - // shutting down the nodes, to avoid the leakage check tripping - // on the states associated with the commit requests we may have dropped - internalCluster().stopRandomNonMasterNode(); - } - - // simulate handling of sending shard failure during an isolation - public void testSendingShardFailure() throws Exception { - List nodes = startCluster(3, 2); - String masterNode = internalCluster().getMasterName(); - List nonMasterNodes = nodes.stream().filter(node -> !node.equals(masterNode)).collect(Collectors.toList()); - String nonMasterNode = randomFrom(nonMasterNodes); - assertAcked(prepareCreate("test") - .setSettings(Settings.builder() - .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 3) - .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 2) - )); - ensureGreen(); - String nonMasterNodeId = internalCluster().clusterService(nonMasterNode).localNode().getId(); - - // fail a random shard - ShardRouting failedShard = - randomFrom(clusterService().state().getRoutingNodes().node(nonMasterNodeId).shardsWithState(ShardRoutingState.STARTED)); - ShardStateAction service = internalCluster().getInstance(ShardStateAction.class, nonMasterNode); - CountDownLatch latch = new CountDownLatch(1); - AtomicBoolean success = new AtomicBoolean(); - - String isolatedNode = randomBoolean() ? masterNode : nonMasterNode; - TwoPartitions partitions = isolateNode(isolatedNode); - // we cannot use the NetworkUnresponsive disruption type here as it will swallow the "shard failed" request, calling neither - // onSuccess nor onFailure on the provided listener. - NetworkLinkDisruptionType disruptionType = new NetworkDisconnect(); - NetworkDisruption networkDisruption = new NetworkDisruption(partitions, disruptionType); - setDisruptionScheme(networkDisruption); - networkDisruption.startDisrupting(); - - service.localShardFailed(failedShard, "simulated", new CorruptIndexException("simulated", (String) null), new - ShardStateAction.Listener() { - @Override - public void onSuccess() { - success.set(true); - latch.countDown(); - } - - @Override - public void onFailure(Exception e) { - success.set(false); - latch.countDown(); - assert false; - } - }); - - if (isolatedNode.equals(nonMasterNode)) { - assertNoMaster(nonMasterNode); - } else { - ensureStableCluster(2, nonMasterNode); - } - - // heal the partition - networkDisruption.removeAndEnsureHealthy(internalCluster()); - - // the cluster should stabilize - ensureStableCluster(3); - - latch.await(); - - // the listener should be notified - assertTrue(success.get()); - - // the failed shard should be gone - List shards = clusterService().state().getRoutingTable().allShards("test"); - for (ShardRouting shard : shards) { - assertThat(shard.allocationId(), not(equalTo(failedShard.allocationId()))); - } - } - - public void testClusterFormingWithASlowNode() throws Exception { - configureCluster(3, null, 2); - - SlowClusterStateProcessing disruption = new SlowClusterStateProcessing(random(), 0, 0, 1000, 2000); - - // don't wait for initial state, wat want to add the disruption while the cluster is forming.. - internalCluster().startNodes(3, Settings.builder().put(DiscoverySettings.PUBLISH_TIMEOUT_SETTING.getKey(), "3s").build()); - - logger.info("applying disruption while cluster is forming ..."); - - internalCluster().setDisruptionScheme(disruption); - disruption.startDisrupting(); - - ensureStableCluster(3); - } - - /** - * Adds an asymmetric break between a master and one of the nodes and makes - * sure that the node is removed form the cluster, that the node start pinging and that - * the cluster reforms when healed. - */ - public void testNodeNotReachableFromMaster() throws Exception { - startCluster(3); - - String masterNode = internalCluster().getMasterName(); - String nonMasterNode = null; - while (nonMasterNode == null) { - nonMasterNode = randomFrom(internalCluster().getNodeNames()); - if (nonMasterNode.equals(masterNode)) { - nonMasterNode = null; - } - } - - logger.info("blocking request from master [{}] to [{}]", masterNode, nonMasterNode); - MockTransportService masterTransportService = (MockTransportService) internalCluster().getInstance(TransportService.class, - masterNode); - if (randomBoolean()) { - masterTransportService.addUnresponsiveRule(internalCluster().getInstance(TransportService.class, nonMasterNode)); - } else { - masterTransportService.addFailToSendNoConnectRule(internalCluster().getInstance(TransportService.class, nonMasterNode)); - } - - logger.info("waiting for [{}] to be removed from cluster", nonMasterNode); - ensureStableCluster(2, masterNode); - - logger.info("waiting for [{}] to have no master", nonMasterNode); - assertNoMaster(nonMasterNode); - - logger.info("healing partition and checking cluster reforms"); - masterTransportService.clearAllRules(); - - ensureStableCluster(3); - } - - /** - * This test creates a scenario where a primary shard (0 replicas) relocates and is in POST_RECOVERY on the target - * node but already deleted on the source node. Search request should still work. - */ - public void testSearchWithRelocationAndSlowClusterStateProcessing() throws Exception { - // don't use DEFAULT settings (which can cause node disconnects on a slow CI machine) - configureCluster(Settings.EMPTY, 3, null, 1); - final String masterNode = internalCluster().startMasterOnlyNode(); - final String node_1 = internalCluster().startDataOnlyNode(); - - logger.info("--> creating index [test] with one shard and on replica"); - assertAcked(prepareCreate("test").setSettings( - Settings.builder().put(indexSettings()) - .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) - .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)) - ); - ensureGreen("test"); - - final String node_2 = internalCluster().startDataOnlyNode(); - List indexRequestBuilderList = new ArrayList<>(); - for (int i = 0; i < 100; i++) { - indexRequestBuilderList.add(client().prepareIndex().setIndex("test").setType("doc") - .setSource("{\"int_field\":1}", XContentType.JSON)); - } - indexRandom(true, indexRequestBuilderList); - - IndicesStoreIntegrationIT.relocateAndBlockCompletion(logger, "test", 0, node_1, node_2); - // now search for the documents and see if we get a reply - assertThat(client().prepareSearch().setSize(0).get().getHits().getTotalHits(), equalTo(100L)); - } - - public void testIndexImportedFromDataOnlyNodesIfMasterLostDataFolder() throws Exception { - // test for https://github.com/elastic/elasticsearch/issues/8823 - configureCluster(2, null, 1); - String masterNode = internalCluster().startMasterOnlyNode(Settings.EMPTY); - internalCluster().startDataOnlyNode(Settings.EMPTY); - - ensureStableCluster(2); - assertAcked(prepareCreate("index").setSettings(Settings.builder().put("index.number_of_replicas", 0))); - index("index", "doc", "1", jsonBuilder().startObject().field("text", "some text").endObject()); - ensureGreen(); - - internalCluster().restartNode(masterNode, new InternalTestCluster.RestartCallback() { - @Override - public boolean clearData(String nodeName) { - return true; - } - }); - - ensureGreen("index"); - assertTrue(client().prepareGet("index", "doc", "1").get().isExists()); - } - - /** - * Tests that indices are properly deleted even if there is a master transition in between. - * Test for https://github.com/elastic/elasticsearch/issues/11665 - */ - public void testIndicesDeleted() throws Exception { - final Settings settings = Settings.builder() - .put(DEFAULT_SETTINGS) - .put(DiscoverySettings.PUBLISH_TIMEOUT_SETTING.getKey(), "0s") // don't wait on isolated data node - .put(DiscoverySettings.COMMIT_TIMEOUT_SETTING.getKey(), "30s") // wait till cluster state is committed - .build(); - final String idxName = "test"; - configureCluster(settings, 3, null, 2); - final List allMasterEligibleNodes = internalCluster().startMasterOnlyNodes(2); - final String dataNode = internalCluster().startDataOnlyNode(); - ensureStableCluster(3); - assertAcked(prepareCreate("test")); - - final String masterNode1 = internalCluster().getMasterName(); - NetworkDisruption networkDisruption = new NetworkDisruption(new TwoPartitions(masterNode1, dataNode), - new NetworkUnresponsive()); - internalCluster().setDisruptionScheme(networkDisruption); - networkDisruption.startDisrupting(); - // We know this will time out due to the partition, we check manually below to not proceed until - // the delete has been applied to the master node and the master eligible node. - internalCluster().client(masterNode1).admin().indices().prepareDelete(idxName).setTimeout("0s").get(); - // Don't restart the master node until we know the index deletion has taken effect on master and the master eligible node. - assertBusy(() -> { - for (String masterNode : allMasterEligibleNodes) { - final ClusterState masterState = internalCluster().clusterService(masterNode).state(); - assertTrue("index not deleted on " + masterNode, masterState.metaData().hasIndex(idxName) == false); - } - }); - internalCluster().restartNode(masterNode1, InternalTestCluster.EMPTY_CALLBACK); - ensureYellow(); - assertFalse(client().admin().indices().prepareExists(idxName).get().isExists()); - } - - public void testElectMasterWithLatestVersion() throws Exception { - configureCluster(3, null, 2); - final Set nodes = new HashSet<>(internalCluster().startNodes(3)); - ensureStableCluster(3); - ServiceDisruptionScheme isolateAllNodes = new NetworkDisruption(new NetworkDisruption.IsolateAllNodes(nodes), new NetworkDisconnect()); - internalCluster().setDisruptionScheme(isolateAllNodes); - - logger.info("--> forcing a complete election to make sure \"preferred\" master is elected"); - isolateAllNodes.startDisrupting(); - for (String node : nodes) { - assertNoMaster(node); - } - internalCluster().clearDisruptionScheme(); - ensureStableCluster(3); - final String preferredMasterName = internalCluster().getMasterName(); - final DiscoveryNode preferredMaster = internalCluster().clusterService(preferredMasterName).localNode(); - for (String node : nodes) { - DiscoveryNode discoveryNode = internalCluster().clusterService(node).localNode(); - assertThat(discoveryNode.getId(), greaterThanOrEqualTo(preferredMaster.getId())); - } - - logger.info("--> preferred master is {}", preferredMaster); - final Set nonPreferredNodes = new HashSet<>(nodes); - nonPreferredNodes.remove(preferredMasterName); - final ServiceDisruptionScheme isolatePreferredMaster = - new NetworkDisruption( - new NetworkDisruption.TwoPartitions( - Collections.singleton(preferredMasterName), nonPreferredNodes), - new NetworkDisconnect()); - internalCluster().setDisruptionScheme(isolatePreferredMaster); - isolatePreferredMaster.startDisrupting(); - - assertAcked(client(randomFrom(nonPreferredNodes)).admin().indices().prepareCreate("test").setSettings( - INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1, - INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0 - )); - - internalCluster().clearDisruptionScheme(false); - internalCluster().setDisruptionScheme(isolateAllNodes); - - logger.info("--> forcing a complete election again"); - isolateAllNodes.startDisrupting(); - for (String node : nodes) { - assertNoMaster(node); - } - - isolateAllNodes.stopDisrupting(); - - final ClusterState state = client().admin().cluster().prepareState().get().getState(); - if (state.metaData().hasIndex("test") == false) { - fail("index 'test' was lost. current cluster state: " + state); - } - - } - - protected NetworkDisruption addRandomDisruptionType(TwoPartitions partitions) { - final NetworkLinkDisruptionType disruptionType; - if (randomBoolean()) { - disruptionType = new NetworkUnresponsive(); - } else { - disruptionType = new NetworkDisconnect(); - } - NetworkDisruption partition = new NetworkDisruption(partitions, disruptionType); - - setDisruptionScheme(partition); - - return partition; - } - - protected TwoPartitions isolateNode(String isolatedNode) { - Set side1 = new HashSet<>(); - Set side2 = new HashSet<>(Arrays.asList(internalCluster().getNodeNames())); - side1.add(isolatedNode); - side2.remove(isolatedNode); - - return new TwoPartitions(side1, side2); - } - - private ServiceDisruptionScheme addRandomDisruptionScheme() { - // TODO: add partial partitions - final DisruptedLinks disruptedLinks; - if (randomBoolean()) { - disruptedLinks = TwoPartitions.random(random(), internalCluster().getNodeNames()); - } else { - disruptedLinks = Bridge.random(random(), internalCluster().getNodeNames()); - } - final NetworkLinkDisruptionType disruptionType; - switch (randomInt(2)) { - case 0: - disruptionType = new NetworkUnresponsive(); - break; - case 1: - disruptionType = new NetworkDisconnect(); - break; - case 2: - disruptionType = NetworkDelay.random(random()); - break; - default: - throw new IllegalArgumentException(); - } - final ServiceDisruptionScheme scheme; - if (rarely()) { - scheme = new SlowClusterStateProcessing(random()); - } else { - scheme = new NetworkDisruption(disruptedLinks, disruptionType); - } - setDisruptionScheme(scheme); - return scheme; - } - - private ClusterState getNodeClusterState(String node) { - return client(node).admin().cluster().prepareState().setLocal(true).get().getState(); - } - - private void assertNoMaster(final String node) throws Exception { - assertNoMaster(node, null, TimeValue.timeValueSeconds(10)); - } - - private void assertNoMaster(final String node, TimeValue maxWaitTime) throws Exception { - assertNoMaster(node, null, maxWaitTime); - } - - private void assertNoMaster(final String node, @Nullable final ClusterBlock expectedBlocks, TimeValue maxWaitTime) throws Exception { - assertBusy(new Runnable() { - @Override - public void run() { - ClusterState state = getNodeClusterState(node); - assertNull("node [" + node + "] still has [" + state.nodes().getMasterNode() + "] as master", state.nodes().getMasterNode()); - if (expectedBlocks != null) { - for (ClusterBlockLevel level : expectedBlocks.levels()) { - assertTrue("node [" + node + "] does have level [" + level + "] in it's blocks", state.getBlocks().hasGlobalBlock - (level)); - } - } - } - }, maxWaitTime.getMillis(), TimeUnit.MILLISECONDS); - } - - private void assertDifferentMaster(final String node, final String oldMasterNode) throws Exception { - assertBusy(new Runnable() { - @Override - public void run() { - ClusterState state = getNodeClusterState(node); - String masterNode = null; - if (state.nodes().getMasterNode() != null) { - masterNode = state.nodes().getMasterNode().getName(); - } - logger.trace("[{}] master is [{}]", node, state.nodes().getMasterNode()); - assertThat("node [" + node + "] still has [" + masterNode + "] as master", - oldMasterNode, not(equalTo(masterNode))); - } - }, 10, TimeUnit.SECONDS); - } - - private void assertMaster(String masterNode, List nodes) throws Exception { - assertBusy(() -> { - for (String node : nodes) { - ClusterState state = getNodeClusterState(node); - String failMsgSuffix = "cluster_state:\n" + state; - assertThat("wrong node count on [" + node + "]. " + failMsgSuffix, state.nodes().getSize(), equalTo(nodes.size())); - String otherMasterNodeName = state.nodes().getMasterNode() != null ? state.nodes().getMasterNode().getName() : null; - assertThat("wrong master on node [" + node + "]. " + failMsgSuffix, otherMasterNodeName, equalTo(masterNode)); - } - }); - } - - private void assertDiscoveryCompleted(List nodes) throws InterruptedException { - for (final String node : nodes) { - assertTrue( - "node [" + node + "] is still joining master", - awaitBusy( - () -> !((ZenDiscovery) internalCluster().getInstance(Discovery.class, node)).joiningCluster(), - 30, - TimeUnit.SECONDS - ) - ); - } - } -} diff --git a/core/src/test/java/org/elasticsearch/discovery/MasterDisruptionIT.java b/core/src/test/java/org/elasticsearch/discovery/MasterDisruptionIT.java new file mode 100644 index 0000000000000..4225b6802ce96 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/discovery/MasterDisruptionIT.java @@ -0,0 +1,466 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.discovery; + +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.logging.log4j.util.Supplier; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateUpdateTask; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.Priority; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.discovery.zen.ElectMasterService; +import org.elasticsearch.discovery.zen.ZenDiscovery; +import org.elasticsearch.monitor.jvm.HotThreads; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.disruption.IntermittentLongGCDisruption; +import org.elasticsearch.test.disruption.LongGCDisruption; +import org.elasticsearch.test.disruption.NetworkDisruption; +import org.elasticsearch.test.disruption.NetworkDisruption.TwoPartitions; +import org.elasticsearch.test.disruption.SingleNodeDisruption; +import org.elasticsearch.test.junit.annotations.TestLogging; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +/** + * Tests relating to the loss of the master. + */ +@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0, transportClientRatio = 0, autoMinMasterNodes = false) +@TestLogging("_root:DEBUG,org.elasticsearch.cluster.service:TRACE") +public class MasterDisruptionIT extends AbstractDisruptionTestCase { + + /** + * Test that no split brain occurs under partial network partition. See https://github.com/elastic/elasticsearch/issues/2488 + */ + public void testFailWithMinimumMasterNodesConfigured() throws Exception { + List nodes = startCluster(3); + + // Figure out what is the elected master node + final String masterNode = internalCluster().getMasterName(); + logger.info("---> legit elected master node={}", masterNode); + + // Pick a node that isn't the elected master. + Set nonMasters = new HashSet<>(nodes); + nonMasters.remove(masterNode); + final String unluckyNode = randomFrom(nonMasters.toArray(Strings.EMPTY_ARRAY)); + + + // Simulate a network issue between the unlucky node and elected master node in both directions. + + NetworkDisruption networkDisconnect = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(masterNode, unluckyNode), + new NetworkDisruption.NetworkDisconnect()); + setDisruptionScheme(networkDisconnect); + networkDisconnect.startDisrupting(); + + // Wait until elected master has removed that the unlucky node... + ensureStableCluster(2, masterNode); + + // The unlucky node must report *no* master node, since it can't connect to master and in fact it should + // continuously ping until network failures have been resolved. However + // It may a take a bit before the node detects it has been cut off from the elected master + assertNoMaster(unluckyNode); + + networkDisconnect.stopDisrupting(); + + // Wait until the master node sees all 3 nodes again. + ensureStableCluster(3); + + // The elected master shouldn't have changed, since the unlucky node never could have elected himself as + // master since m_m_n of 2 could never be satisfied. + assertMaster(masterNode, nodes); + } + + /** + * Verify that nodes fault detection works after master (re) election + */ + public void testNodesFDAfterMasterReelection() throws Exception { + startCluster(4); + + logger.info("--> stopping current master"); + internalCluster().stopCurrentMasterNode(); + + ensureStableCluster(3); + + logger.info("--> reducing min master nodes to 2"); + assertAcked(client().admin().cluster().prepareUpdateSettings() + .setTransientSettings(Settings.builder().put(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING.getKey(), 2)) + .get()); + + String master = internalCluster().getMasterName(); + String nonMaster = null; + for (String node : internalCluster().getNodeNames()) { + if (!node.equals(master)) { + nonMaster = node; + } + } + + logger.info("--> isolating [{}]", nonMaster); + NetworkDisruption.TwoPartitions partitions = isolateNode(nonMaster); + NetworkDisruption networkDisruption = addRandomDisruptionType(partitions); + networkDisruption.startDisrupting(); + + logger.info("--> waiting for master to remove it"); + ensureStableCluster(2, master); + } + + /** + * Tests that emulates a frozen elected master node that unfreezes and pushes his cluster state to other nodes + * that already are following another elected master node. These nodes should reject this cluster state and prevent + * them from following the stale master. + */ + @TestLogging("_root:DEBUG,org.elasticsearch.cluster.service:TRACE,org.elasticsearch.test.disruption:TRACE") + public void testStaleMasterNotHijackingMajority() throws Exception { + // 3 node cluster with unicast discovery and minimum_master_nodes set to 2: + final List nodes = startCluster(3, 2); + + // Save the current master node as old master node, because that node will get frozen + final String oldMasterNode = internalCluster().getMasterName(); + for (String node : nodes) { + ensureStableCluster(3, node); + } + assertMaster(oldMasterNode, nodes); + + // Simulating a painful gc by suspending all threads for a long time on the current elected master node. + SingleNodeDisruption masterNodeDisruption = new LongGCDisruption(random(), oldMasterNode); + + // Save the majority side + final List majoritySide = new ArrayList<>(nodes); + majoritySide.remove(oldMasterNode); + + // Keeps track of the previous and current master when a master node transition took place on each node on the majority side: + final Map>> masters = Collections.synchronizedMap(new HashMap>>()); + for (final String node : majoritySide) { + masters.put(node, new ArrayList>()); + internalCluster().getInstance(ClusterService.class, node).addListener(event -> { + DiscoveryNode previousMaster = event.previousState().nodes().getMasterNode(); + DiscoveryNode currentMaster = event.state().nodes().getMasterNode(); + if (!Objects.equals(previousMaster, currentMaster)) { + logger.info("node {} received new cluster state: {} \n and had previous cluster state: {}", node, event.state(), + event.previousState()); + String previousMasterNodeName = previousMaster != null ? previousMaster.getName() : null; + String currentMasterNodeName = currentMaster != null ? currentMaster.getName() : null; + masters.get(node).add(new Tuple<>(previousMasterNodeName, currentMasterNodeName)); + } + }); + } + + final CountDownLatch oldMasterNodeSteppedDown = new CountDownLatch(1); + internalCluster().getInstance(ClusterService.class, oldMasterNode).addListener(event -> { + if (event.state().nodes().getMasterNodeId() == null) { + oldMasterNodeSteppedDown.countDown(); + } + }); + + internalCluster().setDisruptionScheme(masterNodeDisruption); + logger.info("freezing node [{}]", oldMasterNode); + masterNodeDisruption.startDisrupting(); + + // Wait for the majority side to get stable + assertDifferentMaster(majoritySide.get(0), oldMasterNode); + assertDifferentMaster(majoritySide.get(1), oldMasterNode); + + // the test is periodically tripping on the following assertion. To find out which threads are blocking the nodes from making + // progress we print a stack dump + boolean failed = true; + try { + assertDiscoveryCompleted(majoritySide); + failed = false; + } finally { + if (failed) { + logger.error("discovery failed to complete, probably caused by a blocked thread: {}", + new HotThreads().busiestThreads(Integer.MAX_VALUE).ignoreIdleThreads(false).detect()); + } + } + + // The old master node is frozen, but here we submit a cluster state update task that doesn't get executed, + // but will be queued and once the old master node un-freezes it gets executed. + // The old master node will send this update + the cluster state where he is flagged as master to the other + // nodes that follow the new master. These nodes should ignore this update. + internalCluster().getInstance(ClusterService.class, oldMasterNode).submitStateUpdateTask("sneaky-update", new + ClusterStateUpdateTask(Priority.IMMEDIATE) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + return ClusterState.builder(currentState).build(); + } + + @Override + public void onFailure(String source, Exception e) { + logger.warn((Supplier) () -> new ParameterizedMessage("failure [{}]", source), e); + } + }); + + // Save the new elected master node + final String newMasterNode = internalCluster().getMasterName(majoritySide.get(0)); + logger.info("new detected master node [{}]", newMasterNode); + + // Stop disruption + logger.info("Unfreeze node [{}]", oldMasterNode); + masterNodeDisruption.stopDisrupting(); + + oldMasterNodeSteppedDown.await(30, TimeUnit.SECONDS); + // Make sure that the end state is consistent on all nodes: + assertDiscoveryCompleted(nodes); + assertMaster(newMasterNode, nodes); + + assertThat(masters.size(), equalTo(2)); + for (Map.Entry>> entry : masters.entrySet()) { + String nodeName = entry.getKey(); + List> recordedMasterTransition = entry.getValue(); + assertThat("[" + nodeName + "] Each node should only record two master node transitions", recordedMasterTransition.size(), + equalTo(2)); + assertThat("[" + nodeName + "] First transition's previous master should be [null]", recordedMasterTransition.get(0).v1(), + equalTo(oldMasterNode)); + assertThat("[" + nodeName + "] First transition's current master should be [" + newMasterNode + "]", recordedMasterTransition + .get(0).v2(), nullValue()); + assertThat("[" + nodeName + "] Second transition's previous master should be [null]", recordedMasterTransition.get(1).v1(), + nullValue()); + assertThat("[" + nodeName + "] Second transition's current master should be [" + newMasterNode + "]", + recordedMasterTransition.get(1).v2(), equalTo(newMasterNode)); + } + } + + /** + * Test that cluster recovers from a long GC on master that causes other nodes to elect a new one + */ + public void testMasterNodeGCs() throws Exception { + List nodes = startCluster(3, -1); + + String oldMasterNode = internalCluster().getMasterName(); + // a very long GC, but it's OK as we remove the disruption when it has had an effect + SingleNodeDisruption masterNodeDisruption = new IntermittentLongGCDisruption(random(), oldMasterNode, 100, 200, 30000, 60000); + internalCluster().setDisruptionScheme(masterNodeDisruption); + masterNodeDisruption.startDisrupting(); + + Set oldNonMasterNodesSet = new HashSet<>(nodes); + oldNonMasterNodesSet.remove(oldMasterNode); + + List oldNonMasterNodes = new ArrayList<>(oldNonMasterNodesSet); + + logger.info("waiting for nodes to de-elect master [{}]", oldMasterNode); + for (String node : oldNonMasterNodesSet) { + assertDifferentMaster(node, oldMasterNode); + } + + logger.info("waiting for nodes to elect a new master"); + ensureStableCluster(2, oldNonMasterNodes.get(0)); + + logger.info("waiting for any pinging to stop"); + assertDiscoveryCompleted(oldNonMasterNodes); + + // restore GC + masterNodeDisruption.stopDisrupting(); + final TimeValue waitTime = new TimeValue(DISRUPTION_HEALING_OVERHEAD.millis() + masterNodeDisruption.expectedTimeToHeal().millis()); + ensureStableCluster(3, waitTime, false, oldNonMasterNodes.get(0)); + + // make sure all nodes agree on master + String newMaster = internalCluster().getMasterName(); + assertThat(newMaster, not(equalTo(oldMasterNode))); + assertMaster(newMaster, nodes); + } + + /** + * This test isolates the master from rest of the cluster, waits for a new master to be elected, restores the partition + * and verifies that all node agree on the new cluster state + */ + @TestLogging( + "_root:DEBUG," + + "org.elasticsearch.cluster.service:TRACE," + + "org.elasticsearch.gateway:TRACE," + + "org.elasticsearch.indices.store:TRACE") + public void testIsolateMasterAndVerifyClusterStateConsensus() throws Exception { + final List nodes = startCluster(3); + + assertAcked(prepareCreate("test") + .setSettings(Settings.builder() + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1 + randomInt(2)) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, randomInt(2)) + )); + + ensureGreen(); + String isolatedNode = internalCluster().getMasterName(); + TwoPartitions partitions = isolateNode(isolatedNode); + NetworkDisruption networkDisruption = addRandomDisruptionType(partitions); + networkDisruption.startDisrupting(); + + String nonIsolatedNode = partitions.getMajoritySide().iterator().next(); + + // make sure cluster reforms + ensureStableCluster(2, nonIsolatedNode); + + // make sure isolated need picks up on things. + assertNoMaster(isolatedNode, TimeValue.timeValueSeconds(40)); + + // restore isolation + networkDisruption.stopDisrupting(); + + for (String node : nodes) { + ensureStableCluster(3, new TimeValue(DISRUPTION_HEALING_OVERHEAD.millis() + networkDisruption.expectedTimeToHeal().millis()), + true, node); + } + + logger.info("issue a reroute"); + // trigger a reroute now, instead of waiting for the background reroute of RerouteService + assertAcked(client().admin().cluster().prepareReroute()); + // and wait for it to finish and for the cluster to stabilize + ensureGreen("test"); + + // verify all cluster states are the same + // use assert busy to wait for cluster states to be applied (as publish_timeout has low value) + assertBusy(() -> { + ClusterState state = null; + for (String node : nodes) { + ClusterState nodeState = getNodeClusterState(node); + if (state == null) { + state = nodeState; + continue; + } + // assert nodes are identical + try { + assertEquals("unequal versions", state.version(), nodeState.version()); + assertEquals("unequal node count", state.nodes().getSize(), nodeState.nodes().getSize()); + assertEquals("different masters ", state.nodes().getMasterNodeId(), nodeState.nodes().getMasterNodeId()); + assertEquals("different meta data version", state.metaData().version(), nodeState.metaData().version()); + assertEquals("different routing", state.routingTable().toString(), nodeState.routingTable().toString()); + } catch (AssertionError t) { + fail("failed comparing cluster state: " + t.getMessage() + "\n" + + "--- cluster state of node [" + nodes.get(0) + "]: ---\n" + state + + "\n--- cluster state [" + node + "]: ---\n" + nodeState); + } + + } + }); + } + + /** + * Verify that the proper block is applied when nodes loose their master + */ + public void testVerifyApiBlocksDuringPartition() throws Exception { + startCluster(3); + + // Makes sure that the get request can be executed on each node locally: + assertAcked(prepareCreate("test").setSettings(Settings.builder() + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 2) + )); + + // Everything is stable now, it is now time to simulate evil... + // but first make sure we have no initializing shards and all is green + // (waiting for green here, because indexing / search in a yellow index is fine as long as no other nodes go down) + ensureGreen("test"); + + TwoPartitions partitions = TwoPartitions.random(random(), internalCluster().getNodeNames()); + NetworkDisruption networkDisruption = addRandomDisruptionType(partitions); + + assertEquals(1, partitions.getMinoritySide().size()); + final String isolatedNode = partitions.getMinoritySide().iterator().next(); + assertEquals(2, partitions.getMajoritySide().size()); + final String nonIsolatedNode = partitions.getMajoritySide().iterator().next(); + + // Simulate a network issue between the unlucky node and the rest of the cluster. + networkDisruption.startDisrupting(); + + + // The unlucky node must report *no* master node, since it can't connect to master and in fact it should + // continuously ping until network failures have been resolved. However + // It may a take a bit before the node detects it has been cut off from the elected master + logger.info("waiting for isolated node [{}] to have no master", isolatedNode); + assertNoMaster(isolatedNode, DiscoverySettings.NO_MASTER_BLOCK_WRITES, TimeValue.timeValueSeconds(10)); + + + logger.info("wait until elected master has been removed and a new 2 node cluster was from (via [{}])", isolatedNode); + ensureStableCluster(2, nonIsolatedNode); + + for (String node : partitions.getMajoritySide()) { + ClusterState nodeState = getNodeClusterState(node); + boolean success = true; + if (nodeState.nodes().getMasterNode() == null) { + success = false; + } + if (!nodeState.blocks().global().isEmpty()) { + success = false; + } + if (!success) { + fail("node [" + node + "] has no master or has blocks, despite of being on the right side of the partition. State dump:\n" + + nodeState); + } + } + + + networkDisruption.stopDisrupting(); + + // Wait until the master node sees al 3 nodes again. + ensureStableCluster(3, new TimeValue(DISRUPTION_HEALING_OVERHEAD.millis() + networkDisruption.expectedTimeToHeal().millis())); + + logger.info("Verify no master block with {} set to {}", DiscoverySettings.NO_MASTER_BLOCK_SETTING.getKey(), "all"); + client().admin().cluster().prepareUpdateSettings() + .setTransientSettings(Settings.builder().put(DiscoverySettings.NO_MASTER_BLOCK_SETTING.getKey(), "all")) + .get(); + + networkDisruption.startDisrupting(); + + + // The unlucky node must report *no* master node, since it can't connect to master and in fact it should + // continuously ping until network failures have been resolved. However + // It may a take a bit before the node detects it has been cut off from the elected master + logger.info("waiting for isolated node [{}] to have no master", isolatedNode); + assertNoMaster(isolatedNode, DiscoverySettings.NO_MASTER_BLOCK_ALL, TimeValue.timeValueSeconds(10)); + + // make sure we have stable cluster & cross partition recoveries are canceled by the removal of the missing node + // the unresponsive partition causes recoveries to only time out after 15m (default) and these will cause + // the test to fail due to unfreed resources + ensureStableCluster(2, nonIsolatedNode); + + } + + void assertDiscoveryCompleted(List nodes) throws InterruptedException { + for (final String node : nodes) { + assertTrue( + "node [" + node + "] is still joining master", + awaitBusy( + () -> !((ZenDiscovery) internalCluster().getInstance(Discovery.class, node)).joiningCluster(), + 30, + TimeUnit.SECONDS + ) + ); + } + } + +} diff --git a/test/framework/src/main/java/org/elasticsearch/test/disruption/NetworkDisruption.java b/test/framework/src/main/java/org/elasticsearch/test/disruption/NetworkDisruption.java index 5d731b094d1da..8054847b642af 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/disruption/NetworkDisruption.java +++ b/test/framework/src/main/java/org/elasticsearch/test/disruption/NetworkDisruption.java @@ -410,6 +410,7 @@ public void removeDisruption(MockTransportService sourceTransportService, MockTr public TimeValue expectedTimeToHeal() { return TimeValue.timeValueMillis(0); } + } /** @@ -501,4 +502,5 @@ public String toString() { return "network delays for [" + delay + "]"; } } + } diff --git a/test/framework/src/test/java/org/elasticsearch/test/disruption/NetworkDisruptionIT.java b/test/framework/src/test/java/org/elasticsearch/test/disruption/NetworkDisruptionIT.java index da035a2712330..65850022aaaa5 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/disruption/NetworkDisruptionIT.java +++ b/test/framework/src/test/java/org/elasticsearch/test/disruption/NetworkDisruptionIT.java @@ -22,7 +22,6 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalTestCluster; -import org.elasticsearch.test.disruption.NetworkDisruption.NetworkUnresponsive; import org.elasticsearch.test.disruption.NetworkDisruption.TwoPartitions; import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.transport.TransportService; @@ -44,8 +43,8 @@ protected Collection> nodePlugins() { public void testNetworkPartitionWithNodeShutdown() throws IOException { internalCluster().ensureAtLeastNumDataNodes(2); String[] nodeNames = internalCluster().getNodeNames(); - NetworkDisruption networkDisruption = new NetworkDisruption(new TwoPartitions(nodeNames[0], nodeNames[1]), - new NetworkUnresponsive()); + NetworkDisruption networkDisruption = + new NetworkDisruption(new TwoPartitions(nodeNames[0], nodeNames[1]), new NetworkDisruption.NetworkUnresponsive()); internalCluster().setDisruptionScheme(networkDisruption); networkDisruption.startDisrupting(); internalCluster().stopRandomNode(InternalTestCluster.nameFilter(nodeNames[0])); From 0b688a873354b6e2f9bf94bb458e03f99fe05fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Mon, 15 May 2017 15:06:01 +0200 Subject: [PATCH 348/619] Small improvement in InternalAggregationTestCase test setup after changes in master (#24675) --- .../search/aggregations/AggregationsTests.java | 8 +++----- .../join/aggregations/InternalChildrenTests.java | 11 +++++++---- .../test/InternalAggregationTestCase.java | 9 +++++++-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java index a70c3c2d1b460..48fa42ad3ef85 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java @@ -115,8 +115,6 @@ private static List getAggsTests() { aggsTests.add(new InternalMissingTests()); aggsTests.add(new InternalNestedTests()); aggsTests.add(new InternalReverseNestedTests()); - // TODO can we find a way to include the children aggregation in this test? - //aggsTests.add(new InternalChildrenTests()); aggsTests.add(new InternalGlobalTests()); aggsTests.add(new InternalFilterTests()); aggsTests.add(new InternalSamplerTests()); @@ -129,7 +127,7 @@ private static List getAggsTests() { @Override protected NamedXContentRegistry xContentRegistry() { - return new NamedXContentRegistry(InternalAggregationTestCase.getNamedXContents()); + return new NamedXContentRegistry(InternalAggregationTestCase.getDefaultNamedXContents()); } @Before @@ -147,9 +145,9 @@ public void cleanUp() throws Exception { } public void testAllAggsAreBeingTested() { - assertEquals(InternalAggregationTestCase.getNamedXContents().size(), aggsTests.size()); + assertEquals(InternalAggregationTestCase.getDefaultNamedXContents().size(), aggsTests.size()); Set aggs = aggsTests.stream().map((testCase) -> testCase.createTestInstance().getType()).collect(Collectors.toSet()); - for (NamedXContentRegistry.Entry entry : InternalAggregationTestCase.getNamedXContents()) { + for (NamedXContentRegistry.Entry entry : InternalAggregationTestCase.getDefaultNamedXContents()) { assertTrue(aggs.contains(entry.name.getPreferredName())); } } diff --git a/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/InternalChildrenTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/InternalChildrenTests.java index 089f0a19bd94c..95cff14ef00cd 100644 --- a/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/InternalChildrenTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/InternalChildrenTests.java @@ -22,22 +22,25 @@ import org.elasticsearch.common.ParseField; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.NamedXContentRegistry.Entry; import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.InternalSingleBucketAggregationTestCase; import org.elasticsearch.search.aggregations.bucket.ParsedSingleBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import org.junit.BeforeClass; +import java.util.ArrayList; import java.util.List; import java.util.Map; public class InternalChildrenTests extends InternalSingleBucketAggregationTestCase { - @BeforeClass - public static void init() { - namedXContents.add(new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(ChildrenAggregationBuilder.NAME), + @Override + protected List getNamedXContents() { + List extendedNamedXContents = new ArrayList<>(super.getNamedXContents()); + extendedNamedXContents.add(new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(ChildrenAggregationBuilder.NAME), (p, c) -> ParsedChildren.fromXContent(p, (String) c))); + return extendedNamedXContents ; } @Override diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java index d85f41fd20c67..bd084bd5c08a3 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java @@ -130,7 +130,8 @@ public abstract class InternalAggregationTestCase new SearchModule(Settings.EMPTY, false, emptyList()).getNamedWriteables()); private final NamedXContentRegistry namedXContentRegistry = new NamedXContentRegistry(getNamedXContents()); - protected static final List namedXContents; + + private static final List namedXContents; static { Map> map = new HashMap<>(); map.put(CardinalityAggregationBuilder.NAME, (p, c) -> ParsedCardinality.fromXContent(p, (String) c)); @@ -175,7 +176,11 @@ public abstract class InternalAggregationTestCase .collect(Collectors.toList()); } - public static List getNamedXContents() { + public static List getDefaultNamedXContents() { + return namedXContents; + } + + protected List getNamedXContents() { return namedXContents; } From 578223f6792d65588ae6d83fbc4684ac63ff3b2b Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Mon, 15 May 2017 15:14:07 +0200 Subject: [PATCH 349/619] Make SignificantTerms.Bucket an interface rather than an abstract class (#24670) This commit changes SignificantTerms.Bucket so that it is not an abstract class anymore but an interface. It will be easier for the Java High Level Rest Client to provide its own implementation of SignificantTerms and SignificantTerms.Bucket. Also, it is now more coherent with the others aggregations. --- .../InternalMappedSignificantTerms.java | 8 +++- .../significant/InternalSignificantTerms.java | 36 +++++++------- .../significant/SignificantLongTerms.java | 2 +- .../significant/SignificantStringTerms.java | 3 +- .../bucket/significant/SignificantTerms.java | 48 ++++--------------- .../significant/UnmappedSignificantTerms.java | 10 +++- .../SignificantTermsSignificanceScoreIT.java | 4 +- 7 files changed, 47 insertions(+), 64 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalMappedSignificantTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalMappedSignificantTerms.java index 34fada101f81e..e8d856ebf24b9 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalMappedSignificantTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalMappedSignificantTerms.java @@ -26,6 +26,7 @@ import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.io.IOException; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -74,7 +75,12 @@ protected final void writeTermTypeInfoTo(StreamOutput out) throws IOException { } @Override - protected List getBucketsInternal() { + public Iterator iterator() { + return buckets.stream().map(bucket -> (SignificantTerms.Bucket) bucket).collect(Collectors.toList()).iterator(); + } + + @Override + public List getBuckets() { return buckets; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTerms.java index 6414a5eb8255a..82ba2826dccaa 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTerms.java @@ -33,20 +33,19 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; -import static java.util.Collections.unmodifiableList; - /** * Result of the significant terms aggregation. */ public abstract class InternalSignificantTerms, B extends InternalSignificantTerms.Bucket> extends InternalMultiBucketAggregation implements SignificantTerms, ToXContent { + @SuppressWarnings("PMD.ConstructorCallsOverridableMethod") - public abstract static class Bucket> extends SignificantTerms.Bucket { + public abstract static class Bucket> extends InternalMultiBucketAggregation.InternalBucket + implements SignificantTerms.Bucket { /** * Reads a bucket. Should be a constructor reference. */ @@ -55,14 +54,21 @@ public interface Reader> { B read(StreamInput in, long subsetSize, long supersetSize, DocValueFormat format) throws IOException; } + long subsetDf; + long subsetSize; + long supersetDf; + long supersetSize; long bucketOrd; - protected InternalAggregations aggregations; double score; + protected InternalAggregations aggregations; final transient DocValueFormat format; protected Bucket(long subsetDf, long subsetSize, long supersetDf, long supersetSize, InternalAggregations aggregations, DocValueFormat format) { - super(subsetDf, subsetSize, supersetDf, supersetSize); + this.subsetSize = subsetSize; + this.supersetSize = supersetSize; + this.subsetDf = subsetDf; + this.supersetDf = supersetDf; this.aggregations = aggregations; this.format = format; } @@ -71,7 +77,8 @@ protected Bucket(long subsetDf, long subsetSize, long supersetDf, long supersetS * Read from a stream. */ protected Bucket(StreamInput in, long subsetSize, long supersetSize, DocValueFormat format) { - super(in, subsetSize, supersetSize); + this.subsetSize = subsetSize; + this.supersetSize = supersetSize; this.format = format; } @@ -95,7 +102,7 @@ public long getSubsetSize() { return subsetSize; } - public void updateScore(SignificanceHeuristic significanceHeuristic) { + void updateScore(SignificanceHeuristic significanceHeuristic) { score = significanceHeuristic.getScore(subsetDf, subsetSize, supersetDf, supersetSize); } @@ -179,16 +186,7 @@ protected final void doWriteTo(StreamOutput out) throws IOException { protected abstract void writeTermTypeInfoTo(StreamOutput out) throws IOException; @Override - public Iterator iterator() { - return getBuckets().iterator(); - } - - @Override - public List getBuckets() { - return unmodifiableList(getBucketsInternal()); - } - - protected abstract List getBucketsInternal(); + public abstract List getBuckets(); @Override public InternalAggregation doReduce(List aggregations, ReduceContext reduceContext) { @@ -206,7 +204,7 @@ public InternalAggregation doReduce(List aggregations, Redu for (InternalAggregation aggregation : aggregations) { @SuppressWarnings("unchecked") InternalSignificantTerms terms = (InternalSignificantTerms) aggregation; - for (B bucket : terms.getBucketsInternal()) { + for (B bucket : terms.getBuckets()) { List existingBuckets = buckets.get(bucket.getKeyAsString()); if (existingBuckets == null) { existingBuckets = new ArrayList<>(aggregations.size()); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTerms.java index 6a714a6b035d6..c1556609f1de7 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTerms.java @@ -77,7 +77,7 @@ public Object getKey() { } @Override - int compareTerm(SignificantTerms.Bucket other) { + public int compareTerm(SignificantTerms.Bucket other) { return Long.compare(term, ((Number) other.getKey()).longValue()); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantStringTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantStringTerms.java index d62213bc47aca..84832325cef35 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantStringTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantStringTerms.java @@ -28,6 +28,7 @@ import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.io.IOException; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -82,7 +83,7 @@ public Number getKeyAsNumber() { } @Override - int compareTerm(SignificantTerms.Bucket other) { + public int compareTerm(SignificantTerms.Bucket other) { return termBytes.compareTo(((Bucket) other).termBytes); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTerms.java index aa0a25ac45ff0..ae09feef938f2 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTerms.java @@ -18,8 +18,6 @@ */ package org.elasticsearch.search.aggregations.bucket.significant; -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.search.aggregations.InternalMultiBucketAggregation; import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import java.util.List; @@ -28,54 +26,26 @@ * An aggregation that collects significant terms in comparison to a background set. */ public interface SignificantTerms extends MultiBucketsAggregation, Iterable { - abstract class Bucket extends InternalMultiBucketAggregation.InternalBucket { - long subsetDf; - long subsetSize; - long supersetDf; - long supersetSize; + interface Bucket extends MultiBucketsAggregation.Bucket { - Bucket(long subsetDf, long subsetSize, long supersetDf, long supersetSize) { - this.subsetSize = subsetSize; - this.supersetSize = supersetSize; - this.subsetDf = subsetDf; - this.supersetDf = supersetDf; - } + double getSignificanceScore(); - /** - * Read from a stream. - */ - protected Bucket(StreamInput in, long subsetSize, long supersetSize) { - this.subsetSize = subsetSize; - this.supersetSize = supersetSize; - } + Number getKeyAsNumber(); - abstract int compareTerm(SignificantTerms.Bucket other); + long getSubsetDf(); - public abstract double getSignificanceScore(); + long getSupersetDf(); - abstract Number getKeyAsNumber(); + long getSupersetSize(); - public long getSubsetDf() { - return subsetDf; - } - - public long getSupersetDf() { - return supersetDf; - } - - public long getSupersetSize() { - return supersetSize; - } - - public long getSubsetSize() { - return subsetSize; - } + long getSubsetSize(); + int compareTerm(SignificantTerms.Bucket other); } @Override - List getBuckets(); + List getBuckets(); /** * Get the bucket for the given term, or null if there is no such bucket. diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/UnmappedSignificantTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/UnmappedSignificantTerms.java index f9d8375af8c5b..7252909bf19e6 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/UnmappedSignificantTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/UnmappedSignificantTerms.java @@ -31,15 +31,18 @@ import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.io.IOException; +import java.util.Iterator; import java.util.List; import java.util.Map; +import static java.util.Collections.emptyIterator; import static java.util.Collections.emptyList; /** * Result of the running the significant terms aggregation on an unmapped field. */ public class UnmappedSignificantTerms extends InternalSignificantTerms { + public static final String NAME = "umsigterms"; /** @@ -117,7 +120,12 @@ protected Bucket[] createBucketsArray(int size) { } @Override - protected List getBucketsInternal() { + public Iterator iterator() { + return emptyIterator(); + } + + @Override + public List getBuckets() { return emptyList(); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java index 09cec9a458fce..bacd68f63793e 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java @@ -443,8 +443,8 @@ public void testScoresEqualForPositiveAndNegative(SignificanceHeuristic heuristi Aggregations aggregations = classBuckets.next().getAggregations(); SignificantTerms sigTerms = aggregations.get("mySignificantTerms"); - Collection classA = sigTerms.getBuckets(); - Iterator classBBucketIterator = sigTerms.getBuckets().iterator(); + List classA = sigTerms.getBuckets(); + Iterator classBBucketIterator = sigTerms.iterator(); assertThat(classA.size(), greaterThan(0)); for (SignificantTerms.Bucket classABucket : classA) { SignificantTerms.Bucket classBBucket = classBBucketIterator.next(); From 60505c9100e6a06a09b37cc99706957cea5abd42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Mon, 15 May 2017 15:26:35 +0200 Subject: [PATCH 350/619] Add parsing for InternalFilters aggregation (#24648) This adds parsing to the InternalFilters aggregation. --- .../bucket/filters/ParsedFilters.java | 141 ++++++++++++++++++ .../aggregations/AggregationsTests.java | 2 + .../bucket/filters/InternalFiltersTests.java | 30 ++-- .../test/InternalAggregationTestCase.java | 3 + 4 files changed, 167 insertions(+), 9 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/filters/ParsedFilters.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filters/ParsedFilters.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filters/ParsedFilters.java new file mode 100644 index 0000000000000..a77577a3ccce3 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filters/ParsedFilters.java @@ -0,0 +1,141 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.filters; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParserUtils; +import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.aggregations.Aggregations; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; + +public class ParsedFilters extends ParsedMultiBucketAggregation implements Filters { + + private Map bucketMap; + + @Override + public String getType() { + return FiltersAggregationBuilder.NAME; + } + + @Override + public List getBuckets() { + return buckets; + } + + @Override + public ParsedBucket getBucketByKey(String key) { + if (bucketMap == null) { + bucketMap = new HashMap<>(buckets.size()); + for (ParsedBucket bucket : buckets) { + bucketMap.put(bucket.getKey(), bucket); + } + } + return bucketMap.get(key); + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedFilters.class.getSimpleName(), true, ParsedFilters::new); + static { + declareMultiBucketAggregationFields(PARSER, + parser -> ParsedBucket.fromXContent(parser, false), + parser -> ParsedBucket.fromXContent(parser, true)); + } + + public static ParsedFilters fromXContent(XContentParser parser, String name) throws IOException { + ParsedFilters aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + // in case this is not a keyed aggregation, we need to add numeric keys to the buckets + if (aggregation.keyed == false) { + int i = 0; + for (ParsedBucket bucket : aggregation.buckets) { + assert bucket.key == null; + bucket.key = String.valueOf(i); + i++; + } + } + return aggregation; + } + + public static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements Filters.Bucket { + + private String key; + + @Override + public String getKey() { + return key; + } + + @Override + public String getKeyAsString() { + return key; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + if (isKeyed()) { + builder.startObject(key); + } else { + builder.startObject(); + } + builder.field(CommonFields.DOC_COUNT.getPreferredName(), getDocCount()); + getAggregations().toXContentInternal(builder, params); + builder.endObject(); + return builder; + } + + + static ParsedBucket fromXContent(XContentParser parser, boolean keyed) throws IOException { + final ParsedBucket bucket = new ParsedBucket(); + bucket.setKeyed(keyed); + XContentParser.Token token = parser.currentToken(); + String currentFieldName = parser.currentName(); + if (keyed) { + ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation); + bucket.key = currentFieldName; + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + } + + List aggregations = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (CommonFields.DOC_COUNT.getPreferredName().equals(currentFieldName)) { + bucket.setDocCount(parser.longValue()); + } + } else if (token == XContentParser.Token.START_OBJECT) { + aggregations.add(XContentParserUtils.parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Aggregation.class)); + } + } + bucket.setAggregations(new Aggregations(aggregations)); + return bucket; + } + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java index 48fa42ad3ef85..871cf2756f2fe 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java @@ -27,6 +27,7 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.search.aggregations.bucket.filter.InternalFilterTests; +import org.elasticsearch.search.aggregations.bucket.filters.InternalFiltersTests; import org.elasticsearch.search.aggregations.bucket.geogrid.InternalGeoHashGridTests; import org.elasticsearch.search.aggregations.bucket.global.InternalGlobalTests; import org.elasticsearch.search.aggregations.bucket.histogram.InternalDateHistogramTests; @@ -122,6 +123,7 @@ private static List getAggsTests() { aggsTests.add(new InternalRangeTests()); aggsTests.add(new InternalDateRangeTests()); aggsTests.add(new InternalGeoDistanceTests()); + aggsTests.add(new InternalFiltersTests()); return Collections.unmodifiableList(aggsTests); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filters/InternalFiltersTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filters/InternalFiltersTests.java index b21f201e79bec..03b5cb13d9c70 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filters/InternalFiltersTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/filters/InternalFiltersTests.java @@ -21,8 +21,9 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.InternalMultiBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import org.elasticsearch.test.InternalAggregationTestCase; import org.junit.Before; import java.util.ArrayList; @@ -30,30 +31,36 @@ import java.util.Map; import java.util.TreeMap; -public class InternalFiltersTests extends InternalAggregationTestCase { +public class InternalFiltersTests extends InternalMultiBucketAggregationTestCase { private boolean keyed; - private final List keys = new ArrayList<>(); + private List keys; @Override @Before public void setUp() throws Exception { super.setUp(); keyed = randomBoolean(); - int numKeys = randomIntBetween(1,10); - for (int i = 0; i < numKeys; i++) { - keys.add(randomAlphaOfLength(5)); + keys = new ArrayList<>(); + int numBuckets = randomIntBetween(1, 5); + for (int i = 0; i < numBuckets; i++) { + if (keyed) { + keys.add(randomAlphaOfLength(5)); + } else { + // this is what the FiltersAggregationBuilder ctor does when not providing KeyedFilter + keys.add(String.valueOf(i)); + } } - } @Override - protected InternalFilters createTestInstance(String name, List pipelineAggregators, Map metaData) { + protected InternalFilters createTestInstance(String name, List pipelineAggregators, Map metaData, + InternalAggregations aggregations) { final List buckets = new ArrayList<>(); for (int i = 0; i < keys.size(); ++i) { String key = keys.get(i); int docCount = randomIntBetween(0, 1000); - buckets.add( new InternalFilters.InternalBucket(key, docCount, InternalAggregations.EMPTY, keyed)); + buckets.add(new InternalFilters.InternalBucket(key, docCount, aggregations, keyed)); } return new InternalFilters(name, buckets, keyed, pipelineAggregators, metaData); } @@ -80,4 +87,9 @@ protected Reader instanceReader() { return InternalFilters::new; } + @Override + protected Class implementationClass() { + return ParsedFilters.class; + } + } diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java index bd084bd5c08a3..7bd02f82c5e6d 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java @@ -40,6 +40,8 @@ import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter; +import org.elasticsearch.search.aggregations.bucket.filters.FiltersAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.filters.ParsedFilters; import org.elasticsearch.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.geogrid.ParsedGeoHashGrid; import org.elasticsearch.search.aggregations.bucket.global.GlobalAggregationBuilder; @@ -170,6 +172,7 @@ public abstract class InternalAggregationTestCase map.put(RangeAggregationBuilder.NAME, (p, c) -> ParsedRange.fromXContent(p, (String) c)); map.put(DateRangeAggregationBuilder.NAME, (p, c) -> ParsedDateRange.fromXContent(p, (String) c)); map.put(GeoDistanceAggregationBuilder.NAME, (p, c) -> ParsedGeoDistance.fromXContent(p, (String) c)); + map.put(FiltersAggregationBuilder.NAME, (p, c) -> ParsedFilters.fromXContent(p, (String) c)); namedXContents = map.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) From dcb63ab8bcf0cd133e80c32770ce65fd1303087c Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Mon, 15 May 2017 16:52:02 +0200 Subject: [PATCH 351/619] Share XContent rendering code in significant terms aggregations (#24677) The rendering methods in String and Long Significant String aggregations and buckets are very similar. They can be factored out in the InternalSignificantTerms class an InternalMappedSignificantTerms class. --- .../InternalMappedSignificantTerms.java | 16 ++++++++++++ .../significant/InternalSignificantTerms.java | 18 +++++++++++++ .../significant/SignificantLongTerms.java | 19 +------------- .../significant/SignificantStringTerms.java | 26 ++----------------- 4 files changed, 37 insertions(+), 42 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalMappedSignificantTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalMappedSignificantTerms.java index e8d856ebf24b9..da346a8a1e43f 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalMappedSignificantTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalMappedSignificantTerms.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.bucket.significant.heuristics.SignificanceHeuristic; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; @@ -123,4 +124,19 @@ protected boolean doEquals(Object obj) { protected int doHashCode() { return Objects.hash(super.doHashCode(), format, subsetSize, supersetSize, significanceHeuristic, buckets, bucketMap); } + + @Override + public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + builder.field(CommonFields.DOC_COUNT.getPreferredName(), subsetSize); + builder.startArray(CommonFields.BUCKETS.getPreferredName()); + for (Bucket bucket : buckets) { + //There is a condition (presumably when only one shard has a bucket?) where reduce is not called + // and I end up with buckets that contravene the user's min_doc_count criteria in my reducer + if (bucket.subsetDf >= minDocCount) { + bucket.toXContent(builder, params); + } + } + builder.endArray(); + return builder; + } } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTerms.java index 82ba2826dccaa..acb655b19b462 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTerms.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.Aggregations; import org.elasticsearch.search.aggregations.InternalAggregation; @@ -43,6 +44,9 @@ public abstract class InternalSignificantTerms, B extends InternalSignificantTerms.Bucket> extends InternalMultiBucketAggregation implements SignificantTerms, ToXContent { + private static final String SCORE = "score"; + private static final String BG_COUNT = "bg_count"; + @SuppressWarnings("PMD.ConstructorCallsOverridableMethod") public abstract static class Bucket> extends InternalMultiBucketAggregation.InternalBucket implements SignificantTerms.Bucket { @@ -156,6 +160,20 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(getClass(), bucketOrd, aggregations, score, format); } + + @Override + public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + keyToXContent(builder); + builder.field(CommonFields.DOC_COUNT.getPreferredName(), getDocCount()); + builder.field(SCORE, score); + builder.field(BG_COUNT, supersetDf); + aggregations.toXContentInternal(builder, params); + builder.endObject(); + return builder; + } + + protected abstract XContentBuilder keyToXContent(XContentBuilder builder) throws IOException; } protected final int requiredSize; diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTerms.java index c1556609f1de7..c88c7c8e1fec8 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTerms.java @@ -97,17 +97,11 @@ Bucket newBucket(long subsetDf, long subsetSize, long supersetDf, long supersetS } @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); + protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException { builder.field(CommonFields.KEY.getPreferredName(), term); if (format != DocValueFormat.RAW) { builder.field(CommonFields.KEY_AS_STRING.getPreferredName(), format.format(term)); } - builder.field(CommonFields.DOC_COUNT.getPreferredName(), getDocCount()); - builder.field("score", score); - builder.field("bg_count", supersetDf); - aggregations.toXContentInternal(builder, params); - builder.endObject(); return builder; } @@ -159,17 +153,6 @@ protected SignificantLongTerms create(long subsetSize, long supersetSize, List= minDocCount) { - bucket.toXContent(builder, params); - } - } - builder.endArray(); - return builder; - } - @Override protected Bucket[] createBucketsArray(int size) { return new Bucket[size]; From e14ba81ac1bf4c576f96e62fbe1c6f6024b10f5b Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Mon, 15 May 2017 18:15:20 +0200 Subject: [PATCH 352/619] Make ObjectParser support string to boolean conversion (#24668) We generally accept string values when a boolean is expected. We've been doing that in our parsing code, but we missed that bit when moving parsing code to ObjectParser, which throws an error instead. This commit makes ObjectParser parse also string values into booleans. It throws an error in case the value is not `true` or `false`. Closes #21802 --- .../common/xcontent/ObjectParser.java | 2 +- .../common/xcontent/ObjectParserTests.java | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java b/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java index c07f4d11d8405..6dc6697594a5f 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java @@ -398,7 +398,7 @@ public enum ValueType { LONG(VALUE_NUMBER, VALUE_STRING), LONG_OR_NULL(VALUE_NUMBER, VALUE_STRING, VALUE_NULL), INT(VALUE_NUMBER, VALUE_STRING), - BOOLEAN(VALUE_BOOLEAN), + BOOLEAN(VALUE_BOOLEAN, VALUE_STRING), STRING_ARRAY(START_ARRAY, VALUE_STRING), FLOAT_ARRAY(START_ARRAY, VALUE_NUMBER, VALUE_STRING), DOUBLE_ARRAY(START_ARRAY, VALUE_NUMBER, VALUE_STRING), diff --git a/core/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java b/core/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java index 84c9518051c53..0d879e4813116 100644 --- a/core/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java +++ b/core/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java @@ -31,6 +31,7 @@ import java.net.URI; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import static org.hamcrest.Matchers.hasSize; @@ -108,7 +109,6 @@ public void testObjectOrDefault() throws IOException { s = objectParser.parse(parser, null); assertNotNull(s.object); assertEquals(s.object.test, 0); - } /** @@ -353,7 +353,11 @@ public void testAllVariants() throws IOException { builder.field("string_array_field", "5"); } boolean nullValue = randomBoolean(); - builder.field("boolean_field", nullValue); + if (randomBoolean()) { + builder.field("boolean_field", nullValue); + } else { + builder.field("boolean_field", Boolean.toString(nullValue)); + } builder.field("string_or_null", nullValue ? null : "5"); builder.endObject(); XContentParser parser = createParser(JsonXContent.jsonXContent, builder.string()); @@ -424,19 +428,19 @@ public void setString_or_null(String string_or_null) { objectParser.declareStringOrNull(TestStruct::setString_or_null, new ParseField("string_or_null")); objectParser.declareBoolean(TestStruct::setNull_value, new ParseField("boolean_field")); TestStruct parse = objectParser.parse(parser, new TestStruct(), null); - assertArrayEquals(parse.double_array_field.toArray(), Arrays.asList(2.1d).toArray()); + assertArrayEquals(parse.double_array_field.toArray(), Collections.singletonList(2.1d).toArray()); assertEquals(parse.double_field, 2.1d, 0.0d); - assertArrayEquals(parse.long_array_field.toArray(), Arrays.asList(4L).toArray()); + assertArrayEquals(parse.long_array_field.toArray(), Collections.singletonList(4L).toArray()); assertEquals(parse.long_field, 4L); - assertArrayEquals(parse.string_array_field.toArray(), Arrays.asList("5").toArray()); + assertArrayEquals(parse.string_array_field.toArray(), Collections.singletonList("5").toArray()); assertEquals(parse.string_field, "5"); - assertArrayEquals(parse.int_array_field.toArray(), Arrays.asList(1).toArray()); + assertArrayEquals(parse.int_array_field.toArray(), Collections.singletonList(1).toArray()); assertEquals(parse.int_field, 1); - assertArrayEquals(parse.float_array_field.toArray(), Arrays.asList(3.1f).toArray()); + assertArrayEquals(parse.float_array_field.toArray(), Collections.singletonList(3.1f).toArray()); assertEquals(parse.float_field, 3.1f, 0.0f); assertEquals(nullValue, parse.null_value); From 563e7ddc830c991dea210f5ed936b06f5f613ac7 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Mon, 15 May 2017 18:15:42 +0200 Subject: [PATCH 353/619] Pass over _routing value with more_like_this items to be retrieved (#24679) When retrieving documents to extract terms from as part of a more like this query, the _routing value can be set, yet it gets lost. That leads to not being able to retrieve the documents, hence more_like_this used to return no matches all the time. Closes #23699 --- .../common/lucene/search/MoreLikeThisQuery.java | 8 ++------ .../index/query/MoreLikeThisQueryBuilder.java | 7 ++++--- .../query/MoreLikeThisQueryBuilderTests.java | 15 +++++++++++++++ .../search/morelikethis/MoreLikeThisIT.java | 14 ++++++++++++++ 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/common/lucene/search/MoreLikeThisQuery.java b/core/src/main/java/org/elasticsearch/common/lucene/search/MoreLikeThisQuery.java index 81a8dfdd1e989..28971fc9ca45e 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/search/MoreLikeThisQuery.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/search/MoreLikeThisQuery.java @@ -224,10 +224,6 @@ public String[] getLikeTexts() { return likeText; } - public void setLikeText(String likeText) { - setLikeText(new String[]{likeText}); - } - public void setLikeText(String... likeText) { this.likeText = likeText; } @@ -236,7 +232,7 @@ public Fields[] getLikeFields() { return likeFields; } - public void setLikeText(Fields... likeFields) { + public void setLikeFields(Fields... likeFields) { this.likeFields = likeFields; } @@ -244,7 +240,7 @@ public void setLikeText(List likeText) { setLikeText(likeText.toArray(Strings.EMPTY_ARRAY)); } - public void setUnlikeText(Fields... unlikeFields) { + public void setUnlikeFields(Fields... unlikeFields) { this.unlikeFields = unlikeFields; } diff --git a/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java index fd83465b7e2f7..553adf88b76ed 100644 --- a/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java @@ -178,6 +178,7 @@ public Item() { this.index = copy.index; this.type = copy.type; this.id = copy.id; + this.routing = copy.routing; this.doc = copy.doc; this.xContentType = copy.xContentType; this.fields = copy.fields; @@ -343,7 +344,7 @@ XContentType xContentType() { /** * Convert this to a {@link TermVectorsRequest} for fetching the terms of the document. */ - public TermVectorsRequest toTermVectorsRequest() { + TermVectorsRequest toTermVectorsRequest() { TermVectorsRequest termVectorsRequest = new TermVectorsRequest(index, type, id) .selectedFields(fields) .routing(routing) @@ -1085,14 +1086,14 @@ private Query handleItems(QueryShardContext context, MoreLikeThisQuery mltQuery, // fetching the items with multi-termvectors API MultiTermVectorsResponse likeItemsResponse = fetchResponse(context.getClient(), likeItems); // getting the Fields for liked items - mltQuery.setLikeText(getFieldsFor(likeItemsResponse)); + mltQuery.setLikeFields(getFieldsFor(likeItemsResponse)); // getting the Fields for unliked items if (unlikeItems.length > 0) { MultiTermVectorsResponse unlikeItemsResponse = fetchResponse(context.getClient(), unlikeItems); org.apache.lucene.index.Fields[] unlikeFields = getFieldsFor(unlikeItemsResponse); if (unlikeFields.length > 0) { - mltQuery.setUnlikeText(unlikeFields); + mltQuery.setUnlikeFields(unlikeFields); } } diff --git a/core/src/test/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilderTests.java index c321ffa965a57..d19e8e32ffa06 100644 --- a/core/src/test/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilderTests.java @@ -23,6 +23,7 @@ import org.apache.lucene.index.Fields; import org.apache.lucene.index.MultiFields; import org.apache.lucene.index.memory.MemoryIndex; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.Query; import org.elasticsearch.ElasticsearchException; @@ -61,6 +62,7 @@ import static org.elasticsearch.index.query.QueryBuilders.moreLikeThisQuery; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.instanceOf; public class MoreLikeThisQueryBuilderTests extends AbstractQueryTestCase { @@ -264,6 +266,13 @@ private static Fields generateFields(String[] fieldNames, String text) throws IO protected void doAssertLuceneQuery(MoreLikeThisQueryBuilder queryBuilder, Query query, SearchContext context) throws IOException { if (queryBuilder.likeItems() != null && queryBuilder.likeItems().length > 0) { assertThat(query, instanceOf(BooleanQuery.class)); + BooleanQuery booleanQuery = (BooleanQuery) query; + for (BooleanClause booleanClause : booleanQuery) { + if (booleanClause.getQuery() instanceof MoreLikeThisQuery) { + MoreLikeThisQuery moreLikeThisQuery = (MoreLikeThisQuery) booleanClause.getQuery(); + assertThat(moreLikeThisQuery.getLikeFields().length, greaterThan(0)); + } + } } else { // we rely on integration tests for a deeper check here assertThat(query, instanceOf(MoreLikeThisQuery.class)); @@ -310,6 +319,12 @@ public void testItemSerialization() throws IOException { assertEquals(expectedItem, newItem); } + public void testItemCopy() throws IOException { + Item expectedItem = generateRandomItem(); + Item newItem = new Item(expectedItem); + assertEquals(expectedItem, newItem); + } + public void testItemFromXContent() throws IOException { Item expectedItem = generateRandomItem(); String json = expectedItem.toXContent(XContentFactory.jsonBuilder(), ToXContent.EMPTY_PARAMS).string(); diff --git a/core/src/test/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java b/core/src/test/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java index 0d2c5cf1bdc53..8d4f2921f27b9 100644 --- a/core/src/test/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java +++ b/core/src/test/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java @@ -623,4 +623,18 @@ public void testSelectFields() throws IOException, ExecutionException, Interrupt assertSearchResponse(response); assertHitCount(response, 1); } + + public void testWithRouting() throws IOException { + client().prepareIndex("index", "type", "1").setRouting("3").setSource("text", "this is a document").get(); + client().prepareIndex("index", "type", "2").setRouting("1").setSource("text", "this is another document").get(); + client().prepareIndex("index", "type", "3").setRouting("4").setSource("text", "this is yet another document").get(); + refresh("index"); + + Item item = new Item("index", "type", "2").routing("1"); + MoreLikeThisQueryBuilder moreLikeThisQueryBuilder = new MoreLikeThisQueryBuilder(new String[]{"text"}, null, new Item[]{item}); + moreLikeThisQueryBuilder.minTermFreq(1); + moreLikeThisQueryBuilder.minDocFreq(1); + SearchResponse searchResponse = client().prepareSearch("index").setQuery(moreLikeThisQueryBuilder).get(); + assertEquals(2, searchResponse.getHits().totalHits); + } } From 92ba969804875e6d683410db8c0aba49c05f4427 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 15 May 2017 12:40:24 -0400 Subject: [PATCH 354/619] Remove Jython hacks We had a hack in setting up permissions for tests to support testing the lang-python plugin. We also had a hack to prevent Log4j from loading a shaded version of Jansi provided by Jython. This plugin has been removed so these hacks are no longer necessary. Relates #24681 --- distribution/src/main/resources/config/jvm.options | 1 - .../elasticsearch/bootstrap/BootstrapForTesting.java | 12 ------------ .../main/java/org/elasticsearch/test/ESTestCase.java | 1 - 3 files changed, 14 deletions(-) diff --git a/distribution/src/main/resources/config/jvm.options b/distribution/src/main/resources/config/jvm.options index 293e4c092f60b..6d265fe776667 100644 --- a/distribution/src/main/resources/config/jvm.options +++ b/distribution/src/main/resources/config/jvm.options @@ -77,7 +77,6 @@ # log4j 2 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true --Dlog4j.skipJansi=true ## heap dumps diff --git a/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java b/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java index fbdab0b126b4a..62bf89f6d3116 100644 --- a/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java +++ b/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java @@ -101,18 +101,6 @@ public class BootstrapForTesting { // initialize paths the same exact way as bootstrap Permissions perms = new Permissions(); Security.addClasspathPermissions(perms); - // crazy jython - for (URL url : JarHell.parseClassPath()) { - Path path = PathUtils.get(url.toURI()); - - // crazy jython... - String filename = path.getFileName().toString(); - if (filename.contains("jython") && filename.endsWith(".jar")) { - // just enough so it won't fail when it does not exist - perms.add(new FilePermission(path.getParent().toString(), "read,readlink")); - perms.add(new FilePermission(path.getParent().resolve("Lib").toString(), "read,readlink")); - } - } // java.io.tmpdir Security.addPath(perms, "java.io.tmpdir", javaTmpDir, "read,readlink,write,delete"); // custom test config file diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index 56f898b9009f0..101a6d7cb452e 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -177,7 +177,6 @@ public static void resetPortCounter() { static { System.setProperty("log4j.shutdownHookEnabled", "false"); System.setProperty("log4j2.disable.jmx", "true"); - System.setProperty("log4j.skipJansi", "true"); // jython has this crazy shaded Jansi version that log4j2 tries to load // shutdown hook so that when the test JVM exits, logging is shutdown too Runtime.getRuntime().addShutdownHook(new Thread(() -> { From 557390d7d17f640455436e60ccf3d0cf7bc774da Mon Sep 17 00:00:00 2001 From: Vlad Holubiev Date: Mon, 15 May 2017 21:08:46 +0300 Subject: [PATCH 355/619] Fix typo in example (grades_count -> types_count) (#24635) Looks like `doc.grade` was used for examples before. But not anymore - https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-aggregations-metrics-valuecount-aggregation.html --- .../aggregations/metrics/valuecount-aggregation.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/aggregations/metrics/valuecount-aggregation.asciidoc b/docs/reference/aggregations/metrics/valuecount-aggregation.asciidoc index 8b67d64c87791..35eeeef14bcf7 100644 --- a/docs/reference/aggregations/metrics/valuecount-aggregation.asciidoc +++ b/docs/reference/aggregations/metrics/valuecount-aggregation.asciidoc @@ -33,7 +33,7 @@ Response: -------------------------------------------------- // TESTRESPONSE[s/\.\.\./"took": $body.took,"timed_out": false,"_shards": $body._shards,"hits": $body.hits,/] -The name of the aggregation (`grades_count` above) also serves as the key by which the aggregation result can be +The name of the aggregation (`types_count` above) also serves as the key by which the aggregation result can be retrieved from the returned response. ==== Script @@ -65,7 +65,7 @@ This will interpret the `script` parameter as an `inline` script with the `painl POST /sales/_search?size=0 { "aggs" : { - "grades_count" : { + "types_count" : { "value_count" : { "script" : { "file": "my_script", From 243635222a99c138e871844df343b55422e206ab Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Mon, 15 May 2017 14:28:59 -0400 Subject: [PATCH 356/619] Move ReindexAction class to core (#24684) This class is also needed for plugins to use reindex functionality. Relates to #24578 --- .../main/java/org/elasticsearch/index/reindex/ReindexAction.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {modules/reindex => core}/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java (100%) diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java b/core/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java similarity index 100% rename from modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java rename to core/src/main/java/org/elasticsearch/index/reindex/ReindexAction.java From f1fd5350c9920fdd07c4985a797a4b4906b3aa73 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 15 May 2017 12:58:11 -0700 Subject: [PATCH 357/619] Test: Fix how packaging test generates expected plugins --- qa/vagrant/build.gradle | 19 +++++++++++++++---- .../resources/packaging/utils/plugins.bash | 4 +--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/qa/vagrant/build.gradle b/qa/vagrant/build.gradle index aa118f1075b04..a00e2c8173648 100644 --- a/qa/vagrant/build.gradle +++ b/qa/vagrant/build.gradle @@ -19,12 +19,23 @@ apply plugin: 'elasticsearch.vagrant' -dependencies { - // Collect all the plugins - for (Project subproj : project.rootProject.subprojects) { - if (subproj.path.startsWith(':plugins:')) { +List plugins = [] +for (Project subproj : project.rootProject.subprojects) { + if (subproj.path.startsWith(':plugins:')) { + // add plugin as a dep + dependencies { bats project(path: "${subproj.path}", configuration: 'zip') } + plugins.add(subproj.name) + } +} +plugins = plugins.toSorted() + +setupBats { + doFirst { + File expectedPlugins = file('build/plugins/expected') + expectedPlugins.parentFile.mkdirs() + expectedPlugins.setText(plugins.join('\n'), 'UTF-8') } } diff --git a/qa/vagrant/src/test/resources/packaging/utils/plugins.bash b/qa/vagrant/src/test/resources/packaging/utils/plugins.bash index f8c40d2742036..14650b7130b12 100644 --- a/qa/vagrant/src/test/resources/packaging/utils/plugins.bash +++ b/qa/vagrant/src/test/resources/packaging/utils/plugins.bash @@ -173,8 +173,6 @@ install_and_check_plugin() { # $2 description of the source of the plugin list compare_plugins_list() { cat $1 | sort > /tmp/plugins - ls /elasticsearch/plugins/*/build.gradle | cut -d '/' -f 4 | - sort > /tmp/expected echo "Checking plugins from $2 (<) against expected plugins (>):" - diff /tmp/expected /tmp/plugins + diff -w /elasticsearch/qa/vagrant/build/plugins/expected /tmp/plugins } From 2e6dc04025154aa036c97fce7a9137f1e72fb586 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 15 May 2017 13:00:24 -0700 Subject: [PATCH 358/619] Re-enable centos-6 --- qa/vagrant/build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/qa/vagrant/build.gradle b/qa/vagrant/build.gradle index a00e2c8173648..52b6b2e511b9b 100644 --- a/qa/vagrant/build.gradle +++ b/qa/vagrant/build.gradle @@ -38,5 +38,3 @@ setupBats { expectedPlugins.setText(plugins.join('\n'), 'UTF-8') } } - -tasks."vagrantCentos6#packagingTest".onlyIf { false } // fails, see https://github.com/elastic/elasticsearch/issues/24645 From 43292979fd9c4debbf996d28796190c4fbe93a54 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Mon, 15 May 2017 13:37:46 -0700 Subject: [PATCH 359/619] Add New Security Script Settings (#24637) Settings are simplified to allowed_types and allowed_contexts. If a setting is not specified the default is to enable all for that setting. --- .../script/ScriptContextRegistry.java | 4 +- .../org/elasticsearch/script/ScriptModes.java | 60 +++++++++++++++- .../elasticsearch/script/ScriptService.java | 4 +- .../script/ScriptModesTests.java | 10 +-- .../script/ScriptServiceTests.java | 69 ++++++++++++++++++- .../migration/migrate_6_0/scripting.asciidoc | 5 ++ .../migration/migrate_6_0/settings.asciidoc | 5 ++ .../modules/scripting/security.asciidoc | 45 ++++++++++++ 8 files changed, 189 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/script/ScriptContextRegistry.java b/core/src/main/java/org/elasticsearch/script/ScriptContextRegistry.java index 2b7feeb8d7fdf..765be1d437818 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptContextRegistry.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptContextRegistry.java @@ -64,8 +64,8 @@ Collection scriptContexts() { /** * @return true if the provided {@link ScriptContext} is supported, false otherwise */ - boolean isSupportedContext(ScriptContext scriptContext) { - return scriptContexts.containsKey(scriptContext.getKey()); + boolean isSupportedContext(String scriptContext) { + return scriptContexts.containsKey(scriptContext); } //script contexts can be used in fine-grained settings, we need to be careful with what we allow here diff --git a/core/src/main/java/org/elasticsearch/script/ScriptModes.java b/core/src/main/java/org/elasticsearch/script/ScriptModes.java index ae12deaec97f5..357aff8016aa9 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptModes.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptModes.java @@ -19,13 +19,19 @@ package org.elasticsearch.script; +import org.apache.lucene.util.SetOnce; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.TreeMap; +import java.util.function.Function; /** * Holds the boolean indicating the enabled mode for each of the different scripting languages available, each script source and each @@ -38,12 +44,55 @@ public class ScriptModes { final Map scriptEnabled; - ScriptModes(ScriptSettings scriptSettings, Settings settings) { + private static final Setting> TYPES_ALLOWED_SETTING = + Setting.listSetting("script.types_allowed", Collections.emptyList(), Function.identity(), Setting.Property.NodeScope); + private static final Setting> CONTEXTS_ALLOWED_SETTING = + Setting.listSetting("script.contexts_allowed", Collections.emptyList(), Function.identity(), Setting.Property.NodeScope); + + private final Set typesAllowed; + private final Set contextsAllowed; + + ScriptModes(ScriptContextRegistry scriptContextRegistry, ScriptSettings scriptSettings, Settings settings) { HashMap scriptModes = new HashMap<>(); for (Setting scriptModeSetting : scriptSettings.getScriptLanguageSettings()) { scriptModes.put(scriptModeSetting.getKey(), scriptModeSetting.get(settings)); } this.scriptEnabled = Collections.unmodifiableMap(scriptModes); + + typesAllowed = TYPES_ALLOWED_SETTING.exists(settings) ? new HashSet<>() : null; + + if (typesAllowed != null) { + for (String settingType : TYPES_ALLOWED_SETTING.get(settings)) { + boolean found = false; + + for (ScriptType scriptType : ScriptType.values()) { + if (scriptType.getName().equals(settingType)) { + found = true; + typesAllowed.add(settingType); + + break; + } + } + + if (!found) { + throw new IllegalArgumentException( + "unknown script type [" + settingType + "] found in setting [" + TYPES_ALLOWED_SETTING.getKey() + "]."); + } + } + } + + contextsAllowed = CONTEXTS_ALLOWED_SETTING.exists(settings) ? new HashSet<>() : null; + + if (contextsAllowed != null) { + for (String settingContext : CONTEXTS_ALLOWED_SETTING.get(settings)) { + if (scriptContextRegistry.isSupportedContext(settingContext)) { + contextsAllowed.add(settingContext); + } else { + throw new IllegalArgumentException( + "unknown script context [" + settingContext + "] found in setting [" + CONTEXTS_ALLOWED_SETTING.getKey() + "]."); + } + } + } } /** @@ -60,6 +109,15 @@ public boolean getScriptEnabled(String lang, ScriptType scriptType, ScriptContex if (NativeScriptEngine.NAME.equals(lang)) { return true; } + + if (typesAllowed != null && typesAllowed.contains(scriptType.getName()) == false) { + throw new IllegalArgumentException("[" + scriptType.getName() + "] scripts cannot be executed"); + } + + if (contextsAllowed != null && contextsAllowed.contains(scriptContext.getKey()) == false) { + throw new IllegalArgumentException("[" + scriptContext.getKey() + "] scripts cannot be executed"); + } + Boolean scriptMode = scriptEnabled.get(getKey(lang, scriptType, scriptContext)); if (scriptMode == null) { throw new IllegalArgumentException("script mode not found for lang [" + lang + "], script_type [" + scriptType + "], operation [" + scriptContext.getKey() + "]"); diff --git a/core/src/main/java/org/elasticsearch/script/ScriptService.java b/core/src/main/java/org/elasticsearch/script/ScriptService.java index b52e135be5b93..b30da8f464ae9 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptService.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptService.java @@ -152,7 +152,7 @@ public ScriptService(Settings settings, Environment env, this.scriptEnginesByLang = unmodifiableMap(enginesByLangBuilder); this.scriptEnginesByExt = unmodifiableMap(enginesByExtBuilder); - this.scriptModes = new ScriptModes(scriptSettings, settings); + this.scriptModes = new ScriptModes(scriptContextRegistry, scriptSettings, settings); // add file watcher for static scripts scriptsDirectory = env.scriptsFile(); @@ -511,7 +511,7 @@ private boolean isAnyScriptContextEnabled(String lang, ScriptType scriptType) { private boolean canExecuteScript(String lang, ScriptType scriptType, ScriptContext scriptContext) { assert lang != null; - if (scriptContextRegistry.isSupportedContext(scriptContext) == false) { + if (scriptContextRegistry.isSupportedContext(scriptContext.getKey()) == false) { throw new IllegalArgumentException("script context [" + scriptContext.getKey() + "] not supported"); } return scriptModes.getScriptEnabled(lang, scriptType, scriptContext); diff --git a/core/src/test/java/org/elasticsearch/script/ScriptModesTests.java b/core/src/test/java/org/elasticsearch/script/ScriptModesTests.java index 63bd3fd377773..33d8c89ecd8b2 100644 --- a/core/src/test/java/org/elasticsearch/script/ScriptModesTests.java +++ b/core/src/test/java/org/elasticsearch/script/ScriptModesTests.java @@ -97,14 +97,14 @@ public void assertAllSettingsWereChecked() { } public void testDefaultSettings() { - this.scriptModes = new ScriptModes(scriptSettings, Settings.EMPTY); + this.scriptModes = new ScriptModes(scriptContextRegistry, scriptSettings, Settings.EMPTY); assertScriptModesAllOps(true, ScriptType.FILE); assertScriptModesAllOps(false, ScriptType.STORED, ScriptType.INLINE); } public void testMissingSetting() { assertAllSettingsWereChecked = false; - this.scriptModes = new ScriptModes(scriptSettings, Settings.EMPTY); + this.scriptModes = new ScriptModes(scriptContextRegistry, scriptSettings, Settings.EMPTY); try { scriptModes.getScriptEnabled("non_existing", randomFrom(ScriptType.values()), randomFrom(scriptContexts)); fail("Expected IllegalArgumentException"); @@ -131,7 +131,7 @@ public void testScriptTypeGenericSettings() { builder.put("script" + "." + randomScriptTypes[i].getName(), randomScriptModes[i]); deprecated.add("script" + "." + randomScriptTypes[i].getName()); } - this.scriptModes = new ScriptModes(scriptSettings, builder.build()); + this.scriptModes = new ScriptModes(scriptContextRegistry, scriptSettings, builder.build()); for (int i = 0; i < randomInt; i++) { assertScriptModesAllOps(randomScriptModes[i], randomScriptTypes[i]); @@ -167,7 +167,7 @@ public void testScriptContextGenericSettings() { builder.put("script" + "." + randomScriptContexts[i].getKey(), randomScriptModes[i]); deprecated.add("script" + "." + randomScriptContexts[i].getKey()); } - this.scriptModes = new ScriptModes(scriptSettings, builder.build()); + this.scriptModes = new ScriptModes(scriptContextRegistry, scriptSettings, builder.build()); for (int i = 0; i < randomInt; i++) { assertScriptModesAllTypes(randomScriptModes[i], randomScriptContexts[i]); @@ -187,7 +187,7 @@ public void testConflictingScriptTypeAndOpGenericSettings() { .put("script.stored", "true") .put("script.inline", "true"); //operations generic settings have precedence over script type generic settings - this.scriptModes = new ScriptModes(scriptSettings, builder.build()); + this.scriptModes = new ScriptModes(scriptContextRegistry, scriptSettings, builder.build()); assertScriptModesAllTypes(false, scriptContext); ScriptContext[] complementOf = complementOf(scriptContext); assertScriptModes(true, new ScriptType[]{ScriptType.FILE, ScriptType.STORED}, complementOf); diff --git a/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java b/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java index c4fd39550b507..55cf11c98697e 100644 --- a/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java +++ b/core/src/test/java/org/elasticsearch/script/ScriptServiceTests.java @@ -216,6 +216,69 @@ public void testInlineScriptCompiledOnceCache() throws IOException { assertThat(compiledScript1.compiled(), sameInstance(compiledScript2.compiled())); } + public void testAllowAllScriptTypeSettings() throws IOException { + buildScriptService(Settings.EMPTY); + + assertCompileAccepted("painless", "script", ScriptType.INLINE, ScriptContext.Standard.SEARCH); + assertCompileAccepted("painless", "script", ScriptType.STORED, ScriptContext.Standard.SEARCH); + } + + public void testAllowAllScriptContextSettings() throws IOException { + buildScriptService(Settings.EMPTY); + + assertCompileAccepted("painless", "script", ScriptType.INLINE, ScriptContext.Standard.SEARCH); + assertCompileAccepted("painless", "script", ScriptType.INLINE, ScriptContext.Standard.AGGS); + assertCompileAccepted("painless", "script", ScriptType.INLINE, ScriptContext.Standard.UPDATE); + assertCompileAccepted("painless", "script", ScriptType.INLINE, ScriptContext.Standard.INGEST); + } + + public void testAllowSomeScriptTypeSettings() throws IOException { + Settings.Builder builder = Settings.builder(); + builder.put("script.types_allowed", "inline"); + builder.put("script.engine.painless.stored", false); + buildScriptService(builder.build()); + + assertCompileAccepted("painless", "script", ScriptType.INLINE, ScriptContext.Standard.SEARCH); + assertCompileRejected("painless", "script", ScriptType.STORED, ScriptContext.Standard.SEARCH); + + assertSettingDeprecationsAndWarnings( + ScriptSettingsTests.buildDeprecatedSettingsArray(scriptSettings, "script.engine.painless.stored")); + } + + public void testAllowSomeScriptContextSettings() throws IOException { + Settings.Builder builder = Settings.builder(); + builder.put("script.contexts_allowed", "search, aggs"); + builder.put("script.update", false); + buildScriptService(builder.build()); + + assertCompileAccepted("painless", "script", ScriptType.INLINE, ScriptContext.Standard.SEARCH); + assertCompileAccepted("painless", "script", ScriptType.INLINE, ScriptContext.Standard.AGGS); + assertCompileRejected("painless", "script", ScriptType.INLINE, ScriptContext.Standard.UPDATE); + + assertSettingDeprecationsAndWarnings( + ScriptSettingsTests.buildDeprecatedSettingsArray(scriptSettings, "script.update")); + } + + public void testAllowNoScriptTypeSettings() throws IOException { + Settings.Builder builder = Settings.builder(); + builder.put("script.types_allowed", ""); + buildScriptService(builder.build()); + + assertCompileRejected("painless", "script", ScriptType.INLINE, ScriptContext.Standard.SEARCH); + assertCompileRejected("painless", "script", ScriptType.STORED, ScriptContext.Standard.SEARCH); + } + + public void testAllowNoScriptContextSettings() throws IOException { + Settings.Builder builder = Settings.builder(); + builder.put("script.contexts_allowed", ""); + buildScriptService(builder.build()); + + assertCompileRejected("painless", "script", ScriptType.INLINE, ScriptContext.Standard.SEARCH); + assertCompileRejected("painless", "script", ScriptType.INLINE, ScriptContext.Standard.AGGS); + assertCompileRejected("painless", "script", ScriptType.INLINE, ScriptContext.Standard.UPDATE); + assertCompileRejected("painless", "script", ScriptType.INLINE, ScriptContext.Standard.INGEST); + } + public void testDefaultBehaviourFineGrainedSettings() throws IOException { Settings.Builder builder = Settings.builder(); //rarely inject the default settings, which have no effect @@ -345,7 +408,7 @@ public void testCompileNonRegisteredContext() throws IOException { do { pluginName = randomAlphaOfLength(randomIntBetween(1, 10)); unknownContext = randomAlphaOfLength(randomIntBetween(1, 30)); - } while(scriptContextRegistry.isSupportedContext(new ScriptContext.Plugin(pluginName, unknownContext))); + } while(scriptContextRegistry.isSupportedContext(new ScriptContext.Plugin(pluginName, unknownContext).getKey())); String type = scriptEngine.getType(); try { @@ -491,8 +554,8 @@ private void assertCompileRejected(String lang, String script, ScriptType script try { scriptService.compile(new Script(scriptType, lang, script, Collections.emptyMap()), scriptContext); fail("compile should have been rejected for lang [" + lang + "], script_type [" + scriptType + "], scripted_op [" + scriptContext + "]"); - } catch(IllegalStateException e) { - //all good + } catch (IllegalArgumentException | IllegalStateException e) { + // pass } } diff --git a/docs/reference/migration/migrate_6_0/scripting.asciidoc b/docs/reference/migration/migrate_6_0/scripting.asciidoc index 075678b3ab3f7..09580401577c7 100644 --- a/docs/reference/migration/migrate_6_0/scripting.asciidoc +++ b/docs/reference/migration/migrate_6_0/scripting.asciidoc @@ -12,3 +12,8 @@ elasticsearch 5.0 and have now been removed. Use painless instead. milliseconds since epoch as a `long`. The same is true for `doc.some_date_field[some_number]`. Use `doc.some_date_field.value.millis` to fetch the milliseconds since epoch if you need it. + +==== Script Settings + +All of the existing scripting security settings have been deprecated. Instead +they are replaced with `script.allowed_types` and `script.allowed_contexts`. \ No newline at end of file diff --git a/docs/reference/migration/migrate_6_0/settings.asciidoc b/docs/reference/migration/migrate_6_0/settings.asciidoc index fa010aab1fe9e..4147cd5721a44 100644 --- a/docs/reference/migration/migrate_6_0/settings.asciidoc +++ b/docs/reference/migration/migrate_6_0/settings.asciidoc @@ -66,3 +66,8 @@ and `http.tcp.blocking_server` settings are not recognized anymore. The `base` similarity is now ignored as coords and query normalization have been removed. If provided, this setting will be ignored and issue a deprecation warning. + +==== Script Settings + +All of the existing scripting security settings have been deprecated. Instead +they are replaced with `script.allowed_types` and `script.allowed_contexts`. \ No newline at end of file diff --git a/docs/reference/modules/scripting/security.asciidoc b/docs/reference/modules/scripting/security.asciidoc index eaeb1c62143f3..3e09943b46f6d 100644 --- a/docs/reference/modules/scripting/security.asciidoc +++ b/docs/reference/modules/scripting/security.asciidoc @@ -87,6 +87,51 @@ change from the defaults described above. You should be very, very careful when allowing more than the defaults. Any extra permissions weakens the total security of the Elasticsearch deployment. +[[allowed-script-types-setting]] +[float] +=== Allowed script types setting + +By default all script types are allowed to be executed. This can be modified using the +setting `script.allowed_types`. Only the types specified as part of the setting will be +allowed to be executed. + +[source,yaml] +---- +script.allowed_types: inline <1> +---- +<1> This will allow only inline scripts to be executed but not stored scripts +(or any other types). + +[[allowed-script-contexts-setting]] +[float] +=== Allowed script contexts setting + +By default all script contexts are allowed to be executed. This can be modified using the +setting `script.allowed_contexts`. Only the contexts specified as part of the setting will +be allowed to be executed. + +[source,yaml] +---- +script.allowed_contexts: search, update <1> +---- +<1> This will allow only search and update scripts to be executed but not +aggs or plugin scripts (or any other contexts). + +[[deprecated-script=settings]] +[float] +=== Deprecated script settings + +The following settings have all been deprecated and will be removed in 6.0: + + * <> + * <> + * <> + +Use the following instead: + + * <> + * <> + [[security-script-source]] [float] === Script source settings From 548a5c1386c1d0f74d1e85340a142ae37068f344 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 15 May 2017 13:42:28 -0700 Subject: [PATCH 360/619] Docs: Add migration note about .yaml and .json removal (#24689) relates #19391 relates #24633 --- docs/reference/migration/migrate_6_0/settings.asciidoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/reference/migration/migrate_6_0/settings.asciidoc b/docs/reference/migration/migrate_6_0/settings.asciidoc index 4147cd5721a44..7c08b8cc0e7a7 100644 --- a/docs/reference/migration/migrate_6_0/settings.asciidoc +++ b/docs/reference/migration/migrate_6_0/settings.asciidoc @@ -1,6 +1,11 @@ [[breaking_60_settings_changes]] === Settings changes +==== Remove support for elasticsearch.json and elasticsearch.yaml configuration file + +The configuration file found in the Elasticsearch config directory could previously have +a `.yml`, `.yaml` or `.json` extension. Only `elasticsearch.yml` is now supported. + ==== Duplicate keys in configuration file In previous versions of Elasticsearch, the configuration file was allowed to From 6ce597a3789b8f375efb37ee787a9091e5ecb4d3 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 15 May 2017 22:37:31 -0700 Subject: [PATCH 361/619] Scripts: Convert template script engines to return String instead of BytesReference (#24447) Template script engines (mustache, the only one) currently return a BytesReference that users must know is utf8 encoded. This commit modifies all callers and mustache to have the template engine return String. This is much simpler, and does not require decoding in order to use (for example, in ingest). --- .../index/query/QueryRewriteContext.java | 2 +- .../index/query/QueryShardContext.java | 2 +- .../ingest/InternalTemplateService.java | 2 +- .../elasticsearch/script/ScriptService.java | 2 +- .../suggest/phrase/PhraseSuggester.java | 2 +- .../template/CompiledTemplate.java | 2 +- .../search/suggest/SuggestSearchIT.java | 2 +- .../script/mustache/MustacheScriptEngine.java | 22 ++------- .../TransportSearchTemplateAction.java | 6 +-- .../mustache/CustomMustacheFactoryTests.java | 9 ++-- .../mustache/MustacheScriptEngineTests.java | 12 ++--- .../script/mustache/MustacheTests.java | 48 +++++-------------- .../script/MockScriptEngine.java | 3 +- 13 files changed, 36 insertions(+), 78 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java b/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java index 24e42b41b2022..003187f1bbe5d 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java @@ -105,7 +105,7 @@ public long nowInMillis() { return nowInMillis.getAsLong(); } - public BytesReference getTemplateBytes(Script template) { + public String getTemplateBytes(Script template) { CompiledTemplate compiledTemplate = scriptService.compileTemplate(template, ScriptContext.Standard.SEARCH); return compiledTemplate.run(template.getParams()); } diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java b/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java index a6a7108f7a971..ce6439ae1be11 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java @@ -392,7 +392,7 @@ protected final void failIfFrozen() { } @Override - public final BytesReference getTemplateBytes(Script template) { + public final String getTemplateBytes(Script template) { failIfFrozen(); return super.getTemplateBytes(template); } diff --git a/core/src/main/java/org/elasticsearch/ingest/InternalTemplateService.java b/core/src/main/java/org/elasticsearch/ingest/InternalTemplateService.java index b5aa2dbc51a8a..a54b5754a1a87 100644 --- a/core/src/main/java/org/elasticsearch/ingest/InternalTemplateService.java +++ b/core/src/main/java/org/elasticsearch/ingest/InternalTemplateService.java @@ -48,7 +48,7 @@ public Template compile(String template) { return new Template() { @Override public String execute(Map model) { - return compiledTemplate.run(model).utf8ToString(); + return compiledTemplate.run(model); } @Override diff --git a/core/src/main/java/org/elasticsearch/script/ScriptService.java b/core/src/main/java/org/elasticsearch/script/ScriptService.java index b30da8f464ae9..55b326062d5e1 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptService.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptService.java @@ -325,7 +325,7 @@ public CompiledScript compile(Script script, ScriptContext scriptContext) { /** Compiles a template. Note this will be moved to a separate TemplateService in the future. */ public CompiledTemplate compileTemplate(Script script, ScriptContext scriptContext) { CompiledScript compiledScript = compile(script, scriptContext); - return params -> (BytesReference)executable(compiledScript, params).run(); + return params -> (String)executable(compiledScript, params).run(); } /** diff --git a/core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggester.java b/core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggester.java index fc862f6363864..9e9e2c66f79ab 100644 --- a/core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggester.java +++ b/core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggester.java @@ -116,7 +116,7 @@ public Suggestion> innerExecute(String name, P vars.put(SUGGESTION_TEMPLATE_VAR_NAME, spare.toString()); QueryShardContext shardContext = suggestion.getShardContext(); final ExecutableScript executable = collateScript.apply(vars); - final BytesReference querySource = (BytesReference) executable.run(); + final String querySource = (String) executable.run(); try (XContentParser parser = XContentFactory.xContent(querySource).createParser(shardContext.getXContentRegistry(), querySource)) { QueryBuilder innerQueryBuilder = shardContext.newParseContext(parser).parseInnerQueryBuilder(); diff --git a/core/src/main/java/org/elasticsearch/template/CompiledTemplate.java b/core/src/main/java/org/elasticsearch/template/CompiledTemplate.java index 52f46202eb8ba..380c36a590197 100644 --- a/core/src/main/java/org/elasticsearch/template/CompiledTemplate.java +++ b/core/src/main/java/org/elasticsearch/template/CompiledTemplate.java @@ -31,5 +31,5 @@ public interface CompiledTemplate { /** Run a template and return the resulting string, encoded in utf8 bytes. */ - BytesReference run(Map params); + String run(Map params); } diff --git a/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java b/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java index 82b1dc3656c98..864f060be03a8 100644 --- a/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java +++ b/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java @@ -1156,7 +1156,7 @@ public void setNextVar(String name, Object value) { @Override public Object run() { - return new BytesArray(result); + return result; } }; } diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngine.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngine.java index 18189fc99bbc0..774d5cd25b83d 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngine.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngine.java @@ -39,6 +39,7 @@ import org.elasticsearch.search.lookup.SearchLookup; import java.io.Reader; +import java.io.StringWriter; import java.lang.ref.SoftReference; import java.security.AccessController; import java.security.PrivilegedAction; @@ -58,21 +59,6 @@ public final class MustacheScriptEngine implements ScriptEngine { public static final String NAME = "mustache"; - /** Thread local UTF8StreamWriter to store template execution results in, thread local to save object creation.*/ - private static ThreadLocal> utf8StreamWriter = new ThreadLocal<>(); - - /** If exists, reset and return, otherwise create, reset and return a writer.*/ - private static UTF8StreamWriter utf8StreamWriter() { - SoftReference ref = utf8StreamWriter.get(); - UTF8StreamWriter writer = (ref == null) ? null : ref.get(); - if (writer == null) { - writer = new UTF8StreamWriter(1024 * 4); - utf8StreamWriter.set(new SoftReference<>(writer)); - } - writer.reset(); - return writer; - } - /** * Compile a template string to (in this case) a Mustache object than can * later be re-used for execution to fill in missing parameter values. @@ -146,8 +132,8 @@ public void setNextVar(String name, Object value) { @Override public Object run() { - final BytesStreamOutput result = new BytesStreamOutput(); - try (UTF8StreamWriter writer = utf8StreamWriter().setOutput(result)) { + final StringWriter writer = new StringWriter(); + try { // crazy reflection here SpecialPermission.check(); AccessController.doPrivileged((PrivilegedAction) () -> { @@ -158,7 +144,7 @@ public Object run() { logger.error((Supplier) () -> new ParameterizedMessage("Error running {}", template), e); throw new GeneralScriptException("Error running " + template, e); } - return result.bytes(); + return writer.toString(); } } diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java index 8c3e01359128c..a2a9dd8b47555 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java @@ -26,6 +26,7 @@ import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; @@ -102,11 +103,10 @@ static SearchRequest convert(SearchTemplateRequest searchTemplateRequest, Search Script script = new Script(searchTemplateRequest.getScriptType(), TEMPLATE_LANG, searchTemplateRequest.getScript(), searchTemplateRequest.getScriptParams() == null ? Collections.emptyMap() : searchTemplateRequest.getScriptParams()); CompiledTemplate compiledScript = scriptService.compileTemplate(script, SEARCH); - BytesReference source = compiledScript.run(script.getParams()); - response.setSource(source); + String source = compiledScript.run(script.getParams()); + response.setSource(new BytesArray(source)); SearchRequest searchRequest = searchTemplateRequest.getRequest(); - response.setSource(source); if (searchTemplateRequest.isSimulate()) { return null; } diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java index f456f3d6f98c3..eab96a4e991b2 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java @@ -68,8 +68,7 @@ public void testJsonEscapeEncoder() { CompiledScript compiled = new CompiledScript(INLINE, null, MustacheScriptEngine.NAME, script); ExecutableScript executable = engine.executable(compiled, singletonMap("value", "a \"value\"")); - BytesReference result = (BytesReference) executable.run(); - assertThat(result.utf8ToString(), equalTo("{\"field\": \"a \\\"value\\\"\"}")); + assertThat(executable.run(), equalTo("{\"field\": \"a \\\"value\\\"\"}")); } public void testDefaultEncoder() { @@ -80,8 +79,7 @@ public void testDefaultEncoder() { CompiledScript compiled = new CompiledScript(INLINE, null, MustacheScriptEngine.NAME, script); ExecutableScript executable = engine.executable(compiled, singletonMap("value", "a \"value\"")); - BytesReference result = (BytesReference) executable.run(); - assertThat(result.utf8ToString(), equalTo("{\"field\": \"a \"value\"\"}")); + assertThat(executable.run(), equalTo("{\"field\": \"a \"value\"\"}")); } public void testUrlEncoder() { @@ -92,7 +90,6 @@ public void testUrlEncoder() { CompiledScript compiled = new CompiledScript(INLINE, null, MustacheScriptEngine.NAME, script); ExecutableScript executable = engine.executable(compiled, singletonMap("value", "tilde~ AND date:[2016 FROM*]")); - BytesReference result = (BytesReference) executable.run(); - assertThat(result.utf8ToString(), equalTo("{\"field\": \"tilde%7E+AND+date%3A%5B2016+FROM*%5D\"}")); + assertThat(executable.run(), equalTo("{\"field\": \"tilde%7E+AND+date%3A%5B2016+FROM*%5D\"}")); } } diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheScriptEngineTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheScriptEngineTests.java index a92f34b84923c..30cf1fa04a424 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheScriptEngineTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheScriptEngineTests.java @@ -58,11 +58,11 @@ public void testSimpleParameterReplace() { + "\"negative\": {\"term\": {\"body\": {\"value\": \"solr\"}" + "}}, \"negative_boost\": {{boost_val}} } }}"; Map vars = new HashMap<>(); vars.put("boost_val", "0.3"); - BytesReference o = (BytesReference) qe.executable(new CompiledScript(ScriptType.INLINE, "", "mustache", + String o = (String) qe.executable(new CompiledScript(ScriptType.INLINE, "", "mustache", qe.compile(null, template, compileParams)), vars).run(); assertEquals("GET _search {\"query\": {\"boosting\": {\"positive\": {\"match\": {\"body\": \"gift\"}}," + "\"negative\": {\"term\": {\"body\": {\"value\": \"solr\"}}}, \"negative_boost\": 0.3 } }}", - o.utf8ToString()); + o); } { String template = "GET _search {\"query\": " + "{\"boosting\": {" + "\"positive\": {\"match\": {\"body\": \"gift\"}}," @@ -70,11 +70,11 @@ public void testSimpleParameterReplace() { Map vars = new HashMap<>(); vars.put("boost_val", "0.3"); vars.put("body_val", "\"quick brown\""); - BytesReference o = (BytesReference) qe.executable(new CompiledScript(ScriptType.INLINE, "", "mustache", + String o = (String) qe.executable(new CompiledScript(ScriptType.INLINE, "", "mustache", qe.compile(null, template, compileParams)), vars).run(); assertEquals("GET _search {\"query\": {\"boosting\": {\"positive\": {\"match\": {\"body\": \"gift\"}}," + "\"negative\": {\"term\": {\"body\": {\"value\": \"\\\"quick brown\\\"\"}}}, \"negative_boost\": 0.3 } }}", - o.utf8ToString()); + o); } } @@ -89,7 +89,7 @@ public void testSimple() throws IOException { CompiledScript compiledScript = new CompiledScript(ScriptType.INLINE, null, "mustache", qe.compile(null, script.getIdOrCode(), Collections.emptyMap())); ExecutableScript executableScript = qe.executable(compiledScript, script.getParams()); - assertThat(((BytesReference) executableScript.run()).utf8ToString(), equalTo("{\"match_all\":{}}")); + assertThat(executableScript.run(), equalTo("{\"match_all\":{}}")); } public void testParseTemplateAsSingleStringWithConditionalClause() throws IOException { @@ -105,7 +105,7 @@ public void testParseTemplateAsSingleStringWithConditionalClause() throws IOExce CompiledScript compiledScript = new CompiledScript(ScriptType.INLINE, null, "mustache", qe.compile(null, script.getIdOrCode(), Collections.emptyMap())); ExecutableScript executableScript = qe.executable(compiledScript, script.getParams()); - assertThat(((BytesReference) executableScript.run()).utf8ToString(), equalTo("{ \"match_all\":{} }")); + assertThat(executableScript.run(), equalTo("{ \"match_all\":{} }")); } public void testEscapeJson() throws IOException { diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheTests.java index 0becd11380a64..316910a5cd531 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheTests.java @@ -71,7 +71,7 @@ public void testBasics() { "Mustache templating broken", "GET _search {\"query\": {\"boosting\": {\"positive\": {\"match\": {\"body\": \"gift\"}}," + "\"negative\": {\"term\": {\"body\": {\"value\": \"solr\"}}}, \"negative_boost\": 0.2 } }}", - ((BytesReference) result.run()).utf8ToString() + result.run() ); } @@ -83,22 +83,16 @@ public void testArrayAccess() throws Exception { new String[] { "foo", "bar" }, Arrays.asList("foo", "bar")); vars.put("data", data); - Object output = engine.executable(mustache, vars).run(); - assertThat(output, notNullValue()); - assertThat(output, instanceOf(BytesReference.class)); - BytesReference bytes = (BytesReference) output; - assertThat(bytes.utf8ToString(), equalTo("foo bar")); + assertThat(engine.executable(mustache, vars).run(), equalTo("foo bar")); // Sets can come out in any order Set setData = new HashSet<>(); setData.add("foo"); setData.add("bar"); vars.put("data", setData); - output = engine.executable(mustache, vars).run(); - assertThat(output, notNullValue()); - assertThat(output, instanceOf(BytesReference.class)); - bytes = (BytesReference) output; - assertThat(bytes.utf8ToString(), both(containsString("foo")).and(containsString("bar"))); + Object output = engine.executable(mustache, vars).run(); + assertThat(output, instanceOf(String.class)); + assertThat((String)output, both(containsString("foo")).and(containsString("bar"))); } public void testArrayInArrayAccess() throws Exception { @@ -111,11 +105,7 @@ public void testArrayInArrayAccess() throws Exception { singleton(new String[] { "foo", "bar" }) ); vars.put("data", data); - Object output = engine.executable(mustache, vars).run(); - assertThat(output, notNullValue()); - assertThat(output, instanceOf(BytesReference.class)); - BytesReference bytes = (BytesReference) output; - assertThat(bytes.utf8ToString(), equalTo("foo bar")); + assertThat(engine.executable(mustache, vars).run(), equalTo("foo bar")); } public void testMapInArrayAccess() throws Exception { @@ -126,22 +116,16 @@ public void testMapInArrayAccess() throws Exception { new Object[] { singletonMap("key", "foo"), singletonMap("key", "bar") }, Arrays.asList(singletonMap("key", "foo"), singletonMap("key", "bar"))); vars.put("data", data); - Object output = engine.executable(mustache, vars).run(); - assertThat(output, notNullValue()); - assertThat(output, instanceOf(BytesReference.class)); - BytesReference bytes = (BytesReference) output; - assertThat(bytes.utf8ToString(), equalTo("foo bar")); + assertThat(engine.executable(mustache, vars).run(), equalTo("foo bar")); // HashSet iteration order isn't fixed Set setData = new HashSet<>(); setData.add(singletonMap("key", "foo")); setData.add(singletonMap("key", "bar")); vars.put("data", setData); - output = engine.executable(mustache, vars).run(); - assertThat(output, notNullValue()); - assertThat(output, instanceOf(BytesReference.class)); - bytes = (BytesReference) output; - assertThat(bytes.utf8ToString(), both(containsString("foo")).and(containsString("bar"))); + Object output = engine.executable(mustache, vars).run(); + assertThat(output, instanceOf(String.class)); + assertThat((String)output, both(containsString("foo")).and(containsString("bar"))); } @@ -156,14 +140,8 @@ public void testSizeAccessForCollectionsAndArrays() throws Exception { data.put("list", randomList); Map vars = new HashMap<>(); vars.put("data", data); - - Object output = engine.executable(mustache, vars).run(); - assertThat(output, notNullValue()); - assertThat(output, instanceOf(BytesReference.class)); - - BytesReference bytes = (BytesReference) output; String expectedString = String.format(Locale.ROOT, "%s %s", randomArrayValues.length, randomList.size()); - assertThat(bytes.utf8ToString(), equalTo(expectedString)); + assertThat(engine.executable(mustache, vars).run(), equalTo(expectedString)); } public void testPrimitiveToJSON() throws Exception { @@ -399,9 +377,7 @@ public void testUrlEncoderWithJoin() { private void assertScript(String script, Map vars, Matcher matcher) { Object result = engine.executable(new CompiledScript(INLINE, "inline", "mustache", compile(script)), vars).run(); - assertThat(result, notNullValue()); - assertThat(result, instanceOf(BytesReference.class)); - assertThat(((BytesReference) result).utf8ToString(), matcher); + assertThat(result, matcher); } private Object compile(String script) { diff --git a/test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java b/test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java index 12fa092de5036..2f6677d1d6811 100644 --- a/test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java +++ b/test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java @@ -22,7 +22,6 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.Scorer; import org.elasticsearch.common.Nullable; -import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.search.lookup.LeafSearchLookup; import org.elasticsearch.search.lookup.SearchLookup; @@ -132,7 +131,7 @@ public ExecutableScript createExecutableScript(Map vars) { if (vars != null) { context.putAll(vars); } - return new MockExecutableScript(context, script != null ? script : ctx -> new BytesArray(source)); + return new MockExecutableScript(context, script != null ? script : ctx -> source); } public SearchScript createSearchScript(Map vars, SearchLookup lookup) { From f9cfe863209504940ce702d9d95513de687407c4 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Tue, 16 May 2017 09:56:14 +0200 Subject: [PATCH 362/619] Make RemoteClusterConnectionTests more robust against cancelable threads aborts Today we assert hart if failure listeners are invoked more than once. Yet, this can happen if we cancel the execution since the caller and the handler will get the exception on the cancelable threads and will notify the listener concurrently if timinig allows. This commit relaxes the assertion towards handling multiple invocations with `ExecutionCancelledException` Closes #24010 Closes #24179 Closes vagnerclementino/elasticsearch/#98 --- .../transport/RemoteClusterConnectionTests.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java b/core/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java index f7ab29c95c5b4..38e73c209ae6e 100644 --- a/core/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java +++ b/core/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java @@ -498,7 +498,7 @@ public void run() { barrier.await(); CountDownLatch latch = new CountDownLatch(numConnectionAttempts); for (int i = 0; i < numConnectionAttempts; i++) { - AtomicReference executed = new AtomicReference<>(); + AtomicReference executed = new AtomicReference<>(); ActionListener listener = ActionListener.wrap( x -> { if (executed.compareAndSet(null, new RuntimeException())) { @@ -508,10 +508,21 @@ public void run() { } }, x -> { - if (executed.compareAndSet(null, new RuntimeException())) { + if (executed.compareAndSet(null, x)) { latch.countDown(); } else { - throw new AssertionError("shit's been called twice", executed.get()); + final String message = x.getMessage(); + if ((executed.get().getClass() == x.getClass() + && "operation was cancelled reason [connect handler is closed]".equals(message) + && message.equals(executed.get().getMessage())) == false) { + // we do cancel the operation and that means that if timing allows it, the caller + // of a blocking call as well as the handler will get the exception from the + // ExecutionCancelledException concurrently. unless that is the case we fail + // if we get called more than once! + AssertionError assertionError = new AssertionError("shit's been called twice", x); + assertionError.addSuppressed(executed.get()); + throw assertionError; + } } if (x instanceof RejectedExecutionException || x instanceof AlreadyClosedException || x instanceof CancellableThreads.ExecutionCancelledException) { From d966034298f1cf376735a18021f1132efafe4077 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 16 May 2017 01:41:29 -0700 Subject: [PATCH 363/619] Scripting: Deprecate index lookup (#24691) This commit adds a deprecation warning if `_index` is used in scripts. It is emitted each time a script is invoked, but not per document. There is no test because constructing a LeafIndexLookup is quite difficult, but the deprecation warning does show up in IndexLookupIT, there is just no way to assert warnings in integ tests. relates #19359 --- .../search/lookup/LeafIndexLookup.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/core/src/main/java/org/elasticsearch/search/lookup/LeafIndexLookup.java b/core/src/main/java/org/elasticsearch/search/lookup/LeafIndexLookup.java index a7b965dbfb1b0..9908f2830f85b 100644 --- a/core/src/main/java/org/elasticsearch/search/lookup/LeafIndexLookup.java +++ b/core/src/main/java/org/elasticsearch/search/lookup/LeafIndexLookup.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.search.lookup; +import org.apache.logging.log4j.Logger; import org.apache.lucene.index.Fields; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReaderContext; @@ -26,6 +27,8 @@ import org.apache.lucene.index.ReaderUtil; import org.apache.lucene.search.IndexSearcher; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.common.logging.DeprecationLogger; +import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.util.MinimalMap; import java.io.IOException; @@ -64,7 +67,19 @@ public class LeafIndexLookup extends MinimalMap { // computation is expensive private int numDeletedDocs = -1; + private boolean deprecationEmitted = false; + + private void logDeprecation() { + if (deprecationEmitted == false) { + Logger logger = Loggers.getLogger(getClass()); + DeprecationLogger deprecationLogger = new DeprecationLogger(logger); + deprecationLogger.deprecated("Using _index is deprecated. Create a custom ScriptEngine to access index internals."); + deprecationEmitted = true; + } + } + public int numDocs() { + logDeprecation(); if (numDocs == -1) { numDocs = parentReader.numDocs(); } @@ -72,6 +87,7 @@ public int numDocs() { } public int maxDoc() { + logDeprecation(); if (maxDoc == -1) { maxDoc = parentReader.maxDoc(); } @@ -79,6 +95,7 @@ public int maxDoc() { } public int numDeletedDocs() { + logDeprecation(); if (numDeletedDocs == -1) { numDeletedDocs = parentReader.numDeletedDocs(); } @@ -127,6 +144,7 @@ protected void setNextDocIdInFields() { */ @Override public IndexField get(Object key) { + logDeprecation(); String stringField = (String) key; IndexField indexField = indexFields.get(key); if (indexField == null) { @@ -146,19 +164,23 @@ public IndexField get(Object key) { * * */ public Fields termVectors() throws IOException { + logDeprecation(); assert reader != null; return reader.getTermVectors(docId); } LeafReader getReader() { + logDeprecation(); return reader; } public int getDocId() { + logDeprecation(); return docId; } public IndexReader getParentReader() { + logDeprecation(); if (parentReader == null) { return reader; } @@ -166,10 +188,12 @@ public IndexReader getParentReader() { } public IndexSearcher getIndexSearcher() { + logDeprecation(); return indexSearcher; } public IndexReaderContext getReaderContext() { + logDeprecation(); return getParentReader().getContext(); } } From a474a13911fd8cbdbc5a21e023805bc1f5414d04 Mon Sep 17 00:00:00 2001 From: Boaz Leskes Date: Tue, 16 May 2017 09:32:54 +0200 Subject: [PATCH 364/619] testRerouteRecovery should use assertBusy when checking recovery counters The test check that the number of outgoing/incoming recoveries of a shard is 0 after recoveries were done. Sadly that is not guaranteed by the current recovery logic as we decrement the counters only when all references to the relevant RecoveryTarget object have been released. This may happen in an async fashion to the recovery completion which causes the test to fail. I looked at options to change the recovery logic to have the recovery counters decrease before the recovery is done *under normally circumstances* but I don't see a clean way to do it. Since it won't give hard guarantees anyway I opted to add assertBusy to the test Closes #24669 --- .../indices/recovery/IndexRecoveryIT.java | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java b/core/src/test/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java index f0f38ba48c477..7542545bc3ab8 100644 --- a/core/src/test/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java +++ b/core/src/test/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java @@ -77,6 +77,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import static org.elasticsearch.node.RecoverySettingsChunkSizePlugin.CHUNK_SIZE_SETTING; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; @@ -348,39 +349,28 @@ public void run() { assertRecoveryState(recoveryStates.get(0), 0, PeerRecoverySource.INSTANCE, true, Stage.DONE, nodeA, nodeB); validateIndexRecoveryState(recoveryStates.get(0).getIndex()); - - statsResponse = client().admin().cluster().prepareNodesStats().clear().setIndices(new CommonStatsFlags(CommonStatsFlags.Flag.Recovery)).get(); - assertThat(statsResponse.getNodes(), hasSize(2)); - for (NodeStats nodeStats : statsResponse.getNodes()) { + Consumer assertNodeHasThrottleTimeAndNoRecoveries = nodeName -> { + NodesStatsResponse nodesStatsResponse = client().admin().cluster().prepareNodesStats().setNodesIds(nodeName) + .clear().setIndices(new CommonStatsFlags(CommonStatsFlags.Flag.Recovery)).get(); + assertThat(nodesStatsResponse.getNodes(), hasSize(1)); + NodeStats nodeStats = nodesStatsResponse.getNodes().get(0); final RecoveryStats recoveryStats = nodeStats.getIndices().getRecoveryStats(); assertThat(recoveryStats.currentAsSource(), equalTo(0)); assertThat(recoveryStats.currentAsTarget(), equalTo(0)); - if (nodeStats.getNode().getName().equals(nodeA)) { - assertThat("node A throttling should be >0", recoveryStats.throttleTime().millis(), greaterThan(0L)); - } - if (nodeStats.getNode().getName().equals(nodeB)) { - assertThat("node B throttling should be >0 ", recoveryStats.throttleTime().millis(), greaterThan(0L)); - } - } + assertThat(nodeName + " throttling should be >0", recoveryStats.throttleTime().millis(), greaterThan(0L)); + }; + // we have to use assertBusy as recovery counters are decremented only when the last reference to the RecoveryTarget + // is decremented, which may happen after the recovery was done. + assertBusy(() -> assertNodeHasThrottleTimeAndNoRecoveries.accept(nodeA)); + assertBusy(() -> assertNodeHasThrottleTimeAndNoRecoveries.accept(nodeB)); logger.info("--> bump replica count"); client().admin().indices().prepareUpdateSettings(INDEX_NAME) .setSettings(Settings.builder().put("number_of_replicas", 1)).execute().actionGet(); ensureGreen(); - statsResponse = client().admin().cluster().prepareNodesStats().clear().setIndices(new CommonStatsFlags(CommonStatsFlags.Flag.Recovery)).get(); - assertThat(statsResponse.getNodes(), hasSize(2)); - for (NodeStats nodeStats : statsResponse.getNodes()) { - final RecoveryStats recoveryStats = nodeStats.getIndices().getRecoveryStats(); - assertThat(recoveryStats.currentAsSource(), equalTo(0)); - assertThat(recoveryStats.currentAsTarget(), equalTo(0)); - if (nodeStats.getNode().getName().equals(nodeA)) { - assertThat("node A throttling should be >0", recoveryStats.throttleTime().millis(), greaterThan(0L)); - } - if (nodeStats.getNode().getName().equals(nodeB)) { - assertThat("node B throttling should be >0 ", recoveryStats.throttleTime().millis(), greaterThan(0L)); - } - } + assertBusy(() -> assertNodeHasThrottleTimeAndNoRecoveries.accept(nodeA)); + assertBusy(() -> assertNodeHasThrottleTimeAndNoRecoveries.accept(nodeB)); logger.info("--> start node C"); String nodeC = internalCluster().startNode(); From 8c6b5a953e2b3497f495c5947669c494a6af8639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Tue, 16 May 2017 11:51:45 +0200 Subject: [PATCH 365/619] [Tests] Add unit test for InternalAdjecencyMatrix aggregation (#24698) Adding a unit test to InternalAdjecencyMatrix that extends the shared InternalAggregationTestCase that we use for testing aggregations. Relates to #22278 --- .../adjacency/InternalAdjacencyMatrix.java | 42 +++++++-- .../InternalAdjacencyMatrixTests.java | 92 +++++++++++++++++++ 2 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/bucket/adjacency/InternalAdjacencyMatrixTests.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/InternalAdjacencyMatrix.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/InternalAdjacencyMatrix.java index 3d0839b7fb477..602a0964ee9e9 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/InternalAdjacencyMatrix.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/InternalAdjacencyMatrix.java @@ -35,9 +35,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; -public class InternalAdjacencyMatrix - extends InternalMultiBucketAggregation +public class InternalAdjacencyMatrix + extends InternalMultiBucketAggregation implements AdjacencyMatrix { public static class InternalBucket extends InternalMultiBucketAggregation.InternalBucket implements AdjacencyMatrix.Bucket { @@ -111,12 +112,31 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.endObject(); return builder; } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + InternalBucket that = (InternalBucket) other; + return Objects.equals(key, that.key) + && Objects.equals(docCount, that.docCount) + && Objects.equals(aggregations, that.aggregations); + } + + @Override + public int hashCode() { + return Objects.hash(getClass(), key, docCount, aggregations); + } } private final List buckets; private Map bucketMap; - public InternalAdjacencyMatrix(String name, List buckets, + public InternalAdjacencyMatrix(String name, List buckets, List pipelineAggregators, Map metaData) { super(name, pipelineAggregators, metaData); this.buckets = buckets; @@ -193,13 +213,13 @@ public InternalAggregation doReduce(List aggregations, Redu ArrayList reducedBuckets = new ArrayList<>(bucketsMap.size()); for (List sameRangeList : bucketsMap.values()) { InternalBucket reducedBucket = sameRangeList.get(0).reduce(sameRangeList, reduceContext); - if(reducedBucket.docCount >= 1){ + if(reducedBucket.docCount >= 1){ reducedBuckets.add(reducedBucket); } } Collections.sort(reducedBuckets, Comparator.comparing(InternalBucket::getKey)); - - InternalAdjacencyMatrix reduced = new InternalAdjacencyMatrix(name, reducedBuckets, pipelineAggregators(), + + InternalAdjacencyMatrix reduced = new InternalAdjacencyMatrix(name, reducedBuckets, pipelineAggregators(), getMetaData()); return reduced; @@ -215,4 +235,14 @@ public XContentBuilder doXContentBody(XContentBuilder builder, Params params) th return builder; } + @Override + protected int doHashCode() { + return Objects.hash(buckets); + } + + @Override + protected boolean doEquals(Object obj) { + InternalAdjacencyMatrix that = (InternalAdjacencyMatrix) obj; + return Objects.equals(buckets, that.buckets); + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/adjacency/InternalAdjacencyMatrixTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/adjacency/InternalAdjacencyMatrixTests.java new file mode 100644 index 0000000000000..53ae1db4544b1 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/adjacency/InternalAdjacencyMatrixTests.java @@ -0,0 +1,92 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.adjacency; + +import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.test.InternalAggregationTestCase; +import org.junit.Before; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class InternalAdjacencyMatrixTests extends InternalAggregationTestCase { + + private List keys; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + keys = new ArrayList<>(); + int numFilters = randomIntBetween(2, 4); + String[] filters = new String[numFilters]; + for (int i = 0; i < numFilters; i++) { + filters[i] = randomAlphaOfLength(5); + } + for (int i = 0; i < filters.length; i++) { + keys.add(filters[i]); + for (int j = i + 1; j < filters.length; j++) { + if (filters[i].compareTo(filters[j]) <= 0) { + keys.add(filters[i] + "&" + filters[j]); + } else { + keys.add(filters[j] + "&" + filters[i]); + } + } + } + } + + @Override + protected InternalAdjacencyMatrix createTestInstance(String name, List pipelineAggregators, + Map metaData) { + final List buckets = new ArrayList<>(); + for (int i = 0; i < keys.size(); ++i) { + String key = keys.get(i); + int docCount = randomIntBetween(0, 1000); + buckets.add(new InternalAdjacencyMatrix.InternalBucket(key, docCount, InternalAggregations.EMPTY)); + } + return new InternalAdjacencyMatrix(name, buckets, pipelineAggregators, metaData); + } + + @Override + protected void assertReduced(InternalAdjacencyMatrix reduced, List inputs) { + final Map expectedCounts = new TreeMap<>(); + for (InternalAdjacencyMatrix input : inputs) { + for (InternalAdjacencyMatrix.InternalBucket bucket : input.getBuckets()) { + expectedCounts.compute(bucket.getKeyAsString(), + (key, oldValue) -> (oldValue == null ? 0 : oldValue) + bucket.getDocCount()); + } + } + final Map actualCounts = new TreeMap<>(); + for (InternalAdjacencyMatrix.InternalBucket bucket : reduced.getBuckets()) { + actualCounts.compute(bucket.getKeyAsString(), + (key, oldValue) -> (oldValue == null ? 0 : oldValue) + bucket.getDocCount()); + } + assertEquals(expectedCounts, actualCounts); + } + + @Override + protected Reader instanceReader() { + return InternalAdjacencyMatrix::new; + } +} From f6e19dcedc6030923808ff86ec2dc644f1e2140f Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Mon, 15 May 2017 07:23:06 +0200 Subject: [PATCH 366/619] percolator: Fix range queries with date range based on current time. Range queries with now based date ranges were previously not allowed, but since #23921 these queries were allowed. This change should really fix range queries with now based date ranges. --- .../org/elasticsearch/index/IndexService.java | 4 +--- .../percolator/PercolatorQuerySearchIT.java | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/IndexService.java b/core/src/main/java/org/elasticsearch/index/IndexService.java index 94b5782a22cbf..cf41ad8ec1d17 100644 --- a/core/src/main/java/org/elasticsearch/index/IndexService.java +++ b/core/src/main/java/org/elasticsearch/index/IndexService.java @@ -146,9 +146,7 @@ public IndexService( this.mapperService = new MapperService(indexSettings, registry.build(indexSettings), xContentRegistry, similarityService, mapperRegistry, // we parse all percolator queries as they would be parsed on shard 0 - () -> newQueryShardContext(0, null, () -> { - throw new IllegalArgumentException("Percolator queries are not allowed to use the current timestamp"); - })); + () -> newQueryShardContext(0, null, System::currentTimeMillis)); this.indexFieldData = new IndexFieldDataService(indexSettings, indicesFieldDataCache, circuitBreakerService, mapperService); if (indexSettings.getIndexSortConfig().hasIndexSort()) { // we delay the actual creation of the sort order for this index because the mapping has not been merged yet. diff --git a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java index 40dee843e93b3..21775d2a53ab5 100644 --- a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java +++ b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java @@ -161,7 +161,8 @@ public void testPercolatorQuery() throws Exception { public void testPercolatorRangeQueries() throws Exception { createIndex("test", client().admin().indices().prepareCreate("test") - .addMapping("type", "field1", "type=long", "field2", "type=double", "field3", "type=ip", "query", "type=percolator") + .addMapping("type", "field1", "type=long", "field2", "type=double", "field3", "type=ip", "field4", "type=date", + "query", "type=percolator") ); client().prepareIndex("test", "type", "1") @@ -203,6 +204,11 @@ public void testPercolatorRangeQueries() throws Exception { .must(rangeQuery("field3").from("192.168.1.0").to("192.168.1.5")) .must(rangeQuery("field3").from("192.168.1.5").to("192.168.1.10")) ).endObject()).get(); + client().prepareIndex("test", "type", "10") + .setSource(jsonBuilder().startObject().field("query", boolQuery() + .must(rangeQuery("field4").from("2010-01-01").to("2018-01-01")) + .must(rangeQuery("field4").from("2010-01-01").to("now")) + ).endObject()).get(); client().admin().indices().prepareRefresh().get(); // Test long range: @@ -252,6 +258,14 @@ public void testPercolatorRangeQueries() throws Exception { .get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("7")); + + // Test date range: + source = jsonBuilder().startObject().field("field4", "2016-05-15").endObject().bytes(); + response = client().prepareSearch() + .setQuery(new PercolateQueryBuilder("query", "type", source, XContentType.JSON)) + .get(); + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).getId(), equalTo("10")); } public void testPercolatorQueryExistingDocument() throws Exception { From ef7c2e62c3818e77997c84908f4308307ef184c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Tue, 16 May 2017 14:35:49 +0200 Subject: [PATCH 367/619] Add parsing for InternalAdjacencyMatrix aggregation (#24700) --- .../adjacency/ParsedAdjacencyMatrix.java | 88 +++++++++++++++++++ .../aggregations/AggregationsTests.java | 2 + .../InternalAdjacencyMatrixTests.java | 14 ++- .../test/InternalAggregationTestCase.java | 3 + 4 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/ParsedAdjacencyMatrix.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/ParsedAdjacencyMatrix.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/ParsedAdjacencyMatrix.java new file mode 100644 index 0000000000000..1fb356d45c28c --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/ParsedAdjacencyMatrix.java @@ -0,0 +1,88 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.adjacency; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ParsedAdjacencyMatrix extends ParsedMultiBucketAggregation implements AdjacencyMatrix { + + private Map bucketMap; + + @Override + public String getType() { + return AdjacencyMatrixAggregationBuilder.NAME; + } + + @Override + public List getBuckets() { + return buckets; + } + + @Override + public ParsedBucket getBucketByKey(String key) { + if (bucketMap == null) { + bucketMap = new HashMap<>(buckets.size()); + for (ParsedBucket bucket : buckets) { + bucketMap.put(bucket.getKey(), bucket); + } + } + return bucketMap.get(key); + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedAdjacencyMatrix.class.getSimpleName(), true, ParsedAdjacencyMatrix::new); + static { + declareMultiBucketAggregationFields(PARSER, + parser -> ParsedBucket.fromXContent(parser), + parser -> ParsedBucket.fromXContent(parser)); + } + + public static ParsedAdjacencyMatrix fromXContent(XContentParser parser, String name) throws IOException { + ParsedAdjacencyMatrix aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } + + public static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements AdjacencyMatrix.Bucket { + + private String key; + + @Override + public String getKey() { + return key; + } + + @Override + public String getKeyAsString() { + return key; + } + + static ParsedBucket fromXContent(XContentParser parser) throws IOException { + return parseXContent(parser, false, ParsedBucket::new, (p, bucket) -> bucket.key = p.text()); + } + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java index 871cf2756f2fe..fd4b5ae11c2bb 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.action.search.RestSearchAction; +import org.elasticsearch.search.aggregations.bucket.adjacency.InternalAdjacencyMatrixTests; import org.elasticsearch.search.aggregations.bucket.filter.InternalFilterTests; import org.elasticsearch.search.aggregations.bucket.filters.InternalFiltersTests; import org.elasticsearch.search.aggregations.bucket.geogrid.InternalGeoHashGridTests; @@ -124,6 +125,7 @@ private static List getAggsTests() { aggsTests.add(new InternalDateRangeTests()); aggsTests.add(new InternalGeoDistanceTests()); aggsTests.add(new InternalFiltersTests()); + aggsTests.add(new InternalAdjacencyMatrixTests()); return Collections.unmodifiableList(aggsTests); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/adjacency/InternalAdjacencyMatrixTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/adjacency/InternalAdjacencyMatrixTests.java index 53ae1db4544b1..58c3cc8a22ca3 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/adjacency/InternalAdjacencyMatrixTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/adjacency/InternalAdjacencyMatrixTests.java @@ -21,8 +21,9 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.InternalMultiBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import org.elasticsearch.test.InternalAggregationTestCase; import org.junit.Before; import java.util.ArrayList; @@ -30,7 +31,7 @@ import java.util.Map; import java.util.TreeMap; -public class InternalAdjacencyMatrixTests extends InternalAggregationTestCase { +public class InternalAdjacencyMatrixTests extends InternalMultiBucketAggregationTestCase { private List keys; @@ -58,12 +59,12 @@ public void setUp() throws Exception { @Override protected InternalAdjacencyMatrix createTestInstance(String name, List pipelineAggregators, - Map metaData) { + Map metaData, InternalAggregations aggregations) { final List buckets = new ArrayList<>(); for (int i = 0; i < keys.size(); ++i) { String key = keys.get(i); int docCount = randomIntBetween(0, 1000); - buckets.add(new InternalAdjacencyMatrix.InternalBucket(key, docCount, InternalAggregations.EMPTY)); + buckets.add(new InternalAdjacencyMatrix.InternalBucket(key, docCount, aggregations)); } return new InternalAdjacencyMatrix(name, buckets, pipelineAggregators, metaData); } @@ -89,4 +90,9 @@ protected void assertReduced(InternalAdjacencyMatrix reduced, List instanceReader() { return InternalAdjacencyMatrix::new; } + + @Override + protected Class implementationClass() { + return ParsedAdjacencyMatrix.class; + } } diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java index 7bd02f82c5e6d..99b0046a88702 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java @@ -38,6 +38,8 @@ import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.ParsedAggregation; +import org.elasticsearch.search.aggregations.bucket.adjacency.AdjacencyMatrixAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.adjacency.ParsedAdjacencyMatrix; import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter; import org.elasticsearch.search.aggregations.bucket.filters.FiltersAggregationBuilder; @@ -173,6 +175,7 @@ public abstract class InternalAggregationTestCase map.put(DateRangeAggregationBuilder.NAME, (p, c) -> ParsedDateRange.fromXContent(p, (String) c)); map.put(GeoDistanceAggregationBuilder.NAME, (p, c) -> ParsedGeoDistance.fromXContent(p, (String) c)); map.put(FiltersAggregationBuilder.NAME, (p, c) -> ParsedFilters.fromXContent(p, (String) c)); + map.put(AdjacencyMatrixAggregationBuilder.NAME, (p, c) -> ParsedAdjacencyMatrix.fromXContent(p, (String) c)); namedXContents = map.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) From d5fc520741943c83cfac58b4f3289f65af233c6d Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Tue, 16 May 2017 14:54:42 +0200 Subject: [PATCH 368/619] Add parsing to Significant Terms aggregations (#24682) Related to #23331 --- .../significant/InternalSignificantTerms.java | 4 +- .../ParsedSignificantLongTerms.java | 87 +++++++++ .../ParsedSignificantStringTerms.java | 84 +++++++++ .../significant/ParsedSignificantTerms.java | 166 ++++++++++++++++++ .../aggregations/AggregationsTests.java | 4 + ...nternalMultiBucketAggregationTestCase.java | 29 +-- .../InternalSignificantTermsTestCase.java | 40 ++++- .../SignificantLongTermsTests.java | 21 ++- .../SignificantStringTermsTests.java | 18 +- .../test/InternalAggregationTestCase.java | 6 + 10 files changed, 432 insertions(+), 27 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/ParsedSignificantLongTerms.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/ParsedSignificantStringTerms.java create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/ParsedSignificantTerms.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTerms.java index acb655b19b462..ac60829c4ca9e 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTerms.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTerms.java @@ -44,8 +44,8 @@ public abstract class InternalSignificantTerms, B extends InternalSignificantTerms.Bucket> extends InternalMultiBucketAggregation implements SignificantTerms, ToXContent { - private static final String SCORE = "score"; - private static final String BG_COUNT = "bg_count"; + public static final String SCORE = "score"; + public static final String BG_COUNT = "bg_count"; @SuppressWarnings("PMD.ConstructorCallsOverridableMethod") public abstract static class Bucket> extends InternalMultiBucketAggregation.InternalBucket diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/ParsedSignificantLongTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/ParsedSignificantLongTerms.java new file mode 100644 index 0000000000000..79e05f4be9a30 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/ParsedSignificantLongTerms.java @@ -0,0 +1,87 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.significant; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; + +public class ParsedSignificantLongTerms extends ParsedSignificantTerms { + + @Override + public String getType() { + return SignificantLongTerms.NAME; + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedSignificantLongTerms.class.getSimpleName(), true, ParsedSignificantLongTerms::new); + static { + declareParsedSignificantTermsFields(PARSER, ParsedBucket::fromXContent); + } + + public static ParsedSignificantLongTerms fromXContent(XContentParser parser, String name) throws IOException { + ParsedSignificantLongTerms aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } + + public static class ParsedBucket extends ParsedSignificantTerms.ParsedBucket { + + private Long key; + + @Override + public Object getKey() { + return key; + } + + @Override + public String getKeyAsString() { + String keyAsString = super.getKeyAsString(); + if (keyAsString != null) { + return keyAsString; + } + return Long.toString(key); + } + + public Number getKeyAsNumber() { + return key; + } + + @Override + public int compareTerm(SignificantTerms.Bucket other) { + return key.compareTo(((ParsedBucket) other).key); + } + + @Override + protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException { + builder.field(CommonFields.KEY.getPreferredName(), key); + if (super.getKeyAsString() != null) { + builder.field(CommonFields.KEY_AS_STRING.getPreferredName(), getKeyAsString()); + } + return builder; + } + + static ParsedBucket fromXContent(XContentParser parser) throws IOException { + return parseSignificantTermsBucketXContent(parser, new ParsedBucket(), (p, bucket) -> bucket.key = p.longValue()); + } + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/ParsedSignificantStringTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/ParsedSignificantStringTerms.java new file mode 100644 index 0000000000000..28d889b15c213 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/ParsedSignificantStringTerms.java @@ -0,0 +1,84 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.significant; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; + +public class ParsedSignificantStringTerms extends ParsedSignificantTerms { + + @Override + public String getType() { + return SignificantStringTerms.NAME; + } + + private static ObjectParser PARSER = + new ObjectParser<>(ParsedSignificantStringTerms.class.getSimpleName(), true, ParsedSignificantStringTerms::new); + static { + declareParsedSignificantTermsFields(PARSER, ParsedBucket::fromXContent); + } + + public static ParsedSignificantStringTerms fromXContent(XContentParser parser, String name) throws IOException { + ParsedSignificantStringTerms aggregation = PARSER.parse(parser, null); + aggregation.setName(name); + return aggregation; + } + + public static class ParsedBucket extends ParsedSignificantTerms.ParsedBucket { + + private BytesRef key; + + @Override + public Object getKey() { + return getKeyAsString(); + } + + @Override + public String getKeyAsString() { + String keyAsString = super.getKeyAsString(); + if (keyAsString != null) { + return keyAsString; + } + return key.utf8ToString(); + } + + public Number getKeyAsNumber() { + return Double.parseDouble(key.utf8ToString()); + } + + @Override + public int compareTerm(SignificantTerms.Bucket other) { + return key.compareTo(((ParsedBucket) other).key); + } + + @Override + protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException { + return builder.field(CommonFields.KEY.getPreferredName(), getKey()); + } + + static ParsedBucket fromXContent(XContentParser parser) throws IOException { + return parseSignificantTermsBucketXContent(parser, new ParsedBucket(), (p, bucket) -> bucket.key = p.utf8BytesOrNull()); + } + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/ParsedSignificantTerms.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/ParsedSignificantTerms.java new file mode 100644 index 0000000000000..56be0aa60711f --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/ParsedSignificantTerms.java @@ -0,0 +1,166 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.bucket.significant; + +import org.elasticsearch.common.CheckedBiConsumer; +import org.elasticsearch.common.CheckedFunction; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParserUtils; +import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.aggregations.Aggregations; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public abstract class ParsedSignificantTerms extends ParsedMultiBucketAggregation + implements SignificantTerms { + + private Map bucketMap; + protected long subsetSize; + + protected long getSubsetSize() { + return subsetSize; + } + + @Override + public List getBuckets() { + return buckets; + } + + @Override + public SignificantTerms.Bucket getBucketByKey(String term) { + if (bucketMap == null) { + bucketMap = buckets.stream().collect(Collectors.toMap(SignificantTerms.Bucket::getKeyAsString, Function.identity())); + } + return bucketMap.get(term); + } + + @Override + public Iterator iterator() { + return buckets.stream().map(bucket -> (SignificantTerms.Bucket) bucket).collect(Collectors.toList()).iterator(); + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + builder.field(CommonFields.DOC_COUNT.getPreferredName(), subsetSize); + builder.startArray(CommonFields.BUCKETS.getPreferredName()); + for (SignificantTerms.Bucket bucket : buckets) { + bucket.toXContent(builder, params); + } + builder.endArray(); + return builder; + } + + static void declareParsedSignificantTermsFields(final ObjectParser objectParser, + final CheckedFunction bucketParser) { + declareMultiBucketAggregationFields(objectParser, bucketParser::apply, bucketParser::apply); + objectParser.declareLong((parsedTerms, value) -> parsedTerms.subsetSize = value , CommonFields.DOC_COUNT); + } + + public abstract static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements SignificantTerms.Bucket { + + protected long subsetDf; + protected long supersetDf; + protected double score; + + @Override + public long getDocCount() { + return getSubsetDf(); + } + + @Override + public long getSubsetDf() { + return subsetDf; + } + + @Override + public long getSupersetDf() { + return supersetDf; + } + + @Override + public double getSignificanceScore() { + return score; + } + + @Override + public long getSupersetSize() { + throw new UnsupportedOperationException(); + } + + @Override + public long getSubsetSize() { + throw new UnsupportedOperationException(); + } + + @Override + public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + keyToXContent(builder); + builder.field(CommonFields.DOC_COUNT.getPreferredName(), getDocCount()); + builder.field(InternalSignificantTerms.SCORE, getSignificanceScore()); + builder.field(InternalSignificantTerms.BG_COUNT, getSupersetDf()); + getAggregations().toXContentInternal(builder, params); + builder.endObject(); + return builder; + } + + protected abstract XContentBuilder keyToXContent(XContentBuilder builder) throws IOException; + + static B parseSignificantTermsBucketXContent(final XContentParser parser, final B bucket, + final CheckedBiConsumer keyConsumer) throws IOException { + + final List aggregations = new ArrayList<>(); + XContentParser.Token token; + String currentFieldName = parser.currentName(); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (CommonFields.KEY_AS_STRING.getPreferredName().equals(currentFieldName)) { + bucket.setKeyAsString(parser.text()); + } else if (CommonFields.KEY.getPreferredName().equals(currentFieldName)) { + keyConsumer.accept(parser, bucket); + } else if (CommonFields.DOC_COUNT.getPreferredName().equals(currentFieldName)) { + long value = parser.longValue(); + bucket.subsetDf = value; + bucket.setDocCount(value); + } else if (InternalSignificantTerms.SCORE.equals(currentFieldName)) { + bucket.score = parser.longValue(); + } else if (InternalSignificantTerms.BG_COUNT.equals(currentFieldName)) { + bucket.supersetDf = parser.longValue(); + } + } else if (token == XContentParser.Token.START_OBJECT) { + aggregations.add(XContentParserUtils.parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Aggregation.class)); + } + } + bucket.setAggregations(new Aggregations(aggregations)); + return bucket; + } + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java index fd4b5ae11c2bb..85deb604a6677 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java @@ -40,6 +40,8 @@ import org.elasticsearch.search.aggregations.bucket.range.date.InternalDateRangeTests; import org.elasticsearch.search.aggregations.bucket.range.geodistance.InternalGeoDistanceTests; import org.elasticsearch.search.aggregations.bucket.sampler.InternalSamplerTests; +import org.elasticsearch.search.aggregations.bucket.significant.SignificantLongTermsTests; +import org.elasticsearch.search.aggregations.bucket.significant.SignificantStringTermsTests; import org.elasticsearch.search.aggregations.bucket.terms.DoubleTermsTests; import org.elasticsearch.search.aggregations.bucket.terms.LongTermsTests; import org.elasticsearch.search.aggregations.bucket.terms.StringTermsTests; @@ -126,6 +128,8 @@ private static List getAggsTests() { aggsTests.add(new InternalGeoDistanceTests()); aggsTests.add(new InternalFiltersTests()); aggsTests.add(new InternalAdjacencyMatrixTests()); + aggsTests.add(new SignificantLongTermsTests()); + aggsTests.add(new SignificantStringTermsTests()); return Collections.unmodifiableList(aggsTests); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java index df977abbe8118..38e8624eeb9db 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalMultiBucketAggregationTestCase.java @@ -67,28 +67,22 @@ protected abstract T createTestInstance(String name, List pi @Override protected final void assertFromXContent(T aggregation, ParsedAggregation parsedAggregation) { - assertMultiBucketsAggregation(aggregation, parsedAggregation, false); + assertMultiBucketsAggregations(aggregation, parsedAggregation, false); } public void testIterators() throws IOException { final T aggregation = createTestInstance(); - assertMultiBucketsAggregation(aggregation, parseAndAssert(aggregation, false), true); + assertMultiBucketsAggregations(aggregation, parseAndAssert(aggregation, false), true); } - private void assertMultiBucketsAggregation(Aggregation expected, Aggregation actual, boolean checkOrder) { + private void assertMultiBucketsAggregations(Aggregation expected, Aggregation actual, boolean checkOrder) { assertTrue(expected instanceof MultiBucketsAggregation); MultiBucketsAggregation expectedMultiBucketsAggregation = (MultiBucketsAggregation) expected; assertTrue(actual instanceof MultiBucketsAggregation); MultiBucketsAggregation actualMultiBucketsAggregation = (MultiBucketsAggregation) actual; - Class parsedClass = implementationClass(); - assertTrue(parsedClass != null && parsedClass.isInstance(actual)); - - assertTrue(expected instanceof InternalAggregation && actual instanceof ParsedAggregation); - assertEquals(expected.getName(), actual.getName()); - assertEquals(expected.getMetaData(), actual.getMetaData()); - assertEquals(((InternalAggregation) expected).getType(), ((ParsedAggregation) actual).getType()); + assertMultiBucketsAggregation(expectedMultiBucketsAggregation, actualMultiBucketsAggregation, checkOrder); List expectedBuckets = expectedMultiBucketsAggregation.getBuckets(); List actualBuckets = actualMultiBucketsAggregation.getBuckets(); @@ -117,6 +111,17 @@ private void assertMultiBucketsAggregation(Aggregation expected, Aggregation act } } + protected void assertMultiBucketsAggregation(MultiBucketsAggregation expected, MultiBucketsAggregation actual, boolean checkOrder) { + Class parsedClass = implementationClass(); + assertNotNull("Parsed aggregation class must not be null", parsedClass); + assertTrue(parsedClass.isInstance(actual)); + + assertTrue(expected instanceof InternalAggregation); + assertEquals(expected.getName(), actual.getName()); + assertEquals(expected.getMetaData(), actual.getMetaData()); + assertEquals(((InternalAggregation) expected).getType(), ((ParsedAggregation) actual).getType()); + } + protected void assertBucket(MultiBucketsAggregation.Bucket expected, MultiBucketsAggregation.Bucket actual, boolean checkOrder) { assertTrue(expected instanceof InternalMultiBucketAggregation.InternalBucket); assertTrue(actual instanceof ParsedMultiBucketAggregation.ParsedBucket); @@ -136,13 +141,13 @@ protected void assertBucket(MultiBucketsAggregation.Bucket expected, MultiBucket while (expectedIt.hasNext()) { Aggregation expectedAggregation = expectedIt.next(); Aggregation actualAggregation = actualIt.next(); - assertMultiBucketsAggregation(expectedAggregation, actualAggregation, true); + assertMultiBucketsAggregations(expectedAggregation, actualAggregation, true); } } else { for (Aggregation expectedAggregation : expectedAggregations) { Aggregation actualAggregation = actualAggregations.get(expectedAggregation.getName()); assertNotNull(actualAggregation); - assertMultiBucketsAggregation(expectedAggregation, actualAggregation, false); + assertMultiBucketsAggregations(expectedAggregation, actualAggregation, false); } } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTermsTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTermsTestCase.java index f1c54c4b503dc..9a86e44b2ac01 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTermsTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/InternalSignificantTermsTestCase.java @@ -19,8 +19,9 @@ package org.elasticsearch.search.aggregations.bucket.significant; +import org.elasticsearch.search.aggregations.InternalMultiBucketAggregationTestCase; +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import org.elasticsearch.test.InternalAggregationTestCase; import java.util.Arrays; import java.util.HashMap; @@ -30,7 +31,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public abstract class InternalSignificantTermsTestCase extends InternalAggregationTestCase> { +public abstract class InternalSignificantTermsTestCase extends InternalMultiBucketAggregationTestCase> { @Override protected InternalSignificantTerms createUnmappedInstance(String name, @@ -61,6 +62,41 @@ protected void assertReduced(InternalSignificantTerms reduced, List toCounts(Stream buckets, Function fn) { return buckets.collect(Collectors.toMap(SignificantTerms.Bucket::getKey, fn, Long::sum)); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTermsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTermsTests.java index 7e80cf61608e5..5b0e715c8a6a3 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTermsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTermsTests.java @@ -21,6 +21,8 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.bucket.significant.heuristics.ChiSquare; import org.elasticsearch.search.aggregations.bucket.significant.heuristics.GND; import org.elasticsearch.search.aggregations.bucket.significant.heuristics.JLHScore; @@ -35,22 +37,24 @@ import java.util.Map; import java.util.Set; -import static org.elasticsearch.search.aggregations.InternalAggregations.EMPTY; - public class SignificantLongTermsTests extends InternalSignificantTermsTestCase { private SignificanceHeuristic significanceHeuristic; + private DocValueFormat format; + @Override @Before - public void setUpSignificanceHeuristic() { + public void setUp() throws Exception { + super.setUp(); significanceHeuristic = randomSignificanceHeuristic(); + format = randomNumericDocValueFormat(); } @Override protected InternalSignificantTerms createTestInstance(String name, List pipelineAggregators, - Map metaData) { - DocValueFormat format = DocValueFormat.RAW; + Map metaData, + InternalAggregations aggregations) { int requiredSize = randomIntBetween(1, 5); int shardSize = requiredSize + 2; final int numBuckets = randomInt(shardSize); @@ -70,7 +74,7 @@ protected InternalSignificantTerms createTestInstance(String name, globalSubsetSize += subsetDf; globalSupersetSize += supersetSize; - buckets.add(new SignificantLongTerms.Bucket(subsetDf, subsetDf, supersetDf, supersetSize, term, EMPTY, format)); + buckets.add(new SignificantLongTerms.Bucket(subsetDf, subsetDf, supersetDf, supersetSize, term, aggregations, format)); } return new SignificantLongTerms(name, requiredSize, 1L, pipelineAggregators, metaData, format, globalSubsetSize, globalSupersetSize, significanceHeuristic, buckets); @@ -81,6 +85,11 @@ protected InternalSignificantTerms createTestInstance(String name, return SignificantLongTerms::new; } + @Override + protected Class implementationClass() { + return ParsedSignificantLongTerms.class; + } + private static SignificanceHeuristic randomSignificanceHeuristic() { return randomFrom( new JLHScore(), diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantStringTermsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantStringTermsTests.java index 82cd21cdf3873..07fe4c1ae8273 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantStringTermsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantStringTermsTests.java @@ -22,6 +22,8 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.bucket.significant.heuristics.ChiSquare; import org.elasticsearch.search.aggregations.bucket.significant.heuristics.GND; import org.elasticsearch.search.aggregations.bucket.significant.heuristics.JLHScore; @@ -36,21 +38,22 @@ import java.util.Map; import java.util.Set; -import static org.elasticsearch.search.aggregations.InternalAggregations.EMPTY; - public class SignificantStringTermsTests extends InternalSignificantTermsTestCase { private SignificanceHeuristic significanceHeuristic; + @Override @Before - public void setUpSignificanceHeuristic() { + public void setUp() throws Exception { + super.setUp(); significanceHeuristic = randomSignificanceHeuristic(); } @Override protected InternalSignificantTerms createTestInstance(String name, List pipelineAggregators, - Map metaData) { + Map metaData, + InternalAggregations aggregations) { DocValueFormat format = DocValueFormat.RAW; int requiredSize = randomIntBetween(1, 5); int shardSize = requiredSize + 2; @@ -71,7 +74,7 @@ protected InternalSignificantTerms createTestInstance(String name, globalSubsetSize += subsetDf; globalSupersetSize += supersetSize; - buckets.add(new SignificantStringTerms.Bucket(term, subsetDf, subsetDf, supersetDf, supersetSize, EMPTY, format)); + buckets.add(new SignificantStringTerms.Bucket(term, subsetDf, subsetDf, supersetDf, supersetSize, aggregations, format)); } return new SignificantStringTerms(name, requiredSize, 1L, pipelineAggregators, metaData, format, globalSubsetSize, globalSupersetSize, significanceHeuristic, buckets); @@ -82,6 +85,11 @@ protected InternalSignificantTerms createTestInstance(String name, return SignificantStringTerms::new; } + @Override + protected Class implementationClass() { + return ParsedSignificantStringTerms.class; + } + private static SignificanceHeuristic randomSignificanceHeuristic() { return randomFrom( new JLHScore(), diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java index 99b0046a88702..c256275b99482 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java @@ -66,6 +66,10 @@ import org.elasticsearch.search.aggregations.bucket.range.geodistance.ParsedGeoDistance; import org.elasticsearch.search.aggregations.bucket.sampler.InternalSampler; import org.elasticsearch.search.aggregations.bucket.sampler.ParsedSampler; +import org.elasticsearch.search.aggregations.bucket.significant.ParsedSignificantLongTerms; +import org.elasticsearch.search.aggregations.bucket.significant.ParsedSignificantStringTerms; +import org.elasticsearch.search.aggregations.bucket.significant.SignificantLongTerms; +import org.elasticsearch.search.aggregations.bucket.significant.SignificantStringTerms; import org.elasticsearch.search.aggregations.bucket.terms.DoubleTerms; import org.elasticsearch.search.aggregations.bucket.terms.LongTerms; import org.elasticsearch.search.aggregations.bucket.terms.ParsedDoubleTerms; @@ -176,6 +180,8 @@ public abstract class InternalAggregationTestCase map.put(GeoDistanceAggregationBuilder.NAME, (p, c) -> ParsedGeoDistance.fromXContent(p, (String) c)); map.put(FiltersAggregationBuilder.NAME, (p, c) -> ParsedFilters.fromXContent(p, (String) c)); map.put(AdjacencyMatrixAggregationBuilder.NAME, (p, c) -> ParsedAdjacencyMatrix.fromXContent(p, (String) c)); + map.put(SignificantLongTerms.NAME, (p, c) -> ParsedSignificantLongTerms.fromXContent(p, (String) c)); + map.put(SignificantStringTerms.NAME, (p, c) -> ParsedSignificantStringTerms.fromXContent(p, (String) c)); namedXContents = map.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) From 1e9718451940984ac2ec1ed9e290dd1c26577798 Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Tue, 16 May 2017 09:01:38 -0400 Subject: [PATCH 369/619] Automatically close releasables after test (#24687) This moves the releasing logic to the base test, so that individual test cases don't need to worry about releasing the aggregators. It's not a big deal for individual aggs, but once tests start using sub-aggs, it can become tricky to free (without double-freeing) all the aggregators. --- .../bucket/GlobalAggregatorTests.java | 19 ++--- .../geogrid/GeoHashGridAggregatorTests.java | 13 +-- .../bucket/terms/TermsAggregatorTests.java | 42 +++++----- .../metrics/CardinalityAggregatorTests.java | 14 ++-- .../metrics/MaxAggregatorTests.java | 13 +-- .../metrics/avg/AvgAggregatorTests.java | 13 +-- .../metrics/min/MinAggregatorTests.java | 60 +++++++------- .../hdr/HDRPercentilesAggregatorTests.java | 12 +-- .../TDigestPercentilesAggregatorTests.java | 11 ++- .../metrics/sum/SumAggregatorTests.java | 11 ++- .../valuecount/ValueCountAggregatorTests.java | 12 ++- .../aggregations/AggregatorTestCase.java | 82 +++++++++---------- 12 files changed, 151 insertions(+), 151 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/GlobalAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/GlobalAggregatorTests.java index 571a32b87b735..67bec2acf7a42 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/GlobalAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/GlobalAggregatorTests.java @@ -78,17 +78,14 @@ private void testCase(CheckedConsumer buildIndex aggregationBuilder.subAggregation(new MinAggregationBuilder("in_global").field("number")); MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.LONG); fieldType.setName("number"); - try (GlobalAggregator aggregator = createAggregator(aggregationBuilder, indexSearcher, fieldType)) { - try { - aggregator.preCollection(); - indexSearcher.search(new MatchAllDocsQuery(), aggregator); - aggregator.postCollection(); - InternalGlobal result = (InternalGlobal) aggregator.buildAggregation(0L); - verify.accept(result, (InternalMin) result.getAggregations().asMap().get("in_global")); - } finally { - IOUtils.close(aggregator.subAggregators()); - } - } + + GlobalAggregator aggregator = createAggregator(aggregationBuilder, indexSearcher, fieldType); + aggregator.preCollection(); + indexSearcher.search(new MatchAllDocsQuery(), aggregator); + aggregator.postCollection(); + InternalGlobal result = (InternalGlobal) aggregator.buildAggregation(0L); + verify.accept(result, (InternalMin) result.getAggregations().asMap().get("in_global")); + indexReader.close(); directory.close(); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java index 04147b245c5e5..45b6b64cddc35 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java @@ -112,12 +112,13 @@ private void testCase(Query query, String field, int precision, CheckedConsumer< MappedFieldType fieldType = new GeoPointFieldMapper.GeoPointFieldType(); fieldType.setHasDocValues(true); fieldType.setName(FIELD_NAME); - try (Aggregator aggregator = createAggregator(aggregationBuilder, indexSearcher, fieldType)) { - aggregator.preCollection(); - indexSearcher.search(query, aggregator); - aggregator.postCollection(); - verify.accept((InternalGeoHashGrid) aggregator.buildAggregation(0L)); - } + + Aggregator aggregator = createAggregator(aggregationBuilder, indexSearcher, fieldType); + aggregator.preCollection(); + indexSearcher.search(query, aggregator); + aggregator.postCollection(); + verify.accept((InternalGeoHashGrid) aggregator.buildAggregation(0L)); + indexReader.close(); directory.close(); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java index 7b93653fff868..f54cb902d96b8 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java @@ -75,21 +75,22 @@ public void testTermsAggregator() throws Exception { MappedFieldType fieldType = new KeywordFieldMapper.KeywordFieldType(); fieldType.setName("string"); fieldType.setHasDocValues(true ); - try (TermsAggregator aggregator = createAggregator(aggregationBuilder, indexSearcher, fieldType)) { - aggregator.preCollection(); - indexSearcher.search(new MatchAllDocsQuery(), aggregator); - aggregator.postCollection(); - Terms result = (Terms) aggregator.buildAggregation(0L); - assertEquals(4, result.getBuckets().size()); - assertEquals("a", result.getBuckets().get(0).getKeyAsString()); - assertEquals(2L, result.getBuckets().get(0).getDocCount()); - assertEquals("b", result.getBuckets().get(1).getKeyAsString()); - assertEquals(2L, result.getBuckets().get(1).getDocCount()); - assertEquals("c", result.getBuckets().get(2).getKeyAsString()); - assertEquals(1L, result.getBuckets().get(2).getDocCount()); - assertEquals("d", result.getBuckets().get(3).getKeyAsString()); - assertEquals(1L, result.getBuckets().get(3).getDocCount()); - } + + TermsAggregator aggregator = createAggregator(aggregationBuilder, indexSearcher, fieldType); + aggregator.preCollection(); + indexSearcher.search(new MatchAllDocsQuery(), aggregator); + aggregator.postCollection(); + Terms result = (Terms) aggregator.buildAggregation(0L); + assertEquals(4, result.getBuckets().size()); + assertEquals("a", result.getBuckets().get(0).getKeyAsString()); + assertEquals(2L, result.getBuckets().get(0).getDocCount()); + assertEquals("b", result.getBuckets().get(1).getKeyAsString()); + assertEquals(2L, result.getBuckets().get(1).getDocCount()); + assertEquals("c", result.getBuckets().get(2).getKeyAsString()); + assertEquals(1L, result.getBuckets().get(2).getDocCount()); + assertEquals("d", result.getBuckets().get(3).getKeyAsString()); + assertEquals(1L, result.getBuckets().get(3).getDocCount()); + } indexReader.close(); directory.close(); @@ -191,12 +192,11 @@ private IndexReader createIndexWithDoubles() throws IOException { private InternalAggregation buildInternalAggregation(TermsAggregationBuilder builder, MappedFieldType fieldType, IndexSearcher searcher) throws IOException { - try (TermsAggregator aggregator = createAggregator(builder, searcher, fieldType)) { - aggregator.preCollection(); - searcher.search(new MatchAllDocsQuery(), aggregator); - aggregator.postCollection(); - return aggregator.buildAggregation(0L); - } + TermsAggregator aggregator = createAggregator(builder, searcher, fieldType); + aggregator.preCollection(); + searcher.search(new MatchAllDocsQuery(), aggregator); + aggregator.postCollection(); + return aggregator.buildAggregation(0L); } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityAggregatorTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityAggregatorTests.java index b80dd163fc97f..90afe095293c4 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityAggregatorTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityAggregatorTests.java @@ -118,13 +118,13 @@ private void testCase(Query query, CheckedConsumer A search(IndexSe AggregationBuilder builder, MappedFieldType... fieldTypes) throws IOException { C a = createAggregator(builder, searcher, fieldTypes); - try { - a.preCollection(); - searcher.search(query, a); - a.postCollection(); - @SuppressWarnings("unchecked") - A internalAgg = (A) a.buildAggregation(0L); - return internalAgg; - } finally { - Releasables.close(releasables); - releasables.clear(); - } + a.preCollection(); + searcher.search(query, a); + a.postCollection(); + @SuppressWarnings("unchecked") + A internalAgg = (A) a.buildAggregation(0L); + return internalAgg; + } /** @@ -245,38 +242,35 @@ protected A searchAndReduc Query rewritten = searcher.rewrite(query); Weight weight = searcher.createWeight(rewritten, true, 1f); C root = createAggregator(builder, searcher, fieldTypes); - try { - for (ShardSearcher subSearcher : subSearchers) { - C a = createAggregator(builder, subSearcher, fieldTypes); - a.preCollection(); - subSearcher.search(weight, a); - a.postCollection(); - aggs.add(a.buildAggregation(0L)); - } - if (aggs.isEmpty()) { - return null; - } else { - if (randomBoolean() && aggs.size() > 1) { - // sometimes do an incremental reduce - int toReduceSize = aggs.size(); - Collections.shuffle(aggs, random()); - int r = randomIntBetween(1, toReduceSize); - List toReduce = aggs.subList(0, r); - A reduced = (A) aggs.get(0).doReduce(toReduce, - new InternalAggregation.ReduceContext(root.context().bigArrays(), null, false)); - aggs = new ArrayList<>(aggs.subList(r, toReduceSize)); - aggs.add(reduced); - } - // now do the final reduce - @SuppressWarnings("unchecked") - A internalAgg = (A) aggs.get(0).doReduce(aggs, new InternalAggregation.ReduceContext(root.context().bigArrays(), null, - true)); - return internalAgg; + + for (ShardSearcher subSearcher : subSearchers) { + C a = createAggregator(builder, subSearcher, fieldTypes); + a.preCollection(); + subSearcher.search(weight, a); + a.postCollection(); + aggs.add(a.buildAggregation(0L)); + } + if (aggs.isEmpty()) { + return null; + } else { + if (randomBoolean() && aggs.size() > 1) { + // sometimes do an incremental reduce + int toReduceSize = aggs.size(); + Collections.shuffle(aggs, random()); + int r = randomIntBetween(1, toReduceSize); + List toReduce = aggs.subList(0, r); + A reduced = (A) aggs.get(0).doReduce(toReduce, + new InternalAggregation.ReduceContext(root.context().bigArrays(), null, false)); + aggs = new ArrayList<>(aggs.subList(r, toReduceSize)); + aggs.add(reduced); } - } finally { - Releasables.close(releasables); - releasables.clear(); + // now do the final reduce + @SuppressWarnings("unchecked") + A internalAgg = (A) aggs.get(0).doReduce(aggs, new InternalAggregation.ReduceContext(root.context().bigArrays(), null, + true)); + return internalAgg; } + } private static class ShardSearcher extends IndexSearcher { @@ -300,4 +294,10 @@ public String toString() { protected static DirectoryReader wrap(DirectoryReader directoryReader) throws IOException { return ElasticsearchDirectoryReader.wrap(directoryReader, new ShardId(new Index("_index", "_na_"), 0)); } + + @After + private void cleanupReleasables() { + Releasables.close(releasables); + releasables.clear(); + } } From 77762fcbb0eba283f0b904175865630b3253ea2b Mon Sep 17 00:00:00 2001 From: Daniel Mitterdorfer Date: Tue, 16 May 2017 15:57:05 +0200 Subject: [PATCH 370/619] Use correct script name in docs for Windows With this commit we correct the name of the ES batch script to `elasticsearch.bat` in the docs and use backslashes in path names. --- docs/reference/setup/install/windows.asciidoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/setup/install/windows.asciidoc b/docs/reference/setup/install/windows.asciidoc index 5595a38ff61aa..40a413087bee8 100644 --- a/docs/reference/setup/install/windows.asciidoc +++ b/docs/reference/setup/install/windows.asciidoc @@ -45,7 +45,7 @@ Elasticsearch can be started from the command line as follows: [source,sh] -------------------------------------------- -.\bin\elasticsearch +.\bin\elasticsearch.bat -------------------------------------------- By default, Elasticsearch runs in the foreground, prints its logs to `STDOUT`, @@ -54,7 +54,7 @@ and can be stopped by pressing `Ctrl-C`. [[windows-configuring]] ==== Configuring Elasticsearch on the command line -Elasticsearch loads its configuration from the `%ES_HOME%/config/elasticsearch.yml` +Elasticsearch loads its configuration from the `%ES_HOME%\config\elasticsearch.yml` file by default. The format of this config file is explained in <>. @@ -63,7 +63,7 @@ the command line, using the `-E` syntax as follows: [source,sh] -------------------------------------------- -./bin/elasticsearch -Ecluster.name=my_cluster -Enode.name=node_1 +.\bin\elasticsearch.bat -Ecluster.name=my_cluster -Enode.name=node_1 -------------------------------------------- NOTE: Values that contain spaces must be surrounded with quotes. For instance `-Epath.logs="C:\My Logs\logs"`. From 65218fea6d8f690754975928856a379add40abad Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Tue, 16 May 2017 16:06:24 +0200 Subject: [PATCH 371/619] Allow resetting settings that use and IP validator (#24713) The IP validator doesn't expect a null value for a setting that causes NPEs if a user tries to reset a setting that uses this validator. Closes #24709 --- .../cluster/node/DiscoveryNodeFilters.java | 3 +++ .../common/settings/ScopedSettingsTests.java | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodeFilters.java b/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodeFilters.java index 3fcfdc08722ea..1c9aad322c8c2 100644 --- a/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodeFilters.java +++ b/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodeFilters.java @@ -47,6 +47,9 @@ public enum OpType { Map settingsMap = settings.getAsMap(); for (Map.Entry entry : settingsMap.entrySet()) { String propertyKey = entry.getKey(); + if (entry.getValue() == null) { + continue; // this setting gets reset + } if ("_ip".equals(propertyKey) || "_host_ip".equals(propertyKey) || "_publish_ip".equals(propertyKey)) { for (String value : Strings.tokenizeToStringArray(entry.getValue(), ",")) { if (InetAddresses.isInetAddress(value) == false) { diff --git a/core/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java b/core/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java index 01ace21ad1274..dd96acdd6c75d 100644 --- a/core/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java +++ b/core/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java @@ -103,6 +103,30 @@ public void testResetSetting() { assertNull(target.build().getAsInt("archived.foo.bar", null)); } + public void testResetSettingWithIPValidator() { + Settings currentSettings = Settings.builder().put("index.routing.allocation.require._ip", "192.168.0.1,127.0.0.1") + .put("index.some.dyn.setting", 1) + .build(); + Setting dynamicSetting = Setting.intSetting("index.some.dyn.setting", 1, Property.Dynamic, Property.IndexScope); + + IndexScopedSettings settings = new IndexScopedSettings(currentSettings, + new HashSet<>(Arrays.asList(dynamicSetting, IndexMetaData.INDEX_ROUTING_REQUIRE_GROUP_SETTING))); + Settings s = IndexMetaData.INDEX_ROUTING_REQUIRE_GROUP_SETTING.get(currentSettings); + assertEquals(1, s.size()); + assertEquals("192.168.0.1,127.0.0.1", s.get("_ip")); + Settings.Builder builder = Settings.builder(); + Settings updates = Settings.builder().putNull("index.routing.allocation.require._ip") + .put("index.some.dyn.setting", 1).build(); + settings.validate(updates); + settings.updateDynamicSettings(updates, + Settings.builder().put(currentSettings), builder, "node"); + currentSettings = builder.build(); + s = IndexMetaData.INDEX_ROUTING_REQUIRE_GROUP_SETTING.get(currentSettings); + assertEquals(0, s.size()); + assertEquals(1, dynamicSetting.get(currentSettings).intValue()); + assertEquals(1, currentSettings.size()); + } + public void testAddConsumer() { Setting testSetting = Setting.intSetting("foo.bar", 1, Property.Dynamic, Property.NodeScope); Setting testSetting2 = Setting.intSetting("foo.bar.baz", 1, Property.Dynamic, Property.NodeScope); From 788d8c1ddc62ed1c4e2397647937a132292c89bc Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Tue, 16 May 2017 09:50:26 -0500 Subject: [PATCH 372/619] Docs: Link to new native ColdFusion (CFML) client (#24690) --- docs/community-clients/index.asciidoc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/community-clients/index.asciidoc b/docs/community-clients/index.asciidoc index fbb4a3ddce83c..21c6d20e23503 100644 --- a/docs/community-clients/index.asciidoc +++ b/docs/community-clients/index.asciidoc @@ -9,7 +9,7 @@ a number of clients that have been contributed by the community for various lang * <> * <> -* <> +* <> * <> * <> * <> @@ -40,13 +40,16 @@ a number of clients that have been contributed by the community for various lang * http://github.com/clojurewerkz/elastisch[Elastisch]: Clojure client. -[[cold-fusion]] -== Cold Fusion +[[coldfusion]] +== ColdFusion (CFML) + +* https://www.forgebox.io/view/cbelasticsearch[cbElasticSearch] + Native ColdFusion (CFML) support for the ColdBox MVC Platform which provides you with a fluent search interface for Elasticsearch, in addition to a CacheBox Cache provider and a Logbox Appender for logging. The following project appears to be abandoned: * https://github.com/jasonfill/ColdFusion-ElasticSearch-Client[ColdFusion-Elasticsearch-Client] - Cold Fusion client for Elasticsearch + ColdFusion client for Elasticsearch [[erlang]] == Erlang From c38b3360b63903f344b89cec923394a241db48ea Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Tue, 16 May 2017 11:16:12 -0400 Subject: [PATCH 373/619] Allow unstashing values into keys (#24685) This is almost exclusively for docs test which frequently match the entire response. This allow something like: ``` - set: {nodes.$master.http.publish_address: host} - match: $body: { "nodes": { $host: { ... stuff in here ... } } } ``` This should make it possible for the docs tests to work with unpredictable keys. --- .../doc/RestTestsFromSnippetsTask.groovy | 1 + .../test/rest/yaml/Features.java | 1 + .../elasticsearch/test/rest/yaml/Stash.java | 43 ++++++---- .../test/rest/yaml/StashTests.java | 84 +++++++++++++++++-- 4 files changed, 109 insertions(+), 20 deletions(-) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/doc/RestTestsFromSnippetsTask.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/doc/RestTestsFromSnippetsTask.groovy index 9270edbb5690e..af637267d119f 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/doc/RestTestsFromSnippetsTask.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/doc/RestTestsFromSnippetsTask.groovy @@ -167,6 +167,7 @@ public class RestTestsFromSnippetsTask extends SnippetsTask { * warning every time. */ current.println(" - skip:") current.println(" features: ") + current.println(" - stash_in_key") current.println(" - warnings") } if (test.skipTest) { diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/Features.java b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/Features.java index 850eb802caf76..d380d0e6d8c00 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/Features.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/Features.java @@ -39,6 +39,7 @@ public final class Features { "catch_unauthorized", "embedded_stash_key", "headers", + "stash_in_key", "stash_in_path", "warnings", "yaml")); diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/Stash.java b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/Stash.java index d9a4d957a2590..590601d0b99cc 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/Stash.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/Stash.java @@ -26,9 +26,11 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -121,35 +123,46 @@ private Object unstash(String key) throws IOException { * Goes recursively against each map entry and replaces any string value starting with "$" with its * corresponding value retrieved from the stash */ + @SuppressWarnings("unchecked") // Safe because we check that all the map keys are string in unstashObject public Map replaceStashedValues(Map map) throws IOException { - Map copy = new HashMap<>(map); - unstashObject(copy); - return copy; + return (Map) unstashObject(map); } - @SuppressWarnings("unchecked") - private void unstashObject(Object obj) throws IOException { + private Object unstashObject(Object obj) throws IOException { if (obj instanceof List) { - List list = (List) obj; - for (int i = 0; i < list.size(); i++) { - Object o = list.get(i); + List list = (List) obj; + List result = new ArrayList<>(); + for (Object o : list) { if (containsStashedValue(o)) { - list.set(i, getValue(o.toString())); + result.add(getValue(o.toString())); } else { - unstashObject(o); + result.add(unstashObject(o)); } } + return result; } if (obj instanceof Map) { - Map map = (Map) obj; - for (Map.Entry entry : map.entrySet()) { - if (containsStashedValue(entry.getValue())) { - entry.setValue(getValue(entry.getValue().toString())); + Map map = (Map) obj; + Map result = new HashMap<>(); + for (Map.Entry entry : map.entrySet()) { + String key = (String) entry.getKey(); + Object value = entry.getValue(); + if (containsStashedValue(key)) { + key = getValue(key).toString(); + } + if (containsStashedValue(value)) { + value = getValue(value.toString()); } else { - unstashObject(entry.getValue()); + value = unstashObject(value); + } + if (null != result.putIfAbsent(key, value)) { + throw new IllegalArgumentException("Unstashing has caused a key conflict! The map is [" + result + "] and the key is [" + + entry.getKey() + "] which unstashes to [" + key + "]"); } } + return result; } + return obj; } @Override diff --git a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/StashTests.java b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/StashTests.java index a8d32a316ac9b..ca2fae958cfab 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/StashTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/StashTests.java @@ -20,27 +20,101 @@ package org.elasticsearch.test.rest.yaml; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.rest.yaml.Stash; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import static java.util.Collections.singletonMap; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.sameInstance; public class StashTests extends ESTestCase { - public void testReplaceStashedValuesEmbeddedStashKey() throws IOException { + public void testReplaceStashedValuesStashKeyInMapValue() throws IOException { Stash stash = new Stash(); - stash.stashValue("stashed", "bar"); - + Map expected = new HashMap<>(); expected.put("key", singletonMap("a", "foobar")); Map map = new HashMap<>(); Map map2 = new HashMap<>(); - map2.put("a", "foo${stashed}"); + if (randomBoolean()) { + stash.stashValue("stashed", "bar"); + map2.put("a", "foo${stashed}"); + } else { + stash.stashValue("stashed", "foobar"); + map2.put("a", "$stashed"); + } + map.put("key", map2); + + Map actual = stash.replaceStashedValues(map); + assertEquals(expected, actual); + assertThat(actual, not(sameInstance(map))); + } + + public void testReplaceStashedValuesStashKeyInMapKey() throws IOException { + Stash stash = new Stash(); + + Map expected = new HashMap<>(); + expected.put("key", singletonMap("foobar", "a")); + Map map = new HashMap<>(); + Map map2 = new HashMap<>(); + if (randomBoolean()) { + stash.stashValue("stashed", "bar"); + map2.put("foo${stashed}", "a"); + } else { + stash.stashValue("stashed", "foobar"); + map2.put("$stashed", "a"); + } + map.put("key", map2); + + Map actual = stash.replaceStashedValues(map); + assertEquals(expected, actual); + assertThat(actual, not(sameInstance(map))); + } + + public void testReplaceStashedValuesStashKeyInMapKeyConflicts() throws IOException { + Stash stash = new Stash(); + + Map map = new HashMap<>(); + Map map2 = new HashMap<>(); + String key; + if (randomBoolean()) { + stash.stashValue("stashed", "bar"); + key = "foo${stashed}"; + } else { + stash.stashValue("stashed", "foobar"); + key = "$stashed"; + } + map2.put(key, "a"); + map2.put("foobar", "whatever"); map.put("key", map2); + Exception e = expectThrows(IllegalArgumentException.class, () -> stash.replaceStashedValues(map)); + assertEquals(e.getMessage(), "Unstashing has caused a key conflict! The map is [{foobar=whatever}] and the key is [" + + key + "] which unstashes to [foobar]"); + } + + + public void testReplaceStashedValuesStashKeyInList() throws IOException { + Stash stash = new Stash(); + stash.stashValue("stashed", "bar"); + + Map expected = new HashMap<>(); + expected.put("key", Arrays.asList("foot", "foobar", 1)); + Map map = new HashMap<>(); + Object value; + if (randomBoolean()) { + stash.stashValue("stashed", "bar"); + value = "foo${stashed}"; + } else { + stash.stashValue("stashed", "foobar"); + value = "$stashed"; + } + map.put("key", Arrays.asList("foot", value, 1)); + Map actual = stash.replaceStashedValues(map); assertEquals(expected, actual); + assertThat(actual, not(sameInstance(map))); } } From 1cae850cf5bf15147a89bfae0dd066a485003dd5 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Tue, 16 May 2017 17:34:37 +0200 Subject: [PATCH 374/619] Add a cluster block that allows to delete indices that are read-only (#24678) Today when an index is `read-only` the index is also blocked from being deleted which sometimes is undesired since in-order to make changes to a cluster indices must be deleted to free up space. This is a likely scenario in a hosted environment when disk-space is limited to switch indices read-only but allow deletions to free up space. --- .../cluster/settings/SettingsUpdater.java | 10 +++- .../TransportClusterUpdateSettingsAction.java | 15 +++--- .../delete/TransportDeleteIndexAction.java | 2 +- .../put/TransportUpdateSettingsAction.java | 5 +- .../cluster/block/ClusterBlock.java | 23 ++++++-- .../cluster/block/ClusterBlockLevel.java | 33 ++---------- .../cluster/block/ClusterBlocks.java | 49 +++++++++++------ .../cluster/metadata/IndexMetaData.java | 13 +++-- .../cluster/metadata/MetaData.java | 10 +++- .../metadata/MetaDataIndexStateService.java | 2 +- .../MetaDataUpdateSettingsService.java | 1 + .../common/settings/ClusterSettings.java | 1 + .../common/settings/IndexScopedSettings.java | 1 + .../discovery/DiscoverySettings.java | 4 +- .../elasticsearch/gateway/GatewayService.java | 9 +++- .../org/elasticsearch/rest/RestStatus.java | 2 +- .../org/elasticsearch/tribe/TribeService.java | 6 +-- .../settings/SettingsUpdaterTests.java | 9 ++++ .../cluster/tasks/PendingTasksBlocksIT.java | 4 +- .../clear/ClearIndicesCacheBlocksIT.java | 3 +- .../indices/delete/DeleteIndexBlocksIT.java | 54 +++++++++++++++++-- .../admin/indices/flush/FlushBlocksIT.java | 4 +- .../forcemerge/ForceMergeBlocksIT.java | 3 +- .../action/admin/indices/get/GetIndexIT.java | 3 +- .../indices/refresh/RefreshBlocksIT.java | 5 +- .../segments/IndicesSegmentsBlocksIT.java | 4 +- .../indices/stats/IndicesStatsBlocksIT.java | 4 +- .../action/main/MainActionTests.java | 4 +- .../TransportBroadcastByNodeActionTests.java | 4 +- .../TransportMasterNodeActionTests.java | 2 +- .../TransportReplicationActionTests.java | 10 ++-- ...ortInstanceSingleOperationActionTests.java | 2 +- .../cluster/allocation/ClusterRerouteIT.java | 4 +- .../cluster/block/ClusterBlockTests.java | 6 +-- .../cluster/settings/ClusterSettingsIT.java | 25 ++++++++- .../cluster/shards/ClusterSearchShardsIT.java | 4 +- .../exists/indices/IndicesExistsIT.java | 4 +- .../indices/settings/GetSettingsBlocksIT.java | 3 +- .../indices/state/OpenCloseIndexIT.java | 5 +- docs/reference/index-modules.asciidoc | 4 ++ docs/reference/modules/cluster/misc.asciidoc | 5 ++ .../reindex/DeleteByQueryBasicTests.java | 7 ++- .../elasticsearch/test/ESIntegTestCase.java | 3 +- 43 files changed, 262 insertions(+), 109 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/settings/SettingsUpdater.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/settings/SettingsUpdater.java index 575fbcd3b9827..e9fec716a90c7 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/settings/SettingsUpdater.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/settings/SettingsUpdater.java @@ -67,12 +67,20 @@ synchronized ClusterState updateSettings(final ClusterState currentState, Settin .transientSettings(transientSettings.build()); ClusterBlocks.Builder blocks = ClusterBlocks.builder().blocks(currentState.blocks()); - boolean updatedReadOnly = MetaData.SETTING_READ_ONLY_SETTING.get(metaData.persistentSettings()) || MetaData.SETTING_READ_ONLY_SETTING.get(metaData.transientSettings()); + boolean updatedReadOnly = MetaData.SETTING_READ_ONLY_SETTING.get(metaData.persistentSettings()) + || MetaData.SETTING_READ_ONLY_SETTING.get(metaData.transientSettings()); if (updatedReadOnly) { blocks.addGlobalBlock(MetaData.CLUSTER_READ_ONLY_BLOCK); } else { blocks.removeGlobalBlock(MetaData.CLUSTER_READ_ONLY_BLOCK); } + boolean updatedReadOnlyAllowDelete = MetaData.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.get(metaData.persistentSettings()) + || MetaData.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.get(metaData.transientSettings()); + if (updatedReadOnlyAllowDelete) { + blocks.addGlobalBlock(MetaData.CLUSTER_READ_ONLY_ALLOW_DELETE_BLOCK); + } else { + blocks.removeGlobalBlock(MetaData.CLUSTER_READ_ONLY_ALLOW_DELETE_BLOCK); + } ClusterState build = builder(currentState).metaData(metaData).blocks(blocks).build(); Settings settings = build.metaData().settings(); // now we try to apply things and if they are invalid we fail diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/settings/TransportClusterUpdateSettingsAction.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/settings/TransportClusterUpdateSettingsAction.java index 913dbfff20ccd..dae55b2fc048a 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/settings/TransportClusterUpdateSettingsAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/settings/TransportClusterUpdateSettingsAction.java @@ -67,12 +67,15 @@ protected String executor() { @Override protected ClusterBlockException checkBlock(ClusterUpdateSettingsRequest request, ClusterState state) { // allow for dedicated changes to the metadata blocks, so we don't block those to allow to "re-enable" it - if ((request.transientSettings().isEmpty() && - request.persistentSettings().size() == 1 && - MetaData.SETTING_READ_ONLY_SETTING.exists(request.persistentSettings())) || - (request.persistentSettings().isEmpty() && request.transientSettings().size() == 1 && - MetaData.SETTING_READ_ONLY_SETTING.exists(request.transientSettings()))) { - return null; + if (request.transientSettings().size() + request.persistentSettings().size() == 1) { + // only one setting + if (MetaData.SETTING_READ_ONLY_SETTING.exists(request.persistentSettings()) + || MetaData.SETTING_READ_ONLY_SETTING.exists(request.transientSettings()) + || MetaData.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.exists(request.transientSettings()) + || MetaData.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.exists(request.persistentSettings())) { + // one of the settings above as the only setting in the request means - resetting the block! + return null; + } } return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); } diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/delete/TransportDeleteIndexAction.java b/core/src/main/java/org/elasticsearch/action/admin/indices/delete/TransportDeleteIndexAction.java index 251eed8bdb88b..f5c63bd470d40 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/delete/TransportDeleteIndexAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/delete/TransportDeleteIndexAction.java @@ -78,7 +78,7 @@ protected void doExecute(Task task, DeleteIndexRequest request, ActionListener levels) { + public ClusterBlock(int id, String description, boolean retryable, boolean disableStatePersistence, boolean allowReleaseResources, RestStatus status, + EnumSet levels) { this.id = id; this.description = description; this.retryable = retryable; this.disableStatePersistence = disableStatePersistence; this.status = status; this.levels = levels; + this.allowReleaseResources = allowReleaseResources; } public int id() { @@ -127,12 +132,17 @@ public void readFrom(StreamInput in) throws IOException { final int len = in.readVInt(); ArrayList levels = new ArrayList<>(len); for (int i = 0; i < len; i++) { - levels.add(ClusterBlockLevel.fromId(in.readVInt())); + levels.add(in.readEnum(ClusterBlockLevel.class)); } this.levels = EnumSet.copyOf(levels); retryable = in.readBoolean(); disableStatePersistence = in.readBoolean(); status = RestStatus.readFrom(in); + if (in.getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { + allowReleaseResources = in.readBoolean(); + } else { + allowReleaseResources = false; + } } @Override @@ -141,11 +151,14 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(description); out.writeVInt(levels.size()); for (ClusterBlockLevel level : levels) { - out.writeVInt(level.id()); + out.writeEnum(level); } out.writeBoolean(retryable); out.writeBoolean(disableStatePersistence); RestStatus.writeTo(out, status); + if (out.getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { + out.writeBoolean(allowReleaseResources); + } } @Override @@ -176,4 +189,8 @@ public boolean equals(Object o) { public int hashCode() { return id; } + + public boolean isAllowReleaseResources() { + return allowReleaseResources; + } } diff --git a/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlockLevel.java b/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlockLevel.java index 9d39d410d03aa..177de711a6c66 100644 --- a/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlockLevel.java +++ b/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlockLevel.java @@ -23,34 +23,11 @@ import java.util.EnumSet; public enum ClusterBlockLevel { - READ(0), - WRITE(1), - METADATA_READ(2), - METADATA_WRITE(3); + READ, + WRITE, + METADATA_READ, + METADATA_WRITE; - public static final EnumSet ALL = EnumSet.of(READ, WRITE, METADATA_READ, METADATA_WRITE); + public static final EnumSet ALL = EnumSet.allOf(ClusterBlockLevel.class); public static final EnumSet READ_WRITE = EnumSet.of(READ, WRITE); - - private final int id; - - ClusterBlockLevel(int id) { - this.id = id; - } - - public int id() { - return this.id; - } - - static ClusterBlockLevel fromId(int id) { - if (id == 0) { - return READ; - } else if (id == 1) { - return WRITE; - } else if (id == 2) { - return METADATA_READ; - } else if (id == 3) { - return METADATA_WRITE; - } - throw new IllegalArgumentException("No cluster block level matching [" + id + "]"); - } } diff --git a/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlocks.java b/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlocks.java index 2bdf560580be7..9e05d50831882 100644 --- a/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlocks.java +++ b/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlocks.java @@ -70,11 +70,11 @@ public ImmutableOpenMap> indices() { } public Set global(ClusterBlockLevel level) { - return levelHolders[level.id()].global(); + return levelHolders[level.ordinal()].global(); } public ImmutableOpenMap> indices(ClusterBlockLevel level) { - return levelHolders[level.id()].indices(); + return levelHolders[level.ordinal()].indices(); } private Set blocksForIndex(ClusterBlockLevel level, String index) { @@ -97,7 +97,7 @@ private static ImmutableLevelHolder[] generateLevelHolders(Set glo .collect(toSet()))); } - levelHolders[level.id()] = new ImmutableLevelHolder(newGlobal, indicesBuilder.build()); + levelHolders[level.ordinal()] = new ImmutableLevelHolder(newGlobal, indicesBuilder.build()); } return levelHolders; } @@ -203,6 +203,26 @@ public ClusterBlockException indicesBlockedException(ClusterBlockLevel level, St return new ClusterBlockException(unmodifiableSet(blocks.collect(toSet()))); } + /** + * Returns true iff non of the given have a {@link ClusterBlockLevel#METADATA_WRITE} in place where the + * {@link ClusterBlock#isAllowReleaseResources()} returns false. This is used in places where resources will be released + * like the deletion of an index to free up resources on nodes. + * @param indices the indices to check + */ + public ClusterBlockException indicesAllowReleaseResources(String[] indices) { + final Function> blocksForIndexAtLevel = index -> + blocksForIndex(ClusterBlockLevel.METADATA_WRITE, index).stream(); + Stream blocks = concat( + global(ClusterBlockLevel.METADATA_WRITE).stream(), + Stream.of(indices).flatMap(blocksForIndexAtLevel)).filter(clusterBlock -> clusterBlock.isAllowReleaseResources() == false); + Set clusterBlocks = unmodifiableSet(blocks.collect(toSet())); + if (clusterBlocks.isEmpty()) { + return null; + } + return new ClusterBlockException(clusterBlocks); + } + + @Override public String toString() { if (global.isEmpty() && indices().isEmpty()) { @@ -270,8 +290,6 @@ public static Diff readDiffFrom(StreamInput in) throws IOExceptio static class ImmutableLevelHolder { - static final ImmutableLevelHolder EMPTY = new ImmutableLevelHolder(emptySet(), ImmutableOpenMap.of()); - private final Set global; private final ImmutableOpenMap> indices; @@ -314,30 +332,31 @@ public Builder blocks(ClusterBlocks blocks) { } public Builder addBlocks(IndexMetaData indexMetaData) { + String indexName = indexMetaData.getIndex().getName(); if (indexMetaData.getState() == IndexMetaData.State.CLOSE) { - addIndexBlock(indexMetaData.getIndex().getName(), MetaDataIndexStateService.INDEX_CLOSED_BLOCK); + addIndexBlock(indexName, MetaDataIndexStateService.INDEX_CLOSED_BLOCK); } if (IndexMetaData.INDEX_READ_ONLY_SETTING.get(indexMetaData.getSettings())) { - addIndexBlock(indexMetaData.getIndex().getName(), IndexMetaData.INDEX_READ_ONLY_BLOCK); + addIndexBlock(indexName, IndexMetaData.INDEX_READ_ONLY_BLOCK); } if (IndexMetaData.INDEX_BLOCKS_READ_SETTING.get(indexMetaData.getSettings())) { - addIndexBlock(indexMetaData.getIndex().getName(), IndexMetaData.INDEX_READ_BLOCK); + addIndexBlock(indexName, IndexMetaData.INDEX_READ_BLOCK); } if (IndexMetaData.INDEX_BLOCKS_WRITE_SETTING.get(indexMetaData.getSettings())) { - addIndexBlock(indexMetaData.getIndex().getName(), IndexMetaData.INDEX_WRITE_BLOCK); + addIndexBlock(indexName, IndexMetaData.INDEX_WRITE_BLOCK); } if (IndexMetaData.INDEX_BLOCKS_METADATA_SETTING.get(indexMetaData.getSettings())) { - addIndexBlock(indexMetaData.getIndex().getName(), IndexMetaData.INDEX_METADATA_BLOCK); + addIndexBlock(indexName, IndexMetaData.INDEX_METADATA_BLOCK); + } + if (IndexMetaData.INDEX_BLOCKS_READ_ONLY_ALLOW_DELETE_SETTING.get(indexMetaData.getSettings())) { + addIndexBlock(indexName, IndexMetaData.INDEX_READ_ONLY_ALLOW_DELETE_BLOCK); } return this; } public Builder updateBlocks(IndexMetaData indexMetaData) { - removeIndexBlock(indexMetaData.getIndex().getName(), MetaDataIndexStateService.INDEX_CLOSED_BLOCK); - removeIndexBlock(indexMetaData.getIndex().getName(), IndexMetaData.INDEX_READ_ONLY_BLOCK); - removeIndexBlock(indexMetaData.getIndex().getName(), IndexMetaData.INDEX_READ_BLOCK); - removeIndexBlock(indexMetaData.getIndex().getName(), IndexMetaData.INDEX_WRITE_BLOCK); - removeIndexBlock(indexMetaData.getIndex().getName(), IndexMetaData.INDEX_METADATA_BLOCK); + // let's remove all blocks for this index and add them back -- no need to remove all individual blocks.... + indices.remove(indexMetaData.getIndex().getName()); return addBlocks(indexMetaData); } diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java b/core/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java index f1f6f8aee2232..591b83c0eff7c 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java @@ -131,10 +131,11 @@ public static T lookupPrototypeSafe(String type) { return proto; } - public static final ClusterBlock INDEX_READ_ONLY_BLOCK = new ClusterBlock(5, "index read-only (api)", false, false, RestStatus.FORBIDDEN, EnumSet.of(ClusterBlockLevel.WRITE, ClusterBlockLevel.METADATA_WRITE)); - public static final ClusterBlock INDEX_READ_BLOCK = new ClusterBlock(7, "index read (api)", false, false, RestStatus.FORBIDDEN, EnumSet.of(ClusterBlockLevel.READ)); - public static final ClusterBlock INDEX_WRITE_BLOCK = new ClusterBlock(8, "index write (api)", false, false, RestStatus.FORBIDDEN, EnumSet.of(ClusterBlockLevel.WRITE)); - public static final ClusterBlock INDEX_METADATA_BLOCK = new ClusterBlock(9, "index metadata (api)", false, false, RestStatus.FORBIDDEN, EnumSet.of(ClusterBlockLevel.METADATA_WRITE, ClusterBlockLevel.METADATA_READ)); + public static final ClusterBlock INDEX_READ_ONLY_BLOCK = new ClusterBlock(5, "index read-only (api)", false, false, false, RestStatus.FORBIDDEN, EnumSet.of(ClusterBlockLevel.WRITE, ClusterBlockLevel.METADATA_WRITE)); + public static final ClusterBlock INDEX_READ_BLOCK = new ClusterBlock(7, "index read (api)", false, false, false, RestStatus.FORBIDDEN, EnumSet.of(ClusterBlockLevel.READ)); + public static final ClusterBlock INDEX_WRITE_BLOCK = new ClusterBlock(8, "index write (api)", false, false, false, RestStatus.FORBIDDEN, EnumSet.of(ClusterBlockLevel.WRITE)); + public static final ClusterBlock INDEX_METADATA_BLOCK = new ClusterBlock(9, "index metadata (api)", false, false, false, RestStatus.FORBIDDEN, EnumSet.of(ClusterBlockLevel.METADATA_WRITE, ClusterBlockLevel.METADATA_READ)); + public static final ClusterBlock INDEX_READ_ONLY_ALLOW_DELETE_BLOCK = new ClusterBlock(12, "index read-only / allow delete (api)", false, false, true, RestStatus.FORBIDDEN, EnumSet.of(ClusterBlockLevel.METADATA_WRITE, ClusterBlockLevel.WRITE)); public enum State { OPEN((byte) 0), @@ -212,6 +213,10 @@ static Setting buildNumberOfShardsSetting() { public static final Setting INDEX_BLOCKS_METADATA_SETTING = Setting.boolSetting(SETTING_BLOCKS_METADATA, false, Property.Dynamic, Property.IndexScope); + public static final String SETTING_READ_ONLY_ALLOW_DELETE = "index.blocks.read_only_allow_delete"; + public static final Setting INDEX_BLOCKS_READ_ONLY_ALLOW_DELETE_SETTING = + Setting.boolSetting(SETTING_READ_ONLY_ALLOW_DELETE, false, Property.Dynamic, Property.IndexScope); + public static final String SETTING_VERSION_CREATED = "index.version.created"; public static final String SETTING_VERSION_CREATED_STRING = "index.version.created_string"; public static final String SETTING_VERSION_UPGRADED = "index.version.upgraded"; diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java index a13e5e21e5ef2..e47585356a01b 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java @@ -24,7 +24,6 @@ import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.CollectionUtil; -import org.elasticsearch.Version; import org.elasticsearch.cluster.Diff; import org.elasticsearch.cluster.Diffable; import org.elasticsearch.cluster.DiffableUtils; @@ -119,7 +118,14 @@ public interface Custom extends NamedDiffable, ToXContent { public static final Setting SETTING_READ_ONLY_SETTING = Setting.boolSetting("cluster.blocks.read_only", false, Property.Dynamic, Property.NodeScope); - public static final ClusterBlock CLUSTER_READ_ONLY_BLOCK = new ClusterBlock(6, "cluster read-only (api)", false, false, RestStatus.FORBIDDEN, EnumSet.of(ClusterBlockLevel.WRITE, ClusterBlockLevel.METADATA_WRITE)); + public static final ClusterBlock CLUSTER_READ_ONLY_BLOCK = new ClusterBlock(6, "cluster read-only (api)", false, false, + false, RestStatus.FORBIDDEN, EnumSet.of(ClusterBlockLevel.WRITE, ClusterBlockLevel.METADATA_WRITE)); + + public static final Setting SETTING_READ_ONLY_ALLOW_DELETE_SETTING = + Setting.boolSetting("cluster.blocks.read_only_allow_delete", false, Property.Dynamic, Property.NodeScope); + + public static final ClusterBlock CLUSTER_READ_ONLY_ALLOW_DELETE_BLOCK = new ClusterBlock(13, "cluster read-only / allow delete (api)", + false, false, true, RestStatus.FORBIDDEN, EnumSet.of(ClusterBlockLevel.WRITE, ClusterBlockLevel.METADATA_WRITE)); public static final MetaData EMPTY_META_DATA = builder().build(); diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexStateService.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexStateService.java index 2a2c6c65b967c..7f8a176243aa6 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexStateService.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexStateService.java @@ -54,7 +54,7 @@ */ public class MetaDataIndexStateService extends AbstractComponent { - public static final ClusterBlock INDEX_CLOSED_BLOCK = new ClusterBlock(4, "index closed", false, false, RestStatus.FORBIDDEN, ClusterBlockLevel.READ_WRITE); + public static final ClusterBlock INDEX_CLOSED_BLOCK = new ClusterBlock(4, "index closed", false, false, false, RestStatus.FORBIDDEN, ClusterBlockLevel.READ_WRITE); private final ClusterService clusterService; diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java index b4b1fc8051961..653edcb9e89e8 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java @@ -230,6 +230,7 @@ public ClusterState execute(ClusterState currentState) { ClusterBlocks.Builder blocks = ClusterBlocks.builder().blocks(currentState.blocks()); maybeUpdateClusterBlock(actualIndices, blocks, IndexMetaData.INDEX_READ_ONLY_BLOCK, IndexMetaData.INDEX_READ_ONLY_SETTING, openSettings); + maybeUpdateClusterBlock(actualIndices, blocks, IndexMetaData.INDEX_READ_ONLY_ALLOW_DELETE_BLOCK, IndexMetaData.INDEX_BLOCKS_READ_ONLY_ALLOW_DELETE_SETTING, openSettings); maybeUpdateClusterBlock(actualIndices, blocks, IndexMetaData.INDEX_METADATA_BLOCK, IndexMetaData.INDEX_BLOCKS_METADATA_SETTING, openSettings); maybeUpdateClusterBlock(actualIndices, blocks, IndexMetaData.INDEX_WRITE_BLOCK, IndexMetaData.INDEX_BLOCKS_WRITE_SETTING, openSettings); maybeUpdateClusterBlock(actualIndices, blocks, IndexMetaData.INDEX_READ_BLOCK, IndexMetaData.INDEX_BLOCKS_READ_SETTING, openSettings); diff --git a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java index 0b3f4d4cbc990..e79d46bb55569 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java +++ b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java @@ -187,6 +187,7 @@ public void apply(Settings value, Settings current, Settings previous) { IndicesQueryCache.INDICES_QUERIES_CACHE_ALL_SEGMENTS_SETTING, MappingUpdatedAction.INDICES_MAPPING_DYNAMIC_TIMEOUT_SETTING, MetaData.SETTING_READ_ONLY_SETTING, + MetaData.SETTING_READ_ONLY_ALLOW_DELETE_SETTING, RecoverySettings.INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING, RecoverySettings.INDICES_RECOVERY_RETRY_DELAY_STATE_SYNC_SETTING, RecoverySettings.INDICES_RECOVERY_RETRY_DELAY_NETWORK_SETTING, diff --git a/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java b/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java index 1b3d2f249b9cc..9fcafcea3b2f3 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java +++ b/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java @@ -74,6 +74,7 @@ public final class IndexScopedSettings extends AbstractScopedSettings { IndexMetaData.INDEX_BLOCKS_READ_SETTING, IndexMetaData.INDEX_BLOCKS_WRITE_SETTING, IndexMetaData.INDEX_BLOCKS_METADATA_SETTING, + IndexMetaData.INDEX_BLOCKS_READ_ONLY_ALLOW_DELETE_SETTING, IndexMetaData.INDEX_PRIORITY_SETTING, IndexMetaData.INDEX_DATA_PATH_SETTING, SearchSlowLog.INDEX_SEARCH_SLOWLOG_THRESHOLD_FETCH_DEBUG_SETTING, diff --git a/core/src/main/java/org/elasticsearch/discovery/DiscoverySettings.java b/core/src/main/java/org/elasticsearch/discovery/DiscoverySettings.java index 6f5a6c9a745a8..e9a83678f8a2c 100644 --- a/core/src/main/java/org/elasticsearch/discovery/DiscoverySettings.java +++ b/core/src/main/java/org/elasticsearch/discovery/DiscoverySettings.java @@ -37,8 +37,8 @@ public class DiscoverySettings extends AbstractComponent { public static final int NO_MASTER_BLOCK_ID = 2; - public static final ClusterBlock NO_MASTER_BLOCK_ALL = new ClusterBlock(NO_MASTER_BLOCK_ID, "no master", true, true, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL); - public static final ClusterBlock NO_MASTER_BLOCK_WRITES = new ClusterBlock(NO_MASTER_BLOCK_ID, "no master", true, false, RestStatus.SERVICE_UNAVAILABLE, EnumSet.of(ClusterBlockLevel.WRITE, ClusterBlockLevel.METADATA_WRITE)); + public static final ClusterBlock NO_MASTER_BLOCK_ALL = new ClusterBlock(NO_MASTER_BLOCK_ID, "no master", true, true, false, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL); + public static final ClusterBlock NO_MASTER_BLOCK_WRITES = new ClusterBlock(NO_MASTER_BLOCK_ID, "no master", true, false, false, RestStatus.SERVICE_UNAVAILABLE, EnumSet.of(ClusterBlockLevel.WRITE, ClusterBlockLevel.METADATA_WRITE)); /** * sets the timeout for a complete publishing cycle, including both sending and committing. the master * will continue to process the next cluster state update after this time has elapsed diff --git a/core/src/main/java/org/elasticsearch/gateway/GatewayService.java b/core/src/main/java/org/elasticsearch/gateway/GatewayService.java index 6e884af3b8948..6b61e03443eae 100644 --- a/core/src/main/java/org/elasticsearch/gateway/GatewayService.java +++ b/core/src/main/java/org/elasticsearch/gateway/GatewayService.java @@ -65,7 +65,7 @@ public class GatewayService extends AbstractLifecycleComponent implements Cluste public static final Setting RECOVER_AFTER_MASTER_NODES_SETTING = Setting.intSetting("gateway.recover_after_master_nodes", 0, 0, Property.NodeScope); - public static final ClusterBlock STATE_NOT_RECOVERED_BLOCK = new ClusterBlock(1, "state not recovered / initialized", true, true, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL); + public static final ClusterBlock STATE_NOT_RECOVERED_BLOCK = new ClusterBlock(1, "state not recovered / initialized", true, true, false, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL); public static final TimeValue DEFAULT_RECOVER_AFTER_TIME_IF_EXPECTED_NODES_IS_SET = TimeValue.timeValueMinutes(5); @@ -246,9 +246,14 @@ public ClusterState execute(ClusterState currentState) { // automatically generate a UID for the metadata if we need to metaDataBuilder.generateClusterUuidIfNeeded(); - if (MetaData.SETTING_READ_ONLY_SETTING.get(recoveredState.metaData().settings()) || MetaData.SETTING_READ_ONLY_SETTING.get(currentState.metaData().settings())) { + if (MetaData.SETTING_READ_ONLY_SETTING.get(recoveredState.metaData().settings()) + || MetaData.SETTING_READ_ONLY_SETTING.get(currentState.metaData().settings())) { blocks.addGlobalBlock(MetaData.CLUSTER_READ_ONLY_BLOCK); } + if (MetaData.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.get(recoveredState.metaData().settings()) + || MetaData.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.get(currentState.metaData().settings())) { + blocks.addGlobalBlock(MetaData.CLUSTER_READ_ONLY_ALLOW_DELETE_BLOCK); + } for (IndexMetaData indexMetaData : recoveredState.metaData()) { metaDataBuilder.put(indexMetaData, false); diff --git a/core/src/main/java/org/elasticsearch/rest/RestStatus.java b/core/src/main/java/org/elasticsearch/rest/RestStatus.java index d72eb2d11f4c8..e7c07f21147d0 100644 --- a/core/src/main/java/org/elasticsearch/rest/RestStatus.java +++ b/core/src/main/java/org/elasticsearch/rest/RestStatus.java @@ -479,7 +479,7 @@ public enum RestStatus { * is considered to be temporary. If the request that received this status code was the result of a user action, * the request MUST NOT be repeated until it is requested by a separate user action. */ - INSUFFICIENT_STORAGE(506); + INSUFFICIENT_STORAGE(507); private static final Map CODE_TO_STATUS; static { diff --git a/core/src/main/java/org/elasticsearch/tribe/TribeService.java b/core/src/main/java/org/elasticsearch/tribe/TribeService.java index cec01732c4286..a89fe23edb3df 100644 --- a/core/src/main/java/org/elasticsearch/tribe/TribeService.java +++ b/core/src/main/java/org/elasticsearch/tribe/TribeService.java @@ -61,7 +61,6 @@ import org.elasticsearch.discovery.DiscoverySettings; import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.gateway.GatewayService; import org.elasticsearch.node.Node; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.transport.TransportSettings; @@ -74,7 +73,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Function; @@ -103,9 +101,9 @@ public class TribeService extends AbstractLifecycleComponent { public static final ClusterBlock TRIBE_METADATA_BLOCK = new ClusterBlock(10, "tribe node, metadata not allowed", false, false, - RestStatus.BAD_REQUEST, EnumSet.of(ClusterBlockLevel.METADATA_READ, ClusterBlockLevel.METADATA_WRITE)); + false, RestStatus.BAD_REQUEST, EnumSet.of(ClusterBlockLevel.METADATA_READ, ClusterBlockLevel.METADATA_WRITE)); public static final ClusterBlock TRIBE_WRITE_BLOCK = new ClusterBlock(11, "tribe node, write not allowed", false, false, - RestStatus.BAD_REQUEST, EnumSet.of(ClusterBlockLevel.WRITE)); + false, RestStatus.BAD_REQUEST, EnumSet.of(ClusterBlockLevel.WRITE)); public static Settings processSettings(Settings settings) { if (TRIBE_NAME_SETTING.exists(settings)) { diff --git a/core/src/test/java/org/elasticsearch/action/admin/cluster/settings/SettingsUpdaterTests.java b/core/src/test/java/org/elasticsearch/action/admin/cluster/settings/SettingsUpdaterTests.java index bd1377b89feb4..ad03d4b001db6 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/cluster/settings/SettingsUpdaterTests.java +++ b/core/src/test/java/org/elasticsearch/action/admin/cluster/settings/SettingsUpdaterTests.java @@ -122,5 +122,14 @@ public void testClusterBlock() { Settings.builder().put(MetaData.SETTING_READ_ONLY_SETTING.getKey(), false).build()); assertEquals(clusterState.blocks().global().size(), 0); + + clusterState = updater.updateSettings(build, Settings.builder().put(MetaData.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.getKey(), true).build(), + Settings.builder().put(BalancedShardsAllocator.INDEX_BALANCE_FACTOR_SETTING.getKey(), 1.6).put(BalancedShardsAllocator.SHARD_BALANCE_FACTOR_SETTING.getKey(), 1.0f).build()); + assertEquals(clusterState.blocks().global().size(), 1); + assertEquals(clusterState.blocks().global().iterator().next(), MetaData.CLUSTER_READ_ONLY_ALLOW_DELETE_BLOCK); + clusterState = updater.updateSettings(build, Settings.EMPTY, + Settings.builder().put(MetaData.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.getKey(), false).build()); + assertEquals(clusterState.blocks().global().size(), 0); + } } diff --git a/core/src/test/java/org/elasticsearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java b/core/src/test/java/org/elasticsearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java index 95fa5b2600f02..2aaf2507e3ba7 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java +++ b/core/src/test/java/org/elasticsearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java @@ -28,6 +28,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; @ClusterScope(scope = ESIntegTestCase.Scope.TEST) public class PendingTasksBlocksIT extends ESIntegTestCase { @@ -36,7 +37,8 @@ public void testPendingTasksWithBlocks() { ensureGreen("test"); // This test checks that the Pending Cluster Tasks operation is never blocked, even if an index is read only or whatever. - for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, SETTING_BLOCKS_METADATA)) { + for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, SETTING_BLOCKS_METADATA, + SETTING_READ_ONLY_ALLOW_DELETE)) { try { enableIndexBlock("test", blockSetting); PendingClusterTasksResponse response = client().admin().cluster().preparePendingClusterTasks().execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/cache/clear/ClearIndicesCacheBlocksIT.java b/core/src/test/java/org/elasticsearch/action/admin/indices/cache/clear/ClearIndicesCacheBlocksIT.java index dbc7e5cddc3f5..ee1f4dd24e2f4 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/cache/clear/ClearIndicesCacheBlocksIT.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/cache/clear/ClearIndicesCacheBlocksIT.java @@ -28,6 +28,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; @@ -52,7 +53,7 @@ public void testClearIndicesCacheWithBlocks() { } } // Request is blocked - for (String blockSetting : Arrays.asList(SETTING_READ_ONLY, SETTING_BLOCKS_METADATA)) { + for (String blockSetting : Arrays.asList(SETTING_READ_ONLY, SETTING_BLOCKS_METADATA, SETTING_READ_ONLY_ALLOW_DELETE)) { try { enableIndexBlock("test", blockSetting); assertBlocked(client().admin().indices().prepareClearCache("test").setFieldDataCache(true).setQueryCache(true).setFieldDataCache(true)); diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/delete/DeleteIndexBlocksIT.java b/core/src/test/java/org/elasticsearch/action/admin/indices/delete/DeleteIndexBlocksIT.java index a83c209a3c22d..63cfc5da43b2f 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/delete/DeleteIndexBlocksIT.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/delete/DeleteIndexBlocksIT.java @@ -19,22 +19,68 @@ package org.elasticsearch.action.admin.indices.delete; +import org.elasticsearch.action.support.IndicesOptions; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.cluster.metadata.MetaData; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.test.ESIntegTestCase.ClusterScope; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; -@ClusterScope(scope = ESIntegTestCase.Scope.TEST) public class DeleteIndexBlocksIT extends ESIntegTestCase { public void testDeleteIndexWithBlocks() { createIndex("test"); ensureGreen("test"); - try { setClusterReadOnly(true); - assertBlocked(client().admin().indices().prepareDelete("test")); + assertBlocked(client().admin().indices().prepareDelete("test"), MetaData.CLUSTER_READ_ONLY_BLOCK); } finally { setClusterReadOnly(false); } } + + public void testDeleteIndexOnIndexReadOnlyAllowDeleteSetting() { + createIndex("test"); + ensureGreen("test"); + client().prepareIndex().setIndex("test").setType("doc").setId("1").setSource("foo", "bar").get(); + refresh(); + try { + Settings settings = Settings.builder().put(IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE, true).build(); + assertAcked(client().admin().indices().prepareUpdateSettings("test").setSettings(settings).get()); + assertSearchHits(client().prepareSearch().get(), "1"); + assertBlocked(client().prepareIndex().setIndex("test").setType("doc").setId("2").setSource("foo", "bar"), + IndexMetaData.INDEX_READ_ONLY_ALLOW_DELETE_BLOCK); + assertBlocked(client().admin().indices().prepareUpdateSettings("test") + .setSettings(Settings.builder().put("index.number_of_replicas", 2)), IndexMetaData.INDEX_READ_ONLY_ALLOW_DELETE_BLOCK); + assertSearchHits(client().prepareSearch().get(), "1"); + assertAcked(client().admin().indices().prepareDelete("test")); + } finally { + Settings settings = Settings.builder().putNull(IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE).build(); + assertAcked(client().admin().indices().prepareUpdateSettings("test").setIndicesOptions(IndicesOptions.lenientExpandOpen()). + setSettings(settings).get()); + } + } + + public void testDeleteIndexOnReadOnlyAllowDeleteSetting() { + createIndex("test"); + ensureGreen("test"); + client().prepareIndex().setIndex("test").setType("doc").setId("1").setSource("foo", "bar").get(); + refresh(); + try { + Settings settings = Settings.builder().put(MetaData.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.getKey(), true).build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings).get()); + assertSearchHits(client().prepareSearch().get(), "1"); + assertBlocked(client().prepareIndex().setIndex("test").setType("doc").setId("2").setSource("foo", "bar"), + MetaData.CLUSTER_READ_ONLY_ALLOW_DELETE_BLOCK); + assertBlocked(client().admin().indices().prepareUpdateSettings("test") + .setSettings(Settings.builder().put("index.number_of_replicas", 2)), MetaData.CLUSTER_READ_ONLY_ALLOW_DELETE_BLOCK); + assertSearchHits(client().prepareSearch().get(), "1"); + assertAcked(client().admin().indices().prepareDelete("test")); + } finally { + Settings settings = Settings.builder().putNull(MetaData.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.getKey()).build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings).get()); + } + } } diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/flush/FlushBlocksIT.java b/core/src/test/java/org/elasticsearch/action/admin/indices/flush/FlushBlocksIT.java index 3ba349ffca8c7..1ace701572c45 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/flush/FlushBlocksIT.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/flush/FlushBlocksIT.java @@ -28,6 +28,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; @@ -46,7 +47,8 @@ public void testFlushWithBlocks() { } // Request is not blocked - for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, SETTING_BLOCKS_METADATA)) { + for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, SETTING_BLOCKS_METADATA, + SETTING_READ_ONLY_ALLOW_DELETE)) { try { enableIndexBlock("test", blockSetting); FlushResponse response = client().admin().indices().prepareFlush("test").execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/forcemerge/ForceMergeBlocksIT.java b/core/src/test/java/org/elasticsearch/action/admin/indices/forcemerge/ForceMergeBlocksIT.java index e1f498b09bbfc..aa6b7c61386bc 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/forcemerge/ForceMergeBlocksIT.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/forcemerge/ForceMergeBlocksIT.java @@ -28,6 +28,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; @@ -59,7 +60,7 @@ public void testForceMergeWithBlocks() { } // Request is blocked - for (String blockSetting : Arrays.asList(SETTING_READ_ONLY, SETTING_BLOCKS_METADATA)) { + for (String blockSetting : Arrays.asList(SETTING_READ_ONLY, SETTING_BLOCKS_METADATA, SETTING_READ_ONLY_ALLOW_DELETE)) { try { enableIndexBlock("test", blockSetting); assertBlocked(client().admin().indices().prepareForceMerge("test")); diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/get/GetIndexIT.java b/core/src/test/java/org/elasticsearch/action/admin/indices/get/GetIndexIT.java index 722482837a30f..2bd13669fee26 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/get/GetIndexIT.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/get/GetIndexIT.java @@ -38,6 +38,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; import static org.hamcrest.Matchers.anyOf; @@ -178,7 +179,7 @@ public void testEmptyMixedFeatures() { } public void testGetIndexWithBlocks() { - for (String block : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY)) { + for (String block : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, SETTING_READ_ONLY_ALLOW_DELETE)) { try { enableIndexBlock("idx", block); GetIndexResponse response = client().admin().indices().prepareGetIndex().addIndices("idx") diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/refresh/RefreshBlocksIT.java b/core/src/test/java/org/elasticsearch/action/admin/indices/refresh/RefreshBlocksIT.java index cc74f7c734198..d69f7842bb662 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/refresh/RefreshBlocksIT.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/refresh/RefreshBlocksIT.java @@ -29,7 +29,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; @@ -42,7 +42,8 @@ public void testRefreshWithBlocks() { NumShards numShards = getNumShards("test"); // Request is not blocked - for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, SETTING_BLOCKS_METADATA)) { + for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, SETTING_BLOCKS_METADATA, + SETTING_READ_ONLY_ALLOW_DELETE)) { try { enableIndexBlock("test", blockSetting); RefreshResponse response = client().admin().indices().prepareRefresh("test").execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/segments/IndicesSegmentsBlocksIT.java b/core/src/test/java/org/elasticsearch/action/admin/indices/segments/IndicesSegmentsBlocksIT.java index 035c760d84b4c..bcf106eda8040 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/segments/IndicesSegmentsBlocksIT.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/segments/IndicesSegmentsBlocksIT.java @@ -28,6 +28,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; @@ -44,7 +45,8 @@ public void testIndicesSegmentsWithBlocks() { client().admin().indices().prepareFlush("test-blocks").get(); // Request is not blocked - for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY)) { + for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, + SETTING_READ_ONLY_ALLOW_DELETE)) { try { enableIndexBlock("test-blocks", blockSetting); IndicesSegmentResponse response = client().admin().indices().prepareSegments("test-blocks").execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsBlocksIT.java b/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsBlocksIT.java index 25fdb7a84dbc3..e7b477f61b2a0 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsBlocksIT.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsBlocksIT.java @@ -29,6 +29,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; @ClusterScope(scope = ESIntegTestCase.Scope.TEST) public class IndicesStatsBlocksIT extends ESIntegTestCase { @@ -37,7 +38,8 @@ public void testIndicesStatsWithBlocks() { ensureGreen("ro"); // Request is not blocked - for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY)) { + for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, + SETTING_READ_ONLY_ALLOW_DELETE)) { try { enableIndexBlock("ro", blockSetting); IndicesStatsResponse indicesStatsResponse = client().admin().indices().prepareStats("ro").execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/action/main/MainActionTests.java b/core/src/test/java/org/elasticsearch/action/main/MainActionTests.java index 3407007d64707..92e093350aa3b 100644 --- a/core/src/test/java/org/elasticsearch/action/main/MainActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/main/MainActionTests.java @@ -108,13 +108,13 @@ public void testMainActionClusterAvailable() { } else { blocks = ClusterBlocks.builder() .addGlobalBlock(new ClusterBlock(randomIntBetween(1, 16), "test global block 400", randomBoolean(), randomBoolean(), - RestStatus.BAD_REQUEST, ClusterBlockLevel.ALL)) + false, RestStatus.BAD_REQUEST, ClusterBlockLevel.ALL)) .build(); } } else { blocks = ClusterBlocks.builder() .addGlobalBlock(new ClusterBlock(randomIntBetween(1, 16), "test global block 503", randomBoolean(), randomBoolean(), - RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)) + false, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)) .build(); } ClusterState state = ClusterState.builder(clusterName).blocks(blocks).build(); diff --git a/core/src/test/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeActionTests.java b/core/src/test/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeActionTests.java index 93d8be990de8c..7e04e99b1742e 100644 --- a/core/src/test/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeActionTests.java @@ -262,7 +262,7 @@ public void testGlobalBlock() { PlainActionFuture listener = new PlainActionFuture<>(); ClusterBlocks.Builder block = ClusterBlocks.builder() - .addGlobalBlock(new ClusterBlock(1, "test-block", false, true, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)); + .addGlobalBlock(new ClusterBlock(1, "test-block", false, true, false, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)); setState(clusterService, ClusterState.builder(clusterService.state()).blocks(block)); try { action.new AsyncAction(null, request, listener).start(); @@ -277,7 +277,7 @@ public void testRequestBlock() { PlainActionFuture listener = new PlainActionFuture<>(); ClusterBlocks.Builder block = ClusterBlocks.builder() - .addIndexBlock(TEST_INDEX, new ClusterBlock(1, "test-block", false, true, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)); + .addIndexBlock(TEST_INDEX, new ClusterBlock(1, "test-block", false, true, false, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)); setState(clusterService, ClusterState.builder(clusterService.state()).blocks(block)); try { action.new AsyncAction(null, request, listener).start(); diff --git a/core/src/test/java/org/elasticsearch/action/support/master/TransportMasterNodeActionTests.java b/core/src/test/java/org/elasticsearch/action/support/master/TransportMasterNodeActionTests.java index ae543aa14cdf0..f8975a5369e8c 100644 --- a/core/src/test/java/org/elasticsearch/action/support/master/TransportMasterNodeActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/master/TransportMasterNodeActionTests.java @@ -205,7 +205,7 @@ public void testLocalOperationWithBlocks() throws ExecutionException, Interrupte PlainActionFuture listener = new PlainActionFuture<>(); ClusterBlock block = new ClusterBlock(1, "", retryableBlock, true, - randomFrom(RestStatus.values()), ClusterBlockLevel.ALL); + false, randomFrom(RestStatus.values()), ClusterBlockLevel.ALL); ClusterState stateWithBlock = ClusterState.builder(ClusterStateCreationUtils.state(localNode, localNode, allNodes)) .blocks(ClusterBlocks.builder().addGlobalBlock(block)).build(); setState(clusterService, stateWithBlock); diff --git a/core/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java b/core/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java index db8855aaddc02..b402feb6d81dd 100644 --- a/core/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java @@ -194,8 +194,8 @@ protected ClusterBlockLevel globalBlockLevel() { } }; - ClusterBlocks.Builder block = ClusterBlocks.builder() - .addGlobalBlock(new ClusterBlock(1, "non retryable", false, true, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)); + ClusterBlocks.Builder block = ClusterBlocks.builder().addGlobalBlock(new ClusterBlock(1, "non retryable", false, true, + false, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)); setState(clusterService, ClusterState.builder(clusterService.state()).blocks(block)); TestAction.ReroutePhase reroutePhase = action.new ReroutePhase(task, request, listener); reroutePhase.run(); @@ -203,7 +203,7 @@ protected ClusterBlockLevel globalBlockLevel() { assertPhase(task, "failed"); block = ClusterBlocks.builder() - .addGlobalBlock(new ClusterBlock(1, "retryable", true, true, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)); + .addGlobalBlock(new ClusterBlock(1, "retryable", true, true, false, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)); setState(clusterService, ClusterState.builder(clusterService.state()).blocks(block)); listener = new PlainActionFuture<>(); reroutePhase = action.new ReroutePhase(task, new Request().timeout("5ms"), listener); @@ -219,8 +219,8 @@ protected ClusterBlockLevel globalBlockLevel() { assertPhase(task, "waiting_for_retry"); assertTrue(request.isRetrySet.get()); - block = ClusterBlocks.builder() - .addGlobalBlock(new ClusterBlock(1, "non retryable", false, true, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)); + block = ClusterBlocks.builder().addGlobalBlock(new ClusterBlock(1, "non retryable", false, true, false, + RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)); setState(clusterService, ClusterState.builder(clusterService.state()).blocks(block)); assertListenerThrows("primary phase should fail operation when moving from a retryable block to a non-retryable one", listener, ClusterBlockException.class); diff --git a/core/src/test/java/org/elasticsearch/action/support/single/instance/TransportInstanceSingleOperationActionTests.java b/core/src/test/java/org/elasticsearch/action/support/single/instance/TransportInstanceSingleOperationActionTests.java index c83a76ddc1efe..ba488cecb38f8 100644 --- a/core/src/test/java/org/elasticsearch/action/support/single/instance/TransportInstanceSingleOperationActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/single/instance/TransportInstanceSingleOperationActionTests.java @@ -176,7 +176,7 @@ public void testGlobalBlock() { Request request = new Request(); PlainActionFuture listener = new PlainActionFuture<>(); ClusterBlocks.Builder block = ClusterBlocks.builder() - .addGlobalBlock(new ClusterBlock(1, "", false, true, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)); + .addGlobalBlock(new ClusterBlock(1, "", false, true, false, RestStatus.SERVICE_UNAVAILABLE, ClusterBlockLevel.ALL)); setState(clusterService, ClusterState.builder(clusterService.state()).blocks(block)); try { action.new AsyncSingleAction(request, listener).start(); diff --git a/core/src/test/java/org/elasticsearch/cluster/allocation/ClusterRerouteIT.java b/core/src/test/java/org/elasticsearch/cluster/allocation/ClusterRerouteIT.java index f9f4a136e1cd6..b6b6b3024b413 100644 --- a/core/src/test/java/org/elasticsearch/cluster/allocation/ClusterRerouteIT.java +++ b/core/src/test/java/org/elasticsearch/cluster/allocation/ClusterRerouteIT.java @@ -59,6 +59,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; import static org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ENABLE_SETTING; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; @@ -327,7 +328,8 @@ public void testClusterRerouteWithBlocks() throws Exception { int toggle = nodesIds.indexOf(node.getName()); // Rerouting shards is not blocked - for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, SETTING_BLOCKS_METADATA)) { + for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, SETTING_BLOCKS_METADATA, + SETTING_READ_ONLY_ALLOW_DELETE)) { try { enableIndexBlock("test-blocks", blockSetting); assertAcked(client().admin().cluster().prepareReroute() diff --git a/core/src/test/java/org/elasticsearch/cluster/block/ClusterBlockTests.java b/core/src/test/java/org/elasticsearch/cluster/block/ClusterBlockTests.java index a7fe1b918c0f0..a84d160cf0c95 100644 --- a/core/src/test/java/org/elasticsearch/cluster/block/ClusterBlockTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/block/ClusterBlockTests.java @@ -49,7 +49,7 @@ public void testSerialization() throws Exception { } ClusterBlock clusterBlock = new ClusterBlock(randomInt(), "cluster block #" + randomInt(), randomBoolean(), - randomBoolean(), randomFrom(RestStatus.values()), levels); + randomBoolean(), false, randomFrom(RestStatus.values()), levels); BytesStreamOutput out = new BytesStreamOutput(); out.setVersion(version); @@ -75,7 +75,7 @@ public void testToStringDanglingComma() { levels.add(randomFrom(ClusterBlockLevel.values())); } ClusterBlock clusterBlock = new ClusterBlock(randomInt(), "cluster block #" + randomInt(), randomBoolean(), - randomBoolean(), randomFrom(RestStatus.values()), levels); + randomBoolean(), false, randomFrom(RestStatus.values()), levels); assertThat(clusterBlock.toString(), not(endsWith(","))); } @@ -86,7 +86,7 @@ public void testGlobalBlocksCheckedIfNoIndicesSpecified() { levels.add(randomFrom(ClusterBlockLevel.values())); } ClusterBlock globalBlock = new ClusterBlock(randomInt(), "cluster block #" + randomInt(), randomBoolean(), - randomBoolean(), randomFrom(RestStatus.values()), levels); + randomBoolean(), false, randomFrom(RestStatus.values()), levels); ClusterBlocks clusterBlocks = new ClusterBlocks(Collections.singleton(globalBlock), ImmutableOpenMap.of()); ClusterBlockException exception = clusterBlocks.indicesBlockedException(randomFrom(globalBlock.levels()), new String[0]); assertNotNull(exception); diff --git a/core/src/test/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java b/core/src/test/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java index bab53b8f35c22..61e31666f3492 100644 --- a/core/src/test/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java +++ b/core/src/test/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java @@ -25,6 +25,7 @@ import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.elasticsearch.common.logging.ESLoggerFactory; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.discovery.Discovery; @@ -33,6 +34,7 @@ import org.elasticsearch.indices.recovery.RecoverySettings; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; +import org.junit.After; import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; @@ -42,8 +44,15 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -@ClusterScope(scope = TEST) public class ClusterSettingsIT extends ESIntegTestCase { + + @After + public void cleanup() throws Exception { + assertAcked(client().admin().cluster().prepareUpdateSettings() + .setPersistentSettings(Settings.builder().putNull("*")) + .setTransientSettings(Settings.builder().putNull("*"))); + } + public void testClusterNonExistingSettingsUpdate() { String key1 = "no_idea_what_you_are_talking_about"; int value1 = 10; @@ -302,13 +311,25 @@ public void testClusterUpdateSettingsWithBlocks() { assertBlocked(request, MetaData.CLUSTER_READ_ONLY_BLOCK); // But it's possible to update the settings to update the "cluster.blocks.read_only" setting - Settings settings = Settings.builder().put(MetaData.SETTING_READ_ONLY_SETTING.getKey(), false).build(); + Settings settings = Settings.builder().putNull(MetaData.SETTING_READ_ONLY_SETTING.getKey()).build(); assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings).get()); } finally { setClusterReadOnly(false); } + // Cluster settings updates are blocked when the cluster is read only + try { + // But it's possible to update the settings to update the "cluster.blocks.read_only" setting + Settings settings = Settings.builder().put(MetaData.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.getKey(), true).build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings).get()); + assertBlocked(request, MetaData.CLUSTER_READ_ONLY_ALLOW_DELETE_BLOCK); + } finally { + // But it's possible to update the settings to update the "cluster.blocks.read_only" setting + Settings s = Settings.builder().putNull(MetaData.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.getKey()).build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(s).get()); + } + // It should work now ClusterUpdateSettingsResponse response = request.execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/cluster/shards/ClusterSearchShardsIT.java b/core/src/test/java/org/elasticsearch/cluster/shards/ClusterSearchShardsIT.java index e648dce6a602e..6e1475d0aebdc 100644 --- a/core/src/test/java/org/elasticsearch/cluster/shards/ClusterSearchShardsIT.java +++ b/core/src/test/java/org/elasticsearch/cluster/shards/ClusterSearchShardsIT.java @@ -33,6 +33,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; import static org.hamcrest.Matchers.equalTo; @@ -136,7 +137,8 @@ public void testClusterSearchShardsWithBlocks() { ensureGreen("test-blocks"); // Request is not blocked - for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY)) { + for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, + SETTING_READ_ONLY_ALLOW_DELETE)) { try { enableIndexBlock("test-blocks", blockSetting); ClusterSearchShardsResponse response = client().admin().cluster().prepareSearchShards("test-blocks").execute().actionGet(); diff --git a/core/src/test/java/org/elasticsearch/indices/exists/indices/IndicesExistsIT.java b/core/src/test/java/org/elasticsearch/indices/exists/indices/IndicesExistsIT.java index 1f56e4cfc573c..dbfa5fb2d077a 100644 --- a/core/src/test/java/org/elasticsearch/indices/exists/indices/IndicesExistsIT.java +++ b/core/src/test/java/org/elasticsearch/indices/exists/indices/IndicesExistsIT.java @@ -30,6 +30,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.hamcrest.Matchers.equalTo; @@ -66,7 +67,8 @@ public void testIndicesExistsWithBlocks() { createIndex("ro"); // Request is not blocked - for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY)) { + for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, + SETTING_READ_ONLY_ALLOW_DELETE)) { try { enableIndexBlock("ro", blockSetting); assertThat(client().admin().indices().prepareExists("ro").execute().actionGet().isExists(), equalTo(true)); diff --git a/core/src/test/java/org/elasticsearch/indices/settings/GetSettingsBlocksIT.java b/core/src/test/java/org/elasticsearch/indices/settings/GetSettingsBlocksIT.java index d53230d3a9ee8..cb45a639c07eb 100644 --- a/core/src/test/java/org/elasticsearch/indices/settings/GetSettingsBlocksIT.java +++ b/core/src/test/java/org/elasticsearch/indices/settings/GetSettingsBlocksIT.java @@ -30,6 +30,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; import static org.hamcrest.Matchers.equalTo; @@ -43,7 +44,7 @@ public void testGetSettingsWithBlocks() throws Exception { .put("index.merge.policy.expunge_deletes_allowed", "30") .put(MapperService.INDEX_MAPPER_DYNAMIC_SETTING.getKey(), false))); - for (String block : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY)) { + for (String block : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY, SETTING_READ_ONLY_ALLOW_DELETE)) { try { enableIndexBlock("test", block); GetSettingsResponse response = client().admin().indices().prepareGetSettings("test").get(); diff --git a/core/src/test/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java b/core/src/test/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java index 8dbaaf3e9477a..a867425f392a8 100644 --- a/core/src/test/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java +++ b/core/src/test/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java @@ -44,6 +44,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; @@ -190,7 +191,7 @@ public void testCloseOpenAllWildcard() { assertThat(openIndexResponse.isAcknowledged(), equalTo(true)); assertIndexIsOpened("test1", "test2", "test3"); } - + public void testCloseNoIndex() { Client client = client(); Exception e = expectThrows(ActionRequestValidationException.class, () -> @@ -380,7 +381,7 @@ public void testOpenCloseIndexWithBlocks() { assertIndexIsClosed("test"); // Opening an index is blocked - for (String blockSetting : Arrays.asList(SETTING_READ_ONLY, SETTING_BLOCKS_METADATA)) { + for (String blockSetting : Arrays.asList(SETTING_READ_ONLY, SETTING_READ_ONLY_ALLOW_DELETE, SETTING_BLOCKS_METADATA)) { try { enableIndexBlock("test", blockSetting); assertBlocked(client().admin().indices().prepareOpen("test")); diff --git a/docs/reference/index-modules.asciidoc b/docs/reference/index-modules.asciidoc index afbd4105663fa..9f1999d6acf17 100644 --- a/docs/reference/index-modules.asciidoc +++ b/docs/reference/index-modules.asciidoc @@ -130,6 +130,10 @@ specific index module: Set to `true` to make the index and index metadata read only, `false` to allow writes and metadata changes. +`index.blocks.read_only_allow_delete`:: + Identical to `index.blocks.read_only` but allows deleting the index to free + up resources. + `index.blocks.read`:: Set to `true` to disable read operations against the index. diff --git a/docs/reference/modules/cluster/misc.asciidoc b/docs/reference/modules/cluster/misc.asciidoc index 1d35025557130..3963312c0f4ea 100644 --- a/docs/reference/modules/cluster/misc.asciidoc +++ b/docs/reference/modules/cluster/misc.asciidoc @@ -12,6 +12,11 @@ An entire cluster may be set to read-only with the following _dynamic_ setting: operations), metadata is not allowed to be modified (create or delete indices). +`cluster.blocks.read_only_allow_delete`:: + + Identical to `cluster.blocks.read_only` but allows to delete indices + to free up resources. + WARNING: Don't rely on this setting to prevent changes to your cluster. Any user with access to the <> API can make the cluster read-write again. diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryBasicTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryBasicTests.java index f0eba7e9fa68f..e316759e0412d 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryBasicTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/DeleteByQueryBasicTests.java @@ -29,6 +29,8 @@ import java.util.ArrayList; import java.util.List; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; import static org.elasticsearch.index.query.QueryBuilders.matchQuery; import static org.elasticsearch.index.query.QueryBuilders.rangeQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery; @@ -200,12 +202,13 @@ public void testDeleteByQueryOnReadOnlyIndex() throws Exception { } indexRandom(true, true, true, builders); + String block = randomFrom(SETTING_READ_ONLY, SETTING_READ_ONLY_ALLOW_DELETE); try { - enableIndexBlock("test", IndexMetaData.SETTING_READ_ONLY); + enableIndexBlock("test", block); assertThat(deleteByQuery().source("test").filter(QueryBuilders.matchAllQuery()).refresh(true).get(), matcher().deleted(0).failures(docs)); } finally { - disableIndexBlock("test", IndexMetaData.SETTING_READ_ONLY); + disableIndexBlock("test", block); } assertHitCount(client().prepareSearch("test").setSize(0).get(), docs); diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java index bd5a809d8cde1..b33142dfabe3a 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java @@ -1482,7 +1482,8 @@ public static void enableIndexBlock(String index, String block) { /** Sets or unsets the cluster read_only mode **/ public static void setClusterReadOnly(boolean value) { - Settings settings = Settings.builder().put(MetaData.SETTING_READ_ONLY_SETTING.getKey(), value).build(); + Settings settings = value ? Settings.builder().put(MetaData.SETTING_READ_ONLY_SETTING.getKey(), value).build() : + Settings.builder().putNull(MetaData.SETTING_READ_ONLY_SETTING.getKey()).build() ; assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings).get()); } From 97d2657e1825821d2256255ab5a23f8ed77b7d63 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 16 May 2017 09:10:09 -0700 Subject: [PATCH 375/619] Remove script access to term statistics (#19462) In scripts (at least some of the languages), the terms dictionary and postings can be access with the special _index variable. This is for very advanced use cases which want to do their own scoring. The problem is segment level statistics must be recomputed for every document. Additionally, this is not friendly to the terms index caching as the order of looking up terms should be controlled by lucene. This change removes _index from scripts. Anyone using it can and should instead write a Similarity plugin, which is explicitly designed to allow doing the calculations needed for a relevance score. closes #19359 --- .../script/AbstractSearchScript.java | 8 - .../search/lookup/CachedPositionIterator.java | 132 --- .../search/lookup/IndexField.java | 128 -- .../search/lookup/IndexFieldTerm.java | 298 ----- .../search/lookup/IndexLookup.java | 74 -- .../search/lookup/LeafIndexLookup.java | 199 ---- .../search/lookup/LeafSearchLookup.java | 13 +- .../search/lookup/PositionIterator.java | 87 -- .../search/lookup/SearchLookup.java | 4 +- .../search/lookup/TermPosition.java | 58 - .../elasticsearch/script/IndexLookupIT.java | 1029 ----------------- .../migration/migrate_6_0/scripting.asciidoc | 6 +- .../modules/scripting/fields.asciidoc | 4 - 13 files changed, 8 insertions(+), 2032 deletions(-) delete mode 100644 core/src/main/java/org/elasticsearch/search/lookup/CachedPositionIterator.java delete mode 100644 core/src/main/java/org/elasticsearch/search/lookup/IndexField.java delete mode 100644 core/src/main/java/org/elasticsearch/search/lookup/IndexFieldTerm.java delete mode 100644 core/src/main/java/org/elasticsearch/search/lookup/IndexLookup.java delete mode 100644 core/src/main/java/org/elasticsearch/search/lookup/LeafIndexLookup.java delete mode 100644 core/src/main/java/org/elasticsearch/search/lookup/PositionIterator.java delete mode 100644 core/src/main/java/org/elasticsearch/search/lookup/TermPosition.java delete mode 100644 core/src/test/java/org/elasticsearch/script/IndexLookupIT.java diff --git a/core/src/main/java/org/elasticsearch/script/AbstractSearchScript.java b/core/src/main/java/org/elasticsearch/script/AbstractSearchScript.java index 929046d4dd8ef..50f90892a70f8 100644 --- a/core/src/main/java/org/elasticsearch/script/AbstractSearchScript.java +++ b/core/src/main/java/org/elasticsearch/script/AbstractSearchScript.java @@ -23,7 +23,6 @@ import org.elasticsearch.index.fielddata.ScriptDocValues; import org.elasticsearch.search.lookup.LeafDocLookup; import org.elasticsearch.search.lookup.LeafFieldsLookup; -import org.elasticsearch.search.lookup.LeafIndexLookup; import org.elasticsearch.search.lookup.LeafSearchLookup; import org.elasticsearch.search.lookup.SourceLookup; @@ -87,13 +86,6 @@ protected final SourceLookup source() { return lookup.source(); } - /** - * Allows to access statistics on terms and fields. - */ - protected final LeafIndexLookup indexLookup() { - return lookup.indexLookup(); - } - /** * Allows to access the *stored* fields. */ diff --git a/core/src/main/java/org/elasticsearch/search/lookup/CachedPositionIterator.java b/core/src/main/java/org/elasticsearch/search/lookup/CachedPositionIterator.java deleted file mode 100644 index 82604b1e0dd84..0000000000000 --- a/core/src/main/java/org/elasticsearch/search/lookup/CachedPositionIterator.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.search.lookup; - -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.BytesRefBuilder; -import org.apache.lucene.util.IntsRefBuilder; - -import java.io.IOException; -import java.util.Iterator; - -/* - * Can iterate over the positions of a term an arbitrary number of times. - * */ -public class CachedPositionIterator extends PositionIterator { - - public CachedPositionIterator(IndexFieldTerm indexFieldTerm) { - super(indexFieldTerm); - } - - // all payloads of the term in the current document in one bytes array. - // payloadStarts and payloadLength mark the start and end of one payload. - final BytesRefBuilder payloads = new BytesRefBuilder(); - - final IntsRefBuilder payloadsLengths = new IntsRefBuilder(); - - final IntsRefBuilder payloadsStarts = new IntsRefBuilder(); - - final IntsRefBuilder positions = new IntsRefBuilder(); - - final IntsRefBuilder startOffsets = new IntsRefBuilder(); - - final IntsRefBuilder endOffsets = new IntsRefBuilder(); - - final BytesRef payload = new BytesRef(); - - @Override - public Iterator reset() { - return new Iterator() { - private int pos = 0; - private final TermPosition termPosition = new TermPosition(); - - @Override - public boolean hasNext() { - return pos < freq; - } - - @Override - public TermPosition next() { - termPosition.position = positions.intAt(pos); - termPosition.startOffset = startOffsets.intAt(pos); - termPosition.endOffset = endOffsets.intAt(pos); - termPosition.payload = payload; - payload.bytes = payloads.bytes(); - payload.offset = payloadsStarts.intAt(pos); - payload.length = payloadsLengths.intAt(pos); - pos++; - return termPosition; - } - - @Override - public void remove() { - } - }; - } - - - private void record() throws IOException { - TermPosition termPosition; - for (int i = 0; i < freq; i++) { - termPosition = super.next(); - positions.setIntAt(i, termPosition.position); - addPayload(i, termPosition.payload); - startOffsets.setIntAt(i, termPosition.startOffset); - endOffsets.setIntAt(i, termPosition.endOffset); - } - } - private void ensureSize(int freq) { - if (freq == 0) { - return; - } - startOffsets.grow(freq); - endOffsets.grow(freq); - positions.grow(freq); - payloadsLengths.grow(freq); - payloadsStarts.grow(freq); - payloads.grow(freq * 8);// this is just a guess.... - - } - - private void addPayload(int i, BytesRef currPayload) { - if (currPayload != null) { - payloadsLengths.setIntAt(i, currPayload.length); - payloadsStarts.setIntAt(i, i == 0 ? 0 : payloadsStarts.intAt(i - 1) + payloadsLengths.intAt(i - 1)); - payloads.grow(payloadsStarts.intAt(i) + currPayload.length); - System.arraycopy(currPayload.bytes, currPayload.offset, payloads.bytes(), payloadsStarts.intAt(i), currPayload.length); - } else { - payloadsLengths.setIntAt(i, 0); - payloadsStarts.setIntAt(i, i == 0 ? 0 : payloadsStarts.intAt(i - 1) + payloadsLengths.intAt(i - 1)); - } - } - - - @Override - public void nextDoc() throws IOException { - super.nextDoc(); - ensureSize(freq); - record(); - } - - @Override - public TermPosition next() { - throw new UnsupportedOperationException(); - } -} diff --git a/core/src/main/java/org/elasticsearch/search/lookup/IndexField.java b/core/src/main/java/org/elasticsearch/search/lookup/IndexField.java deleted file mode 100644 index 21803983d2944..0000000000000 --- a/core/src/main/java/org/elasticsearch/search/lookup/IndexField.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.search.lookup; - -import org.apache.lucene.search.CollectionStatistics; -import org.elasticsearch.common.util.MinimalMap; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -/** - * Script interface to all information regarding a field. - * */ -public class IndexField extends MinimalMap { - - /* - * TermsInfo Objects that represent the Terms are stored in this map when - * requested. Information such as frequency, doc frequency and positions - * information can be retrieved from the TermInfo objects in this map. - */ - private final Map terms = new HashMap<>(); - - // the name of this field - private final String fieldName; - - /* - * The holds the current reader. We need it to populate the field - * statistics. We just delegate all requests there - */ - private final LeafIndexLookup indexLookup; - - /* - * General field statistics such as number of documents containing the - * field. - */ - private final CollectionStatistics fieldStats; - - /* - * Represents a field in a document. Can be used to return information on - * statistics of this field. Information on specific terms in this field can - * be accessed by calling get(String term). - */ - public IndexField(String fieldName, LeafIndexLookup indexLookup) throws IOException { - - assert fieldName != null; - this.fieldName = fieldName; - - assert indexLookup != null; - this.indexLookup = indexLookup; - - fieldStats = this.indexLookup.getIndexSearcher().collectionStatistics(fieldName); - } - - /* get number of documents containing the field */ - public long docCount() throws IOException { - return fieldStats.docCount(); - } - - /* get sum of the number of words over all documents that were indexed */ - public long sumttf() throws IOException { - return fieldStats.sumTotalTermFreq(); - } - - /* - * get the sum of doc frequencies over all words that appear in any document - * that has the field. - */ - public long sumdf() throws IOException { - return fieldStats.sumDocFreq(); - } - - // TODO: might be good to get the field lengths here somewhere? - - /* - * Returns a TermInfo object that can be used to access information on - * specific terms. flags can be set as described in TermInfo. - * - * TODO: here might be potential for running time improvement? If we knew in - * advance which terms are requested, we could provide an array which the - * user could then iterate over. - */ - public IndexFieldTerm get(Object key, int flags) { - String termString = (String) key; - IndexFieldTerm indexFieldTerm = terms.get(termString); - // see if we initialized already... - if (indexFieldTerm == null) { - indexFieldTerm = new IndexFieldTerm(termString, fieldName, indexLookup, flags); - terms.put(termString, indexFieldTerm); - } - indexFieldTerm.validateFlags(flags); - return indexFieldTerm; - } - - /* - * Returns a TermInfo object that can be used to access information on - * specific terms. flags can be set as described in TermInfo. - */ - @Override - public IndexFieldTerm get(Object key) { - // per default, do not initialize any positions info - return get(key, IndexLookup.FLAG_FREQUENCIES); - } - - public void setDocIdInTerms(int docId) { - for (IndexFieldTerm ti : terms.values()) { - ti.setDocument(docId); - } - } - -} diff --git a/core/src/main/java/org/elasticsearch/search/lookup/IndexFieldTerm.java b/core/src/main/java/org/elasticsearch/search/lookup/IndexFieldTerm.java deleted file mode 100644 index fbca4f435b830..0000000000000 --- a/core/src/main/java/org/elasticsearch/search/lookup/IndexFieldTerm.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.search.lookup; - -import org.apache.lucene.index.Fields; -import org.apache.lucene.index.FilterLeafReader.FilterPostingsEnum; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.PostingsEnum; -import org.apache.lucene.index.Term; -import org.apache.lucene.index.TermContext; -import org.apache.lucene.index.Terms; -import org.apache.lucene.index.TermsEnum; -import org.apache.lucene.search.DocIdSetIterator; -import org.apache.lucene.search.TermStatistics; -import org.apache.lucene.util.Bits; -import org.apache.lucene.util.BytesRef; -import org.elasticsearch.ElasticsearchException; - -import java.io.IOException; -import java.util.Iterator; - -/** - * Holds all information on a particular term in a field. - * */ -public class IndexFieldTerm implements Iterable { - - // The posting list for this term. Is null if the term or field does not - // exist. - PostingsEnum postings; - - // Stores if positions, offsets and payloads are requested. - private final int flags; - - private final String fieldName; - - private final String term; - - private final PositionIterator iterator; - - // for lucene calls - private final Term identifier; - - private final TermStatistics termStats; - - // get the document frequency of the term - public long df() throws IOException { - return termStats.docFreq(); - } - - // get the total term frequency of the term, that is, how often does the - // term appear in any document? - public long ttf() throws IOException { - return termStats.totalTermFreq(); - } - - // when the reader changes, we have to get the posting list for this term - // and reader - private void setReader(LeafReader reader) { - try { - postings = getPostings(convertToLuceneFlags(flags), reader); - - if (postings == null) { - // no term or field for this segment, fake out the postings... - final DocIdSetIterator empty = DocIdSetIterator.empty(); - postings = new PostingsEnum() { - @Override - public int docID() { - return empty.docID(); - } - - @Override - public int nextDoc() throws IOException { - return empty.nextDoc(); - } - - @Override - public int advance(int target) throws IOException { - return empty.advance(target); - } - - @Override - public long cost() { - return empty.cost(); - } - - @Override - public int freq() throws IOException { - return 1; - } - - @Override - public int nextPosition() throws IOException { - return -1; - } - - @Override - public int startOffset() throws IOException { - return -1; - } - - @Override - public int endOffset() throws IOException { - return -1; - } - - @Override - public BytesRef getPayload() throws IOException { - return null; - } - }; - } - } catch (IOException e) { - throw new ElasticsearchException("Unable to get postings for field " + fieldName + " and term " + term, e); - } - - } - - private int convertToLuceneFlags(int flags) { - int lucenePositionsFlags = PostingsEnum.NONE; - lucenePositionsFlags |= (flags & IndexLookup.FLAG_FREQUENCIES) > 0 ? PostingsEnum.FREQS : 0x0; - lucenePositionsFlags |= (flags & IndexLookup.FLAG_POSITIONS) > 0 ? PostingsEnum.POSITIONS : 0x0; - lucenePositionsFlags |= (flags & IndexLookup.FLAG_PAYLOADS) > 0 ? PostingsEnum.PAYLOADS : 0x0; - lucenePositionsFlags |= (flags & IndexLookup.FLAG_OFFSETS) > 0 ? PostingsEnum.OFFSETS : 0x0; - return lucenePositionsFlags; - } - - private PostingsEnum getPostings(int luceneFlags, LeafReader reader) throws IOException { - assert identifier.field() != null; - assert identifier.bytes() != null; - final Fields fields = reader.fields(); - PostingsEnum newPostings = null; - if (fields != null) { - final Terms terms = fields.terms(identifier.field()); - if (terms != null) { - TermsEnum termsEnum = terms.iterator(); - if (termsEnum.seekExact(identifier.bytes())) { - newPostings = termsEnum.postings(postings, luceneFlags); - final Bits liveDocs = reader.getLiveDocs(); - if (liveDocs != null) { - newPostings = new FilterPostingsEnum(newPostings) { - private int doNext(int d) throws IOException { - for (; d != NO_MORE_DOCS; d = super.nextDoc()) { - if (liveDocs.get(d)) { - return d; - } - } - return NO_MORE_DOCS; - } - @Override - public int nextDoc() throws IOException { - return doNext(super.nextDoc()); - } - @Override - public int advance(int target) throws IOException { - return doNext(super.advance(target)); - } - }; - } - } - } - } - return newPostings; - } - - private int freq = 0; - - public void setDocument(int docId) { - assert (postings != null); - try { - // we try to advance to the current document. - int currentDocPos = postings.docID(); - if (currentDocPos < docId) { - currentDocPos = postings.advance(docId); - } - if (currentDocPos == docId) { - freq = postings.freq(); - } else { - freq = 0; - } - iterator.nextDoc(); - } catch (IOException e) { - throw new ElasticsearchException("While trying to initialize term positions in IndexFieldTerm.setNextDoc() ", e); - } - } - - public IndexFieldTerm(String term, String fieldName, LeafIndexLookup indexLookup, int flags) { - assert fieldName != null; - this.fieldName = fieldName; - assert term != null; - this.term = term; - assert indexLookup != null; - identifier = new Term(fieldName, (String) term); - this.flags = flags; - boolean doRecord = ((flags & IndexLookup.FLAG_CACHE) > 0); - if (!doRecord) { - iterator = new PositionIterator(this); - } else { - iterator = new CachedPositionIterator(this); - } - setReader(indexLookup.getReader()); - setDocument(indexLookup.getDocId()); - try { - termStats = indexLookup.getIndexSearcher().termStatistics(identifier, - TermContext.build(indexLookup.getReaderContext(), identifier)); - } catch (IOException e) { - throw new ElasticsearchException("Cannot get term statistics: ", e); - } - } - - public int tf() throws IOException { - return freq; - } - - @Override - public Iterator iterator() { - return iterator.reset(); - } - - /* - * A user might decide inside a script to call get with _POSITIONS and then - * a second time with _PAYLOADS. If the positions were recorded but the - * payloads were not, the user will not have access to them. Therefore, throw - * exception here explaining how to call get(). - */ - public void validateFlags(int flags2) { - if ((this.flags & flags2) < flags2) { - throw new ElasticsearchException("You must call get with all required flags! Instead of " + getCalledStatement(flags2) - + "call " + getCallStatement(flags2 | this.flags) + " once"); - } - } - - private String getCalledStatement(int flags2) { - String calledFlagsCall1 = getFlagsString(flags); - String calledFlagsCall2 = getFlagsString(flags2); - String callStatement1 = getCallStatement(calledFlagsCall1); - String callStatement2 = getCallStatement(calledFlagsCall2); - return " " + callStatement1 + " and " + callStatement2 + " "; - } - - private String getCallStatement(String calledFlags) { - return "_index['" + this.fieldName + "'].get('" + this.term + "', " + calledFlags + ")"; - } - - private String getFlagsString(int flags2) { - String flagsString = null; - if ((flags2 & IndexLookup.FLAG_FREQUENCIES) != 0) { - flagsString = anddToFlagsString(flagsString, "_FREQUENCIES"); - } - if ((flags2 & IndexLookup.FLAG_POSITIONS) != 0) { - flagsString = anddToFlagsString(flagsString, "_POSITIONS"); - } - if ((flags2 & IndexLookup.FLAG_OFFSETS) != 0) { - flagsString = anddToFlagsString(flagsString, "_OFFSETS"); - } - if ((flags2 & IndexLookup.FLAG_PAYLOADS) != 0) { - flagsString = anddToFlagsString(flagsString, "_PAYLOADS"); - } - if ((flags2 & IndexLookup.FLAG_CACHE) != 0) { - flagsString = anddToFlagsString(flagsString, "_CACHE"); - } - return flagsString; - } - - private String anddToFlagsString(String flagsString, String flag) { - if (flagsString != null) { - flagsString += " | "; - } else { - flagsString = ""; - } - flagsString += flag; - return flagsString; - } - - private String getCallStatement(int flags2) { - String calledFlags = getFlagsString(flags2); - String callStatement = getCallStatement(calledFlags); - return " " + callStatement + " "; - - } - -} diff --git a/core/src/main/java/org/elasticsearch/search/lookup/IndexLookup.java b/core/src/main/java/org/elasticsearch/search/lookup/IndexLookup.java deleted file mode 100644 index 485c690ef26e4..0000000000000 --- a/core/src/main/java/org/elasticsearch/search/lookup/IndexLookup.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -package org.elasticsearch.search.lookup; - -import org.apache.lucene.index.LeafReaderContext; - -import java.util.HashMap; -import java.util.Map; - -import static java.util.Collections.unmodifiableMap; - -public class IndexLookup { - public static final Map NAMES; - static { - Map names = new HashMap<>(); - names.put("_FREQUENCIES", IndexLookup.FLAG_FREQUENCIES); - names.put("_POSITIONS", IndexLookup.FLAG_POSITIONS); - names.put("_OFFSETS", IndexLookup.FLAG_OFFSETS); - names.put("_PAYLOADS", IndexLookup.FLAG_PAYLOADS); - names.put("_CACHE", IndexLookup.FLAG_CACHE); - NAMES = unmodifiableMap(names); - } - /** - * Flag to pass to {@link IndexField#get(Object, int)} if you require - * offsets in the returned {@link IndexFieldTerm}. - */ - public static final int FLAG_OFFSETS = 2; - - /** - * Flag to pass to {@link IndexField#get(Object, int)} if you require - * payloads in the returned {@link IndexFieldTerm}. - */ - public static final int FLAG_PAYLOADS = 4; - - /** - * Flag to pass to {@link IndexField#get(Object, int)} if you require - * frequencies in the returned {@link IndexFieldTerm}. Frequencies might be - * returned anyway for some lucene codecs even if this flag is no set. - */ - public static final int FLAG_FREQUENCIES = 8; - - /** - * Flag to pass to {@link IndexField#get(Object, int)} if you require - * positions in the returned {@link IndexFieldTerm}. - */ - public static final int FLAG_POSITIONS = 16; - - /** - * Flag to pass to {@link IndexField#get(Object, int)} if you require - * positions in the returned {@link IndexFieldTerm}. - */ - public static final int FLAG_CACHE = 32; - - public static LeafIndexLookup getLeafIndexLookup(LeafReaderContext context) { - return new LeafIndexLookup(context); - } - -} diff --git a/core/src/main/java/org/elasticsearch/search/lookup/LeafIndexLookup.java b/core/src/main/java/org/elasticsearch/search/lookup/LeafIndexLookup.java deleted file mode 100644 index 9908f2830f85b..0000000000000 --- a/core/src/main/java/org/elasticsearch/search/lookup/LeafIndexLookup.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -package org.elasticsearch.search.lookup; - -import org.apache.logging.log4j.Logger; -import org.apache.lucene.index.Fields; -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.IndexReaderContext; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.ReaderUtil; -import org.apache.lucene.search.IndexSearcher; -import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.common.logging.DeprecationLogger; -import org.elasticsearch.common.logging.Loggers; -import org.elasticsearch.common.util.MinimalMap; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -public class LeafIndexLookup extends MinimalMap { - - // Current reader from which we can get the term vectors. No info on term - // and field statistics. - private final LeafReader reader; - - // The parent reader from which we can get proper field and term - // statistics - private final IndexReader parentReader; - - // we need this later to get the field and term statistics of the shard - private final IndexSearcher indexSearcher; - - // current docId - private int docId = -1; - - // stores the objects that are used in the script. we maintain this map - // because we do not want to re-initialize the objects each time a field is - // accessed - private final Map indexFields = new HashMap<>(); - - // number of documents per shard. cached here because the computation is - // expensive - private int numDocs = -1; - - // the maximum doc number of the shard. - private int maxDoc = -1; - - // number of deleted documents per shard. cached here because the - // computation is expensive - private int numDeletedDocs = -1; - - private boolean deprecationEmitted = false; - - private void logDeprecation() { - if (deprecationEmitted == false) { - Logger logger = Loggers.getLogger(getClass()); - DeprecationLogger deprecationLogger = new DeprecationLogger(logger); - deprecationLogger.deprecated("Using _index is deprecated. Create a custom ScriptEngine to access index internals."); - deprecationEmitted = true; - } - } - - public int numDocs() { - logDeprecation(); - if (numDocs == -1) { - numDocs = parentReader.numDocs(); - } - return numDocs; - } - - public int maxDoc() { - logDeprecation(); - if (maxDoc == -1) { - maxDoc = parentReader.maxDoc(); - } - return maxDoc; - } - - public int numDeletedDocs() { - logDeprecation(); - if (numDeletedDocs == -1) { - numDeletedDocs = parentReader.numDeletedDocs(); - } - return numDeletedDocs; - } - - public LeafIndexLookup(LeafReaderContext ctx) { - reader = ctx.reader(); - parentReader = ReaderUtil.getTopLevelContext(ctx).reader(); - indexSearcher = new IndexSearcher(parentReader); - indexSearcher.setQueryCache(null); - } - - public void setDocument(int docId) { - if (this.docId == docId) { // if we are called with the same docId, - // nothing to do - return; - } - // We assume that docs are processed in ascending order of id. If this - // is not the case, we would have to re initialize all posting lists in - // IndexFieldTerm. TODO: Instead of assert we could also call - // setReaderInFields(); here? - if (this.docId > docId) { - // This might happen if the same SearchLookup is used in different - // phases, such as score and fetch phase. - // In this case we do not want to re initialize posting list etc. - // because we do not even know if term and field statistics will be - // needed in this new phase. - // Therefore we just remove all IndexFieldTerms. - indexFields.clear(); - } - this.docId = docId; - setNextDocIdInFields(); - } - - protected void setNextDocIdInFields() { - for (IndexField stat : indexFields.values()) { - stat.setDocIdInTerms(this.docId); - } - } - - /* - * TODO: here might be potential for running time improvement? If we knew in - * advance which terms are requested, we could provide an array which the - * user could then iterate over. - */ - @Override - public IndexField get(Object key) { - logDeprecation(); - String stringField = (String) key; - IndexField indexField = indexFields.get(key); - if (indexField == null) { - try { - indexField = new IndexField(stringField, this); - indexFields.put(stringField, indexField); - } catch (IOException e) { - throw new ElasticsearchException(e); - } - } - return indexField; - } - - /* - * Get the lucene term vectors. See - * https://lucene.apache.org/core/4_0_0/core/org/apache/lucene/index/Fields.html - * * - */ - public Fields termVectors() throws IOException { - logDeprecation(); - assert reader != null; - return reader.getTermVectors(docId); - } - - LeafReader getReader() { - logDeprecation(); - return reader; - } - - public int getDocId() { - logDeprecation(); - return docId; - } - - public IndexReader getParentReader() { - logDeprecation(); - if (parentReader == null) { - return reader; - } - return parentReader; - } - - public IndexSearcher getIndexSearcher() { - logDeprecation(); - return indexSearcher; - } - - public IndexReaderContext getReaderContext() { - logDeprecation(); - return getParentReader().getContext(); - } -} diff --git a/core/src/main/java/org/elasticsearch/search/lookup/LeafSearchLookup.java b/core/src/main/java/org/elasticsearch/search/lookup/LeafSearchLookup.java index 5e4115466229b..e76206d6cba2a 100644 --- a/core/src/main/java/org/elasticsearch/search/lookup/LeafSearchLookup.java +++ b/core/src/main/java/org/elasticsearch/search/lookup/LeafSearchLookup.java @@ -35,24 +35,20 @@ public class LeafSearchLookup { final LeafDocLookup docMap; final SourceLookup sourceLookup; final LeafFieldsLookup fieldsLookup; - final LeafIndexLookup indexLookup; final Map asMap; public LeafSearchLookup(LeafReaderContext ctx, LeafDocLookup docMap, SourceLookup sourceLookup, - LeafFieldsLookup fieldsLookup, LeafIndexLookup indexLookup, Map topLevelMap) { + LeafFieldsLookup fieldsLookup) { this.ctx = ctx; this.docMap = docMap; this.sourceLookup = sourceLookup; this.fieldsLookup = fieldsLookup; - this.indexLookup = indexLookup; - Map asMap = new HashMap<>(topLevelMap.size() + 5); - asMap.putAll(topLevelMap); + Map asMap = new HashMap<>(4); asMap.put("doc", docMap); asMap.put("_doc", docMap); asMap.put("_source", sourceLookup); asMap.put("_fields", fieldsLookup); - asMap.put("_index", indexLookup); this.asMap = unmodifiableMap(asMap); } @@ -64,10 +60,6 @@ public SourceLookup source() { return this.sourceLookup; } - public LeafIndexLookup indexLookup() { - return this.indexLookup; - } - public LeafFieldsLookup fields() { return this.fieldsLookup; } @@ -80,6 +72,5 @@ public void setDocument(int docId) { docMap.setDocument(docId); sourceLookup.setSegmentAndDocument(ctx, docId); fieldsLookup.setDocument(docId); - indexLookup.setDocument(docId); } } diff --git a/core/src/main/java/org/elasticsearch/search/lookup/PositionIterator.java b/core/src/main/java/org/elasticsearch/search/lookup/PositionIterator.java deleted file mode 100644 index c36a714894af7..0000000000000 --- a/core/src/main/java/org/elasticsearch/search/lookup/PositionIterator.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.search.lookup; - -import org.apache.lucene.index.PostingsEnum; -import org.elasticsearch.ElasticsearchException; - -import java.io.IOException; -import java.util.Iterator; - -public class PositionIterator implements Iterator { - - private boolean resetted = false; - - protected IndexFieldTerm indexFieldTerm; - - protected int freq = -1; - - // current position of iterator - private int currentPos; - - protected final TermPosition termPosition = new TermPosition(); - - private PostingsEnum postings; - - public PositionIterator(IndexFieldTerm indexFieldTerm) { - this.indexFieldTerm = indexFieldTerm; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Cannot remove anything from TermPosition iterator."); - } - - @Override - public boolean hasNext() { - return currentPos < freq; - } - - - @Override - public TermPosition next() { - try { - termPosition.position = postings.nextPosition(); - termPosition.startOffset = postings.startOffset(); - termPosition.endOffset = postings.endOffset(); - termPosition.payload = postings.getPayload(); - } catch (IOException ex) { - throw new ElasticsearchException("can not advance iterator", ex); - } - currentPos++; - return termPosition; - } - - public void nextDoc() throws IOException { - resetted = false; - currentPos = 0; - freq = indexFieldTerm.tf(); - postings = indexFieldTerm.postings; - } - - public Iterator reset() { - if (resetted) { - throw new ElasticsearchException( - "Cannot iterate twice! If you want to iterate more that once, add _CACHE explicitly."); - } - resetted = true; - return this; - } -} diff --git a/core/src/main/java/org/elasticsearch/search/lookup/SearchLookup.java b/core/src/main/java/org/elasticsearch/search/lookup/SearchLookup.java index aaa2baf62eedc..d2cee1109c3e4 100644 --- a/core/src/main/java/org/elasticsearch/search/lookup/SearchLookup.java +++ b/core/src/main/java/org/elasticsearch/search/lookup/SearchLookup.java @@ -42,9 +42,7 @@ public LeafSearchLookup getLeafSearchLookup(LeafReaderContext context) { return new LeafSearchLookup(context, docMap.getLeafDocLookup(context), sourceLookup, - fieldsLookup.getLeafFieldsLookup(context), - IndexLookup.getLeafIndexLookup(context), - IndexLookup.NAMES); + fieldsLookup.getLeafFieldsLookup(context)); } public DocLookup doc() { diff --git a/core/src/main/java/org/elasticsearch/search/lookup/TermPosition.java b/core/src/main/java/org/elasticsearch/search/lookup/TermPosition.java deleted file mode 100644 index 593452d385807..0000000000000 --- a/core/src/main/java/org/elasticsearch/search/lookup/TermPosition.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.search.lookup; - -import org.apache.lucene.analysis.payloads.PayloadHelper; -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.CharsRefBuilder; - -public class TermPosition { - - public int position = -1; - public int startOffset = -1; - public int endOffset = -1; - public BytesRef payload; - private CharsRefBuilder spare = new CharsRefBuilder(); - - public String payloadAsString() { - if (payload != null && payload.length != 0) { - spare.copyUTF8Bytes(payload); - return spare.toString(); - } else { - return null; - } - } - - public float payloadAsFloat(float defaultMissing) { - if (payload != null && payload.length != 0) { - return PayloadHelper.decodeFloat(payload.bytes, payload.offset); - } else { - return defaultMissing; - } - } - - public int payloadAsInt(int defaultMissing) { - if (payload != null && payload.length != 0) { - return PayloadHelper.decodeInt(payload.bytes, payload.offset); - } else { - return defaultMissing; - } - } -} diff --git a/core/src/test/java/org/elasticsearch/script/IndexLookupIT.java b/core/src/test/java/org/elasticsearch/script/IndexLookupIT.java deleted file mode 100644 index c455870779430..0000000000000 --- a/core/src/test/java/org/elasticsearch/script/IndexLookupIT.java +++ /dev/null @@ -1,1029 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.script; - -import org.elasticsearch.action.search.SearchPhaseExecutionException; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.action.search.ShardSearchFailure; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders; -import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.search.SearchHit; -import org.elasticsearch.search.lookup.IndexField; -import org.elasticsearch.search.lookup.IndexFieldTerm; -import org.elasticsearch.search.lookup.IndexLookup; -import org.elasticsearch.search.lookup.LeafIndexLookup; -import org.elasticsearch.search.lookup.TermPosition; -import org.elasticsearch.test.ESIntegTestCase; -import org.hamcrest.Matchers; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.function.Function; - -import static java.util.Collections.emptyList; - -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; - -public class IndexLookupIT extends ESIntegTestCase { - - private static final String INCLUDE_ALL = "_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS|_CACHE"; - private static final int ALL_FLAGS = IndexLookup.FLAG_FREQUENCIES - | IndexLookup.FLAG_OFFSETS - | IndexLookup.FLAG_PAYLOADS - | IndexLookup.FLAG_POSITIONS - | IndexLookup.FLAG_CACHE; - - private static final String INCLUDE_ALL_BUT_CACHE = "_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS"; - private static final int ALL_FLAGS_WITHOUT_CACHE = IndexLookup.FLAG_FREQUENCIES - | IndexLookup.FLAG_OFFSETS - | IndexLookup.FLAG_PAYLOADS - | IndexLookup.FLAG_POSITIONS; - - private HashMap> expectedEndOffsetsArray; - private HashMap> expectedPayloadsArray; - private HashMap> expectedPositionsArray; - private HashMap> expectedStartOffsetsArray; - - @Override - protected Collection> nodePlugins() { - return Collections.singleton(CustomScriptPlugin.class); - } - - public static class CustomScriptPlugin extends MockScriptPlugin { - - @Override - @SuppressWarnings("unchecked") - protected Map, Object>> pluginScripts() { - Map, Object>> scripts = new HashMap<>(); - - scripts.put("term = _index['int_payload_field']['c']; term.tf()", vars -> tf(vars, "int_payload_field", "c")); - scripts.put("term = _index['int_payload_field']['b']; term.tf()", vars -> tf(vars, "int_payload_field", "b")); - - scripts.put("Sum the payloads of [float_payload_field][b]", vars -> payloadSum(vars, "float_payload_field", "b")); - scripts.put("Sum the payloads of [int_payload_field][b]", vars -> payloadSum(vars, "int_payload_field", "b")); - - scripts.put("createPositionsArrayScriptIterateTwice[b," + INCLUDE_ALL + ",position]", - vars -> createPositionsArrayScriptIterateTwice(vars, "b", ALL_FLAGS, p -> p.position)); - scripts.put("createPositionsArrayScriptIterateTwice[b," + INCLUDE_ALL + ",startOffset]", - vars -> createPositionsArrayScriptIterateTwice(vars, "b", ALL_FLAGS, p -> p.startOffset)); - scripts.put("createPositionsArrayScriptIterateTwice[b," + INCLUDE_ALL + ",endOffset]", - vars -> createPositionsArrayScriptIterateTwice(vars, "b", ALL_FLAGS, p -> p.endOffset)); - scripts.put("createPositionsArrayScriptIterateTwice[b," + INCLUDE_ALL + ",payloadAsInt(-1)]", - vars -> createPositionsArrayScriptIterateTwice(vars, "b", ALL_FLAGS, p -> p.payloadAsInt(-1))); - - scripts.put("createPositionsArrayScriptIterateTwice[b,_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS,position]", - vars -> createPositionsArrayScriptIterateTwice(vars, "b", ALL_FLAGS_WITHOUT_CACHE, p -> p.position)); - scripts.put("createPositionsArrayScriptIterateTwice[b,_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS,startOffset]", - vars -> createPositionsArrayScriptIterateTwice(vars, "b", ALL_FLAGS_WITHOUT_CACHE, p -> p.startOffset)); - scripts.put("createPositionsArrayScriptIterateTwice[b,_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS,endOffset]", - vars -> createPositionsArrayScriptIterateTwice(vars, "b", ALL_FLAGS_WITHOUT_CACHE, p -> p.endOffset)); - scripts.put("createPositionsArrayScriptIterateTwice[b,_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS,payloadAsInt(-1)]", - vars -> createPositionsArrayScriptIterateTwice(vars, "b", ALL_FLAGS_WITHOUT_CACHE, p -> p.payloadAsInt(-1))); - - scripts.put("createPositionsArrayScriptGetInfoObjectTwice[b,_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS,position]", - vars -> createPositionsArrayScriptGetInfoObjectTwice(vars, "b", ALL_FLAGS_WITHOUT_CACHE, p -> p.position)); - scripts.put("createPositionsArrayScriptGetInfoObjectTwice[b,_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS,startOffset]", - vars -> createPositionsArrayScriptGetInfoObjectTwice(vars, "b", ALL_FLAGS_WITHOUT_CACHE, p -> p.startOffset)); - scripts.put("createPositionsArrayScriptGetInfoObjectTwice[b,_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS,endOffset]", - vars -> createPositionsArrayScriptGetInfoObjectTwice(vars, "b", ALL_FLAGS_WITHOUT_CACHE, p -> p.endOffset)); - scripts.put("createPositionsArrayScriptGetInfoObjectTwice[b,_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS,payloadAsInt(-1)]", - vars -> createPositionsArrayScriptGetInfoObjectTwice(vars, "b", ALL_FLAGS_WITHOUT_CACHE, p -> p.payloadAsInt(-1))); - - scripts.put("createPositionsArrayScript[int_payload_field,b,_POSITIONS,position]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", IndexLookup.FLAG_POSITIONS, p -> p.position)); - - scripts.put("createPositionsArrayScript[int_payload_field,b,_OFFSETS,position]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", IndexLookup.FLAG_OFFSETS, p -> p.position)); - scripts.put("createPositionsArrayScript[int_payload_field,b,_OFFSETS,startOffset]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", IndexLookup.FLAG_OFFSETS, p -> p.startOffset)); - scripts.put("createPositionsArrayScript[int_payload_field,b,_OFFSETS,endOffset]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", IndexLookup.FLAG_OFFSETS, p -> p.endOffset)); - scripts.put("createPositionsArrayScript[int_payload_field,b,_OFFSETS,payloadAsInt(-1)]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", IndexLookup.FLAG_OFFSETS, p -> p.payloadAsInt(-1))); - - scripts.put("createPositionsArrayScript[int_payload_field,b,_PAYLOADS,position]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", IndexLookup.FLAG_PAYLOADS, p -> p.position)); - scripts.put("createPositionsArrayScript[int_payload_field,b,_PAYLOADS,startOffset]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", IndexLookup.FLAG_PAYLOADS, p -> p.startOffset)); - scripts.put("createPositionsArrayScript[int_payload_field,b,_PAYLOADS,endOffset]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", IndexLookup.FLAG_PAYLOADS, p -> p.endOffset)); - scripts.put("createPositionsArrayScript[int_payload_field,b,_PAYLOADS,payloadAsInt(-1)]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", IndexLookup.FLAG_PAYLOADS, p -> p.payloadAsInt(-1))); - - int posoffpay = IndexLookup.FLAG_POSITIONS|IndexLookup.FLAG_OFFSETS|IndexLookup.FLAG_PAYLOADS; - scripts.put("createPositionsArrayScript[int_payload_field,b,_POSITIONS|_OFFSETS|_PAYLOADS,position]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", posoffpay, p -> p.position)); - scripts.put("createPositionsArrayScript[int_payload_field,b,_POSITIONS|_OFFSETS|_PAYLOADS,startOffset]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", posoffpay, p -> p.startOffset)); - scripts.put("createPositionsArrayScript[int_payload_field,b,_POSITIONS|_OFFSETS|_PAYLOADS,endOffset]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", posoffpay, p -> p.endOffset)); - scripts.put("createPositionsArrayScript[int_payload_field,b,_POSITIONS|_OFFSETS|_PAYLOADS,payloadAsInt(-1)]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", posoffpay, p -> p.payloadAsInt(-1))); - - scripts.put("createPositionsArrayScript[int_payload_field,b,_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS,position]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", ALL_FLAGS_WITHOUT_CACHE, p -> p.position)); - scripts.put("createPositionsArrayScript[int_payload_field,b,_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS,startOffset]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", ALL_FLAGS_WITHOUT_CACHE, p -> p.startOffset)); - scripts.put("createPositionsArrayScript[int_payload_field,b,_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS,endOffset]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", ALL_FLAGS_WITHOUT_CACHE, p -> p.endOffset)); - scripts.put("createPositionsArrayScript[int_payload_field,b,_FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS,payloadAsInt(-1)]", - vars -> createPositionsArrayScript(vars, "int_payload_field", "b", ALL_FLAGS_WITHOUT_CACHE, p -> p.payloadAsInt(-1))); - - scripts.put("createPositionsArrayScript" + - "[float_payload_field,b," + INCLUDE_ALL + ",payloadAsFloat(-1)]", - vars -> createPositionsArrayScript(vars,"float_payload_field", "b", ALL_FLAGS, p -> p.payloadAsFloat(-1))); - scripts.put("createPositionsArrayScript" + - "[string_payload_field,b," + INCLUDE_ALL + ",payloadAsString()]", - vars -> createPositionsArrayScript(vars,"string_payload_field", "b", ALL_FLAGS, TermPosition::payloadAsString)); - scripts.put("createPositionsArrayScript" + - "[int_payload_field,c," + INCLUDE_ALL + ",payloadAsInt(-1)]", - vars -> createPositionsArrayScript(vars,"int_payload_field", "c", ALL_FLAGS, p -> p.payloadAsInt(-1))); - - // Call with different flags twice, equivalent to: - // term = _index['int_payload_field']['b']; return _index['int_payload_field'].get('b', _POSITIONS).tf(); - scripts.put("Call with different flags twice", vars -> { - LeafIndexLookup leafIndexLookup = (LeafIndexLookup) vars.get("_index"); - IndexField indexField = leafIndexLookup.get("int_payload_field"); - - // 1st call - indexField.get("b"); - try { - // 2nd call, must throws an exception - return indexField.get("b", IndexLookup.FLAG_POSITIONS).tf(); - } catch (IOException e) { - throw new ScriptException(e.getMessage(), e, emptyList(), "Call with different flags twice", CustomScriptPlugin.NAME); - } - }); - - // Call with same flags twice: equivalent to: - // term = _index['int_payload_field'].get('b', _POSITIONS | _FREQUENCIES);return _index['int_payload_field']['b'].tf(); - scripts.put("Call with same flags twice", vars -> { - LeafIndexLookup leafIndexLookup = (LeafIndexLookup) vars.get("_index"); - IndexField indexField = leafIndexLookup.get("int_payload_field"); - - // 1st call - indexField.get("b", IndexLookup.FLAG_POSITIONS | IndexLookup.FLAG_FREQUENCIES); - try { - // 2nd call, must throws an exception - return indexField.get("b").tf(); - } catch (IOException e) { - throw new ScriptException(e.getMessage(), e, emptyList(), "Call with same flags twice", CustomScriptPlugin.NAME); - } - }); - - // get the number of all docs - scripts.put("_index.numDocs()", - vars -> ((LeafIndexLookup) vars.get("_index")).numDocs()); - - // get the number of docs with field float_payload_field - scripts.put("_index['float_payload_field'].docCount()", - vars -> indexFieldScript(vars, "float_payload_field", indexField -> { - try { - return indexField.docCount(); - } catch (IOException e) { - throw new ScriptException(e.getMessage(), e, emptyList(), "docCount()", CustomScriptPlugin.NAME); - } - })); - - // corner case: what if the field does not exist? - scripts.put("_index['non_existent_field'].docCount()", - vars -> indexFieldScript(vars, "non_existent_field", indexField -> { - try { - return indexField.docCount(); - } catch (IOException e) { - throw new ScriptException(e.getMessage(), e, emptyList(), "docCount()", CustomScriptPlugin.NAME); - } - })); - - // get the number of all tokens in all docs - scripts.put("_index['float_payload_field'].sumttf()", - vars -> indexFieldScript(vars, "float_payload_field", indexField -> { - try { - return indexField.sumttf(); - } catch (IOException e) { - throw new ScriptException(e.getMessage(), e, emptyList(), "sumttf()", CustomScriptPlugin.NAME); - } - })); - - // corner case get the number of all tokens in all docs for non existent - // field - scripts.put("_index['non_existent_field'].sumttf()", - vars -> indexFieldScript(vars, "non_existent_field", indexField -> { - try { - return indexField.sumttf(); - } catch (IOException e) { - throw new ScriptException(e.getMessage(), e, emptyList(), "sumttf()", CustomScriptPlugin.NAME); - } - })); - - // get the sum of doc freqs in all docs - scripts.put("_index['float_payload_field'].sumdf()", - vars -> indexFieldScript(vars, "float_payload_field", indexField -> { - try { - return indexField.sumdf(); - } catch (IOException e) { - throw new ScriptException(e.getMessage(), e, emptyList(), "sumdf()", CustomScriptPlugin.NAME); - } - })); - - // get the sum of doc freqs in all docs for non existent field - scripts.put("_index['non_existent_field'].sumdf()", - vars -> indexFieldScript(vars, "non_existent_field", indexField -> { - try { - return indexField.sumdf(); - } catch (IOException e) { - throw new ScriptException(e.getMessage(), e, emptyList(), "sumdf()", CustomScriptPlugin.NAME); - } - })); - - // check term frequencies for 'a' - scripts.put("term = _index['float_payload_field']['a']; if (term != null) {term.tf()}", - vars -> indexFieldTermScript(vars, "float_payload_field", "a", indexFieldTerm -> { - try { - if (indexFieldTerm != null) { - return indexFieldTerm.tf(); - } - } catch (IOException e) { - throw new ScriptException(e.getMessage(), e, emptyList(), "term.tf()", CustomScriptPlugin.NAME); - } - return null; - })); - - // check doc frequencies for 'c' - scripts.put("term = _index['float_payload_field']['c']; if (term != null) {term.df()}", - vars -> indexFieldTermScript(vars, "float_payload_field", "c", indexFieldTerm -> { - try { - if (indexFieldTerm != null) { - return indexFieldTerm.df(); - } - } catch (IOException e) { - throw new ScriptException(e.getMessage(), e, emptyList(), "term.df()", CustomScriptPlugin.NAME); - } - return null; - })); - - // check doc frequencies for term that does not exist - scripts.put("term = _index['float_payload_field']['non_existent_term']; if (term != null) {term.df()}", - vars -> indexFieldTermScript(vars, "float_payload_field", "non_existent_term", indexFieldTerm -> { - try { - if (indexFieldTerm != null) { - return indexFieldTerm.df(); - } - } catch (IOException e) { - throw new ScriptException(e.getMessage(), e, emptyList(), "term.df()", CustomScriptPlugin.NAME); - } - return null; - })); - - // check doc frequencies for term that does not exist - scripts.put("term = _index['non_existent_field']['non_existent_term']; if (term != null) {term.tf()}", - vars -> indexFieldTermScript(vars, "non_existent_field", "non_existent_term", indexFieldTerm -> { - try { - if (indexFieldTerm != null) { - return indexFieldTerm.tf(); - } - } catch (IOException e) { - throw new ScriptException(e.getMessage(), e, emptyList(), "term.tf()", CustomScriptPlugin.NAME); - } - return null; - })); - - // check total term frequencies for 'a' - scripts.put("term = _index['float_payload_field']['a']; if (term != null) {term.ttf()}", - vars -> indexFieldTermScript(vars, "float_payload_field", "a", indexFieldTerm -> { - try { - if (indexFieldTerm != null) { - return indexFieldTerm.ttf(); - } - } catch (IOException e) { - throw new ScriptException(e.getMessage(), e, emptyList(), "term.ttf()", CustomScriptPlugin.NAME); - } - return null; - })); - - return scripts; - } - - @SuppressWarnings("unchecked") - static Object indexFieldScript(Map vars, String fieldName, Function fn) { - LeafIndexLookup leafIndexLookup = (LeafIndexLookup) vars.get("_index"); - return fn.apply(leafIndexLookup.get(fieldName)); - } - - @SuppressWarnings("unchecked") - static Object indexFieldTermScript(Map vars, String fieldName, String term, Function fn) { - return indexFieldScript(vars, fieldName, indexField -> fn.apply(indexField.get(term))); - } - - @SuppressWarnings("unchecked") - static Object tf(Map vars, String fieldName, String term) { - return indexFieldTermScript(vars, fieldName, term, indexFieldTerm -> { - try { - return indexFieldTerm.tf(); - } catch (IOException e) { - throw new RuntimeException("Mocked script error when retrieving TF for [" + fieldName + "][" + term + "]"); - } - }); - } - - // Sum the payloads for a given field term, equivalent to: - // term = _index['float_payload_field'].get('b', _FREQUENCIES|_OFFSETS|_PAYLOADS|_POSITIONS|_CACHE); - // payloadSum=0; - // for (pos in term) { - // payloadSum += pos.payloadAsInt(0) - // }; - // return payloadSum; - @SuppressWarnings("unchecked") - static Object payloadSum(Map vars, String fieldName, String term) { - return indexFieldScript(vars, fieldName, indexField -> { - IndexFieldTerm indexFieldTerm = indexField.get(term, IndexLookup.FLAG_FREQUENCIES - | IndexLookup.FLAG_OFFSETS - | IndexLookup.FLAG_PAYLOADS - | IndexLookup.FLAG_POSITIONS - | IndexLookup.FLAG_CACHE); - int payloadSum = 0; - for (TermPosition position : indexFieldTerm) { - payloadSum += position.payloadAsInt(0); - } - return payloadSum; - }); - } - - @SuppressWarnings("unchecked") - static List createPositionsArrayScriptGetInfoObjectTwice(Map vars, String term, int flags, - Function fn) { - LeafIndexLookup leafIndexLookup = (LeafIndexLookup) vars.get("_index"); - IndexField indexField = leafIndexLookup.get("int_payload_field"); - - // 1st call - IndexFieldTerm indexFieldTerm = indexField.get(term, flags); - - List array = new ArrayList<>(); - for (TermPosition position : indexFieldTerm) { - array.add(fn.apply(position)); - } - - // 2nd call - indexField.get(term, flags); - - array = new ArrayList<>(); - for (TermPosition position : indexFieldTerm) { - array.add(fn.apply(position)); - } - - return array; - } - - @SuppressWarnings("unchecked") - static List createPositionsArrayScriptIterateTwice(Map vars, String term, int flags, - Function fn) { - LeafIndexLookup leafIndexLookup = (LeafIndexLookup) vars.get("_index"); - IndexField indexField = leafIndexLookup.get("int_payload_field"); - - IndexFieldTerm indexFieldTerm = indexField.get(term, flags); - - // 1st iteration - List array = new ArrayList<>(); - for (TermPosition position : indexFieldTerm) { - array.add(fn.apply(position)); - } - - // 2nd iteration - array = new ArrayList<>(); - for (TermPosition position : indexFieldTerm) { - array.add(fn.apply(position)); - } - - return array; - } - - @SuppressWarnings("unchecked") - static List createPositionsArrayScript(Map vars, String field, String term, int flags, - Function fn) { - - LeafIndexLookup leafIndexLookup = (LeafIndexLookup) vars.get("_index"); - IndexField indexField = leafIndexLookup.get(field); - - IndexFieldTerm indexFieldTerm = indexField.get(term, flags); - List array = new ArrayList<>(); - for (TermPosition position : indexFieldTerm) { - array.add(fn.apply(position)); - } - return array; - } - } - - void initTestData() throws InterruptedException, ExecutionException, IOException { - HashMap> emptyArray = new HashMap<>(); - List empty1 = new ArrayList<>(); - empty1.add(-1); - empty1.add(-1); - emptyArray.put("1", empty1); - List empty2 = new ArrayList<>(); - empty2.add(-1); - empty2.add(-1); - emptyArray.put("2", empty2); - List empty3 = new ArrayList<>(); - empty3.add(-1); - empty3.add(-1); - emptyArray.put("3", empty3); - - expectedPositionsArray = new HashMap<>(); - - List pos1 = new ArrayList<>(); - pos1.add(1); - pos1.add(2); - expectedPositionsArray.put("1", pos1); - List pos2 = new ArrayList<>(); - pos2.add(0); - pos2.add(1); - expectedPositionsArray.put("2", pos2); - List pos3 = new ArrayList<>(); - pos3.add(0); - pos3.add(4); - expectedPositionsArray.put("3", pos3); - - expectedPayloadsArray = new HashMap<>(); - List pay1 = new ArrayList<>(); - pay1.add(2); - pay1.add(3); - expectedPayloadsArray.put("1", pay1); - List pay2 = new ArrayList<>(); - pay2.add(1); - pay2.add(2); - expectedPayloadsArray.put("2", pay2); - List pay3 = new ArrayList<>(); - pay3.add(1); - pay3.add(-1); - expectedPayloadsArray.put("3", pay3); - /* - * "a|1 b|2 b|3 c|4 d " "b|1 b|2 c|3 d|4 a " "b|1 c|2 d|3 a|4 b " - */ - expectedStartOffsetsArray = new HashMap<>(); - List starts1 = new ArrayList<>(); - starts1.add(4); - starts1.add(8); - expectedStartOffsetsArray.put("1", starts1); - List starts2 = new ArrayList<>(); - starts2.add(0); - starts2.add(4); - expectedStartOffsetsArray.put("2", starts2); - List starts3 = new ArrayList<>(); - starts3.add(0); - starts3.add(16); - expectedStartOffsetsArray.put("3", starts3); - - expectedEndOffsetsArray = new HashMap<>(); - List ends1 = new ArrayList<>(); - ends1.add(7); - ends1.add(11); - expectedEndOffsetsArray.put("1", ends1); - List ends2 = new ArrayList<>(); - ends2.add(3); - ends2.add(7); - expectedEndOffsetsArray.put("2", ends2); - List ends3 = new ArrayList<>(); - ends3.add(3); - ends3.add(17); - expectedEndOffsetsArray.put("3", ends3); - - XContentBuilder mapping = XContentFactory.jsonBuilder() - .startObject() - .startObject("type1") - .startObject("properties") - .startObject("int_payload_field") - .field("type", "text") - .field("index_options", "offsets") - .field("analyzer", "payload_int") - .endObject() - .endObject() - .endObject() - .endObject(); - - assertAcked(prepareCreate("test").addMapping("type1", mapping).setSettings( - Settings.builder() - .put(indexSettings()) - .put("index.analysis.analyzer.payload_int.tokenizer", "whitespace") - .putArray("index.analysis.analyzer.payload_int.filter", "delimited_int") - .put("index.analysis.filter.delimited_int.delimiter", "|") - .put("index.analysis.filter.delimited_int.encoding", "int") - .put("index.analysis.filter.delimited_int.type", "delimited_payload_filter"))); - indexRandom(true, client().prepareIndex("test", "type1", "1").setSource("int_payload_field", "a|1 b|2 b|3 c|4 d "), client() - .prepareIndex("test", "type1", "2").setSource("int_payload_field", "b|1 b|2 c|3 d|4 a "), - client().prepareIndex("test", "type1", "3").setSource("int_payload_field", "b|1 c|2 d|3 a|4 b ")); - ensureGreen(); - } - - public void testTwoScripts() throws Exception { - initTestData(); - - Script scriptFieldScript = createScript("term = _index['int_payload_field']['c']; term.tf()"); - Script scoreScript = createScript("term = _index['int_payload_field']['b']; term.tf()"); - Map expectedResultsField = new HashMap<>(); - expectedResultsField.put("1", 1); - expectedResultsField.put("2", 1); - expectedResultsField.put("3", 1); - Map expectedResultsScore = new HashMap<>(); - expectedResultsScore.put("1", 2f); - expectedResultsScore.put("2", 2f); - expectedResultsScore.put("3", 2f); - checkOnlyFunctionScore(scoreScript, expectedResultsScore, 3); - checkValueInEachDocWithFunctionScore(scriptFieldScript, expectedResultsField, scoreScript, expectedResultsScore, 3); - - } - - public void testCallWithDifferentFlagsFails() throws Exception { - initTestData(); - final int numPrimaries = getNumShards("test").numPrimaries; - final String expectedError = "You must call get with all required flags! " + - "Instead of _index['int_payload_field'].get('b', _FREQUENCIES) and _index['int_payload_field'].get('b', _POSITIONS)" + - " call _index['int_payload_field'].get('b', _FREQUENCIES | _POSITIONS) once]"; - - // should throw an exception, we cannot call with different flags twice - // if the flags of the second call were not included in the first call. - Script script = createScript("Call with different flags twice"); - try { - SearchResponse response = client().prepareSearch("test") - .setQuery(QueryBuilders.matchAllQuery()) - .addScriptField("tvtest", script) - .get(); - - // (partial) success when at least one shard succeeds - assertThat(numPrimaries, greaterThan(response.getShardFailures().length)); - assertThat(response.getFailedShards(), greaterThanOrEqualTo(1)); - - for (ShardSearchFailure failure : response.getShardFailures()) { - assertThat(failure.reason(), containsString(expectedError)); - } - } catch (SearchPhaseExecutionException e) { - // Exception thrown when *all* shards fail - assertThat(numPrimaries, equalTo(e.shardFailures().length)); - for (ShardSearchFailure failure : e.shardFailures()) { - assertThat(failure.reason(), containsString(expectedError)); - } - } - - // Should not throw an exception this way round - script = createScript("Call with same flags twice"); - assertThat(client().prepareSearch("test") - .setQuery(QueryBuilders.matchAllQuery()) - .addScriptField("tvtest", script) - .get().getHits().getTotalHits(), greaterThan(0L)); - } - - private void checkOnlyFunctionScore(Script scoreScript, Map expectedScore, int numExpectedDocs) { - SearchResponse sr = client().prepareSearch("test") - .setQuery(QueryBuilders.functionScoreQuery(ScoreFunctionBuilders.scriptFunction(scoreScript))).execute() - .actionGet(); - assertHitCount(sr, numExpectedDocs); - for (SearchHit hit : sr.getHits().getHits()) { - assertThat("for doc " + hit.getId(), ((Float) expectedScore.get(hit.getId())).doubleValue(), - Matchers.closeTo(hit.getScore(), 1.e-4)); - } - } - - public void testDocumentationExample() throws Exception { - initTestData(); - - Script script = createScript("Sum the payloads of [float_payload_field][b]"); - - // non existing field: sum should be 0 - HashMap zeroArray = new HashMap<>(); - zeroArray.put("1", 0); - zeroArray.put("2", 0); - zeroArray.put("3", 0); - checkValueInEachDoc(script, zeroArray, 3); - - script = createScript("Sum the payloads of [int_payload_field][b]"); - - // existing field: sums should be as here: - zeroArray.put("1", 5); - zeroArray.put("2", 3); - zeroArray.put("3", 1); - checkValueInEachDoc(script, zeroArray, 3); - } - - public void testIteratorAndRecording() throws Exception { - initTestData(); - - // call twice with record: should work as expected - Script script = createPositionsArrayScriptIterateTwice("b", INCLUDE_ALL, "position"); - checkArrayValsInEachDoc(script, expectedPositionsArray, 3); - script = createPositionsArrayScriptIterateTwice("b", INCLUDE_ALL, "startOffset"); - checkArrayValsInEachDoc(script, expectedStartOffsetsArray, 3); - script = createPositionsArrayScriptIterateTwice("b", INCLUDE_ALL, "endOffset"); - checkArrayValsInEachDoc(script, expectedEndOffsetsArray, 3); - script = createPositionsArrayScriptIterateTwice("b", INCLUDE_ALL, "payloadAsInt(-1)"); - checkArrayValsInEachDoc(script, expectedPayloadsArray, 3); - - // no record and get iterator twice: should fail - script = createPositionsArrayScriptIterateTwice("b", INCLUDE_ALL_BUT_CACHE, "position"); - checkExceptions(script); - script = createPositionsArrayScriptIterateTwice("b", INCLUDE_ALL_BUT_CACHE, "startOffset"); - checkExceptions(script); - script = createPositionsArrayScriptIterateTwice("b", INCLUDE_ALL_BUT_CACHE, "endOffset"); - checkExceptions(script); - script = createPositionsArrayScriptIterateTwice("b", INCLUDE_ALL_BUT_CACHE, "payloadAsInt(-1)"); - checkExceptions(script); - - // no record and get termObject twice and iterate: should fail - script = createPositionsArrayScriptGetInfoObjectTwice("b", INCLUDE_ALL_BUT_CACHE, "position"); - checkExceptions(script); - script = createPositionsArrayScriptGetInfoObjectTwice("b", INCLUDE_ALL_BUT_CACHE, "startOffset"); - checkExceptions(script); - script = createPositionsArrayScriptGetInfoObjectTwice("b", INCLUDE_ALL_BUT_CACHE, "endOffset"); - checkExceptions(script); - script = createPositionsArrayScriptGetInfoObjectTwice("b", INCLUDE_ALL_BUT_CACHE, "payloadAsInt(-1)"); - checkExceptions(script); - - } - - private Script createPositionsArrayScriptGetInfoObjectTwice(String term, String flags, String what) { - return createScript("createPositionsArrayScriptGetInfoObjectTwice[" + term + "," + flags + "," + what + "]"); - } - - private Script createPositionsArrayScriptIterateTwice(String term, String flags, String what) { - return createScript("createPositionsArrayScriptIterateTwice[" + term + "," + flags + "," + what + "]"); - } - - private Script createPositionsArrayScript(String field, String term, String flags, String what) { - return createScript("createPositionsArrayScript[" + field + "," + term + "," + flags + "," + what + "]"); - } - - private Script createPositionsArrayScriptDefaultGet(String field, String term, String what) { - return createScript("createPositionsArrayScriptDefaultGet[" + field + "," + term + "," + what + "]"); - } - - private Script createScript(String script) { - return new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, script, Collections.emptyMap()); - } - - public void testFlags() throws Exception { - initTestData(); - - // check default flag - Script script = createPositionsArrayScriptDefaultGet("int_payload_field", "b", "position"); - // there should be no positions - /* TODO: the following tests fail with the new postings enum apis because of a bogus assert in BlockDocsEnum - checkArrayValsInEachDoc(script, emptyArray, 3); - script = createPositionsArrayScriptDefaultGet("int_payload_field", "b", "startOffset"); - // there should be no offsets - checkArrayValsInEachDoc(script, emptyArray, 3); - script = createPositionsArrayScriptDefaultGet("int_payload_field", "b", "endOffset"); - // there should be no offsets - checkArrayValsInEachDoc(script, emptyArray, 3); - script = createPositionsArrayScriptDefaultGet("int_payload_field", "b", "payloadAsInt(-1)"); - // there should be no payload - checkArrayValsInEachDoc(script, emptyArray, 3); - - // check FLAG_FREQUENCIES flag - script = createPositionsArrayScript("int_payload_field", "b", "_FREQUENCIES", "position"); - // there should be no positions - checkArrayValsInEachDoc(script, emptyArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", "_FREQUENCIES", "startOffset"); - // there should be no offsets - checkArrayValsInEachDoc(script, emptyArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", "_FREQUENCIES", "endOffset"); - // there should be no offsets - checkArrayValsInEachDoc(script, emptyArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", "_FREQUENCIES", "payloadAsInt(-1)"); - // there should be no payloads - checkArrayValsInEachDoc(script, emptyArray, 3);*/ - - // check FLAG_POSITIONS flag - script = createPositionsArrayScript("int_payload_field", "b", "_POSITIONS", "position"); - // there should be positions - checkArrayValsInEachDoc(script, expectedPositionsArray, 3); - /* TODO: these tests make a bogus assumption that asking for positions will return only positions - script = createPositionsArrayScript("int_payload_field", "b", "_POSITIONS", "startOffset"); - // there should be no offsets - checkArrayValsInEachDoc(script, emptyArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", "_POSITIONS", "endOffset"); - // there should be no offsets - checkArrayValsInEachDoc(script, emptyArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", "_POSITIONS", "payloadAsInt(-1)"); - // there should be no payloads - checkArrayValsInEachDoc(script, emptyArray, 3);*/ - - // check FLAG_OFFSETS flag - script = createPositionsArrayScript("int_payload_field", "b", "_OFFSETS", "position"); - // there should be positions and s forth ... - checkArrayValsInEachDoc(script, expectedPositionsArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", "_OFFSETS", "startOffset"); - checkArrayValsInEachDoc(script, expectedStartOffsetsArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", "_OFFSETS", "endOffset"); - checkArrayValsInEachDoc(script, expectedEndOffsetsArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", "_OFFSETS", "payloadAsInt(-1)"); - checkArrayValsInEachDoc(script, expectedPayloadsArray, 3); - - // check FLAG_PAYLOADS flag - script = createPositionsArrayScript("int_payload_field", "b", "_PAYLOADS", "position"); - checkArrayValsInEachDoc(script, expectedPositionsArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", "_PAYLOADS", "startOffset"); - checkArrayValsInEachDoc(script, expectedStartOffsetsArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", "_PAYLOADS", "endOffset"); - checkArrayValsInEachDoc(script, expectedEndOffsetsArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", "_PAYLOADS", "payloadAsInt(-1)"); - checkArrayValsInEachDoc(script, expectedPayloadsArray, 3); - - // check all flags - String allFlags = "_POSITIONS|_OFFSETS|_PAYLOADS"; - script = createPositionsArrayScript("int_payload_field", "b", allFlags, "position"); - checkArrayValsInEachDoc(script, expectedPositionsArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", allFlags, "startOffset"); - checkArrayValsInEachDoc(script, expectedStartOffsetsArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", allFlags, "endOffset"); - checkArrayValsInEachDoc(script, expectedEndOffsetsArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", allFlags, "payloadAsInt(-1)"); - checkArrayValsInEachDoc(script, expectedPayloadsArray, 3); - - // check all flags without record - script = createPositionsArrayScript("int_payload_field", "b", INCLUDE_ALL_BUT_CACHE, "position"); - checkArrayValsInEachDoc(script, expectedPositionsArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", INCLUDE_ALL_BUT_CACHE, "startOffset"); - checkArrayValsInEachDoc(script, expectedStartOffsetsArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", INCLUDE_ALL_BUT_CACHE, "endOffset"); - checkArrayValsInEachDoc(script, expectedEndOffsetsArray, 3); - script = createPositionsArrayScript("int_payload_field", "b", INCLUDE_ALL_BUT_CACHE, "payloadAsInt(-1)"); - checkArrayValsInEachDoc(script, expectedPayloadsArray, 3); - - } - - private void checkArrayValsInEachDoc(Script script, HashMap> expectedArray, int expectedHitSize) { - SearchResponse sr = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()).addScriptField("tvtest", script) - .execute().actionGet(); - assertHitCount(sr, expectedHitSize); - int nullCounter = 0; - for (SearchHit hit : sr.getHits().getHits()) { - Object result = hit.getFields().get("tvtest").getValues(); - Object expectedResult = expectedArray.get(hit.getId()); - assertThat("for doc " + hit.getId(), result, equalTo(expectedResult)); - if (expectedResult != null) { - nullCounter++; - } - } - assertThat(nullCounter, equalTo(expectedArray.size())); - } - - public void testAllExceptPosAndOffset() throws Exception { - XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject("type1").startObject("properties") - .startObject("float_payload_field").field("type", "text").field("index_options", "offsets").field("term_vector", "no") - .field("analyzer", "payload_float").endObject().startObject("string_payload_field").field("type", "text") - .field("index_options", "offsets").field("term_vector", "no").field("analyzer", "payload_string").endObject() - .startObject("int_payload_field").field("type", "text").field("index_options", "offsets") - .field("analyzer", "payload_int").endObject().endObject().endObject().endObject(); - assertAcked(prepareCreate("test").addMapping("type1", mapping).setSettings( - Settings.builder() - .put(indexSettings()) - .put("index.analysis.analyzer.payload_float.tokenizer", "whitespace") - .putArray("index.analysis.analyzer.payload_float.filter", "delimited_float") - .put("index.analysis.filter.delimited_float.delimiter", "|") - .put("index.analysis.filter.delimited_float.encoding", "float") - .put("index.analysis.filter.delimited_float.type", "delimited_payload_filter") - .put("index.analysis.analyzer.payload_string.tokenizer", "whitespace") - .putArray("index.analysis.analyzer.payload_string.filter", "delimited_string") - .put("index.analysis.filter.delimited_string.delimiter", "|") - .put("index.analysis.filter.delimited_string.encoding", "identity") - .put("index.analysis.filter.delimited_string.type", "delimited_payload_filter") - .put("index.analysis.analyzer.payload_int.tokenizer", "whitespace") - .putArray("index.analysis.analyzer.payload_int.filter", "delimited_int") - .put("index.analysis.filter.delimited_int.delimiter", "|") - .put("index.analysis.filter.delimited_int.encoding", "int") - .put("index.analysis.filter.delimited_int.type", "delimited_payload_filter") - .put("index.number_of_shards", 1))); - indexRandom(true, client().prepareIndex("test", "type1", "1").setSource("float_payload_field", "a|1 b|2 a|3 b "), client() - .prepareIndex("test", "type1", "2").setSource("string_payload_field", "a|a b|b a|a b "), - client().prepareIndex("test", "type1", "3").setSource("float_payload_field", "a|4 b|5 a|6 b "), - client().prepareIndex("test", "type1", "4").setSource("string_payload_field", "a|b b|a a|b b "), - client().prepareIndex("test", "type1", "5").setSource("float_payload_field", "c "), - client().prepareIndex("test", "type1", "6").setSource("int_payload_field", "c|1")); - - // get the number of all docs - Script script = createScript("_index.numDocs()"); - checkValueInEachDoc(6, script, 6); - - // get the number of docs with field float_payload_field - script = createScript("_index['float_payload_field'].docCount()"); - checkValueInEachDoc(3, script, 6); - - // corner case: what if the field does not exist? - script = createScript("_index['non_existent_field'].docCount()"); - checkValueInEachDoc(0, script, 6); - - // get the number of all tokens in all docs - script = createScript("_index['float_payload_field'].sumttf()"); - checkValueInEachDoc(9, script, 6); - - // corner case get the number of all tokens in all docs for non existent - // field - script = createScript("_index['non_existent_field'].sumttf()"); - checkValueInEachDoc(0, script, 6); - - // get the sum of doc freqs in all docs - script = createScript("_index['float_payload_field'].sumdf()"); - checkValueInEachDoc(5, script, 6); - - // get the sum of doc freqs in all docs for non existent field - script = createScript("_index['non_existent_field'].sumdf()"); - checkValueInEachDoc(0, script, 6); - - // check term frequencies for 'a' - script = createScript("term = _index['float_payload_field']['a']; if (term != null) {term.tf()}"); - Map expectedResults = new HashMap<>(); - expectedResults.put("1", 2); - expectedResults.put("2", 0); - expectedResults.put("3", 2); - expectedResults.put("4", 0); - expectedResults.put("5", 0); - expectedResults.put("6", 0); - checkValueInEachDoc(script, expectedResults, 6); - expectedResults.clear(); - - // check doc frequencies for 'c' - script = createScript("term = _index['float_payload_field']['c']; if (term != null) {term.df()}"); - expectedResults.put("1", 1L); - expectedResults.put("2", 1L); - expectedResults.put("3", 1L); - expectedResults.put("4", 1L); - expectedResults.put("5", 1L); - expectedResults.put("6", 1L); - checkValueInEachDoc(script, expectedResults, 6); - expectedResults.clear(); - - // check doc frequencies for term that does not exist - script = createScript("term = _index['float_payload_field']['non_existent_term']; if (term != null) {term.df()}"); - expectedResults.put("1", 0L); - expectedResults.put("2", 0L); - expectedResults.put("3", 0L); - expectedResults.put("4", 0L); - expectedResults.put("5", 0L); - expectedResults.put("6", 0L); - checkValueInEachDoc(script, expectedResults, 6); - expectedResults.clear(); - - // check doc frequencies for term that does not exist - script = createScript("term = _index['non_existent_field']['non_existent_term']; if (term != null) {term.tf()}"); - expectedResults.put("1", 0); - expectedResults.put("2", 0); - expectedResults.put("3", 0); - expectedResults.put("4", 0); - expectedResults.put("5", 0); - expectedResults.put("6", 0); - checkValueInEachDoc(script, expectedResults, 6); - expectedResults.clear(); - - // check total term frequencies for 'a' - script = createScript("term = _index['float_payload_field']['a']; if (term != null) {term.ttf()}"); - expectedResults.put("1", 4L); - expectedResults.put("2", 4L); - expectedResults.put("3", 4L); - expectedResults.put("4", 4L); - expectedResults.put("5", 4L); - expectedResults.put("6", 4L); - checkValueInEachDoc(script, expectedResults, 6); - expectedResults.clear(); - - // check float payload for 'b' - HashMap> expectedPayloadsArray = new HashMap<>(); - script = createPositionsArrayScript("float_payload_field", "b", INCLUDE_ALL, "payloadAsFloat(-1)"); - float missingValue = -1; - List payloadsFor1 = new ArrayList<>(); - payloadsFor1.add(2f); - payloadsFor1.add(missingValue); - expectedPayloadsArray.put("1", payloadsFor1); - List payloadsFor2 = new ArrayList<>(); - payloadsFor2.add(5f); - payloadsFor2.add(missingValue); - expectedPayloadsArray.put("3", payloadsFor2); - expectedPayloadsArray.put("6", new ArrayList<>()); - expectedPayloadsArray.put("5", new ArrayList<>()); - expectedPayloadsArray.put("4", new ArrayList<>()); - expectedPayloadsArray.put("2", new ArrayList<>()); - checkArrayValsInEachDoc(script, expectedPayloadsArray, 6); - - // check string payload for 'b' - expectedPayloadsArray.clear(); - payloadsFor1.clear(); - payloadsFor2.clear(); - script = createPositionsArrayScript("string_payload_field", "b", INCLUDE_ALL, "payloadAsString()"); - payloadsFor1.add("b"); - payloadsFor1.add(null); - expectedPayloadsArray.put("2", payloadsFor1); - payloadsFor2.add("a"); - payloadsFor2.add(null); - expectedPayloadsArray.put("4", payloadsFor2); - expectedPayloadsArray.put("6", new ArrayList<>()); - expectedPayloadsArray.put("5", new ArrayList<>()); - expectedPayloadsArray.put("3", new ArrayList<>()); - expectedPayloadsArray.put("1", new ArrayList<>()); - checkArrayValsInEachDoc(script, expectedPayloadsArray, 6); - - // check int payload for 'c' - expectedPayloadsArray.clear(); - payloadsFor1.clear(); - payloadsFor2.clear(); - script = createPositionsArrayScript("int_payload_field", "c", INCLUDE_ALL, "payloadAsInt(-1)"); - payloadsFor1 = new ArrayList<>(); - payloadsFor1.add(1); - expectedPayloadsArray.put("6", payloadsFor1); - expectedPayloadsArray.put("5", new ArrayList<>()); - expectedPayloadsArray.put("4", new ArrayList<>()); - expectedPayloadsArray.put("3", new ArrayList<>()); - expectedPayloadsArray.put("2", new ArrayList<>()); - expectedPayloadsArray.put("1", new ArrayList<>()); - checkArrayValsInEachDoc(script, expectedPayloadsArray, 6); - - } - - private void checkExceptions(Script script) { - try { - SearchResponse sr = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()).addScriptField("tvtest", script) - .execute().actionGet(); - assertThat(sr.getHits().getHits().length, equalTo(0)); - ShardSearchFailure[] shardFails = sr.getShardFailures(); - for (ShardSearchFailure fail : shardFails) { - assertThat(fail.reason().indexOf("Cannot iterate twice! If you want to iterate more that once, add _CACHE explicitly."), - Matchers.greaterThan(-1)); - } - } catch (SearchPhaseExecutionException ex) { - assertThat( - "got " + ex.toString(), - ex.toString().indexOf("Cannot iterate twice! If you want to iterate more that once, add _CACHE explicitly."), - Matchers.greaterThan(-1)); - } - } - - private void checkValueInEachDocWithFunctionScore(Script fieldScript, Map expectedFieldVals, Script scoreScript, - Map expectedScore, int numExpectedDocs) { - SearchResponse sr = client().prepareSearch("test") - .setQuery(QueryBuilders.functionScoreQuery(ScoreFunctionBuilders.scriptFunction(scoreScript))) - .addScriptField("tvtest", fieldScript).execute().actionGet(); - assertHitCount(sr, numExpectedDocs); - for (SearchHit hit : sr.getHits().getHits()) { - Object result = hit.getFields().get("tvtest").getValues().get(0); - Object expectedResult = expectedFieldVals.get(hit.getId()); - assertThat("for doc " + hit.getId(), result, equalTo(expectedResult)); - assertThat("for doc " + hit.getId(), ((Float) expectedScore.get(hit.getId())).doubleValue(), - Matchers.closeTo(hit.getScore(), 1.e-4)); - } - } - - private void checkValueInEachDoc(Script script, Map expectedResults, int numExpectedDocs) { - SearchResponse sr = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()).addScriptField("tvtest", script) - .execute().actionGet(); - assertHitCount(sr, numExpectedDocs); - for (SearchHit hit : sr.getHits().getHits()) { - Object result = hit.getFields().get("tvtest").getValues().get(0); - Object expectedResult = expectedResults.get(hit.getId()); - assertThat("for doc " + hit.getId(), result, equalTo(expectedResult)); - } - } - - private void checkValueInEachDoc(int value, Script script, int numExpectedDocs) { - SearchResponse sr = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()).addScriptField("tvtest", script) - .execute().actionGet(); - assertHitCount(sr, numExpectedDocs); - for (SearchHit hit : sr.getHits().getHits()) { - Object result = hit.getFields().get("tvtest").getValues().get(0); - if (result instanceof Integer) { - assertThat(result, equalTo(value)); - } else if (result instanceof Long) { - assertThat(((Long) result).intValue(), equalTo(value)); - } else { - fail(); - } - } - } -} diff --git a/docs/reference/migration/migrate_6_0/scripting.asciidoc b/docs/reference/migration/migrate_6_0/scripting.asciidoc index 09580401577c7..e5c6c95979cff 100644 --- a/docs/reference/migration/migrate_6_0/scripting.asciidoc +++ b/docs/reference/migration/migrate_6_0/scripting.asciidoc @@ -13,7 +13,11 @@ milliseconds since epoch as a `long`. The same is true for `doc.some_date_field[some_number]`. Use `doc.some_date_field.value.millis` to fetch the milliseconds since epoch if you need it. +==== Removed access to index internal via the _index variable + +The `_index` variable has been removed. If you used it for advanced scoring, consider writing a `Similarity` plugin. + ==== Script Settings All of the existing scripting security settings have been deprecated. Instead -they are replaced with `script.allowed_types` and `script.allowed_contexts`. \ No newline at end of file +they are replaced with `script.allowed_types` and `script.allowed_contexts`. diff --git a/docs/reference/modules/scripting/fields.asciidoc b/docs/reference/modules/scripting/fields.asciidoc index 0dbb5b6b42e6d..e8019f01d1cec 100644 --- a/docs/reference/modules/scripting/fields.asciidoc +++ b/docs/reference/modules/scripting/fields.asciidoc @@ -29,10 +29,6 @@ Field values can be accessed from a script using <>, or <>, which are explained below. -Scripts may also have access to the document's relevance -<> and, via the experimental `_index` variable, -to term statistics for <>. - [[scripting-score]] [float] === Accessing the score of a document within a script From a00165913b4c8115570e8c898d031186ba585168 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 16 May 2017 12:17:12 -0400 Subject: [PATCH 376/619] Adjust comment lengths in IndexShard This commit adjusts the length of some comments in IndexShard.java that were written when the column width was set to 100. --- .../elasticsearch/index/shard/IndexShard.java | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java index 84c3ee2ededc5..8da19d7caccd2 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -805,10 +805,9 @@ public Engine.CommitId flush(FlushRequest request) { final boolean force = request.force(); logger.trace("flush with {}", request); /* - * We allow flushes while recovery since we allow operations to happen while recovering and - * we want to keep the translog under control (up to deletes, which we do not GC). Yet, we - * do not use flush internally to clear deletes and flush the index writer since we use - * Engine#writeIndexingBuffer for this now. + * We allow flushes while recovery since we allow operations to happen while recovering and we want to keep the translog under + * control (up to deletes, which we do not GC). Yet, we do not use flush internally to clear deletes and flush the index writer + * since we use Engine#writeIndexingBuffer for this now. */ verifyNotClosed(); final Engine engine = getEngine(); @@ -1316,8 +1315,8 @@ public boolean restoreFromRepository(Repository repository) { } /** - * Tests whether or not the translog should be flushed. This test is based on the current size - * of the translog comparted to the configured flush threshold size. + * Tests whether or not the translog should be flushed. This test is based on the current size of the translog comparted to the + * configured flush threshold size. * * @return {@code true} if the translog should be flushed */ @@ -1335,9 +1334,8 @@ boolean shouldFlush() { } /** - * Tests whether or not the translog generation should be rolled to a new generation. This test - * is based on the size of the current generation compared to the configured generation - * threshold size. + * Tests whether or not the translog generation should be rolled to a new generation. This test is based on the size of the current + * generation compared to the configured generation threshold size. * * @return {@code true} if the current generation should be rolled to a new generation */ @@ -1919,21 +1917,19 @@ public Translog.Durability getTranslogDurability() { private final AtomicBoolean flushOrRollRunning = new AtomicBoolean(); /** - * Schedules a flush or translog generation roll if needed but will not schedule more than one - * concurrently. The operation will be executed asynchronously on the flush thread pool. + * Schedules a flush or translog generation roll if needed but will not schedule more than one concurrently. The operation will be + * executed asynchronously on the flush thread pool. */ public void afterWriteOperation() { if (shouldFlush() || shouldRollTranslogGeneration()) { if (flushOrRollRunning.compareAndSet(false, true)) { /* - * We have to check again since otherwise there is a race when a thread passes the - * first check next to another thread which performs the operation quickly enough to - * finish before the current thread could flip the flag. In that situation, we have - * an extra operation. + * We have to check again since otherwise there is a race when a thread passes the first check next to another thread which + * performs the operation quickly enough to finish before the current thread could flip the flag. In that situation, we + * have an extra operation. * - * Additionally, a flush implicitly executes a translog generation roll so if we - * execute a flush then we do not need to check if we should roll the translog - * generation. + * Additionally, a flush implicitly executes a translog generation roll so if we execute a flush then we do not need to + * check if we should roll the translog generation. */ if (shouldFlush()) { logger.debug("submitting async flush request"); From 7ef390068a94acd7f20d18ec88721aad722a5ddb Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Tue, 16 May 2017 13:10:24 -0400 Subject: [PATCH 377/619] Move remaining pre-configured token filters into analysis-common (#24716) Moves the remaining preconfigured token figured into the analysis-common module. There were a couple of tests in core that depended on the pre-configured token filters so I had to touch them: * `GetTermVectorsCheckDocFreqIT` depended on `type_as_payload` but didn't do anything important with it. I dropped the dependency. Then I moved the test to a single node test case because we're trying to cut down on the number of `ESIntegTestCase` subclasses. * `AbstractTermVectorsTestCase` and its subclasses depended on `type_as_payload`. I dropped their usage of the token filter and added an integration test for the termvectors API that uses `type_as_payload` to the `analysis-common` module. * `AnalysisModuleTests` expected a few pre-configured token filtes be registered by default. They aren't any more so I dropped this assertion. We assert that the `CommonAnalysisPlugin` registers these pre-built token filters in `CommonAnalysisFactoryTests` * `SearchQueryIT` and `SuggestSearchIT` had tests that depended on the specific behavior of the token filters so I moved the tests to integration tests in `analysis-common`. --- .../indices/analysis/AnalysisModule.java | 16 -- .../analysis/PreBuiltTokenFilters.java | 252 +---------------- .../AbstractTermVectorsTestCase.java | 9 +- .../GetTermVectorsCheckDocFreqIT.java | 259 ------------------ .../action/termvectors/GetTermVectorsIT.java | 11 +- .../termvectors/TermVectorsServiceTests.java | 45 +++ .../indices/analysis/AnalysisModuleTests.java | 9 - .../search/query/SearchQueryIT.java | 24 -- .../search/suggest/SuggestSearchIT.java | 101 ------- .../analysis/common/CommonAnalysisPlugin.java | 61 ++++- .../common/CommonAnalysisFactoryTests.java | 26 ++ .../test/search.query/10_match.yml | 65 +++++ .../test/search.suggest/20_phrase.yaml | 158 +++++++++++ .../test/termvectors/10_payloads.yml | 40 +++ .../analysis/AnalysisFactoryTestCase.java | 26 -- 15 files changed, 402 insertions(+), 700 deletions(-) delete mode 100644 core/src/test/java/org/elasticsearch/action/termvectors/GetTermVectorsCheckDocFreqIT.java create mode 100644 modules/analysis-common/src/test/resources/rest-api-spec/test/search.query/10_match.yml create mode 100644 modules/analysis-common/src/test/resources/rest-api-spec/test/search.suggest/20_phrase.yaml create mode 100644 modules/analysis-common/src/test/resources/rest-api-spec/test/termvectors/10_payloads.yml diff --git a/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java b/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java index ffe80a0f5f446..d49edb33eb354 100644 --- a/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java +++ b/core/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java @@ -278,22 +278,6 @@ static Map setupPreConfiguredTokenFilters(List * version uses a set of English stop words that are in * lucene-analyzers-common so "stop" is defined in the analysis-common * module. */ - - // Add token filters declared in PreBuiltTokenFilters until they have all been migrated - for (PreBuiltTokenFilters preBuilt : PreBuiltTokenFilters.values()) { - switch (preBuilt) { - case LOWERCASE: - // This has been migrated but has to stick around until PreBuiltTokenizers is removed. - continue; - default: - if (CachingStrategy.ONE != preBuilt.getCachingStrategy()) { - throw new UnsupportedOperationException("shim not available for " + preBuilt.getCachingStrategy()); - } - String name = preBuilt.name().toLowerCase(Locale.ROOT); - preConfiguredTokenFilters.register(name, PreConfiguredTokenFilter.singleton(name, preBuilt.isMultiTermAware(), - tokenStream -> preBuilt.create(tokenStream, Version.CURRENT))); - } - } for (AnalysisPlugin plugin: plugins) { for (PreConfiguredTokenFilter filter : plugin.getPreConfiguredTokenFilters()) { diff --git a/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltTokenFilters.java b/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltTokenFilters.java index 427c0431fb550..ba66c41e6390e 100644 --- a/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltTokenFilters.java +++ b/core/src/main/java/org/elasticsearch/indices/analysis/PreBuiltTokenFilters.java @@ -20,38 +20,10 @@ import org.apache.lucene.analysis.LowerCaseFilter; import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.ar.ArabicNormalizationFilter; -import org.apache.lucene.analysis.ar.ArabicStemFilter; -import org.apache.lucene.analysis.br.BrazilianStemFilter; -import org.apache.lucene.analysis.cjk.CJKBigramFilter; -import org.apache.lucene.analysis.cjk.CJKWidthFilter; -import org.apache.lucene.analysis.ckb.SoraniNormalizationFilter; -import org.apache.lucene.analysis.core.DecimalDigitFilter; -import org.apache.lucene.analysis.cz.CzechStemFilter; -import org.apache.lucene.analysis.de.GermanNormalizationFilter; -import org.apache.lucene.analysis.de.GermanStemFilter; -import org.apache.lucene.analysis.fa.PersianNormalizationFilter; -import org.apache.lucene.analysis.fr.FrenchAnalyzer; -import org.apache.lucene.analysis.hi.HindiNormalizationFilter; -import org.apache.lucene.analysis.in.IndicNormalizationFilter; -import org.apache.lucene.analysis.miscellaneous.KeywordRepeatFilter; -import org.apache.lucene.analysis.miscellaneous.LimitTokenCountFilter; -import org.apache.lucene.analysis.miscellaneous.ScandinavianFoldingFilter; -import org.apache.lucene.analysis.miscellaneous.ScandinavianNormalizationFilter; -import org.apache.lucene.analysis.payloads.DelimitedPayloadTokenFilter; -import org.apache.lucene.analysis.payloads.TypeAsPayloadTokenFilter; -import org.apache.lucene.analysis.shingle.ShingleFilter; -import org.apache.lucene.analysis.snowball.SnowballFilter; -import org.apache.lucene.analysis.tr.ApostropheFilter; -import org.apache.lucene.analysis.util.ElisionFilter; import org.elasticsearch.Version; -import org.elasticsearch.index.analysis.DelimitedPayloadTokenFilterFactory; -import org.elasticsearch.index.analysis.LimitTokenCountFilterFactory; import org.elasticsearch.index.analysis.MultiTermAwareComponent; import org.elasticsearch.index.analysis.TokenFilterFactory; import org.elasticsearch.indices.analysis.PreBuiltCacheFactory.CachingStrategy; -import org.tartarus.snowball.ext.DutchStemmer; -import org.tartarus.snowball.ext.FrenchStemmer; import java.util.Locale; @@ -66,229 +38,7 @@ public TokenStream create(TokenStream tokenStream, Version version) { protected boolean isMultiTermAware() { return true; } - }, - - // Extended Token Filters - ELISION(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new ElisionFilter(tokenStream, FrenchAnalyzer.DEFAULT_ARTICLES); - } - @Override - protected boolean isMultiTermAware() { - return true; - } - }, - - ARABIC_STEM(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new ArabicStemFilter(tokenStream); - } - }, - - BRAZILIAN_STEM(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new BrazilianStemFilter(tokenStream); - } - }, - - CZECH_STEM(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new CzechStemFilter(tokenStream); - } - }, - - DUTCH_STEM(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new SnowballFilter(tokenStream, new DutchStemmer()); - } - }, - - FRENCH_STEM(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new SnowballFilter(tokenStream, new FrenchStemmer()); - } - }, - - GERMAN_STEM(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new GermanStemFilter(tokenStream); - } - }, - - RUSSIAN_STEM(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new SnowballFilter(tokenStream, "Russian"); - } - }, - - KEYWORD_REPEAT(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new KeywordRepeatFilter(tokenStream); - } - }, - - ARABIC_NORMALIZATION(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new ArabicNormalizationFilter(tokenStream); - } - @Override - protected boolean isMultiTermAware() { - return true; - } - }, - - PERSIAN_NORMALIZATION(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new PersianNormalizationFilter(tokenStream); - } - @Override - protected boolean isMultiTermAware() { - return true; - } - }, - - TYPE_AS_PAYLOAD(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new TypeAsPayloadTokenFilter(tokenStream); - } - }, - - SHINGLE(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new ShingleFilter(tokenStream); - } - }, - - GERMAN_NORMALIZATION(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new GermanNormalizationFilter(tokenStream); - } - @Override - protected boolean isMultiTermAware() { - return true; - } - }, - - HINDI_NORMALIZATION(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new HindiNormalizationFilter(tokenStream); - } - @Override - protected boolean isMultiTermAware() { - return true; - } - }, - - INDIC_NORMALIZATION(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new IndicNormalizationFilter(tokenStream); - } - @Override - protected boolean isMultiTermAware() { - return true; - } - }, - - SORANI_NORMALIZATION(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new SoraniNormalizationFilter(tokenStream); - } - @Override - protected boolean isMultiTermAware() { - return true; - } - }, - - SCANDINAVIAN_NORMALIZATION(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new ScandinavianNormalizationFilter(tokenStream); - } - @Override - protected boolean isMultiTermAware() { - return true; - } - }, - - SCANDINAVIAN_FOLDING(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new ScandinavianFoldingFilter(tokenStream); - } - @Override - protected boolean isMultiTermAware() { - return true; - } - }, - - APOSTROPHE(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new ApostropheFilter(tokenStream); - } - }, - - CJK_WIDTH(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new CJKWidthFilter(tokenStream); - } - @Override - protected boolean isMultiTermAware() { - return true; - } - }, - - DECIMAL_DIGIT(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new DecimalDigitFilter(tokenStream); - } - @Override - protected boolean isMultiTermAware() { - return true; - } - }, - - CJK_BIGRAM(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new CJKBigramFilter(tokenStream); - } - }, - - DELIMITED_PAYLOAD_FILTER(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new DelimitedPayloadTokenFilter(tokenStream, DelimitedPayloadTokenFilterFactory.DEFAULT_DELIMITER, DelimitedPayloadTokenFilterFactory.DEFAULT_ENCODER); - } - }, - - LIMIT(CachingStrategy.ONE) { - @Override - public TokenStream create(TokenStream tokenStream, Version version) { - return new LimitTokenCountFilter(tokenStream, LimitTokenCountFilterFactory.DEFAULT_MAX_TOKEN_COUNT, LimitTokenCountFilterFactory.DEFAULT_CONSUME_ALL_TOKENS); - } - }, - - ; + }; protected boolean isMultiTermAware() { return false; diff --git a/core/src/test/java/org/elasticsearch/action/termvectors/AbstractTermVectorsTestCase.java b/core/src/test/java/org/elasticsearch/action/termvectors/AbstractTermVectorsTestCase.java index 05e30d7e2d7f4..15a2f9e74a461 100644 --- a/core/src/test/java/org/elasticsearch/action/termvectors/AbstractTermVectorsTestCase.java +++ b/core/src/test/java/org/elasticsearch/action/termvectors/AbstractTermVectorsTestCase.java @@ -66,7 +66,6 @@ import static org.hamcrest.Matchers.equalTo; public abstract class AbstractTermVectorsTestCase extends ESIntegTestCase { - protected static class TestFieldSetting { public final String name; public final boolean storedOffset; @@ -211,7 +210,7 @@ protected void createIndexBasedOnFieldSettings(String index, String alias, TestF Settings.Builder settings = Settings.builder() .put(indexSettings()) .put("index.analysis.analyzer.tv_test.tokenizer", "standard") - .putArray("index.analysis.analyzer.tv_test.filter", "type_as_payload", "lowercase"); + .putArray("index.analysis.analyzer.tv_test.filter", "lowercase"); assertAcked(prepareCreate(index).addMapping("type1", mappingBuilder).setSettings(settings).addAlias(new Alias(alias))); } @@ -395,11 +394,7 @@ protected void validateResponse(TermVectorsResponse esResponse, Fields luceneFie assertThat("Missing offset test failed" + failDesc, esDocsPosEnum.startOffset(), equalTo(-1)); assertThat("Missing offset test failed" + failDesc, esDocsPosEnum.endOffset(), equalTo(-1)); } - if (field.storedPayloads && testConfig.requestPayloads) { - assertThat("Payload test failed" + failDesc, luceneDocsPosEnum.getPayload(), equalTo(esDocsPosEnum.getPayload())); - } else { - assertThat("Missing payload test failed" + failDesc, esDocsPosEnum.getPayload(), equalTo(null)); - } + assertNull("Missing payload test failed" + failDesc, esDocsPosEnum.getPayload()); } } assertNull("Es returned terms are done but lucene isn't", luceneTermEnum.next()); diff --git a/core/src/test/java/org/elasticsearch/action/termvectors/GetTermVectorsCheckDocFreqIT.java b/core/src/test/java/org/elasticsearch/action/termvectors/GetTermVectorsCheckDocFreqIT.java deleted file mode 100644 index 294a0ffde8f30..0000000000000 --- a/core/src/test/java/org/elasticsearch/action/termvectors/GetTermVectorsCheckDocFreqIT.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.action.termvectors; - -import org.apache.lucene.index.Fields; -import org.apache.lucene.index.PostingsEnum; -import org.apache.lucene.index.Terms; -import org.apache.lucene.index.TermsEnum; -import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.test.ESIntegTestCase; -import org.hamcrest.Matchers; - -import java.io.IOException; - -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.hamcrest.Matchers.equalTo; - -public class GetTermVectorsCheckDocFreqIT extends ESIntegTestCase { - - @Override - protected int numberOfShards() { - return 1; - } - - @Override - protected int numberOfReplicas() { - return 0; - } - - @Override - public Settings indexSettings() { - return Settings.builder() - .put(super.indexSettings()) - .put("index.analysis.analyzer.tv_test.tokenizer", "whitespace") - .putArray("index.analysis.analyzer.tv_test.filter", "type_as_payload", "lowercase") - .build(); - } - - public void testSimpleTermVectors() throws IOException { - XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject("type1") - .startObject("properties") - .startObject("field") - .field("type", "text") - .field("term_vector", "with_positions_offsets_payloads") - .field("analyzer", "tv_test") - .endObject() - .endObject() - .endObject().endObject(); - assertAcked(prepareCreate("test").addMapping("type1", mapping)); - ensureGreen(); - int numDocs = 15; - for (int i = 0; i < numDocs; i++) { - client().prepareIndex("test", "type1", Integer.toString(i)) - .setSource(XContentFactory.jsonBuilder().startObject().field("field", "the quick brown fox jumps over the lazy dog") - // 0the3 4quick9 10brown15 16fox19 20jumps25 26over30 - // 31the34 35lazy39 40dog43 - .endObject()).execute().actionGet(); - refresh(); - } - String[] values = { "brown", "dog", "fox", "jumps", "lazy", "over", "quick", "the" }; - int[] freq = { 1, 1, 1, 1, 1, 1, 1, 2 }; - int[][] pos = { { 2 }, { 8 }, { 3 }, { 4 }, { 7 }, { 5 }, { 1 }, { 0, 6 } }; - int[][] startOffset = { { 10 }, { 40 }, { 16 }, { 20 }, { 35 }, { 26 }, { 4 }, { 0, 31 } }; - int[][] endOffset = { { 15 }, { 43 }, { 19 }, { 25 }, { 39 }, { 30 }, { 9 }, { 3, 34 } }; - for (int i = 0; i < numDocs; i++) { - checkAllInfo(numDocs, values, freq, pos, startOffset, endOffset, i); - checkWithoutTermStatistics(numDocs, values, freq, pos, startOffset, endOffset, i); - checkWithoutFieldStatistics(numDocs, values, freq, pos, startOffset, endOffset, i); - } - } - - private void checkWithoutFieldStatistics(int numDocs, String[] values, int[] freq, int[][] pos, int[][] startOffset, int[][] endOffset, - int i) throws IOException { - TermVectorsRequestBuilder resp = client().prepareTermVectors("test", "type1", Integer.toString(i)).setPayloads(true).setOffsets(true) - .setPositions(true).setTermStatistics(true).setFieldStatistics(false).setSelectedFields(); - TermVectorsResponse response = resp.execute().actionGet(); - assertThat("doc id: " + i + " doesn't exists but should", response.isExists(), equalTo(true)); - Fields fields = response.getFields(); - assertThat(fields.size(), equalTo(1)); - Terms terms = fields.terms("field"); - assertThat(terms.size(), equalTo(8L)); - assertThat(terms.getSumTotalTermFreq(), Matchers.equalTo((long) -1)); - assertThat(terms.getDocCount(), Matchers.equalTo(-1)); - assertThat(terms.getSumDocFreq(), equalTo((long) -1)); - TermsEnum iterator = terms.iterator(); - for (int j = 0; j < values.length; j++) { - String string = values[j]; - BytesRef next = iterator.next(); - assertThat(next, Matchers.notNullValue()); - assertThat("expected " + string, string, equalTo(next.utf8ToString())); - assertThat(next, Matchers.notNullValue()); - if (string.equals("the")) { - assertThat("expected ttf of " + string, numDocs * 2, equalTo((int) iterator.totalTermFreq())); - } else { - assertThat("expected ttf of " + string, numDocs, equalTo((int) iterator.totalTermFreq())); - } - - PostingsEnum docsAndPositions = iterator.postings(null, PostingsEnum.ALL); - assertThat(docsAndPositions.nextDoc(), equalTo(0)); - assertThat(freq[j], equalTo(docsAndPositions.freq())); - assertThat(iterator.docFreq(), equalTo(numDocs)); - int[] termPos = pos[j]; - int[] termStartOffset = startOffset[j]; - int[] termEndOffset = endOffset[j]; - assertThat(termPos.length, equalTo(freq[j])); - assertThat(termStartOffset.length, equalTo(freq[j])); - assertThat(termEndOffset.length, equalTo(freq[j])); - for (int k = 0; k < freq[j]; k++) { - int nextPosition = docsAndPositions.nextPosition(); - assertThat("term: " + string, nextPosition, equalTo(termPos[k])); - assertThat("term: " + string, docsAndPositions.startOffset(), equalTo(termStartOffset[k])); - assertThat("term: " + string, docsAndPositions.endOffset(), equalTo(termEndOffset[k])); - assertThat("term: " + string, docsAndPositions.getPayload(), equalTo(new BytesRef("word"))); - } - } - assertThat(iterator.next(), Matchers.nullValue()); - - XContentBuilder xBuilder = XContentFactory.jsonBuilder(); - response.toXContent(xBuilder, null); - String utf8 = xBuilder.bytes().utf8ToString().replaceFirst("\"took\":\\d+,", "");; - String expectedString = "{\"_index\":\"test\",\"_type\":\"type1\",\"_id\":\"" - + i - + "\",\"_version\":1,\"found\":true,\"term_vectors\":{\"field\":{\"terms\":{\"brown\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":2,\"start_offset\":10,\"end_offset\":15,\"payload\":\"d29yZA==\"}]},\"dog\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":8,\"start_offset\":40,\"end_offset\":43,\"payload\":\"d29yZA==\"}]},\"fox\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":3,\"start_offset\":16,\"end_offset\":19,\"payload\":\"d29yZA==\"}]},\"jumps\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":4,\"start_offset\":20,\"end_offset\":25,\"payload\":\"d29yZA==\"}]},\"lazy\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":7,\"start_offset\":35,\"end_offset\":39,\"payload\":\"d29yZA==\"}]},\"over\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":5,\"start_offset\":26,\"end_offset\":30,\"payload\":\"d29yZA==\"}]},\"quick\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":1,\"start_offset\":4,\"end_offset\":9,\"payload\":\"d29yZA==\"}]},\"the\":{\"doc_freq\":15,\"ttf\":30,\"term_freq\":2,\"tokens\":[{\"position\":0,\"start_offset\":0,\"end_offset\":3,\"payload\":\"d29yZA==\"},{\"position\":6,\"start_offset\":31,\"end_offset\":34,\"payload\":\"d29yZA==\"}]}}}}}"; - assertThat(utf8, equalTo(expectedString)); - - } - - private void checkWithoutTermStatistics(int numDocs, String[] values, int[] freq, int[][] pos, int[][] startOffset, int[][] endOffset, - int i) throws IOException { - TermVectorsRequestBuilder resp = client().prepareTermVectors("test", "type1", Integer.toString(i)).setPayloads(true).setOffsets(true) - .setPositions(true).setTermStatistics(false).setFieldStatistics(true).setSelectedFields(); - assertThat(resp.request().termStatistics(), equalTo(false)); - TermVectorsResponse response = resp.execute().actionGet(); - assertThat("doc id: " + i + " doesn't exists but should", response.isExists(), equalTo(true)); - Fields fields = response.getFields(); - assertThat(fields.size(), equalTo(1)); - Terms terms = fields.terms("field"); - assertThat(terms.size(), equalTo(8L)); - assertThat(terms.getSumTotalTermFreq(), Matchers.equalTo((long) (9 * numDocs))); - assertThat(terms.getDocCount(), Matchers.equalTo(numDocs)); - assertThat(terms.getSumDocFreq(), equalTo((long) numDocs * values.length)); - TermsEnum iterator = terms.iterator(); - for (int j = 0; j < values.length; j++) { - String string = values[j]; - BytesRef next = iterator.next(); - assertThat(next, Matchers.notNullValue()); - assertThat("expected " + string, string, equalTo(next.utf8ToString())); - assertThat(next, Matchers.notNullValue()); - - assertThat("expected ttf of " + string, -1, equalTo((int) iterator.totalTermFreq())); - - PostingsEnum docsAndPositions = iterator.postings(null, PostingsEnum.ALL); - assertThat(docsAndPositions.nextDoc(), equalTo(0)); - assertThat(freq[j], equalTo(docsAndPositions.freq())); - assertThat(iterator.docFreq(), equalTo(-1)); - int[] termPos = pos[j]; - int[] termStartOffset = startOffset[j]; - int[] termEndOffset = endOffset[j]; - assertThat(termPos.length, equalTo(freq[j])); - assertThat(termStartOffset.length, equalTo(freq[j])); - assertThat(termEndOffset.length, equalTo(freq[j])); - for (int k = 0; k < freq[j]; k++) { - int nextPosition = docsAndPositions.nextPosition(); - assertThat("term: " + string, nextPosition, equalTo(termPos[k])); - assertThat("term: " + string, docsAndPositions.startOffset(), equalTo(termStartOffset[k])); - assertThat("term: " + string, docsAndPositions.endOffset(), equalTo(termEndOffset[k])); - assertThat("term: " + string, docsAndPositions.getPayload(), equalTo(new BytesRef("word"))); - } - } - assertThat(iterator.next(), Matchers.nullValue()); - - XContentBuilder xBuilder = XContentFactory.jsonBuilder(); - response.toXContent(xBuilder, null); - String utf8 = xBuilder.bytes().utf8ToString().replaceFirst("\"took\":\\d+,", "");; - String expectedString = "{\"_index\":\"test\",\"_type\":\"type1\",\"_id\":\"" - + i - + "\",\"_version\":1,\"found\":true,\"term_vectors\":{\"field\":{\"field_statistics\":{\"sum_doc_freq\":120,\"doc_count\":15,\"sum_ttf\":135},\"terms\":{\"brown\":{\"term_freq\":1,\"tokens\":[{\"position\":2,\"start_offset\":10,\"end_offset\":15,\"payload\":\"d29yZA==\"}]},\"dog\":{\"term_freq\":1,\"tokens\":[{\"position\":8,\"start_offset\":40,\"end_offset\":43,\"payload\":\"d29yZA==\"}]},\"fox\":{\"term_freq\":1,\"tokens\":[{\"position\":3,\"start_offset\":16,\"end_offset\":19,\"payload\":\"d29yZA==\"}]},\"jumps\":{\"term_freq\":1,\"tokens\":[{\"position\":4,\"start_offset\":20,\"end_offset\":25,\"payload\":\"d29yZA==\"}]},\"lazy\":{\"term_freq\":1,\"tokens\":[{\"position\":7,\"start_offset\":35,\"end_offset\":39,\"payload\":\"d29yZA==\"}]},\"over\":{\"term_freq\":1,\"tokens\":[{\"position\":5,\"start_offset\":26,\"end_offset\":30,\"payload\":\"d29yZA==\"}]},\"quick\":{\"term_freq\":1,\"tokens\":[{\"position\":1,\"start_offset\":4,\"end_offset\":9,\"payload\":\"d29yZA==\"}]},\"the\":{\"term_freq\":2,\"tokens\":[{\"position\":0,\"start_offset\":0,\"end_offset\":3,\"payload\":\"d29yZA==\"},{\"position\":6,\"start_offset\":31,\"end_offset\":34,\"payload\":\"d29yZA==\"}]}}}}}"; - assertThat(utf8, equalTo(expectedString)); - - } - - private void checkAllInfo(int numDocs, String[] values, int[] freq, int[][] pos, int[][] startOffset, int[][] endOffset, int i) - throws IOException { - TermVectorsRequestBuilder resp = client().prepareTermVectors("test", "type1", Integer.toString(i)).setPayloads(true).setOffsets(true) - .setPositions(true).setFieldStatistics(true).setTermStatistics(true).setSelectedFields(); - assertThat(resp.request().fieldStatistics(), equalTo(true)); - TermVectorsResponse response = resp.execute().actionGet(); - assertThat("doc id: " + i + " doesn't exists but should", response.isExists(), equalTo(true)); - Fields fields = response.getFields(); - assertThat(fields.size(), equalTo(1)); - Terms terms = fields.terms("field"); - assertThat(terms.size(), equalTo(8L)); - assertThat(terms.getSumTotalTermFreq(), Matchers.equalTo((long) (9 * numDocs))); - assertThat(terms.getDocCount(), Matchers.equalTo(numDocs)); - assertThat(terms.getSumDocFreq(), equalTo((long) numDocs * values.length)); - TermsEnum iterator = terms.iterator(); - for (int j = 0; j < values.length; j++) { - String string = values[j]; - BytesRef next = iterator.next(); - assertThat(next, Matchers.notNullValue()); - assertThat("expected " + string, string, equalTo(next.utf8ToString())); - assertThat(next, Matchers.notNullValue()); - if (string.equals("the")) { - assertThat("expected ttf of " + string, numDocs * 2, equalTo((int) iterator.totalTermFreq())); - } else { - assertThat("expected ttf of " + string, numDocs, equalTo((int) iterator.totalTermFreq())); - } - - PostingsEnum docsAndPositions = iterator.postings(null, PostingsEnum.ALL); - assertThat(docsAndPositions.nextDoc(), equalTo(0)); - assertThat(freq[j], equalTo(docsAndPositions.freq())); - assertThat(iterator.docFreq(), equalTo(numDocs)); - int[] termPos = pos[j]; - int[] termStartOffset = startOffset[j]; - int[] termEndOffset = endOffset[j]; - assertThat(termPos.length, equalTo(freq[j])); - assertThat(termStartOffset.length, equalTo(freq[j])); - assertThat(termEndOffset.length, equalTo(freq[j])); - for (int k = 0; k < freq[j]; k++) { - int nextPosition = docsAndPositions.nextPosition(); - assertThat("term: " + string, nextPosition, equalTo(termPos[k])); - assertThat("term: " + string, docsAndPositions.startOffset(), equalTo(termStartOffset[k])); - assertThat("term: " + string, docsAndPositions.endOffset(), equalTo(termEndOffset[k])); - assertThat("term: " + string, docsAndPositions.getPayload(), equalTo(new BytesRef("word"))); - } - } - assertThat(iterator.next(), Matchers.nullValue()); - - XContentBuilder xBuilder = XContentFactory.jsonBuilder(); - response.toXContent(xBuilder, ToXContent.EMPTY_PARAMS); - String utf8 = xBuilder.bytes().utf8ToString().replaceFirst("\"took\":\\d+,", "");; - String expectedString = "{\"_index\":\"test\",\"_type\":\"type1\",\"_id\":\"" - + i - + "\",\"_version\":1,\"found\":true,\"term_vectors\":{\"field\":{\"field_statistics\":{\"sum_doc_freq\":120,\"doc_count\":15,\"sum_ttf\":135},\"terms\":{\"brown\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":2,\"start_offset\":10,\"end_offset\":15,\"payload\":\"d29yZA==\"}]},\"dog\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":8,\"start_offset\":40,\"end_offset\":43,\"payload\":\"d29yZA==\"}]},\"fox\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":3,\"start_offset\":16,\"end_offset\":19,\"payload\":\"d29yZA==\"}]},\"jumps\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":4,\"start_offset\":20,\"end_offset\":25,\"payload\":\"d29yZA==\"}]},\"lazy\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":7,\"start_offset\":35,\"end_offset\":39,\"payload\":\"d29yZA==\"}]},\"over\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":5,\"start_offset\":26,\"end_offset\":30,\"payload\":\"d29yZA==\"}]},\"quick\":{\"doc_freq\":15,\"ttf\":15,\"term_freq\":1,\"tokens\":[{\"position\":1,\"start_offset\":4,\"end_offset\":9,\"payload\":\"d29yZA==\"}]},\"the\":{\"doc_freq\":15,\"ttf\":30,\"term_freq\":2,\"tokens\":[{\"position\":0,\"start_offset\":0,\"end_offset\":3,\"payload\":\"d29yZA==\"},{\"position\":6,\"start_offset\":31,\"end_offset\":34,\"payload\":\"d29yZA==\"}]}}}}}"; - assertThat(utf8, equalTo(expectedString)); - } - -} diff --git a/core/src/test/java/org/elasticsearch/action/termvectors/GetTermVectorsIT.java b/core/src/test/java/org/elasticsearch/action/termvectors/GetTermVectorsIT.java index ba2f5de24ba03..2ab6292f2d2b6 100644 --- a/core/src/test/java/org/elasticsearch/action/termvectors/GetTermVectorsIT.java +++ b/core/src/test/java/org/elasticsearch/action/termvectors/GetTermVectorsIT.java @@ -193,7 +193,7 @@ public void testSimpleTermVectors() throws IOException { .setSettings(Settings.builder() .put(indexSettings()) .put("index.analysis.analyzer.tv_test.tokenizer", "whitespace") - .putArray("index.analysis.analyzer.tv_test.filter", "type_as_payload", "lowercase"))); + .putArray("index.analysis.analyzer.tv_test.filter", "lowercase"))); for (int i = 0; i < 10; i++) { client().prepareIndex("test", "type1", Integer.toString(i)) .setSource(jsonBuilder().startObject().field("field", "the quick brown fox jumps over the lazy dog") @@ -278,7 +278,7 @@ public void testRandomSingleTermVectors() throws IOException { assertAcked(prepareCreate("test").addMapping("type1", mapping) .setSettings(Settings.builder() .put("index.analysis.analyzer.tv_test.tokenizer", "whitespace") - .putArray("index.analysis.analyzer.tv_test.filter", "type_as_payload", "lowercase"))); + .putArray("index.analysis.analyzer.tv_test.filter", "lowercase"))); for (int i = 0; i < 10; i++) { client().prepareIndex("test", "type1", Integer.toString(i)) .setSource(jsonBuilder().startObject().field("field", "the quick brown fox jumps over the lazy dog") @@ -585,7 +585,7 @@ public void testSimpleTermVectorsWithGenerate() throws IOException { .setSettings(Settings.builder() .put(indexSettings()) .put("index.analysis.analyzer.tv_test.tokenizer", "whitespace") - .putArray("index.analysis.analyzer.tv_test.filter", "type_as_payload", "lowercase"))); + .putArray("index.analysis.analyzer.tv_test.filter", "lowercase"))); ensureGreen(); @@ -645,9 +645,8 @@ private void checkBrownFoxTermVector(Fields fields, String fieldName, boolean wi assertThat("term: " + string, nextPosition, equalTo(termPos[k])); assertThat("term: " + string, docsAndPositions.startOffset(), equalTo(termStartOffset[k])); assertThat("term: " + string, docsAndPositions.endOffset(), equalTo(termEndOffset[k])); - if (withPayloads) { - assertThat("term: " + string, docsAndPositions.getPayload(), equalTo(new BytesRef("word"))); - } + // We never configure an analyzer with payloads for this test so this is never returned + assertNull("term: " + string, docsAndPositions.getPayload()); } } assertThat(iterator.next(), nullValue()); diff --git a/core/src/test/java/org/elasticsearch/index/termvectors/TermVectorsServiceTests.java b/core/src/test/java/org/elasticsearch/index/termvectors/TermVectorsServiceTests.java index c79a61a22b90f..c047235ada454 100644 --- a/core/src/test/java/org/elasticsearch/index/termvectors/TermVectorsServiceTests.java +++ b/core/src/test/java/org/elasticsearch/index/termvectors/TermVectorsServiceTests.java @@ -19,6 +19,9 @@ package org.elasticsearch.index.termvectors; +import org.apache.lucene.index.Terms; +import org.apache.lucene.index.TermsEnum; +import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.termvectors.TermVectorsRequest; import org.elasticsearch.action.termvectors.TermVectorsResponse; import org.elasticsearch.common.settings.Settings; @@ -28,6 +31,7 @@ import org.elasticsearch.indices.IndicesService; import org.elasticsearch.test.ESSingleNodeTestCase; +import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; @@ -71,4 +75,45 @@ public void testTook() throws Exception { assertThat(response, notNullValue()); assertThat(response.getTookInMillis(), equalTo(TimeUnit.NANOSECONDS.toMillis(longs.get(1) - longs.get(0)))); } + + public void testDocFreqs() throws IOException { + XContentBuilder mapping = jsonBuilder() + .startObject() + .startObject("doc") + .startObject("properties") + .startObject("text") + .field("type", "text") + .field("term_vector", "with_positions_offsets_payloads") + .endObject() + .endObject() + .endObject() + .endObject(); + Settings settings = Settings.builder() + .put("number_of_shards", 1) + .build(); + createIndex("test", settings, "doc", mapping); + ensureGreen(); + + int max = between(3, 10); + BulkRequestBuilder bulk = client().prepareBulk(); + for (int i = 0; i < max; i++) { + bulk.add(client().prepareIndex("test", "doc", Integer.toString(i)) + .setSource("text", "the quick brown fox jumped over the lazy dog")); + } + bulk.get(); + + TermVectorsRequest request = new TermVectorsRequest("test", "doc", "0").termStatistics(true); + + IndicesService indicesService = getInstanceFromNode(IndicesService.class); + IndexService test = indicesService.indexService(resolveIndex("test")); + IndexShard shard = test.getShardOrNull(0); + assertThat(shard, notNullValue()); + TermVectorsResponse response = TermVectorsService.getTermVectors(shard, request); + + Terms terms = response.getFields().terms("text"); + TermsEnum iterator = terms.iterator(); + while (iterator.next() != null) { + assertEquals(max, iterator.docFreq()); + } + } } diff --git a/core/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java b/core/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java index 2572b7aeb0f9d..298c8938dd2ef 100644 --- a/core/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java +++ b/core/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java @@ -23,11 +23,8 @@ import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.Tokenizer; -import org.apache.lucene.analysis.ar.ArabicNormalizationFilter; import org.apache.lucene.analysis.core.WhitespaceTokenizer; -import org.apache.lucene.analysis.fa.PersianNormalizationFilter; import org.apache.lucene.analysis.hunspell.Dictionary; -import org.apache.lucene.analysis.miscellaneous.KeywordRepeatFilter; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.store.Directory; @@ -127,12 +124,6 @@ public void testSimpleConfigurationYaml() throws IOException { testSimpleConfiguration(settings); } - public void testDefaultFactoryTokenFilters() throws IOException { - assertTokenFilter("keyword_repeat", KeywordRepeatFilter.class); - assertTokenFilter("persian_normalization", PersianNormalizationFilter.class); - assertTokenFilter("arabic_normalization", ArabicNormalizationFilter.class); - } - public void testAnalyzerAliasNotAllowedPost5x() throws IOException { Settings settings = Settings.builder() .put("index.analysis.analyzer.foobar.type", "standard") diff --git a/core/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java b/core/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java index d920c6a67b45a..316277973ffa7 100644 --- a/core/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java +++ b/core/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java @@ -1550,30 +1550,6 @@ public void testMatchQueryWithSynonyms() throws IOException { assertHitCount(searchResponse, 2); } - public void testMatchQueryWithStackedStems() throws IOException { - CreateIndexRequestBuilder builder = prepareCreate("test").setSettings(Settings.builder() - .put(indexSettings()) - .put("index.analysis.analyzer.index.type", "custom") - .put("index.analysis.analyzer.index.tokenizer", "standard") - .put("index.analysis.analyzer.index.filter", "lowercase") - .put("index.analysis.analyzer.search.type", "custom") - .put("index.analysis.analyzer.search.tokenizer", "standard") - .putArray("index.analysis.analyzer.search.filter", "lowercase", "keyword_repeat", "porter_stem", "unique_stem") - .put("index.analysis.filter.unique_stem.type", "unique") - .put("index.analysis.filter.unique_stem.only_on_same_position", true)); - assertAcked(builder.addMapping("test", "text", "type=text,analyzer=index,search_analyzer=search")); - - client().prepareIndex("test", "test", "1").setSource("text", "the fox runs across the street").get(); - refresh(); - SearchResponse searchResponse = client().prepareSearch("test").setQuery(matchQuery("text", "fox runs").operator(Operator.AND)).get(); - assertHitCount(searchResponse, 1); - - client().prepareIndex("test", "test", "2").setSource("text", "run fox run").get(); - refresh(); - searchResponse = client().prepareSearch("test").setQuery(matchQuery("text", "fox runs").operator(Operator.AND)).get(); - assertHitCount(searchResponse, 2); - } - public void testQueryStringWithSynonyms() throws IOException { CreateIndexRequestBuilder builder = prepareCreate("test").setSettings(Settings.builder() .put(indexSettings()) diff --git a/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java b/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java index 864f060be03a8..46a94e641c57c 100644 --- a/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java +++ b/core/src/test/java/org/elasticsearch/search/suggest/SuggestSearchIT.java @@ -694,107 +694,6 @@ public void testSizeParam() throws IOException { assertSuggestion(searchSuggest, 0, "simple_phrase", "xorr the god jewel"); } - public void testPhraseBoundaryCases() throws IOException, URISyntaxException { - CreateIndexRequestBuilder builder = prepareCreate("test").setSettings(Settings.builder() - .put(indexSettings()).put(SETTING_NUMBER_OF_SHARDS, 1) // to get reliable statistics we should put this all into one shard - .put("index.analysis.analyzer.body.tokenizer", "standard") - .putArray("index.analysis.analyzer.body.filter", "lowercase") - .put("index.analysis.analyzer.bigram.tokenizer", "standard") - .putArray("index.analysis.analyzer.bigram.filter", "my_shingle", "lowercase") - .put("index.analysis.analyzer.ngram.tokenizer", "standard") - .putArray("index.analysis.analyzer.ngram.filter", "my_shingle2", "lowercase") - .put("index.analysis.analyzer.myDefAnalyzer.tokenizer", "standard") - .putArray("index.analysis.analyzer.myDefAnalyzer.filter", "shingle", "lowercase") - .put("index.analysis.filter.my_shingle.type", "shingle") - .put("index.analysis.filter.my_shingle.output_unigrams", false) - .put("index.analysis.filter.my_shingle.min_shingle_size", 2) - .put("index.analysis.filter.my_shingle.max_shingle_size", 2) - .put("index.analysis.filter.my_shingle2.type", "shingle") - .put("index.analysis.filter.my_shingle2.output_unigrams", true) - .put("index.analysis.filter.my_shingle2.min_shingle_size", 2) - .put("index.analysis.filter.my_shingle2.max_shingle_size", 2)); - - XContentBuilder mapping = XContentFactory.jsonBuilder() - .startObject().startObject("type1") - .startObject("properties") - .startObject("body").field("type", "text").field("analyzer", "body").endObject() - .startObject("bigram").field("type", "text").field("analyzer", "bigram").endObject() - .startObject("ngram").field("type", "text").field("analyzer", "ngram").endObject() - .endObject() - .endObject().endObject(); - assertAcked(builder.addMapping("type1", mapping)); - ensureGreen(); - - String[] strings = new String[]{ - "Xorr the God-Jewel", - "Grog the God-Crusher", - "Xorn", - "Walter Newell", - "Wanda Maximoff", - "Captain America", - "American Ace", - "Wundarr the Aquarian", - "Will o' the Wisp", - "Xemnu the Titan" - }; - for (String line : strings) { - index("test", "type1", line, "body", line, "bigram", line, "ngram", line); - } - refresh(); - - NumShards numShards = getNumShards("test"); - - // Lets make sure some things throw exceptions - PhraseSuggestionBuilder phraseSuggestion = phraseSuggestion("bigram") - .analyzer("body") - .addCandidateGenerator(candidateGenerator("does_not_exist").minWordLength(1).suggestMode("always")) - .realWordErrorLikelihood(0.95f) - .maxErrors(0.5f) - .size(1); - phraseSuggestion.clearCandidateGenerators().analyzer(null); - try { - searchSuggest("xor the got-jewel", numShards.numPrimaries, Collections.singletonMap("simple_phrase", phraseSuggestion)); - fail("analyzer does only produce ngrams"); - } catch (SearchPhaseExecutionException e) { - } - - phraseSuggestion.analyzer("bigram"); - try { - searchSuggest("xor the got-jewel", numShards.numPrimaries, Collections.singletonMap("simple_phrase", phraseSuggestion)); - fail("analyzer does only produce ngrams"); - } catch (SearchPhaseExecutionException e) { - } - - // Now we'll make sure some things don't - phraseSuggestion.forceUnigrams(false); - searchSuggest( "xor the got-jewel", 0, Collections.singletonMap("simple_phrase", phraseSuggestion)); - - // Field doesn't produce unigrams but the analyzer does - phraseSuggestion.forceUnigrams(true).analyzer("ngram"); - searchSuggest( "xor the got-jewel", 0, Collections.singletonMap("simple_phrase", phraseSuggestion)); - - phraseSuggestion = phraseSuggestion("ngram") - .analyzer("myDefAnalyzer") - .forceUnigrams(true) - .realWordErrorLikelihood(0.95f) - .maxErrors(0.5f) - .size(1) - .addCandidateGenerator(candidateGenerator("body").minWordLength(1).suggestMode("always")); - Suggest suggest = searchSuggest( "xor the got-jewel", 0, Collections.singletonMap("simple_phrase", phraseSuggestion)); - - // "xorr the god jewel" and and "xorn the god jewel" have identical scores (we are only using unigrams to score), so we tie break by - // earlier term (xorn): - assertSuggestion(suggest, 0, "simple_phrase", "xorn the god jewel"); - - phraseSuggestion.analyzer(null); - suggest = searchSuggest( "xor the got-jewel", 0, Collections.singletonMap("simple_phrase", phraseSuggestion)); - - // In this case xorr has a better score than xorn because we set the field back to the default (my_shingle2) analyzer, so the - // probability that the term is not in the dictionary but is NOT a misspelling is relatively high in this case compared to the - // others that have no n-gram with the other terms in the phrase :) you can set this realWorldErrorLikelyhood - assertSuggestion(suggest, 0, "simple_phrase", "xorr the god jewel"); - } - public void testDifferentShardSize() throws Exception { createIndex("test"); ensureGreen(); diff --git a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java index 1261d15ed6544..290b09edc1d6e 100644 --- a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java +++ b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java @@ -21,13 +21,31 @@ import org.apache.lucene.analysis.CharArraySet; import org.apache.lucene.analysis.StopFilter; +import org.apache.lucene.analysis.ar.ArabicNormalizationFilter; +import org.apache.lucene.analysis.ar.ArabicStemFilter; +import org.apache.lucene.analysis.br.BrazilianStemFilter; +import org.apache.lucene.analysis.cjk.CJKBigramFilter; +import org.apache.lucene.analysis.cjk.CJKWidthFilter; +import org.apache.lucene.analysis.ckb.SoraniNormalizationFilter; import org.apache.lucene.analysis.commongrams.CommonGramsFilter; +import org.apache.lucene.analysis.core.DecimalDigitFilter; import org.apache.lucene.analysis.core.StopAnalyzer; import org.apache.lucene.analysis.core.UpperCaseFilter; +import org.apache.lucene.analysis.cz.CzechStemFilter; +import org.apache.lucene.analysis.de.GermanNormalizationFilter; +import org.apache.lucene.analysis.de.GermanStemFilter; import org.apache.lucene.analysis.en.KStemFilter; import org.apache.lucene.analysis.en.PorterStemFilter; +import org.apache.lucene.analysis.fa.PersianNormalizationFilter; +import org.apache.lucene.analysis.fr.FrenchAnalyzer; +import org.apache.lucene.analysis.hi.HindiNormalizationFilter; +import org.apache.lucene.analysis.in.IndicNormalizationFilter; import org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter; +import org.apache.lucene.analysis.miscellaneous.KeywordRepeatFilter; import org.apache.lucene.analysis.miscellaneous.LengthFilter; +import org.apache.lucene.analysis.miscellaneous.LimitTokenCountFilter; +import org.apache.lucene.analysis.miscellaneous.ScandinavianFoldingFilter; +import org.apache.lucene.analysis.miscellaneous.ScandinavianNormalizationFilter; import org.apache.lucene.analysis.miscellaneous.TrimFilter; import org.apache.lucene.analysis.miscellaneous.TruncateTokenFilter; import org.apache.lucene.analysis.miscellaneous.UniqueTokenFilter; @@ -35,16 +53,25 @@ import org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter; import org.apache.lucene.analysis.ngram.EdgeNGramTokenFilter; import org.apache.lucene.analysis.ngram.NGramTokenFilter; +import org.apache.lucene.analysis.payloads.DelimitedPayloadTokenFilter; +import org.apache.lucene.analysis.payloads.TypeAsPayloadTokenFilter; import org.apache.lucene.analysis.reverse.ReverseStringFilter; +import org.apache.lucene.analysis.shingle.ShingleFilter; import org.apache.lucene.analysis.snowball.SnowballFilter; import org.apache.lucene.analysis.standard.ClassicFilter; +import org.apache.lucene.analysis.tr.ApostropheFilter; +import org.apache.lucene.analysis.util.ElisionFilter; import org.elasticsearch.index.analysis.CharFilterFactory; +import org.elasticsearch.index.analysis.DelimitedPayloadTokenFilterFactory; import org.elasticsearch.index.analysis.HtmlStripCharFilterFactory; +import org.elasticsearch.index.analysis.LimitTokenCountFilterFactory; import org.elasticsearch.index.analysis.PreConfiguredTokenFilter; import org.elasticsearch.index.analysis.TokenFilterFactory; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.plugins.Plugin; +import org.tartarus.snowball.ext.DutchStemmer; +import org.tartarus.snowball.ext.FrenchStemmer; import java.util.ArrayList; import java.util.List; @@ -74,29 +101,61 @@ public Map> getCharFilters() { @Override public List getPreConfiguredTokenFilters() { List filters = new ArrayList<>(); - filters.add(PreConfiguredTokenFilter.singleton("asciifolding", true, input -> new ASCIIFoldingFilter(input))); + filters.add(PreConfiguredTokenFilter.singleton("apostrophe", false, ApostropheFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("arabic_normalization", true, ArabicNormalizationFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("arabic_stem", false, ArabicStemFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("asciifolding", true, ASCIIFoldingFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("brazilian_stem", false, BrazilianStemFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("cjk_bigram", false, CJKBigramFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("cjk_width", true, CJKWidthFilter::new)); filters.add(PreConfiguredTokenFilter.singleton("classic", false, ClassicFilter::new)); filters.add(PreConfiguredTokenFilter.singleton("common_grams", false, input -> new CommonGramsFilter(input, CharArraySet.EMPTY_SET))); + filters.add(PreConfiguredTokenFilter.singleton("czech_stem", false, CzechStemFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("decimal_digit", true, DecimalDigitFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("delimited_payload_filter", false, input -> + new DelimitedPayloadTokenFilter(input, + DelimitedPayloadTokenFilterFactory.DEFAULT_DELIMITER, + DelimitedPayloadTokenFilterFactory.DEFAULT_ENCODER))); + filters.add(PreConfiguredTokenFilter.singleton("dutch_stem", false, input -> new SnowballFilter(input, new DutchStemmer()))); filters.add(PreConfiguredTokenFilter.singleton("edge_ngram", false, input -> new EdgeNGramTokenFilter(input, EdgeNGramTokenFilter.DEFAULT_MIN_GRAM_SIZE, EdgeNGramTokenFilter.DEFAULT_MAX_GRAM_SIZE))); // TODO deprecate edgeNGram filters.add(PreConfiguredTokenFilter.singleton("edgeNGram", false, input -> new EdgeNGramTokenFilter(input, EdgeNGramTokenFilter.DEFAULT_MIN_GRAM_SIZE, EdgeNGramTokenFilter.DEFAULT_MAX_GRAM_SIZE))); + filters.add(PreConfiguredTokenFilter.singleton("elision", true, + input -> new ElisionFilter(input, FrenchAnalyzer.DEFAULT_ARTICLES))); + filters.add(PreConfiguredTokenFilter.singleton("french_stem", false, input -> new SnowballFilter(input, new FrenchStemmer()))); + filters.add(PreConfiguredTokenFilter.singleton("german_normalization", true, GermanNormalizationFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("german_stem", false, GermanStemFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("hindi_normalization", true, HindiNormalizationFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("indic_normalization", true, IndicNormalizationFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("keyword_repeat", false, KeywordRepeatFilter::new)); filters.add(PreConfiguredTokenFilter.singleton("kstem", false, KStemFilter::new)); filters.add(PreConfiguredTokenFilter.singleton("length", false, input -> new LengthFilter(input, 0, Integer.MAX_VALUE))); // TODO this one seems useless + filters.add(PreConfiguredTokenFilter.singleton("limit", false, input -> + new LimitTokenCountFilter(input, + LimitTokenCountFilterFactory.DEFAULT_MAX_TOKEN_COUNT, + LimitTokenCountFilterFactory.DEFAULT_CONSUME_ALL_TOKENS))); filters.add(PreConfiguredTokenFilter.singleton("ngram", false, NGramTokenFilter::new)); // TODO deprecate nGram filters.add(PreConfiguredTokenFilter.singleton("nGram", false, NGramTokenFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("persian_normalization", true, PersianNormalizationFilter::new)); filters.add(PreConfiguredTokenFilter.singleton("porter_stem", false, PorterStemFilter::new)); filters.add(PreConfiguredTokenFilter.singleton("reverse", false, input -> new ReverseStringFilter(input))); + filters.add(PreConfiguredTokenFilter.singleton("russian_stem", false, input -> new SnowballFilter(input, "Russian"))); + filters.add(PreConfiguredTokenFilter.singleton("scandinavian_folding", true, ScandinavianFoldingFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("scandinavian_normalization", true, ScandinavianNormalizationFilter::new)); + filters.add(PreConfiguredTokenFilter.singleton("shingle", false, ShingleFilter::new)); filters.add(PreConfiguredTokenFilter.singleton("snowball", false, input -> new SnowballFilter(input, "English"))); + filters.add(PreConfiguredTokenFilter.singleton("sorani_normalization", true, SoraniNormalizationFilter::new)); filters.add(PreConfiguredTokenFilter.singleton("stemmer", false, PorterStemFilter::new)); // The stop filter is in lucene-core but the English stop words set is in lucene-analyzers-common filters.add(PreConfiguredTokenFilter.singleton("stop", false, input -> new StopFilter(input, StopAnalyzer.ENGLISH_STOP_WORDS_SET))); filters.add(PreConfiguredTokenFilter.singleton("trim", false, TrimFilter::new)); filters.add(PreConfiguredTokenFilter.singleton("truncate", false, input -> new TruncateTokenFilter(input, 10))); + filters.add(PreConfiguredTokenFilter.singleton("type_as_payload", false, TypeAsPayloadTokenFilter::new)); filters.add(PreConfiguredTokenFilter.singleton("unique", false, input -> new UniqueTokenFilter(input))); filters.add(PreConfiguredTokenFilter.singleton("uppercase", true, UpperCaseFilter::new)); filters.add(PreConfiguredTokenFilter.singleton("word_delimiter", false, input -> diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java index cf78f6646a2e8..d250540645703 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisFactoryTests.java @@ -20,6 +20,8 @@ package org.elasticsearch.analysis.common; import org.apache.lucene.analysis.en.PorterStemFilterFactory; +import org.apache.lucene.analysis.miscellaneous.LimitTokenCountFilterFactory; +import org.apache.lucene.analysis.payloads.DelimitedPayloadTokenFilterFactory; import org.apache.lucene.analysis.reverse.ReverseStringFilterFactory; import org.apache.lucene.analysis.snowball.SnowballPorterFilterFactory; import org.elasticsearch.index.analysis.HtmlStripCharFilterFactory; @@ -68,22 +70,46 @@ protected Map> getCharFilters() { @Override protected Map> getPreConfiguredTokenFilters() { Map> filters = new TreeMap<>(super.getPreConfiguredTokenFilters()); + filters.put("apostrophe", null); + filters.put("arabic_normalization", null); + filters.put("arabic_stem", null); filters.put("asciifolding", null); + filters.put("brazilian_stem", null); + filters.put("cjk_bigram", null); + filters.put("cjk_width", null); filters.put("classic", null); filters.put("common_grams", null); + filters.put("czech_stem", null); + filters.put("decimal_digit", null); + filters.put("delimited_payload_filter", DelimitedPayloadTokenFilterFactory.class); + filters.put("dutch_stem", SnowballPorterFilterFactory.class); filters.put("edge_ngram", null); filters.put("edgeNGram", null); + filters.put("elision", null); + filters.put("french_stem", SnowballPorterFilterFactory.class); + filters.put("german_stem", null); + filters.put("hindi_normalization", null); + filters.put("indic_normalization", null); + filters.put("keyword_repeat", null); filters.put("kstem", null); filters.put("length", null); + filters.put("limit", LimitTokenCountFilterFactory.class); filters.put("ngram", null); filters.put("nGram", null); + filters.put("persian_normalization", null); filters.put("porter_stem", null); filters.put("reverse", ReverseStringFilterFactory.class); + filters.put("russian_stem", SnowballPorterFilterFactory.class); + filters.put("scandinavian_normalization", null); + filters.put("scandinavian_folding", null); + filters.put("shingle", null); filters.put("snowball", SnowballPorterFilterFactory.class); + filters.put("sorani_normalization", null); filters.put("stemmer", PorterStemFilterFactory.class); filters.put("stop", null); filters.put("trim", null); filters.put("truncate", null); + filters.put("type_as_payload", null); filters.put("unique", Void.class); filters.put("uppercase", null); filters.put("word_delimiter", null); diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/search.query/10_match.yml b/modules/analysis-common/src/test/resources/rest-api-spec/test/search.query/10_match.yml new file mode 100644 index 0000000000000..d07e06865a1e9 --- /dev/null +++ b/modules/analysis-common/src/test/resources/rest-api-spec/test/search.query/10_match.yml @@ -0,0 +1,65 @@ +# integration tests for queries with specific analysis chains + +"match query with stacked stems": + # Tests the match query stemmed tokens are "stacked" on top of the unstemmed + # versions in the same position. + - do: + indices.create: + index: test + body: + settings: + number_of_shards: 1 + number_of_replicas: 1 + analysis: + analyzer: + index: + tokenizer: standard + filter: [lowercase] + search: + tokenizer: standard + filter: [lowercase, keyword_repeat, porter_stem, unique_stem] + filter: + unique_stem: + type: unique + only_on_same_position: true + mappings: + doc: + properties: + body: + type: text + analyzer: index + search_analyzer: search + + - do: + index: + index: test + type: doc + id: 1 + body: { "text": "the fox runs across the street" } + refresh: true + + - do: + search: + body: + query: + match: + text: fox runs + operator: AND + - match: {hits.count: 1} + + - do: + index: + index: test + type: doc + id: 2 + body: { "text": "run fox run" } + refresh: true + + - do: + search: + body: + query: + match: + text: fox runs + operator: AND + - match: {hits.count: 2} diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/search.suggest/20_phrase.yaml b/modules/analysis-common/src/test/resources/rest-api-spec/test/search.suggest/20_phrase.yaml new file mode 100644 index 0000000000000..cf5ebcea42edb --- /dev/null +++ b/modules/analysis-common/src/test/resources/rest-api-spec/test/search.suggest/20_phrase.yaml @@ -0,0 +1,158 @@ +# Integration tests for the phrase suggester with a few analyzers + +setup: + - do: + indices.create: + index: test + body: + settings: + number_of_shards: 1 + number_of_replicas: 1 + analysis: + analyzer: + body: + tokenizer: standard + filter: [lowercase] + bigram: + tokenizer: standard + filter: [lowercase, bigram] + ngram: + tokenizer: standard + filter: [lowercase, ngram] + filter: + bigram: + type: shingle + output_unigrams: false + min_shingle_size: 2 + max_shingle_size: 2 + ngram: + type: shingle + output_unigrams: true + min_shingle_size: 2 + max_shingle_size: 2 + mappings: + doc: + properties: + body: + type: text + analyzer: body + fields: + bigram: + type: text + analyzer: bigram + ngram: + type: text + analyzer: ngram + + - do: + bulk: + index: test + type: doc + refresh: true + body: | + { "index": {} } + { "body": "Xorr the God-Jewel" } + { "index": {} } + { "body": "Xorn" } + +--- +"sorts by score": + - do: + search: + size: 0 + index: test + body: + suggest: + text: xor the got-jewel + test: + phrase: + field: body.ngram + force_unigrams: true + max_errors: 0.5 + direct_generator: + - field: body.ngram + min_word_length: 1 + suggest_mode: always + + - match: {suggest.test.0.options.0.text: xorr the god jewel} + - match: {suggest.test.0.options.1.text: xorn the god jewel} + +--- +"breaks ties by sorting terms": + # This runs the suggester without bigrams so we can be sure of the sort order + - do: + search: + size: 0 + index: test + body: + suggest: + text: xor the got-jewel + test: + phrase: + field: body + analyzer: body + force_unigrams: true + max_errors: 0.5 + direct_generator: + - field: body + min_word_length: 1 + suggest_mode: always + + # The scores are identical but xorn comes first because it sorts first + - match: {suggest.test.0.options.0.text: xorn the god jewel} + - match: {suggest.test.0.options.1.text: xorr the god jewel} + - match: {suggest.test.0.options.0.score: $body.suggest.test.0.options.0.score} + +--- +"fails when asked to run on a field without unigrams": + - do: + catch: /since it doesn't emit unigrams/ + search: + size: 0 + index: test + body: + suggest: + text: xor the got-jewel + test: + phrase: + field: body.bigram + + - do: + catch: /since it doesn't emit unigrams/ + search: + size: 0 + index: test + body: + suggest: + text: xor the got-jewel + test: + phrase: + field: body.bigram + analyzer: bigram + +--- +"doesn't fail when asked to run on a field without unigrams when force_unigrams=false": + - do: + search: + size: 0 + index: test + body: + suggest: + text: xor the got-jewel + test: + phrase: + field: body.bigram + force_unigrams: false + + - do: + search: + size: 0 + index: test + body: + suggest: + text: xor the got-jewel + test: + phrase: + field: body.bigram + analyzer: bigram + force_unigrams: false diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/termvectors/10_payloads.yml b/modules/analysis-common/src/test/resources/rest-api-spec/test/termvectors/10_payloads.yml new file mode 100644 index 0000000000000..d0e31758340c3 --- /dev/null +++ b/modules/analysis-common/src/test/resources/rest-api-spec/test/termvectors/10_payloads.yml @@ -0,0 +1,40 @@ +"term vectors with payloads tests": + # Tests term vectors with payloads. This is in the analysis-common module + # because there are no token filters that support payloads in core. + - do: + indices.create: + index: test + body: + mappings: + doc: + properties: + text: + type: text + term_vector: with_positions_offsets_payloads + analyzer: has_payloads + settings: + number_of_shards: 1 + number_of_replicas: 1 + analysis: + analyzer: + had_payloads: + tokenizer: standard + filter: [type_as_payload] + + - do: + index: + index: test + type: doc + id: 1 + refresh: true + body: + text: The quick brown fox is brown. + + - do: + termvectors: + index: test + type: doc + id: 1 + payloads: true + - match: {term_vectors.text.field_statistics.sum_doc_freq: 5} + - match: {term_vectors.text.terms.brown.tokens.0.payload: 10} diff --git a/test/framework/src/main/java/org/elasticsearch/indices/analysis/AnalysisFactoryTestCase.java b/test/framework/src/main/java/org/elasticsearch/indices/analysis/AnalysisFactoryTestCase.java index fe22734d97408..040d2fb2dc6f3 100644 --- a/test/framework/src/main/java/org/elasticsearch/indices/analysis/AnalysisFactoryTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/indices/analysis/AnalysisFactoryTestCase.java @@ -19,8 +19,6 @@ package org.elasticsearch.indices.analysis; -import org.apache.lucene.analysis.en.PorterStemFilterFactory; -import org.apache.lucene.analysis.snowball.SnowballPorterFilterFactory; import org.apache.lucene.analysis.util.CharFilterFactory; import org.apache.lucene.analysis.util.TokenFilterFactory; import org.apache.lucene.analysis.util.TokenizerFactory; @@ -97,7 +95,6 @@ import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; -import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -343,29 +340,6 @@ protected Map> getPreConfiguredTokenFilters() { Map> filters = new HashMap<>(); filters.put("standard", null); filters.put("lowercase", null); - // TODO remove the loop below once all the tokenizers are migrated out of PreBuiltTokenFilters - for (PreBuiltTokenFilters tokenizer : PreBuiltTokenFilters.values()) { - Class luceneFactoryClass; - switch (tokenizer) { - case LOWERCASE: - // This has been migrated but has to stick around until PreBuiltTokenizers is removed. - continue; - case DUTCH_STEM: - case FRENCH_STEM: - case RUSSIAN_STEM: - luceneFactoryClass = SnowballPorterFilterFactory.class; - break; - case DELIMITED_PAYLOAD_FILTER: - luceneFactoryClass = org.apache.lucene.analysis.payloads.DelimitedPayloadTokenFilterFactory.class; - break; - case LIMIT: - luceneFactoryClass = org.apache.lucene.analysis.miscellaneous.LimitTokenCountFilterFactory.class; - break; - default: - luceneFactoryClass = null; - } - filters.put(tokenizer.name().toLowerCase(Locale.ROOT), luceneFactoryClass); - } return filters; } From d09e64323f14d6dc5e64cb45b3fa71a1b2229a2f Mon Sep 17 00:00:00 2001 From: Lee Hinman Date: Mon, 20 Mar 2017 11:07:32 -0600 Subject: [PATCH 378/619] Add ability to automatically adjust search threadpool queue_size This PR adds a new thread pool type: `fixed_auto_queue_size`. This thread pool behaves like a regular `fixed` threadpool, except that every `auto_queue_frame_size` operations (default: 10,000) in the thread pool, [Little's Law](https://en.wikipedia.org/wiki/Little's_law) is calculated and used to adjust the pool's `queue_size` either up or down by 50. A minimum and maximum is taken into account also. When the min and max are the same value, a regular fixed executor is used instead. The `SEARCH` threadpool is changed to use this new type of thread pool. However, the min and max are both set to 1000, meaning auto adjustment is opt-in rather than opt-out. Resolves #3890 --- .../common/util/concurrent/EsExecutors.java | 28 +++ .../util/concurrent/EsThreadPoolExecutor.java | 2 +- .../QueueResizingEsThreadPoolExecutor.java | 234 ++++++++++++++++++ .../concurrent/ResizableBlockingQueue.java | 80 ++++++ .../util/concurrent/SizeBlockingQueue.java | 4 +- .../common/util/concurrent/TimedRunnable.java | 56 +++++ .../AutoQueueAdjustingExecutorBuilder.java | 166 +++++++++++++ .../threadpool/ExecutorBuilder.java | 9 + .../threadpool/FixedExecutorBuilder.java | 8 - .../elasticsearch/threadpool/ThreadPool.java | 15 +- ...ueueResizingEsThreadPoolExecutorTests.java | 226 +++++++++++++++++ .../ResizableBlockingQueueTests.java | 52 ++++ ...utoQueueAdjustingExecutorBuilderTests.java | 41 +++ docs/reference/modules/threadpool.asciidoc | 51 +++- 14 files changed, 955 insertions(+), 17 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutor.java create mode 100644 core/src/main/java/org/elasticsearch/common/util/concurrent/ResizableBlockingQueue.java create mode 100644 core/src/main/java/org/elasticsearch/common/util/concurrent/TimedRunnable.java create mode 100644 core/src/main/java/org/elasticsearch/threadpool/AutoQueueAdjustingExecutorBuilder.java create mode 100644 core/src/test/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutorTests.java create mode 100644 core/src/test/java/org/elasticsearch/common/util/concurrent/ResizableBlockingQueueTests.java create mode 100644 core/src/test/java/org/elasticsearch/threadpool/AutoQueueAdjustingExecutorBuilderTests.java diff --git a/core/src/main/java/org/elasticsearch/common/util/concurrent/EsExecutors.java b/core/src/main/java/org/elasticsearch/common/util/concurrent/EsExecutors.java index 3a5e3b4dab465..b37a6e14f02b3 100644 --- a/core/src/main/java/org/elasticsearch/common/util/concurrent/EsExecutors.java +++ b/core/src/main/java/org/elasticsearch/common/util/concurrent/EsExecutors.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.node.Node; import java.util.Arrays; @@ -79,6 +80,33 @@ public static EsThreadPoolExecutor newFixed(String name, int size, int queueCapa return new EsThreadPoolExecutor(name, size, size, 0, TimeUnit.MILLISECONDS, queue, threadFactory, new EsAbortPolicy(), contextHolder); } + /** + * Return a new executor that will automatically adjust the queue size based on queue throughput. + * + * @param size number of fixed threads to use for executing tasks + * @param initialQueueCapacity initial size of the executor queue + * @param minQueueSize minimum queue size that the queue can be adjusted to + * @param maxQueueSize maximum queue size that the queue can be adjusted to + * @param frameSize number of tasks during which stats are collected before adjusting queue size + */ + public static EsThreadPoolExecutor newAutoQueueFixed(String name, int size, int initialQueueCapacity, int minQueueSize, + int maxQueueSize, int frameSize, TimeValue targetedResponseTime, + ThreadFactory threadFactory, ThreadContext contextHolder) { + if (initialQueueCapacity == minQueueSize && initialQueueCapacity == maxQueueSize) { + return newFixed(name, size, initialQueueCapacity, threadFactory, contextHolder); + } + + if (initialQueueCapacity <= 0) { + throw new IllegalArgumentException("initial queue capacity for [" + name + "] executor must be positive, got: " + + initialQueueCapacity); + } + ResizableBlockingQueue queue = + new ResizableBlockingQueue<>(ConcurrentCollections.newBlockingQueue(), initialQueueCapacity); + return new QueueResizingEsThreadPoolExecutor(name, size, size, 0, TimeUnit.MILLISECONDS, + queue, minQueueSize, maxQueueSize, TimedRunnable::new, frameSize, targetedResponseTime, threadFactory, + new EsAbortPolicy(), contextHolder); + } + private static final ExecutorService DIRECT_EXECUTOR_SERVICE = new AbstractExecutorService() { @Override diff --git a/core/src/main/java/org/elasticsearch/common/util/concurrent/EsThreadPoolExecutor.java b/core/src/main/java/org/elasticsearch/common/util/concurrent/EsThreadPoolExecutor.java index 9662292cf69f6..a1ac182b8dca8 100644 --- a/core/src/main/java/org/elasticsearch/common/util/concurrent/EsThreadPoolExecutor.java +++ b/core/src/main/java/org/elasticsearch/common/util/concurrent/EsThreadPoolExecutor.java @@ -37,7 +37,7 @@ public class EsThreadPoolExecutor extends ThreadPoolExecutor { /** * Name used in error reporting. */ - private final String name; + protected final String name; EsThreadPoolExecutor(String name, int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, ThreadContext contextHolder) { diff --git a/core/src/main/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutor.java b/core/src/main/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutor.java new file mode 100644 index 0000000000000..854dc86231562 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutor.java @@ -0,0 +1,234 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.common.util.concurrent; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.logging.ESLoggerFactory; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.util.concurrent.ResizableBlockingQueue; + +import java.util.Locale; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +/** + * An extension to thread pool executor, which automatically adjusts the queue size of the + * {@code ResizableBlockingQueue} according to Little's Law. + */ +public final class QueueResizingEsThreadPoolExecutor extends EsThreadPoolExecutor { + + private static final Logger logger = + ESLoggerFactory.getLogger(QueueResizingEsThreadPoolExecutor.class); + + private final Function runnableWrapper; + private final ResizableBlockingQueue workQueue; + private final int tasksPerFrame; + private final int minQueueSize; + private final int maxQueueSize; + private final long targetedResponseTimeNanos; + // The amount the queue size is adjusted by for each calcuation + private static final int QUEUE_ADJUSTMENT_AMOUNT = 50; + + private final AtomicLong totalTaskNanos = new AtomicLong(0); + private final AtomicInteger taskCount = new AtomicInteger(0); + + private long startNs; + + QueueResizingEsThreadPoolExecutor(String name, int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + ResizableBlockingQueue workQueue, int minQueueSize, int maxQueueSize, + Function runnableWrapper, final int tasksPerFrame, + TimeValue targetedResponseTime, ThreadFactory threadFactory, XRejectedExecutionHandler handler, + ThreadContext contextHolder) { + super(name, corePoolSize, maximumPoolSize, keepAliveTime, unit, + workQueue, threadFactory, handler, contextHolder); + this.runnableWrapper = runnableWrapper; + this.workQueue = workQueue; + this.tasksPerFrame = tasksPerFrame; + this.startNs = System.nanoTime(); + this.minQueueSize = minQueueSize; + this.maxQueueSize = maxQueueSize; + this.targetedResponseTimeNanos = targetedResponseTime.getNanos(); + logger.debug("thread pool [{}] will adjust queue by [{}] when determining automatic queue size", + name, QUEUE_ADJUSTMENT_AMOUNT); + } + + @Override + protected void doExecute(final Runnable command) { + // we are submitting a task, it has not yet started running (because super.excute() has not + // been called), but it could be immediately run, or run at a later time. We need the time + // this task entered the queue, which we get by creating a TimedRunnable, which starts the + // clock as soon as it is created. + super.doExecute(this.runnableWrapper.apply(command)); + } + + /** + * Calculate task rate (λ), for a fixed number of tasks and time it took those tasks to be measured + * + * @param totalNumberOfTasks total number of tasks that were measured + * @param totalFrameFrameNanos nanoseconds during which the tasks were received + * @return the rate of tasks in the system + */ + static double calculateLambda(final int totalNumberOfTasks, final long totalFrameFrameNanos) { + assert totalFrameFrameNanos > 0 : "cannot calculate for instantaneous tasks"; + assert totalNumberOfTasks > 0 : "cannot calculate for no tasks"; + // There is no set execution time, instead we adjust the time window based on the + // number of completed tasks, so there is no background thread required to update the + // queue size at a regular interval. This means we need to calculate our λ by the + // total runtime, rather than a fixed interval. + + // λ = total tasks divided by measurement time + return (double) totalNumberOfTasks / totalFrameFrameNanos; + } + + /** + * Calculate Little's Law (L), which is the "optimal" queue size for a particular task rate (lambda) and targeted response time. + * + * @param lambda the arrival rate of tasks in nanoseconds + * @param targetedResponseTimeNanos nanoseconds for the average targeted response rate of requests + * @return the optimal queue size for the give task rate and targeted response time + */ + static int calculateL(final double lambda, final long targetedResponseTimeNanos) { + assert targetedResponseTimeNanos > 0 : "cannot calculate for instantaneous requests"; + // L = λ * W + return Math.toIntExact((long)(lambda * targetedResponseTimeNanos)); + } + + /** + * Returns the current queue capacity + */ + public int getCurrentCapacity() { + return workQueue.capacity(); + } + + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + // A task has been completed, it has left the building. We should now be able to get the + // total time as a combination of the time in the queue and time spent running the task. We + // only want runnables that did not throw errors though, because they could be fast-failures + // that throw off our timings, so only check when t is null. + assert r instanceof TimedRunnable : "expected only TimedRunnables in queue"; + final long taskNanos = ((TimedRunnable) r).getTotalNanos(); + final long totalNanos = totalTaskNanos.addAndGet(taskNanos); + if (taskCount.incrementAndGet() == this.tasksPerFrame) { + final long endTimeNs = System.nanoTime(); + final long totalRuntime = endTimeNs - this.startNs; + // Reset the start time for all tasks. At first glance this appears to need to be + // volatile, since we are reading from a different thread when it is set, but it + // is protected by the taskCount memory barrier. + // See: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html + startNs = endTimeNs; + + // Calculate the new desired queue size + try { + final double lambda = calculateLambda(tasksPerFrame, totalNanos); + final int desiredQueueSize = calculateL(lambda, targetedResponseTimeNanos); + if (logger.isDebugEnabled()) { + final long avgTaskTime = totalNanos / tasksPerFrame; + logger.debug("[{}]: there were [{}] tasks in [{}], avg task time: [{}], [{} tasks/s], " + + "optimal queue is [{}]", + name, + tasksPerFrame, + TimeValue.timeValueNanos(totalRuntime), + TimeValue.timeValueNanos(avgTaskTime), + String.format(Locale.ROOT, "%.2f", lambda * TimeValue.timeValueSeconds(1).nanos()), + desiredQueueSize); + } + + final int oldCapacity = workQueue.capacity(); + + // Adjust the queue size towards the desired capacity using an adjust of + // QUEUE_ADJUSTMENT_AMOUNT (either up or down), keeping in mind the min and max + // values the queue size can have. + final int newCapacity = + workQueue.adjustCapacity(desiredQueueSize, QUEUE_ADJUSTMENT_AMOUNT, minQueueSize, maxQueueSize); + if (oldCapacity != newCapacity && logger.isDebugEnabled()) { + logger.debug("adjusted [{}] queue size by [{}], old capacity: [{}], new capacity: [{}]", name, + newCapacity > oldCapacity ? QUEUE_ADJUSTMENT_AMOUNT : -QUEUE_ADJUSTMENT_AMOUNT, + oldCapacity, newCapacity); + } + } catch (ArithmeticException e) { + // There was an integer overflow, so just log about it, rather than adjust the queue size + logger.warn((Supplier) () -> new ParameterizedMessage( + "failed to calculate optimal queue size for [{}] thread pool, " + + "total frame time [{}ns], tasks [{}], task execution time [{}ns]", + name, totalRuntime, tasksPerFrame, totalNanos), + e); + } finally { + // Finally, decrement the task count and time back to their starting values. We + // do this at the end so there is no concurrent adjustments happening. We also + // decrement them instead of resetting them back to zero, as resetting them back + // to zero causes operations that came in during the adjustment to be uncounted + int tasks = taskCount.addAndGet(-this.tasksPerFrame); + assert tasks >= 0 : "tasks should never be negative, got: " + tasks; + + if (tasks >= this.tasksPerFrame) { + // Start over, because we can potentially reach a "never adjusting" state, + // + // consider the following: + // - If the frame window is 10, and there are 10 tasks, then an adjustment will begin. (taskCount == 10) + // - Prior to the adjustment being done, 15 more tasks come in, the taskCount is now 25 + // - Adjustment happens and we decrement the tasks by 10, taskCount is now 15 + // - Since taskCount will now be incremented forever, it will never be 10 again, + // so there will be no further adjustments + logger.debug("[{}]: too many incoming tasks while queue size adjustment occurs, resetting measurements to 0", name); + totalTaskNanos.getAndSet(0); + taskCount.getAndSet(0); + startNs = System.nanoTime(); + } else { + // Do a regular adjustment + totalTaskNanos.addAndGet(-totalNanos); + } + } + } + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append(getClass().getSimpleName()).append('['); + b.append(name).append(", "); + + @SuppressWarnings("rawtypes") + ResizableBlockingQueue queue = (ResizableBlockingQueue) getQueue(); + + b.append("queue capacity = ").append(getCurrentCapacity()).append(", "); + b.append("min queue capacity = ").append(minQueueSize).append(", "); + b.append("max queue capacity = ").append(maxQueueSize).append(", "); + b.append("frame size = ").append(tasksPerFrame).append(", "); + b.append("targeted response rate = ").append(TimeValue.timeValueNanos(targetedResponseTimeNanos)).append(", "); + b.append("adjustment amount = ").append(QUEUE_ADJUSTMENT_AMOUNT).append(", "); + /* + * ThreadPoolExecutor has some nice information in its toString but we + * can't get at it easily without just getting the toString. + */ + b.append(super.toString()).append(']'); + return b.toString(); + } +} diff --git a/core/src/main/java/org/elasticsearch/common/util/concurrent/ResizableBlockingQueue.java b/core/src/main/java/org/elasticsearch/common/util/concurrent/ResizableBlockingQueue.java new file mode 100644 index 0000000000000..ca6f6030bb00d --- /dev/null +++ b/core/src/main/java/org/elasticsearch/common/util/concurrent/ResizableBlockingQueue.java @@ -0,0 +1,80 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.common.util.concurrent; + +import java.util.concurrent.BlockingQueue; +import org.elasticsearch.common.SuppressForbidden; + +/** + * Extends the {@code SizeBlockingQueue} to add the {@code adjustCapacity} method, which will adjust + * the capacity by a certain amount towards a maximum or minimum. + */ +final class ResizableBlockingQueue extends SizeBlockingQueue { + + private volatile int capacity; + + ResizableBlockingQueue(BlockingQueue queue, int initialCapacity) { + super(queue, initialCapacity); + this.capacity = initialCapacity; + } + + @SuppressForbidden(reason = "optimalCapacity is non-negative, therefore the difference cannot be < -Integer.MAX_VALUE") + private int getChangeAmount(int optimalCapacity) { + assert optimalCapacity >= 0 : "optimal capacity should always be positive, got: " + optimalCapacity; + return Math.abs(optimalCapacity - this.capacity); + } + + @Override + public int capacity() { + return this.capacity; + } + + @Override + public int remainingCapacity() { + return Math.max(0, this.capacity()); + } + + /** Resize the limit for the queue, returning the new size limit */ + public synchronized int adjustCapacity(int optimalCapacity, int adjustmentAmount, int minCapacity, int maxCapacity) { + assert adjustmentAmount > 0 : "adjustment amount should be a positive value"; + assert optimalCapacity >= 0 : "desired capacity cannot be negative"; + assert minCapacity >= 0 : "cannot have min capacity smaller than 0"; + assert maxCapacity >= minCapacity : "cannot have max capacity smaller than min capacity"; + + if (optimalCapacity == capacity) { + // Yahtzee! + return this.capacity; + } + + if (optimalCapacity > capacity + adjustmentAmount) { + // adjust up + final int newCapacity = Math.min(maxCapacity, capacity + adjustmentAmount); + this.capacity = newCapacity; + return newCapacity; + } else if (optimalCapacity < capacity - adjustmentAmount) { + // adjust down + final int newCapacity = Math.max(minCapacity, capacity - adjustmentAmount); + this.capacity = newCapacity; + return newCapacity; + } else { + return this.capacity; + } + } +} diff --git a/core/src/main/java/org/elasticsearch/common/util/concurrent/SizeBlockingQueue.java b/core/src/main/java/org/elasticsearch/common/util/concurrent/SizeBlockingQueue.java index bff4ee613e16e..c4142152ccf43 100644 --- a/core/src/main/java/org/elasticsearch/common/util/concurrent/SizeBlockingQueue.java +++ b/core/src/main/java/org/elasticsearch/common/util/concurrent/SizeBlockingQueue.java @@ -131,7 +131,7 @@ public void forcePut(E e) throws InterruptedException { @Override public boolean offer(E e) { int count = size.incrementAndGet(); - if (count > capacity) { + if (count > capacity()) { size.decrementAndGet(); return false; } @@ -168,7 +168,7 @@ public E take() throws InterruptedException { @Override public int remainingCapacity() { - return capacity - size.get(); + return capacity() - size.get(); } @Override diff --git a/core/src/main/java/org/elasticsearch/common/util/concurrent/TimedRunnable.java b/core/src/main/java/org/elasticsearch/common/util/concurrent/TimedRunnable.java new file mode 100644 index 0000000000000..91ad6e46efa7b --- /dev/null +++ b/core/src/main/java/org/elasticsearch/common/util/concurrent/TimedRunnable.java @@ -0,0 +1,56 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.common.util.concurrent; + +/** + * A class used to wrap a {@code Runnable} that allows capturing the time the task since creation + * through execution. + */ +class TimedRunnable implements Runnable { + private final Runnable original; + private final long creationTimeNanos; + private long finishTimeNanos = -1; + + TimedRunnable(Runnable original) { + this.original = original; + this.creationTimeNanos = System.nanoTime(); + } + + @Override + public void run() { + try { + original.run(); + } finally { + finishTimeNanos = System.nanoTime(); + } + } + + /** + * Return the time since this task was created until it finished running. + * If the task is still running or has not yet been run, returns -1. + */ + long getTotalNanos() { + if (finishTimeNanos == -1) { + // There must have been an exception thrown, the total time is unknown (-1) + return -1; + } + return finishTimeNanos - creationTimeNanos; + } +} diff --git a/core/src/main/java/org/elasticsearch/threadpool/AutoQueueAdjustingExecutorBuilder.java b/core/src/main/java/org/elasticsearch/threadpool/AutoQueueAdjustingExecutorBuilder.java new file mode 100644 index 0000000000000..265e544d281cd --- /dev/null +++ b/core/src/main/java/org/elasticsearch/threadpool/AutoQueueAdjustingExecutorBuilder.java @@ -0,0 +1,166 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.threadpool; + +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.SizeValue; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.util.concurrent.EsExecutors; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.node.Node; +import org.elasticsearch.threadpool.ExecutorBuilder; +import org.elasticsearch.common.util.concurrent.QueueResizingEsThreadPoolExecutor; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; + +/** + * A builder for executors that automatically adjust the queue length as needed, depending on + * Little's Law. See https://en.wikipedia.org/wiki/Little's_law for more information. + */ +public final class AutoQueueAdjustingExecutorBuilder extends ExecutorBuilder { + + private final Setting sizeSetting; + private final Setting queueSizeSetting; + private final Setting minQueueSizeSetting; + private final Setting maxQueueSizeSetting; + private final Setting targetedResponseTimeSetting; + private final Setting frameSizeSetting; + + AutoQueueAdjustingExecutorBuilder(final Settings settings, final String name, final int size, + final int initialQueueSize, final int minQueueSize, + final int maxQueueSize, final int frameSize) { + super(name); + final String prefix = "thread_pool." + name; + final String sizeKey = settingsKey(prefix, "size"); + this.sizeSetting = + new Setting<>( + sizeKey, + s -> Integer.toString(size), + s -> Setting.parseInt(s, 1, applyHardSizeLimit(settings, name), sizeKey), + Setting.Property.NodeScope); + final String queueSizeKey = settingsKey(prefix, "queue_size"); + final String minSizeKey = settingsKey(prefix, "min_queue_size"); + final String maxSizeKey = settingsKey(prefix, "max_queue_size"); + final String frameSizeKey = settingsKey(prefix, "auto_queue_frame_size"); + final String targetedResponseTimeKey = settingsKey(prefix, "target_response_time"); + this.targetedResponseTimeSetting = Setting.timeSetting(targetedResponseTimeKey, TimeValue.timeValueSeconds(1), + TimeValue.timeValueMillis(10), Setting.Property.NodeScope); + this.queueSizeSetting = Setting.intSetting(queueSizeKey, initialQueueSize, Setting.Property.NodeScope); + // These temp settings are used to validate the min and max settings below + Setting tempMaxQueueSizeSetting = Setting.intSetting(maxSizeKey, maxQueueSize, Setting.Property.NodeScope); + Setting tempMinQueueSizeSetting = Setting.intSetting(minSizeKey, minQueueSize, Setting.Property.NodeScope); + + this.minQueueSizeSetting = new Setting<>( + minSizeKey, + (s) -> Integer.toString(minQueueSize), + (s) -> Setting.parseInt(s, 0, tempMaxQueueSizeSetting.get(settings), minSizeKey), + Setting.Property.NodeScope); + this.maxQueueSizeSetting = new Setting<>( + maxSizeKey, + (s) -> Integer.toString(maxQueueSize), + (s) -> Setting.parseInt(s, tempMinQueueSizeSetting.get(settings), Integer.MAX_VALUE, maxSizeKey), + Setting.Property.NodeScope); + this.frameSizeSetting = Setting.intSetting(frameSizeKey, frameSize, 100, Setting.Property.NodeScope); + } + + @Override + public List> getRegisteredSettings() { + return Arrays.asList(sizeSetting, queueSizeSetting, minQueueSizeSetting, + maxQueueSizeSetting, frameSizeSetting, targetedResponseTimeSetting); + } + + @Override + AutoExecutorSettings getSettings(Settings settings) { + final String nodeName = Node.NODE_NAME_SETTING.get(settings); + final int size = sizeSetting.get(settings); + final int initialQueueSize = queueSizeSetting.get(settings); + final int minQueueSize = minQueueSizeSetting.get(settings); + final int maxQueueSize = maxQueueSizeSetting.get(settings); + final int frameSize = frameSizeSetting.get(settings); + final TimeValue targetedResponseTime = targetedResponseTimeSetting.get(settings); + return new AutoExecutorSettings(nodeName, size, initialQueueSize, minQueueSize, maxQueueSize, frameSize, targetedResponseTime); + } + + @Override + ThreadPool.ExecutorHolder build(final AutoExecutorSettings settings, + final ThreadContext threadContext) { + int size = settings.size; + int initialQueueSize = settings.initialQueueSize; + int minQueueSize = settings.minQueueSize; + int maxQueueSize = settings.maxQueueSize; + int frameSize = settings.frameSize; + TimeValue targetedResponseTime = settings.targetedResponseTime; + final ThreadFactory threadFactory = EsExecutors.daemonThreadFactory(EsExecutors.threadName(settings.nodeName, name())); + final ExecutorService executor = + EsExecutors.newAutoQueueFixed(name(), size, initialQueueSize, minQueueSize, + maxQueueSize, frameSize, targetedResponseTime, threadFactory, threadContext); + // TODO: in a subsequent change we hope to extend ThreadPool.Info to be more specific for the thread pool type + final ThreadPool.Info info = + new ThreadPool.Info(name(), ThreadPool.ThreadPoolType.FIXED_AUTO_QUEUE_SIZE, + size, size, null, new SizeValue(initialQueueSize)); + return new ThreadPool.ExecutorHolder(executor, info); + } + + @Override + String formatInfo(ThreadPool.Info info) { + return String.format( + Locale.ROOT, + "name [%s], size [%d], queue size [%s]", + info.getName(), + info.getMax(), + info.getQueueSize() == null ? "unbounded" : info.getQueueSize()); + } + + static final class AutoExecutorSettings extends ExecutorBuilder.ExecutorSettings { + + private final int size; + private final int initialQueueSize; + private final int minQueueSize; + private final int maxQueueSize; + private final int frameSize; + private final TimeValue targetedResponseTime; + + AutoExecutorSettings(final String nodeName, final int size, final int initialQueueSize, + final int minQueueSize, final int maxQueueSize, final int frameSize, + final TimeValue targetedResponseTime) { + super(nodeName); + this.size = size; + this.initialQueueSize = initialQueueSize; + this.minQueueSize = minQueueSize; + this.maxQueueSize = maxQueueSize; + this.frameSize = frameSize; + this.targetedResponseTime = targetedResponseTime; + } + + } + +} diff --git a/core/src/main/java/org/elasticsearch/threadpool/ExecutorBuilder.java b/core/src/main/java/org/elasticsearch/threadpool/ExecutorBuilder.java index 54f5ab0af38ba..314eb1df71a4b 100644 --- a/core/src/main/java/org/elasticsearch/threadpool/ExecutorBuilder.java +++ b/core/src/main/java/org/elasticsearch/threadpool/ExecutorBuilder.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ThreadContext; import java.util.List; @@ -46,6 +47,14 @@ protected static String settingsKey(final String prefix, final String key) { return String.join(".", prefix, key); } + protected int applyHardSizeLimit(final Settings settings, final String name) { + if (name.equals(ThreadPool.Names.BULK) || name.equals(ThreadPool.Names.INDEX)) { + return 1 + EsExecutors.numberOfProcessors(settings); + } else { + return Integer.MAX_VALUE; + } + } + /** * The list of settings this builder will register. * diff --git a/core/src/main/java/org/elasticsearch/threadpool/FixedExecutorBuilder.java b/core/src/main/java/org/elasticsearch/threadpool/FixedExecutorBuilder.java index 162dcc80d7f06..9bf9569d1ea24 100644 --- a/core/src/main/java/org/elasticsearch/threadpool/FixedExecutorBuilder.java +++ b/core/src/main/java/org/elasticsearch/threadpool/FixedExecutorBuilder.java @@ -76,14 +76,6 @@ public FixedExecutorBuilder(final Settings settings, final String name, final in Setting.intSetting(queueSizeKey, queueSize, Setting.Property.NodeScope); } - private int applyHardSizeLimit(final Settings settings, final String name) { - if (name.equals(ThreadPool.Names.BULK) || name.equals(ThreadPool.Names.INDEX)) { - return 1 + EsExecutors.numberOfProcessors(settings); - } else { - return Integer.MAX_VALUE; - } - } - @Override public List> getRegisteredSettings() { return Arrays.asList(sizeSetting, queueSizeSetting); diff --git a/core/src/main/java/org/elasticsearch/threadpool/ThreadPool.java b/core/src/main/java/org/elasticsearch/threadpool/ThreadPool.java index f72956c4202e3..7b0c4eb752bdd 100644 --- a/core/src/main/java/org/elasticsearch/threadpool/ThreadPool.java +++ b/core/src/main/java/org/elasticsearch/threadpool/ThreadPool.java @@ -23,6 +23,7 @@ import org.apache.logging.log4j.util.Supplier; import org.apache.lucene.util.Counter; import org.apache.lucene.util.IOUtils; +import org.elasticsearch.Version; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.io.stream.StreamInput; @@ -85,6 +86,7 @@ public static class Names { public enum ThreadPoolType { DIRECT("direct"), FIXED("fixed"), + FIXED_AUTO_QUEUE_SIZE("fixed_auto_queue_size"), SCALING("scaling"); private final String type; @@ -126,7 +128,7 @@ public static ThreadPoolType fromType(String type) { map.put(Names.GET, ThreadPoolType.FIXED); map.put(Names.INDEX, ThreadPoolType.FIXED); map.put(Names.BULK, ThreadPoolType.FIXED); - map.put(Names.SEARCH, ThreadPoolType.FIXED); + map.put(Names.SEARCH, ThreadPoolType.FIXED_AUTO_QUEUE_SIZE); map.put(Names.MANAGEMENT, ThreadPoolType.SCALING); map.put(Names.FLUSH, ThreadPoolType.SCALING); map.put(Names.REFRESH, ThreadPoolType.SCALING); @@ -171,7 +173,8 @@ public ThreadPool(final Settings settings, final ExecutorBuilder... customBui builders.put(Names.INDEX, new FixedExecutorBuilder(settings, Names.INDEX, availableProcessors, 200)); builders.put(Names.BULK, new FixedExecutorBuilder(settings, Names.BULK, availableProcessors, 200)); // now that we reuse bulk for index/delete ops builders.put(Names.GET, new FixedExecutorBuilder(settings, Names.GET, availableProcessors, 1000)); - builders.put(Names.SEARCH, new FixedExecutorBuilder(settings, Names.SEARCH, searchThreadPoolSize(availableProcessors), 1000)); + builders.put(Names.SEARCH, new AutoQueueAdjustingExecutorBuilder(settings, + Names.SEARCH, searchThreadPoolSize(availableProcessors), 1000, 1000, 1000, 2000)); builders.put(Names.MANAGEMENT, new ScalingExecutorBuilder(Names.MANAGEMENT, 1, 5, TimeValue.timeValueMinutes(5))); // no queue as this means clients will need to handle rejections on listener queue even if the operation succeeded // the assumption here is that the listeners should be very lightweight on the listeners side @@ -608,7 +611,13 @@ public Info(StreamInput in) throws IOException { @Override public void writeTo(StreamOutput out) throws IOException { out.writeString(name); - out.writeString(type.getType()); + if (type == ThreadPoolType.FIXED_AUTO_QUEUE_SIZE && + out.getVersion().before(Version.V_6_0_0_alpha1_UNRELEASED)) { + // 5.x doesn't know about the "fixed_auto_queue_size" thread pool type, just write fixed. + out.writeString(ThreadPoolType.FIXED.getType()); + } else { + out.writeString(type.getType()); + } out.writeInt(min); out.writeInt(max); out.writeOptionalWriteable(keepAlive); diff --git a/core/src/test/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutorTests.java b/core/src/test/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutorTests.java new file mode 100644 index 0000000000000..732ec94ae1055 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutorTests.java @@ -0,0 +1,226 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.common.util.concurrent; + +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.junit.annotations.TestLogging; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.lessThan; + +/** + * Tests for the automatic queue resizing of the {@code QueueResizingEsThreadPoolExecutorTests} + * based on the time taken for each event. + */ +public class QueueResizingEsThreadPoolExecutorTests extends ESTestCase { + + public void testExactWindowSizeAdjustment() throws Exception { + ThreadContext context = new ThreadContext(Settings.EMPTY); + ResizableBlockingQueue queue = + new ResizableBlockingQueue<>(ConcurrentCollections.newBlockingQueue(), 100); + + int threads = randomIntBetween(1, 3); + int measureWindow = 3; + logger.info("--> auto-queue with a measurement window of {} tasks", measureWindow); + QueueResizingEsThreadPoolExecutor executor = + new QueueResizingEsThreadPoolExecutor( + "test-threadpool", threads, threads, 1000, + TimeUnit.MILLISECONDS, queue, 10, 1000, fastWrapper(), + measureWindow, TimeValue.timeValueMillis(1), EsExecutors.daemonThreadFactory("queuetest"), + new EsAbortPolicy(), context); + executor.prestartAllCoreThreads(); + logger.info("--> executor: {}", executor); + + // Execute exactly 3 (measureWindow) times + executor.execute(() -> {}); + executor.execute(() -> {}); + executor.execute(() -> {}); + + // The queue capacity should have increased by 50 since they were very fast tasks + assertBusy(() -> { + assertThat(queue.capacity(), equalTo(150)); + }); + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + context.close(); + } + + public void testAutoQueueSizingUp() throws Exception { + ThreadContext context = new ThreadContext(Settings.EMPTY); + ResizableBlockingQueue queue = + new ResizableBlockingQueue<>(ConcurrentCollections.newBlockingQueue(), + 2000); + + int threads = randomIntBetween(1, 10); + int measureWindow = randomIntBetween(100, 200); + logger.info("--> auto-queue with a measurement window of {} tasks", measureWindow); + QueueResizingEsThreadPoolExecutor executor = + new QueueResizingEsThreadPoolExecutor( + "test-threadpool", threads, threads, 1000, + TimeUnit.MILLISECONDS, queue, 10, 3000, fastWrapper(), + measureWindow, TimeValue.timeValueMillis(1), EsExecutors.daemonThreadFactory("queuetest"), + new EsAbortPolicy(), context); + executor.prestartAllCoreThreads(); + logger.info("--> executor: {}", executor); + + // Execute a task multiple times that takes 1ms + executeTask(executor, (measureWindow * 5) + 2); + + assertBusy(() -> { + assertThat(queue.capacity(), greaterThan(2000)); + }); + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + context.close(); + } + + public void testAutoQueueSizingDown() throws Exception { + ThreadContext context = new ThreadContext(Settings.EMPTY); + ResizableBlockingQueue queue = + new ResizableBlockingQueue<>(ConcurrentCollections.newBlockingQueue(), + 2000); + + int threads = randomIntBetween(1, 10); + int measureWindow = randomIntBetween(100, 200); + logger.info("--> auto-queue with a measurement window of {} tasks", measureWindow); + QueueResizingEsThreadPoolExecutor executor = + new QueueResizingEsThreadPoolExecutor( + "test-threadpool", threads, threads, 1000, + TimeUnit.MILLISECONDS, queue, 10, 3000, slowWrapper(), measureWindow, TimeValue.timeValueMillis(1), + EsExecutors.daemonThreadFactory("queuetest"), new EsAbortPolicy(), context); + executor.prestartAllCoreThreads(); + logger.info("--> executor: {}", executor); + + // Execute a task multiple times that takes 1m + executeTask(executor, (measureWindow * 5) + 2); + + assertBusy(() -> { + assertThat(queue.capacity(), lessThan(2000)); + }); + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + context.close(); + } + + public void testAutoQueueSizingWithMin() throws Exception { + ThreadContext context = new ThreadContext(Settings.EMPTY); + ResizableBlockingQueue queue = + new ResizableBlockingQueue<>(ConcurrentCollections.newBlockingQueue(), + 5000); + + int threads = randomIntBetween(1, 5); + int measureWindow = randomIntBetween(10, 100);; + int min = randomIntBetween(4981, 4999); + logger.info("--> auto-queue with a measurement window of {} tasks", measureWindow); + QueueResizingEsThreadPoolExecutor executor = + new QueueResizingEsThreadPoolExecutor( + "test-threadpool", threads, threads, 1000, + TimeUnit.MILLISECONDS, queue, min, 100000, slowWrapper(), measureWindow, TimeValue.timeValueMillis(1), + EsExecutors.daemonThreadFactory("queuetest"), new EsAbortPolicy(), context); + executor.prestartAllCoreThreads(); + logger.info("--> executor: {}", executor); + + // Execute a task multiple times that takes 1m + executeTask(executor, (measureWindow * 5)); + + // The queue capacity should decrease, but no lower than the minimum + assertBusy(() -> { + assertThat(queue.capacity(), equalTo(min)); + }); + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + context.close(); + } + + public void testAutoQueueSizingWithMax() throws Exception { + ThreadContext context = new ThreadContext(Settings.EMPTY); + ResizableBlockingQueue queue = + new ResizableBlockingQueue<>(ConcurrentCollections.newBlockingQueue(), + 5000); + + int threads = randomIntBetween(1, 5); + int measureWindow = randomIntBetween(10, 100); + int max = randomIntBetween(5010, 5024); + logger.info("--> auto-queue with a measurement window of {} tasks", measureWindow); + QueueResizingEsThreadPoolExecutor executor = + new QueueResizingEsThreadPoolExecutor( + "test-threadpool", threads, threads, 1000, + TimeUnit.MILLISECONDS, queue, 10, max, fastWrapper(), measureWindow, TimeValue.timeValueMillis(1), + EsExecutors.daemonThreadFactory("queuetest"), new EsAbortPolicy(), context); + executor.prestartAllCoreThreads(); + logger.info("--> executor: {}", executor); + + // Execute a task multiple times that takes 1ms + executeTask(executor, measureWindow * 3); + + // The queue capacity should increase, but no higher than the maximum + assertBusy(() -> { + assertThat(queue.capacity(), equalTo(max)); + }); + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + context.close(); + } + + private Function randomBetweenLimitsWrapper(final int minNs, final int maxNs) { + return (runnable) -> { + return new SettableTimedRunnable(randomIntBetween(minNs, maxNs)); + }; + } + + private Function fastWrapper() { + return (runnable) -> { + return new SettableTimedRunnable(TimeUnit.NANOSECONDS.toNanos(50)); + }; + } + + private Function slowWrapper() { + return (runnable) -> { + return new SettableTimedRunnable(TimeUnit.MINUTES.toNanos(2)); + }; + } + + /** Execute a blank task {@code times} times for the executor */ + private void executeTask(QueueResizingEsThreadPoolExecutor executor, int times) { + logger.info("--> executing a task [{}] times", times); + for (int i = 0; i < times; i++) { + executor.execute(() -> {}); + } + } + + public class SettableTimedRunnable extends TimedRunnable { + private final long timeTaken; + + public SettableTimedRunnable(long timeTaken) { + super(() -> {}); + this.timeTaken = timeTaken; + } + + @Override + public long getTotalNanos() { + return timeTaken; + } + } +} diff --git a/core/src/test/java/org/elasticsearch/common/util/concurrent/ResizableBlockingQueueTests.java b/core/src/test/java/org/elasticsearch/common/util/concurrent/ResizableBlockingQueueTests.java new file mode 100644 index 0000000000000..b1d5b9bc1bccf --- /dev/null +++ b/core/src/test/java/org/elasticsearch/common/util/concurrent/ResizableBlockingQueueTests.java @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.common.util.concurrent; + +import org.elasticsearch.test.ESTestCase; + +import static org.hamcrest.Matchers.equalTo; + +public class ResizableBlockingQueueTests extends ESTestCase { + + public void testAdjustCapacity() throws Exception { + ResizableBlockingQueue queue = + new ResizableBlockingQueue<>(ConcurrentCollections.newBlockingQueue(), + 100); + + assertThat(queue.capacity(), equalTo(100)); + // Queue size already equal to desired capacity + queue.adjustCapacity(100, 25, 1, 1000); + assertThat(queue.capacity(), equalTo(100)); + // Not worth adjusting + queue.adjustCapacity(99, 25, 1, 1000); + assertThat(queue.capacity(), equalTo(100)); + // Not worth adjusting + queue.adjustCapacity(75, 25, 1, 1000); + assertThat(queue.capacity(), equalTo(100)); + queue.adjustCapacity(74, 25, 1, 1000); + assertThat(queue.capacity(), equalTo(75)); + queue.adjustCapacity(1000000, 25, 1, 1000); + assertThat(queue.capacity(), equalTo(100)); + queue.adjustCapacity(1, 25, 80, 1000); + assertThat(queue.capacity(), equalTo(80)); + queue.adjustCapacity(1000000, 25, 80, 100); + assertThat(queue.capacity(), equalTo(100)); + } +} diff --git a/core/src/test/java/org/elasticsearch/threadpool/AutoQueueAdjustingExecutorBuilderTests.java b/core/src/test/java/org/elasticsearch/threadpool/AutoQueueAdjustingExecutorBuilderTests.java new file mode 100644 index 0000000000000..836193423f11b --- /dev/null +++ b/core/src/test/java/org/elasticsearch/threadpool/AutoQueueAdjustingExecutorBuilderTests.java @@ -0,0 +1,41 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.threadpool; + +import org.elasticsearch.common.settings.Settings; + +import static org.hamcrest.CoreMatchers.containsString; + +public class AutoQueueAdjustingExecutorBuilderTests extends ESThreadPoolTestCase { + + public void testValidatingMinMaxSettings() throws Exception { + Settings settings = Settings.builder() + .put("thread_pool.search.min_queue_size", randomIntBetween(30, 100)) + .put("thread_pool.search.max_queue_size", randomIntBetween(1,25)) + .build(); + try { + new AutoQueueAdjustingExecutorBuilder(settings, "test", 1, 15, 1, 100, 10); + fail("should have thrown an exception"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString("Failed to parse value")); + } + } + +} diff --git a/docs/reference/modules/threadpool.asciidoc b/docs/reference/modules/threadpool.asciidoc index 01b204b510f16..a9a5d2993c02e 100644 --- a/docs/reference/modules/threadpool.asciidoc +++ b/docs/reference/modules/threadpool.asciidoc @@ -20,9 +20,10 @@ There are several thread pools, but the important ones include: is `1 + # of available processors`. `search`:: - For count/search/suggest operations. Thread pool type is `fixed` - with a size of `int((# of available_processors * 3) / 2) + 1`, - queue_size of `1000`. + For count/search/suggest operations. Thread pool type is + `fixed_auto_queue_size` with a size of + `int((# of available_processors * 3) / 2) + 1`, and initial queue_size of + `1000`. `get`:: For get operations. Thread pool type is `fixed` @@ -90,6 +91,50 @@ thread_pool: queue_size: 1000 -------------------------------------------------- +[float] +==== `fixed_auto_queue_size` + +The `fixed_auto_queue_size` thread pool holds a fixed size of threads to handle +the requests with a bounded queue for pending requests that have no threads to +service them. It's similar to the `fixed` threadpool, however, the `queue_size` +automatically adjusts according to calculations based on +https://en.wikipedia.org/wiki/Little%27s_law[Little's Law]. These calculations +will potentially adjust the `queue_size` up or down by 50 every time +`auto_queue_frame_size` operations have been completed. + +The `size` parameter controls the number of threads, and defaults to the +number of cores times 5. + +The `queue_size` allows to control the initial size of the queue of pending +requests that have no threads to execute them. + +The `min_queue_size` setting controls the minimum amount the `queue_size` can be +adjusted to. + +The `max_queue_size` setting controls the maximum amount the `queue_size` can be +adjusted to. + +The `auto_queue_frame_size` setting controls the number of operations during +which measurement is taken before the queue is adjusted. It should be large +enough that a single operation cannot unduly bias the calculation. + +The `target_response_rate` is a time value setting that indicates the targeted +average response time for tasks in the thread pool queue. If tasks are routinely +above this time, the thread pool queue will be adjusted down so that tasks are +rejected. + +[source,yaml] +-------------------------------------------------- +thread_pool: + search: + size: 30 + queue_size: 500 + min_queue_size: 10 + max_queue_size: 1000 + auto_queue_frame_size: 2000 + target_response_rate: 1s +-------------------------------------------------- + [float] ==== `scaling` From 25dd64497b677517a261cb706ca2e0b0e46e3850 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 16 May 2017 11:57:18 -0700 Subject: [PATCH 379/619] Scripting: Deprecate native scripts (#24692) Native scripts are no longer documented and instead using a ScriptEngine is recommended. This change adds a deprecation warning for removal in 6.0. relates #19966 --- .../java/org/elasticsearch/script/NativeScriptEngine.java | 8 ++++++++ .../org/elasticsearch/script/NativeScriptFactory.java | 2 ++ .../java/org/elasticsearch/script/NativeScriptTests.java | 2 ++ 3 files changed, 12 insertions(+) diff --git a/core/src/main/java/org/elasticsearch/script/NativeScriptEngine.java b/core/src/main/java/org/elasticsearch/script/NativeScriptEngine.java index 7bd2f62e15145..1b3fb23a7f9a5 100644 --- a/core/src/main/java/org/elasticsearch/script/NativeScriptEngine.java +++ b/core/src/main/java/org/elasticsearch/script/NativeScriptEngine.java @@ -19,9 +19,12 @@ package org.elasticsearch.script; +import org.apache.logging.log4j.Logger; import org.apache.lucene.index.LeafReaderContext; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.component.AbstractComponent; +import org.elasticsearch.common.logging.DeprecationLogger; +import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.search.lookup.SearchLookup; @@ -41,6 +44,11 @@ public class NativeScriptEngine extends AbstractComponent implements ScriptEngin public NativeScriptEngine(Settings settings, Map scripts) { super(settings); + if (scripts.isEmpty() == false) { + Logger logger = Loggers.getLogger(ScriptModule.class); + DeprecationLogger deprecationLogger = new DeprecationLogger(logger); + deprecationLogger.deprecated("Native scripts are deprecated. Use a custom ScriptEngine to write scripts in java."); + } this.scripts = unmodifiableMap(scripts); } diff --git a/core/src/main/java/org/elasticsearch/script/NativeScriptFactory.java b/core/src/main/java/org/elasticsearch/script/NativeScriptFactory.java index 7fca2501903be..a53f7115821d7 100644 --- a/core/src/main/java/org/elasticsearch/script/NativeScriptFactory.java +++ b/core/src/main/java/org/elasticsearch/script/NativeScriptFactory.java @@ -31,7 +31,9 @@ * @see AbstractSearchScript * @see AbstractLongSearchScript * @see AbstractDoubleSearchScript + * @deprecated Create a {@link ScriptEngine} instead of using native scripts */ +@Deprecated public interface NativeScriptFactory { /** diff --git a/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java b/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java index 7f56f3de4b373..0960bc71bea4b 100644 --- a/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java +++ b/core/src/test/java/org/elasticsearch/script/NativeScriptTests.java @@ -55,6 +55,7 @@ public void testNativeScript() throws InterruptedException { CompiledScript compiledScript = scriptModule.getScriptService().compile(script, ScriptContext.Standard.SEARCH); ExecutableScript executable = scriptModule.getScriptService().executable(compiledScript, script.getParams()); assertThat(executable.run().toString(), equalTo("test")); + assertWarnings("Native scripts are deprecated. Use a custom ScriptEngine to write scripts in java."); } public void testFineGrainedSettingsDontAffectNativeScripts() throws IOException { @@ -82,6 +83,7 @@ public void testFineGrainedSettingsDontAffectNativeScripts() throws IOException assertThat(scriptService.compile(new Script(ScriptType.INLINE, NativeScriptEngine.NAME, "my", Collections.emptyMap()), scriptContext), notNullValue()); } + assertWarnings("Native scripts are deprecated. Use a custom ScriptEngine to write scripts in java."); } public static class MyNativeScriptFactory implements NativeScriptFactory { From 11ea588aeee2b7b4dab6715d960b208aea2318ef Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Tue, 16 May 2017 21:00:48 +0200 Subject: [PATCH 380/619] Move BWC versin to 5.5 after backport Relates to #24678 --- .../java/org/elasticsearch/cluster/block/ClusterBlock.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlock.java b/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlock.java index 6449a848b6f38..133c20411fb95 100644 --- a/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlock.java +++ b/core/src/main/java/org/elasticsearch/cluster/block/ClusterBlock.java @@ -138,7 +138,7 @@ public void readFrom(StreamInput in) throws IOException { retryable = in.readBoolean(); disableStatePersistence = in.readBoolean(); status = RestStatus.readFrom(in); - if (in.getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_5_0_UNRELEASED)) { allowReleaseResources = in.readBoolean(); } else { allowReleaseResources = false; @@ -156,7 +156,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeBoolean(retryable); out.writeBoolean(disableStatePersistence); RestStatus.writeTo(out, status); - if (out.getVersion().onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_5_0_UNRELEASED)) { out.writeBoolean(allowReleaseResources); } } From b7f0df626a0271d9f2c043f10d7f4193766d5afa Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Tue, 2 May 2017 14:37:45 -0700 Subject: [PATCH 381/619] [DOCS] Added Painless Language Spec content --- docs/Versions.asciidoc | 1 + docs/reference/index.asciidoc | 2 - docs/reference/modules/scripting.asciidoc | 4 - .../scripting/painless-debugging.asciidoc | 96 ----- .../scripting/painless-syntax.asciidoc | 244 ----------- .../modules/scripting/painless.asciidoc | 383 +----------------- .../reference/painless-api-reference.asciidoc | 17 - .../AbstractChronology.asciidoc | 7 - .../AbstractCollection.asciidoc | 7 - .../AbstractList.asciidoc | 7 - .../AbstractMap.SimpleEntry.asciidoc | 9 - .../AbstractMap.SimpleImmutableEntry.asciidoc | 9 - .../AbstractMap.asciidoc | 7 - .../AbstractQueue.asciidoc | 7 - .../AbstractSequentialList.asciidoc | 7 - .../AbstractSet.asciidoc | 7 - .../Annotation.asciidoc | 9 - .../Appendable.asciidoc | 8 - .../ArithmeticException.asciidoc | 9 - .../ArrayDeque.asciidoc | 10 - .../ArrayIndexOutOfBoundsException.asciidoc | 9 - .../painless-api-reference/ArrayList.asciidoc | 11 - .../ArrayStoreException.asciidoc | 9 - .../painless-api-reference/Arrays.asciidoc | 11 - ...ibutedCharacterIterator.Attribute.asciidoc | 10 - .../AttributedCharacterIterator.asciidoc | 14 - .../AttributedString.asciidoc | 15 - .../Base64.Decoder.asciidoc | 9 - .../Base64.Encoder.asciidoc | 10 - .../painless-api-reference/Base64.asciidoc | 14 - .../BaseStream.asciidoc | 13 - .../BiConsumer.asciidoc | 9 - .../BiFunction.asciidoc | 9 - .../BiPredicate.asciidoc | 11 - .../painless-api-reference/Bidi.asciidoc | 28 -- .../BigDecimal.asciidoc | 57 --- .../BigInteger.asciidoc | 50 --- .../BinaryOperator.asciidoc | 9 - .../painless-api-reference/BitSet.asciidoc | 34 -- .../painless-api-reference/Boolean.asciidoc | 19 - .../BooleanSupplier.asciidoc | 8 - .../BreakIterator.asciidoc | 29 -- .../painless-api-reference/Byte.asciidoc | 22 - .../painless-api-reference/BytesRef.asciidoc | 12 - .../Calendar.Builder.asciidoc | 21 - .../painless-api-reference/Calendar.asciidoc | 102 ----- .../CharSequence.asciidoc | 15 - .../Character.Subset.asciidoc | 7 - .../Character.UnicodeBlock.asciidoc | 229 ----------- .../Character.UnicodeScript.asciidoc | 114 ------ .../painless-api-reference/Character.asciidoc | 125 ------ .../CharacterIterator.asciidoc | 18 - .../ChoiceFormat.asciidoc | 17 - .../ChronoField.asciidoc | 41 -- .../ChronoLocalDate.asciidoc | 31 -- .../ChronoLocalDateTime.asciidoc | 29 -- .../ChronoPeriod.asciidoc | 20 - .../ChronoUnit.asciidoc | 25 -- .../ChronoZonedDateTime.asciidoc | 35 -- .../Chronology.asciidoc | 33 -- .../ClassCastException.asciidoc | 9 - .../ClassNotFoundException.asciidoc | 9 - .../painless-api-reference/Clock.asciidoc | 13 - .../CloneNotSupportedException.asciidoc | 9 - .../CollationElementIterator.asciidoc | 18 - .../CollationKey.asciidoc | 10 - .../painless-api-reference/Collator.asciidoc | 24 -- .../Collection.asciidoc | 28 -- .../Collections.asciidoc | 59 --- .../Collector.Characteristics.asciidoc | 12 - .../painless-api-reference/Collector.asciidoc | 14 - .../Collectors.asciidoc | 38 -- .../Comparable.asciidoc | 8 - .../Comparator.asciidoc | 24 -- .../ConcurrentModificationException.asciidoc | 9 - .../painless-api-reference/Consumer.asciidoc | 9 - .../painless-api-reference/Currency.asciidoc | 16 - .../painless-api-reference/Date.asciidoc | 16 - .../DateFormat.Field.asciidoc | 27 -- .../DateFormat.asciidoc | 51 --- .../DateFormatSymbols.asciidoc | 30 -- .../DateTimeException.asciidoc | 8 - .../DateTimeFormatter.asciidoc | 50 --- .../DateTimeFormatterBuilder.asciidoc | 44 -- .../DateTimeParseException.asciidoc | 10 - .../painless-api-reference/DayOfWeek.asciidoc | 22 - .../painless-api-reference/Debug.asciidoc | 8 - .../DecimalFormat.asciidoc | 32 -- .../DecimalFormatSymbols.asciidoc | 43 -- .../DecimalStyle.asciidoc | 19 - .../painless-api-reference/Deque.asciidoc | 25 -- .../Dictionary.asciidoc | 14 - .../painless-api-reference/Double.asciidoc | 35 -- .../DoubleBinaryOperator.asciidoc | 8 - .../DoubleConsumer.asciidoc | 9 - .../DoubleFunction.asciidoc | 8 - .../DoublePredicate.asciidoc | 11 - .../DoubleStream.Builder.asciidoc | 9 - .../DoubleStream.asciidoc | 43 -- .../DoubleSummaryStatistics.asciidoc | 14 - .../DoubleSupplier.asciidoc | 8 - .../DoubleToIntFunction.asciidoc | 8 - .../DoubleToLongFunction.asciidoc | 8 - .../DoubleUnaryOperator.asciidoc | 11 - .../DuplicateFormatFlagsException.asciidoc | 9 - .../painless-api-reference/Duration.asciidoc | 51 --- .../EmptyStackException.asciidoc | 8 - .../painless-api-reference/Enum.asciidoc | 10 - .../EnumConstantNotPresentException.asciidoc | 8 - .../Enumeration.asciidoc | 9 - .../painless-api-reference/Era.asciidoc | 9 - .../EventListener.asciidoc | 7 - .../EventListenerProxy.asciidoc | 8 - .../EventObject.asciidoc | 9 - .../painless-api-reference/Exception.asciidoc | 12 - .../FieldPosition.asciidoc | 15 - .../painless-api-reference/Float.asciidoc | 35 -- .../Format.Field.asciidoc | 7 - .../painless-api-reference/Format.asciidoc | 13 - ...tFlagsConversionMismatchException.asciidoc | 10 - .../FormatStyle.asciidoc | 13 - .../Formattable.asciidoc | 8 - .../FormattableFlags.asciidoc | 10 - .../Formatter.BigDecimalLayoutForm.asciidoc | 9 - .../painless-api-reference/Formatter.asciidoc | 14 - .../FormatterClosedException.asciidoc | 8 - .../painless-api-reference/Function.asciidoc | 11 - .../GregorianCalendar.asciidoc | 20 - .../painless-api-reference/HashMap.asciidoc | 10 - .../painless-api-reference/HashSet.asciidoc | 10 - .../painless-api-reference/Hashtable.asciidoc | 10 - .../HijrahChronology.asciidoc | 16 - .../HijrahDate.asciidoc | 18 - .../painless-api-reference/HijrahEra.asciidoc | 12 - .../IdentityHashMap.asciidoc | 10 - .../IllegalAccessException.asciidoc | 9 - .../IllegalArgumentException.asciidoc | 9 - .../IllegalFormatCodePointException.asciidoc | 9 - .../IllegalFormatConversionException.asciidoc | 8 - .../IllegalFormatException.asciidoc | 7 - .../IllegalFormatFlagsException.asciidoc | 9 - .../IllegalFormatPrecisionException.asciidoc | 9 - .../IllegalFormatWidthException.asciidoc | 9 - .../IllegalMonitorStateException.asciidoc | 9 - .../IllegalStateException.asciidoc | 9 - .../IllegalThreadStateException.asciidoc | 9 - .../IllformedLocaleException.asciidoc | 11 - .../IndexOutOfBoundsException.asciidoc | 9 - .../InputMismatchException.asciidoc | 9 - .../painless-api-reference/Instant.asciidoc | 36 -- .../InstantiationException.asciidoc | 9 - .../IntBinaryOperator.asciidoc | 8 - .../IntConsumer.asciidoc | 9 - .../IntFunction.asciidoc | 8 - .../IntPredicate.asciidoc | 11 - .../IntStream.Builder.asciidoc | 9 - .../painless-api-reference/IntStream.asciidoc | 47 --- .../IntSummaryStatistics.asciidoc | 14 - .../IntSupplier.asciidoc | 8 - .../IntToDoubleFunction.asciidoc | 8 - .../IntToLongFunction.asciidoc | 8 - .../IntUnaryOperator.asciidoc | 11 - .../painless-api-reference/Integer.asciidoc | 44 -- .../InterruptedException.asciidoc | 9 - .../IsoChronology.asciidoc | 20 - .../painless-api-reference/IsoEra.asciidoc | 13 - .../painless-api-reference/IsoFields.asciidoc | 13 - .../painless-api-reference/Iterable.asciidoc | 21 - .../painless-api-reference/Iterator.asciidoc | 11 - .../JapaneseChronology.asciidoc | 16 - .../JapaneseDate.asciidoc | 17 - .../JapaneseEra.asciidoc | 15 - .../JulianFields.asciidoc | 10 - .../LinkedHashMap.asciidoc | 9 - .../LinkedHashSet.asciidoc | 9 - .../LinkedList.asciidoc | 10 - .../painless-api-reference/List.asciidoc | 22 - .../ListIterator.asciidoc | 12 - .../painless-api-reference/LocalDate.asciidoc | 47 --- .../LocalDateTime.asciidoc | 61 --- .../painless-api-reference/LocalTime.asciidoc | 50 --- .../Locale.Builder.asciidoc | 21 - .../Locale.Category.asciidoc | 11 - .../Locale.FilteringMode.asciidoc | 14 - .../Locale.LanguageRange.asciidoc | 16 - .../painless-api-reference/Locale.asciidoc | 69 ---- .../painless-api-reference/Long.asciidoc | 44 -- .../LongBinaryOperator.asciidoc | 8 - .../LongConsumer.asciidoc | 9 - .../LongFunction.asciidoc | 8 - .../LongPredicate.asciidoc | 11 - .../LongStream.Builder.asciidoc | 9 - .../LongStream.asciidoc | 46 --- .../LongSummaryStatistics.asciidoc | 14 - .../LongSupplier.asciidoc | 8 - .../LongToDoubleFunction.asciidoc | 8 - .../LongToIntFunction.asciidoc | 8 - .../LongUnaryOperator.asciidoc | 11 - .../painless-api-reference/Map.Entry.asciidoc | 16 - .../painless-api-reference/Map.asciidoc | 42 -- .../painless-api-reference/Matcher.asciidoc | 34 -- .../painless-api-reference/Math.asciidoc | 46 --- .../MathContext.asciidoc | 15 - .../MessageFormat.Field.asciidoc | 8 - .../MessageFormat.asciidoc | 20 - .../MinguoChronology.asciidoc | 16 - .../MinguoDate.asciidoc | 17 - .../painless-api-reference/MinguoEra.asciidoc | 13 - .../MissingFormatArgumentException.asciidoc | 9 - .../MissingFormatWidthException.asciidoc | 9 - .../MissingResourceException.asciidoc | 10 - .../painless-api-reference/Month.asciidoc | 32 -- .../painless-api-reference/MonthDay.asciidoc | 23 -- .../NavigableMap.asciidoc | 24 -- .../NavigableSet.asciidoc | 18 - .../NegativeArraySizeException.asciidoc | 9 - .../NoSuchElementException.asciidoc | 9 - .../NoSuchFieldException.asciidoc | 9 - .../NoSuchMethodException.asciidoc | 9 - .../Normalizer.Form.asciidoc | 13 - .../Normalizer.asciidoc | 9 - .../NullPointerException.asciidoc | 9 - .../painless-api-reference/Number.asciidoc | 13 - .../NumberFormat.Field.asciidoc | 18 - .../NumberFormat.asciidoc | 38 -- .../NumberFormatException.asciidoc | 9 - .../ObjDoubleConsumer.asciidoc | 8 - .../ObjIntConsumer.asciidoc | 8 - .../ObjLongConsumer.asciidoc | 8 - .../painless-api-reference/Object.asciidoc | 9 - .../painless-api-reference/Objects.asciidoc | 18 - .../Observable.asciidoc | 15 - .../painless-api-reference/Observer.asciidoc | 8 - .../OffsetDateTime.asciidoc | 75 ---- .../OffsetTime.asciidoc | 47 --- .../painless-api-reference/Optional.asciidoc | 19 - .../OptionalDouble.asciidoc | 15 - .../OptionalInt.asciidoc | 15 - .../OptionalLong.asciidoc | 15 - .../ParseException.asciidoc | 9 - .../ParsePosition.asciidoc | 12 - .../painless-api-reference/Pattern.asciidoc | 15 - .../painless-api-reference/Period.asciidoc | 35 -- .../painless-api-reference/Predicate.asciidoc | 12 - .../PrimitiveIterator.OfDouble.asciidoc | 9 - .../PrimitiveIterator.OfInt.asciidoc | 9 - .../PrimitiveIterator.OfLong.asciidoc | 9 - .../PrimitiveIterator.asciidoc | 8 - .../PriorityQueue.asciidoc | 9 - .../painless-api-reference/Queue.asciidoc | 12 - .../painless-api-reference/Random.asciidoc | 24 -- .../RandomAccess.asciidoc | 7 - .../ReflectiveOperationException.asciidoc | 9 - .../ResolverStyle.asciidoc | 12 - .../RoundingMode.asciidoc | 17 - .../RuleBasedCollator.asciidoc | 10 - .../RuntimeException.asciidoc | 9 - .../SecurityException.asciidoc | 9 - .../painless-api-reference/Set.asciidoc | 10 - .../painless-api-reference/Short.asciidoc | 23 -- .../painless-api-reference/SignStyle.asciidoc | 14 - .../SimpleDateFormat.asciidoc | 18 - .../SimpleTimeZone.asciidoc | 23 -- .../painless-api-reference/SortedMap.asciidoc | 13 - .../painless-api-reference/SortedSet.asciidoc | 13 - .../Spliterator.OfDouble.asciidoc | 8 - .../Spliterator.OfInt.asciidoc | 8 - .../Spliterator.OfLong.asciidoc | 8 - .../Spliterator.OfPrimitive.asciidoc | 10 - .../Spliterator.asciidoc | 23 -- .../Spliterators.asciidoc | 15 - .../painless-api-reference/Stack.asciidoc | 13 - .../StackTraceElement.asciidoc | 13 - .../Stream.Builder.asciidoc | 9 - .../painless-api-reference/Stream.asciidoc | 43 -- .../StrictMath.asciidoc | 46 --- .../painless-api-reference/String.asciidoc | 46 --- .../StringBuffer.asciidoc | 31 -- .../StringBuilder.asciidoc | 31 -- .../StringCharacterIterator.asciidoc | 11 - .../StringIndexOutOfBoundsException.asciidoc | 9 - .../StringJoiner.asciidoc | 13 - .../StringTokenizer.asciidoc | 14 - .../painless-api-reference/Supplier.asciidoc | 8 - .../painless-api-reference/System.asciidoc | 10 - .../painless-api-reference/Temporal.asciidoc | 14 - .../TemporalAccessor.asciidoc | 12 - .../TemporalAdjuster.asciidoc | 8 - .../TemporalAdjusters.asciidoc | 21 - .../TemporalAmount.asciidoc | 11 - .../TemporalField.asciidoc | 19 - .../TemporalQueries.asciidoc | 14 - .../TemporalQuery.asciidoc | 8 - .../TemporalUnit.asciidoc | 15 - .../painless-api-reference/TextStyle.asciidoc | 18 - .../ThaiBuddhistChronology.asciidoc | 16 - .../ThaiBuddhistDate.asciidoc | 17 - .../ThaiBuddhistEra.asciidoc | 13 - .../painless-api-reference/TimeZone.asciidoc | 29 -- .../ToDoubleBiFunction.asciidoc | 8 - .../ToDoubleFunction.asciidoc | 8 - .../ToIntBiFunction.asciidoc | 8 - .../ToIntFunction.asciidoc | 8 - .../ToLongBiFunction.asciidoc | 8 - .../ToLongFunction.asciidoc | 8 - .../TooManyListenersException.asciidoc | 9 - .../painless-api-reference/TreeMap.asciidoc | 10 - .../painless-api-reference/TreeSet.asciidoc | 10 - .../TypeNotPresentException.asciidoc | 8 - .../painless-api-reference/UUID.asciidoc | 18 - .../UnaryOperator.asciidoc | 8 - .../UnknownFormatConversionException.asciidoc | 9 - .../UnknownFormatFlagsException.asciidoc | 9 - .../UnsupportedOperationException.asciidoc | 9 - .../UnsupportedTemporalTypeException.asciidoc | 8 - .../ValueRange.asciidoc | 20 - .../painless-api-reference/Vector.asciidoc | 22 - .../WeekFields.asciidoc | 19 - .../painless-api-reference/Year.asciidoc | 32 -- .../painless-api-reference/YearMonth.asciidoc | 36 -- .../painless-api-reference/ZoneId.asciidoc | 18 - .../ZoneOffset.asciidoc | 17 - .../ZoneOffsetTransition.asciidoc | 19 - ...fsetTransitionRule.TimeDefinition.asciidoc | 13 - .../ZoneOffsetTransitionRule.asciidoc | 18 - .../painless-api-reference/ZoneRules.asciidoc | 21 - .../ZoneRulesException.asciidoc | 8 - .../ZoneRulesProvider.asciidoc | 10 - .../ZonedDateTime.asciidoc | 66 --- .../painless-api-reference/index.asciidoc | 338 ---------------- ...elasticsearch.common.geo.GeoPoint.asciidoc | 9 - ...ielddata.ScriptDocValues.Booleans.asciidoc | 10 - ...elddata.ScriptDocValues.BytesRefs.asciidoc | 10 - ...fielddata.ScriptDocValues.Doubles.asciidoc | 10 - ...elddata.ScriptDocValues.GeoPoints.asciidoc | 20 - ...x.fielddata.ScriptDocValues.Longs.asciidoc | 12 - ...fielddata.ScriptDocValues.Strings.asciidoc | 10 - ...per.IpFieldType.IpScriptDocValues.asciidoc | 10 - ...lasticsearch.painless.FeatureTest.asciidoc | 16 - .../org.joda.time.ReadableDateTime.asciidoc | 27 -- .../org.joda.time.ReadableInstant.asciidoc | 14 - docs/reference/redirects.asciidoc | 19 + 342 files changed, 23 insertions(+), 7086 deletions(-) delete mode 100644 docs/reference/modules/scripting/painless-debugging.asciidoc delete mode 100644 docs/reference/modules/scripting/painless-syntax.asciidoc delete mode 100644 docs/reference/painless-api-reference.asciidoc delete mode 100644 docs/reference/painless-api-reference/AbstractChronology.asciidoc delete mode 100644 docs/reference/painless-api-reference/AbstractCollection.asciidoc delete mode 100644 docs/reference/painless-api-reference/AbstractList.asciidoc delete mode 100644 docs/reference/painless-api-reference/AbstractMap.SimpleEntry.asciidoc delete mode 100644 docs/reference/painless-api-reference/AbstractMap.SimpleImmutableEntry.asciidoc delete mode 100644 docs/reference/painless-api-reference/AbstractMap.asciidoc delete mode 100644 docs/reference/painless-api-reference/AbstractQueue.asciidoc delete mode 100644 docs/reference/painless-api-reference/AbstractSequentialList.asciidoc delete mode 100644 docs/reference/painless-api-reference/AbstractSet.asciidoc delete mode 100644 docs/reference/painless-api-reference/Annotation.asciidoc delete mode 100644 docs/reference/painless-api-reference/Appendable.asciidoc delete mode 100644 docs/reference/painless-api-reference/ArithmeticException.asciidoc delete mode 100644 docs/reference/painless-api-reference/ArrayDeque.asciidoc delete mode 100644 docs/reference/painless-api-reference/ArrayIndexOutOfBoundsException.asciidoc delete mode 100644 docs/reference/painless-api-reference/ArrayList.asciidoc delete mode 100644 docs/reference/painless-api-reference/ArrayStoreException.asciidoc delete mode 100644 docs/reference/painless-api-reference/Arrays.asciidoc delete mode 100644 docs/reference/painless-api-reference/AttributedCharacterIterator.Attribute.asciidoc delete mode 100644 docs/reference/painless-api-reference/AttributedCharacterIterator.asciidoc delete mode 100644 docs/reference/painless-api-reference/AttributedString.asciidoc delete mode 100644 docs/reference/painless-api-reference/Base64.Decoder.asciidoc delete mode 100644 docs/reference/painless-api-reference/Base64.Encoder.asciidoc delete mode 100644 docs/reference/painless-api-reference/Base64.asciidoc delete mode 100644 docs/reference/painless-api-reference/BaseStream.asciidoc delete mode 100644 docs/reference/painless-api-reference/BiConsumer.asciidoc delete mode 100644 docs/reference/painless-api-reference/BiFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/BiPredicate.asciidoc delete mode 100644 docs/reference/painless-api-reference/Bidi.asciidoc delete mode 100644 docs/reference/painless-api-reference/BigDecimal.asciidoc delete mode 100644 docs/reference/painless-api-reference/BigInteger.asciidoc delete mode 100644 docs/reference/painless-api-reference/BinaryOperator.asciidoc delete mode 100644 docs/reference/painless-api-reference/BitSet.asciidoc delete mode 100644 docs/reference/painless-api-reference/Boolean.asciidoc delete mode 100644 docs/reference/painless-api-reference/BooleanSupplier.asciidoc delete mode 100644 docs/reference/painless-api-reference/BreakIterator.asciidoc delete mode 100644 docs/reference/painless-api-reference/Byte.asciidoc delete mode 100644 docs/reference/painless-api-reference/BytesRef.asciidoc delete mode 100644 docs/reference/painless-api-reference/Calendar.Builder.asciidoc delete mode 100644 docs/reference/painless-api-reference/Calendar.asciidoc delete mode 100644 docs/reference/painless-api-reference/CharSequence.asciidoc delete mode 100644 docs/reference/painless-api-reference/Character.Subset.asciidoc delete mode 100644 docs/reference/painless-api-reference/Character.UnicodeBlock.asciidoc delete mode 100644 docs/reference/painless-api-reference/Character.UnicodeScript.asciidoc delete mode 100644 docs/reference/painless-api-reference/Character.asciidoc delete mode 100644 docs/reference/painless-api-reference/CharacterIterator.asciidoc delete mode 100644 docs/reference/painless-api-reference/ChoiceFormat.asciidoc delete mode 100644 docs/reference/painless-api-reference/ChronoField.asciidoc delete mode 100644 docs/reference/painless-api-reference/ChronoLocalDate.asciidoc delete mode 100644 docs/reference/painless-api-reference/ChronoLocalDateTime.asciidoc delete mode 100644 docs/reference/painless-api-reference/ChronoPeriod.asciidoc delete mode 100644 docs/reference/painless-api-reference/ChronoUnit.asciidoc delete mode 100644 docs/reference/painless-api-reference/ChronoZonedDateTime.asciidoc delete mode 100644 docs/reference/painless-api-reference/Chronology.asciidoc delete mode 100644 docs/reference/painless-api-reference/ClassCastException.asciidoc delete mode 100644 docs/reference/painless-api-reference/ClassNotFoundException.asciidoc delete mode 100644 docs/reference/painless-api-reference/Clock.asciidoc delete mode 100644 docs/reference/painless-api-reference/CloneNotSupportedException.asciidoc delete mode 100644 docs/reference/painless-api-reference/CollationElementIterator.asciidoc delete mode 100644 docs/reference/painless-api-reference/CollationKey.asciidoc delete mode 100644 docs/reference/painless-api-reference/Collator.asciidoc delete mode 100644 docs/reference/painless-api-reference/Collection.asciidoc delete mode 100644 docs/reference/painless-api-reference/Collections.asciidoc delete mode 100644 docs/reference/painless-api-reference/Collector.Characteristics.asciidoc delete mode 100644 docs/reference/painless-api-reference/Collector.asciidoc delete mode 100644 docs/reference/painless-api-reference/Collectors.asciidoc delete mode 100644 docs/reference/painless-api-reference/Comparable.asciidoc delete mode 100644 docs/reference/painless-api-reference/Comparator.asciidoc delete mode 100644 docs/reference/painless-api-reference/ConcurrentModificationException.asciidoc delete mode 100644 docs/reference/painless-api-reference/Consumer.asciidoc delete mode 100644 docs/reference/painless-api-reference/Currency.asciidoc delete mode 100644 docs/reference/painless-api-reference/Date.asciidoc delete mode 100644 docs/reference/painless-api-reference/DateFormat.Field.asciidoc delete mode 100644 docs/reference/painless-api-reference/DateFormat.asciidoc delete mode 100644 docs/reference/painless-api-reference/DateFormatSymbols.asciidoc delete mode 100644 docs/reference/painless-api-reference/DateTimeException.asciidoc delete mode 100644 docs/reference/painless-api-reference/DateTimeFormatter.asciidoc delete mode 100644 docs/reference/painless-api-reference/DateTimeFormatterBuilder.asciidoc delete mode 100644 docs/reference/painless-api-reference/DateTimeParseException.asciidoc delete mode 100644 docs/reference/painless-api-reference/DayOfWeek.asciidoc delete mode 100644 docs/reference/painless-api-reference/Debug.asciidoc delete mode 100644 docs/reference/painless-api-reference/DecimalFormat.asciidoc delete mode 100644 docs/reference/painless-api-reference/DecimalFormatSymbols.asciidoc delete mode 100644 docs/reference/painless-api-reference/DecimalStyle.asciidoc delete mode 100644 docs/reference/painless-api-reference/Deque.asciidoc delete mode 100644 docs/reference/painless-api-reference/Dictionary.asciidoc delete mode 100644 docs/reference/painless-api-reference/Double.asciidoc delete mode 100644 docs/reference/painless-api-reference/DoubleBinaryOperator.asciidoc delete mode 100644 docs/reference/painless-api-reference/DoubleConsumer.asciidoc delete mode 100644 docs/reference/painless-api-reference/DoubleFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/DoublePredicate.asciidoc delete mode 100644 docs/reference/painless-api-reference/DoubleStream.Builder.asciidoc delete mode 100644 docs/reference/painless-api-reference/DoubleStream.asciidoc delete mode 100644 docs/reference/painless-api-reference/DoubleSummaryStatistics.asciidoc delete mode 100644 docs/reference/painless-api-reference/DoubleSupplier.asciidoc delete mode 100644 docs/reference/painless-api-reference/DoubleToIntFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/DoubleToLongFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/DoubleUnaryOperator.asciidoc delete mode 100644 docs/reference/painless-api-reference/DuplicateFormatFlagsException.asciidoc delete mode 100644 docs/reference/painless-api-reference/Duration.asciidoc delete mode 100644 docs/reference/painless-api-reference/EmptyStackException.asciidoc delete mode 100644 docs/reference/painless-api-reference/Enum.asciidoc delete mode 100644 docs/reference/painless-api-reference/EnumConstantNotPresentException.asciidoc delete mode 100644 docs/reference/painless-api-reference/Enumeration.asciidoc delete mode 100644 docs/reference/painless-api-reference/Era.asciidoc delete mode 100644 docs/reference/painless-api-reference/EventListener.asciidoc delete mode 100644 docs/reference/painless-api-reference/EventListenerProxy.asciidoc delete mode 100644 docs/reference/painless-api-reference/EventObject.asciidoc delete mode 100644 docs/reference/painless-api-reference/Exception.asciidoc delete mode 100644 docs/reference/painless-api-reference/FieldPosition.asciidoc delete mode 100644 docs/reference/painless-api-reference/Float.asciidoc delete mode 100644 docs/reference/painless-api-reference/Format.Field.asciidoc delete mode 100644 docs/reference/painless-api-reference/Format.asciidoc delete mode 100644 docs/reference/painless-api-reference/FormatFlagsConversionMismatchException.asciidoc delete mode 100644 docs/reference/painless-api-reference/FormatStyle.asciidoc delete mode 100644 docs/reference/painless-api-reference/Formattable.asciidoc delete mode 100644 docs/reference/painless-api-reference/FormattableFlags.asciidoc delete mode 100644 docs/reference/painless-api-reference/Formatter.BigDecimalLayoutForm.asciidoc delete mode 100644 docs/reference/painless-api-reference/Formatter.asciidoc delete mode 100644 docs/reference/painless-api-reference/FormatterClosedException.asciidoc delete mode 100644 docs/reference/painless-api-reference/Function.asciidoc delete mode 100644 docs/reference/painless-api-reference/GregorianCalendar.asciidoc delete mode 100644 docs/reference/painless-api-reference/HashMap.asciidoc delete mode 100644 docs/reference/painless-api-reference/HashSet.asciidoc delete mode 100644 docs/reference/painless-api-reference/Hashtable.asciidoc delete mode 100644 docs/reference/painless-api-reference/HijrahChronology.asciidoc delete mode 100644 docs/reference/painless-api-reference/HijrahDate.asciidoc delete mode 100644 docs/reference/painless-api-reference/HijrahEra.asciidoc delete mode 100644 docs/reference/painless-api-reference/IdentityHashMap.asciidoc delete mode 100644 docs/reference/painless-api-reference/IllegalAccessException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IllegalArgumentException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IllegalFormatCodePointException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IllegalFormatConversionException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IllegalFormatException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IllegalFormatFlagsException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IllegalFormatPrecisionException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IllegalFormatWidthException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IllegalMonitorStateException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IllegalStateException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IllegalThreadStateException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IllformedLocaleException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IndexOutOfBoundsException.asciidoc delete mode 100644 docs/reference/painless-api-reference/InputMismatchException.asciidoc delete mode 100644 docs/reference/painless-api-reference/Instant.asciidoc delete mode 100644 docs/reference/painless-api-reference/InstantiationException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IntBinaryOperator.asciidoc delete mode 100644 docs/reference/painless-api-reference/IntConsumer.asciidoc delete mode 100644 docs/reference/painless-api-reference/IntFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/IntPredicate.asciidoc delete mode 100644 docs/reference/painless-api-reference/IntStream.Builder.asciidoc delete mode 100644 docs/reference/painless-api-reference/IntStream.asciidoc delete mode 100644 docs/reference/painless-api-reference/IntSummaryStatistics.asciidoc delete mode 100644 docs/reference/painless-api-reference/IntSupplier.asciidoc delete mode 100644 docs/reference/painless-api-reference/IntToDoubleFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/IntToLongFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/IntUnaryOperator.asciidoc delete mode 100644 docs/reference/painless-api-reference/Integer.asciidoc delete mode 100644 docs/reference/painless-api-reference/InterruptedException.asciidoc delete mode 100644 docs/reference/painless-api-reference/IsoChronology.asciidoc delete mode 100644 docs/reference/painless-api-reference/IsoEra.asciidoc delete mode 100644 docs/reference/painless-api-reference/IsoFields.asciidoc delete mode 100644 docs/reference/painless-api-reference/Iterable.asciidoc delete mode 100644 docs/reference/painless-api-reference/Iterator.asciidoc delete mode 100644 docs/reference/painless-api-reference/JapaneseChronology.asciidoc delete mode 100644 docs/reference/painless-api-reference/JapaneseDate.asciidoc delete mode 100644 docs/reference/painless-api-reference/JapaneseEra.asciidoc delete mode 100644 docs/reference/painless-api-reference/JulianFields.asciidoc delete mode 100644 docs/reference/painless-api-reference/LinkedHashMap.asciidoc delete mode 100644 docs/reference/painless-api-reference/LinkedHashSet.asciidoc delete mode 100644 docs/reference/painless-api-reference/LinkedList.asciidoc delete mode 100644 docs/reference/painless-api-reference/List.asciidoc delete mode 100644 docs/reference/painless-api-reference/ListIterator.asciidoc delete mode 100644 docs/reference/painless-api-reference/LocalDate.asciidoc delete mode 100644 docs/reference/painless-api-reference/LocalDateTime.asciidoc delete mode 100644 docs/reference/painless-api-reference/LocalTime.asciidoc delete mode 100644 docs/reference/painless-api-reference/Locale.Builder.asciidoc delete mode 100644 docs/reference/painless-api-reference/Locale.Category.asciidoc delete mode 100644 docs/reference/painless-api-reference/Locale.FilteringMode.asciidoc delete mode 100644 docs/reference/painless-api-reference/Locale.LanguageRange.asciidoc delete mode 100644 docs/reference/painless-api-reference/Locale.asciidoc delete mode 100644 docs/reference/painless-api-reference/Long.asciidoc delete mode 100644 docs/reference/painless-api-reference/LongBinaryOperator.asciidoc delete mode 100644 docs/reference/painless-api-reference/LongConsumer.asciidoc delete mode 100644 docs/reference/painless-api-reference/LongFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/LongPredicate.asciidoc delete mode 100644 docs/reference/painless-api-reference/LongStream.Builder.asciidoc delete mode 100644 docs/reference/painless-api-reference/LongStream.asciidoc delete mode 100644 docs/reference/painless-api-reference/LongSummaryStatistics.asciidoc delete mode 100644 docs/reference/painless-api-reference/LongSupplier.asciidoc delete mode 100644 docs/reference/painless-api-reference/LongToDoubleFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/LongToIntFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/LongUnaryOperator.asciidoc delete mode 100644 docs/reference/painless-api-reference/Map.Entry.asciidoc delete mode 100644 docs/reference/painless-api-reference/Map.asciidoc delete mode 100644 docs/reference/painless-api-reference/Matcher.asciidoc delete mode 100644 docs/reference/painless-api-reference/Math.asciidoc delete mode 100644 docs/reference/painless-api-reference/MathContext.asciidoc delete mode 100644 docs/reference/painless-api-reference/MessageFormat.Field.asciidoc delete mode 100644 docs/reference/painless-api-reference/MessageFormat.asciidoc delete mode 100644 docs/reference/painless-api-reference/MinguoChronology.asciidoc delete mode 100644 docs/reference/painless-api-reference/MinguoDate.asciidoc delete mode 100644 docs/reference/painless-api-reference/MinguoEra.asciidoc delete mode 100644 docs/reference/painless-api-reference/MissingFormatArgumentException.asciidoc delete mode 100644 docs/reference/painless-api-reference/MissingFormatWidthException.asciidoc delete mode 100644 docs/reference/painless-api-reference/MissingResourceException.asciidoc delete mode 100644 docs/reference/painless-api-reference/Month.asciidoc delete mode 100644 docs/reference/painless-api-reference/MonthDay.asciidoc delete mode 100644 docs/reference/painless-api-reference/NavigableMap.asciidoc delete mode 100644 docs/reference/painless-api-reference/NavigableSet.asciidoc delete mode 100644 docs/reference/painless-api-reference/NegativeArraySizeException.asciidoc delete mode 100644 docs/reference/painless-api-reference/NoSuchElementException.asciidoc delete mode 100644 docs/reference/painless-api-reference/NoSuchFieldException.asciidoc delete mode 100644 docs/reference/painless-api-reference/NoSuchMethodException.asciidoc delete mode 100644 docs/reference/painless-api-reference/Normalizer.Form.asciidoc delete mode 100644 docs/reference/painless-api-reference/Normalizer.asciidoc delete mode 100644 docs/reference/painless-api-reference/NullPointerException.asciidoc delete mode 100644 docs/reference/painless-api-reference/Number.asciidoc delete mode 100644 docs/reference/painless-api-reference/NumberFormat.Field.asciidoc delete mode 100644 docs/reference/painless-api-reference/NumberFormat.asciidoc delete mode 100644 docs/reference/painless-api-reference/NumberFormatException.asciidoc delete mode 100644 docs/reference/painless-api-reference/ObjDoubleConsumer.asciidoc delete mode 100644 docs/reference/painless-api-reference/ObjIntConsumer.asciidoc delete mode 100644 docs/reference/painless-api-reference/ObjLongConsumer.asciidoc delete mode 100644 docs/reference/painless-api-reference/Object.asciidoc delete mode 100644 docs/reference/painless-api-reference/Objects.asciidoc delete mode 100644 docs/reference/painless-api-reference/Observable.asciidoc delete mode 100644 docs/reference/painless-api-reference/Observer.asciidoc delete mode 100644 docs/reference/painless-api-reference/OffsetDateTime.asciidoc delete mode 100644 docs/reference/painless-api-reference/OffsetTime.asciidoc delete mode 100644 docs/reference/painless-api-reference/Optional.asciidoc delete mode 100644 docs/reference/painless-api-reference/OptionalDouble.asciidoc delete mode 100644 docs/reference/painless-api-reference/OptionalInt.asciidoc delete mode 100644 docs/reference/painless-api-reference/OptionalLong.asciidoc delete mode 100644 docs/reference/painless-api-reference/ParseException.asciidoc delete mode 100644 docs/reference/painless-api-reference/ParsePosition.asciidoc delete mode 100644 docs/reference/painless-api-reference/Pattern.asciidoc delete mode 100644 docs/reference/painless-api-reference/Period.asciidoc delete mode 100644 docs/reference/painless-api-reference/Predicate.asciidoc delete mode 100644 docs/reference/painless-api-reference/PrimitiveIterator.OfDouble.asciidoc delete mode 100644 docs/reference/painless-api-reference/PrimitiveIterator.OfInt.asciidoc delete mode 100644 docs/reference/painless-api-reference/PrimitiveIterator.OfLong.asciidoc delete mode 100644 docs/reference/painless-api-reference/PrimitiveIterator.asciidoc delete mode 100644 docs/reference/painless-api-reference/PriorityQueue.asciidoc delete mode 100644 docs/reference/painless-api-reference/Queue.asciidoc delete mode 100644 docs/reference/painless-api-reference/Random.asciidoc delete mode 100644 docs/reference/painless-api-reference/RandomAccess.asciidoc delete mode 100644 docs/reference/painless-api-reference/ReflectiveOperationException.asciidoc delete mode 100644 docs/reference/painless-api-reference/ResolverStyle.asciidoc delete mode 100644 docs/reference/painless-api-reference/RoundingMode.asciidoc delete mode 100644 docs/reference/painless-api-reference/RuleBasedCollator.asciidoc delete mode 100644 docs/reference/painless-api-reference/RuntimeException.asciidoc delete mode 100644 docs/reference/painless-api-reference/SecurityException.asciidoc delete mode 100644 docs/reference/painless-api-reference/Set.asciidoc delete mode 100644 docs/reference/painless-api-reference/Short.asciidoc delete mode 100644 docs/reference/painless-api-reference/SignStyle.asciidoc delete mode 100644 docs/reference/painless-api-reference/SimpleDateFormat.asciidoc delete mode 100644 docs/reference/painless-api-reference/SimpleTimeZone.asciidoc delete mode 100644 docs/reference/painless-api-reference/SortedMap.asciidoc delete mode 100644 docs/reference/painless-api-reference/SortedSet.asciidoc delete mode 100644 docs/reference/painless-api-reference/Spliterator.OfDouble.asciidoc delete mode 100644 docs/reference/painless-api-reference/Spliterator.OfInt.asciidoc delete mode 100644 docs/reference/painless-api-reference/Spliterator.OfLong.asciidoc delete mode 100644 docs/reference/painless-api-reference/Spliterator.OfPrimitive.asciidoc delete mode 100644 docs/reference/painless-api-reference/Spliterator.asciidoc delete mode 100644 docs/reference/painless-api-reference/Spliterators.asciidoc delete mode 100644 docs/reference/painless-api-reference/Stack.asciidoc delete mode 100644 docs/reference/painless-api-reference/StackTraceElement.asciidoc delete mode 100644 docs/reference/painless-api-reference/Stream.Builder.asciidoc delete mode 100644 docs/reference/painless-api-reference/Stream.asciidoc delete mode 100644 docs/reference/painless-api-reference/StrictMath.asciidoc delete mode 100644 docs/reference/painless-api-reference/String.asciidoc delete mode 100644 docs/reference/painless-api-reference/StringBuffer.asciidoc delete mode 100644 docs/reference/painless-api-reference/StringBuilder.asciidoc delete mode 100644 docs/reference/painless-api-reference/StringCharacterIterator.asciidoc delete mode 100644 docs/reference/painless-api-reference/StringIndexOutOfBoundsException.asciidoc delete mode 100644 docs/reference/painless-api-reference/StringJoiner.asciidoc delete mode 100644 docs/reference/painless-api-reference/StringTokenizer.asciidoc delete mode 100644 docs/reference/painless-api-reference/Supplier.asciidoc delete mode 100644 docs/reference/painless-api-reference/System.asciidoc delete mode 100644 docs/reference/painless-api-reference/Temporal.asciidoc delete mode 100644 docs/reference/painless-api-reference/TemporalAccessor.asciidoc delete mode 100644 docs/reference/painless-api-reference/TemporalAdjuster.asciidoc delete mode 100644 docs/reference/painless-api-reference/TemporalAdjusters.asciidoc delete mode 100644 docs/reference/painless-api-reference/TemporalAmount.asciidoc delete mode 100644 docs/reference/painless-api-reference/TemporalField.asciidoc delete mode 100644 docs/reference/painless-api-reference/TemporalQueries.asciidoc delete mode 100644 docs/reference/painless-api-reference/TemporalQuery.asciidoc delete mode 100644 docs/reference/painless-api-reference/TemporalUnit.asciidoc delete mode 100644 docs/reference/painless-api-reference/TextStyle.asciidoc delete mode 100644 docs/reference/painless-api-reference/ThaiBuddhistChronology.asciidoc delete mode 100644 docs/reference/painless-api-reference/ThaiBuddhistDate.asciidoc delete mode 100644 docs/reference/painless-api-reference/ThaiBuddhistEra.asciidoc delete mode 100644 docs/reference/painless-api-reference/TimeZone.asciidoc delete mode 100644 docs/reference/painless-api-reference/ToDoubleBiFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/ToDoubleFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/ToIntBiFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/ToIntFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/ToLongBiFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/ToLongFunction.asciidoc delete mode 100644 docs/reference/painless-api-reference/TooManyListenersException.asciidoc delete mode 100644 docs/reference/painless-api-reference/TreeMap.asciidoc delete mode 100644 docs/reference/painless-api-reference/TreeSet.asciidoc delete mode 100644 docs/reference/painless-api-reference/TypeNotPresentException.asciidoc delete mode 100644 docs/reference/painless-api-reference/UUID.asciidoc delete mode 100644 docs/reference/painless-api-reference/UnaryOperator.asciidoc delete mode 100644 docs/reference/painless-api-reference/UnknownFormatConversionException.asciidoc delete mode 100644 docs/reference/painless-api-reference/UnknownFormatFlagsException.asciidoc delete mode 100644 docs/reference/painless-api-reference/UnsupportedOperationException.asciidoc delete mode 100644 docs/reference/painless-api-reference/UnsupportedTemporalTypeException.asciidoc delete mode 100644 docs/reference/painless-api-reference/ValueRange.asciidoc delete mode 100644 docs/reference/painless-api-reference/Vector.asciidoc delete mode 100644 docs/reference/painless-api-reference/WeekFields.asciidoc delete mode 100644 docs/reference/painless-api-reference/Year.asciidoc delete mode 100644 docs/reference/painless-api-reference/YearMonth.asciidoc delete mode 100644 docs/reference/painless-api-reference/ZoneId.asciidoc delete mode 100644 docs/reference/painless-api-reference/ZoneOffset.asciidoc delete mode 100644 docs/reference/painless-api-reference/ZoneOffsetTransition.asciidoc delete mode 100644 docs/reference/painless-api-reference/ZoneOffsetTransitionRule.TimeDefinition.asciidoc delete mode 100644 docs/reference/painless-api-reference/ZoneOffsetTransitionRule.asciidoc delete mode 100644 docs/reference/painless-api-reference/ZoneRules.asciidoc delete mode 100644 docs/reference/painless-api-reference/ZoneRulesException.asciidoc delete mode 100644 docs/reference/painless-api-reference/ZoneRulesProvider.asciidoc delete mode 100644 docs/reference/painless-api-reference/ZonedDateTime.asciidoc delete mode 100644 docs/reference/painless-api-reference/index.asciidoc delete mode 100644 docs/reference/painless-api-reference/org.elasticsearch.common.geo.GeoPoint.asciidoc delete mode 100644 docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Booleans.asciidoc delete mode 100644 docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.BytesRefs.asciidoc delete mode 100644 docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Doubles.asciidoc delete mode 100644 docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints.asciidoc delete mode 100644 docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Longs.asciidoc delete mode 100644 docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Strings.asciidoc delete mode 100644 docs/reference/painless-api-reference/org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues.asciidoc delete mode 100644 docs/reference/painless-api-reference/org.elasticsearch.painless.FeatureTest.asciidoc delete mode 100644 docs/reference/painless-api-reference/org.joda.time.ReadableDateTime.asciidoc delete mode 100644 docs/reference/painless-api-reference/org.joda.time.ReadableInstant.asciidoc diff --git a/docs/Versions.asciidoc b/docs/Versions.asciidoc index e8bba54d40fa0..9aa9b54eb73e4 100644 --- a/docs/Versions.asciidoc +++ b/docs/Versions.asciidoc @@ -13,6 +13,7 @@ release-state can be: released | prerelease | unreleased :ref: https://www.elastic.co/guide/en/elasticsearch/reference/{branch} :defguide: https://www.elastic.co/guide/en/elasticsearch/guide/master +:painless: https://www.elastic.co/guide/en/elasticsearch/painless/master :plugins: https://www.elastic.co/guide/en/elasticsearch/plugins/{branch} :javaclient: https://www.elastic.co/guide/en/elasticsearch/client/java-api/{branch} :xpack: https://www.elastic.co/guide/en/x-pack/5.4 diff --git a/docs/reference/index.asciidoc b/docs/reference/index.asciidoc index cb97bcf6d9f1f..77a9aefe29a1a 100644 --- a/docs/reference/index.asciidoc +++ b/docs/reference/index.asciidoc @@ -44,6 +44,4 @@ include::glossary.asciidoc[] include::release-notes.asciidoc[] -include::painless-api-reference.asciidoc[] - include::redirects.asciidoc[] diff --git a/docs/reference/modules/scripting.asciidoc b/docs/reference/modules/scripting.asciidoc index c6a8a252b9334..44696ea94bb89 100644 --- a/docs/reference/modules/scripting.asciidoc +++ b/docs/reference/modules/scripting.asciidoc @@ -77,10 +77,6 @@ include::scripting/security.asciidoc[] include::scripting/painless.asciidoc[] -include::scripting/painless-syntax.asciidoc[] - -include::scripting/painless-debugging.asciidoc[] - include::scripting/expression.asciidoc[] include::scripting/engine.asciidoc[] diff --git a/docs/reference/modules/scripting/painless-debugging.asciidoc b/docs/reference/modules/scripting/painless-debugging.asciidoc deleted file mode 100644 index 5bd7fe7354175..0000000000000 --- a/docs/reference/modules/scripting/painless-debugging.asciidoc +++ /dev/null @@ -1,96 +0,0 @@ -[[modules-scripting-painless-debugging]] -=== Painless Debugging - -experimental[The Painless scripting language is new and is still marked as experimental. The syntax or API may be changed in the future in non-backwards compatible ways if required.] - -==== Debug.Explain - -Painless doesn't have a -https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop[REPL] -and while it'd be nice for it to have one one day, it wouldn't tell you the -whole story around debugging painless scripts embedded in Elasticsearch because -the data that the scripts have access to or "context" is so important. For now -the best way to debug embedded scripts is by throwing exceptions at choice -places. While you can throw your own exceptions -(`throw new Exception('whatever')`), Painless's sandbox prevents you from -accessing useful information like the type of an object. So Painless has a -utility method, `Debug.explain` which throws the exception for you. For -example, you can use the <> to explore the context available to -a <>. - -[source,js] ---------------------------------------------------------- -PUT /hockey/player/1?refresh -{"first":"johnny","last":"gaudreau","goals":[9,27,1],"assists":[17,46,0],"gp":[26,82,1]} - -POST /hockey/player/1/_explain -{ - "query": { - "script": { - "script": "Debug.explain(doc.goals)" - } - } -} ---------------------------------------------------------- -// CONSOLE -// TEST[s/_explain/_explain?error_trace=false/ catch:/painless_explain_error/] -// The test system sends error_trace=true by default for easier debugging so -// we have to override it to get a normal shaped response - -Which shows that the class of `doc.first` is -`org.elasticsearch.index.fielddata.ScriptDocValues.Longs` by responding with: - -[source,js] ---------------------------------------------------------- -{ - "error": { - "type": "script_exception", - "to_string": "[1, 9, 27]", - "painless_class": "org.elasticsearch.index.fielddata.ScriptDocValues.Longs", - "java_class": "org.elasticsearch.index.fielddata.ScriptDocValues$Longs", - ... - }, - "status": 500 -} ---------------------------------------------------------- -// TESTRESPONSE[s/\.\.\./"script_stack": $body.error.script_stack, "script": $body.error.script, "lang": $body.error.lang, "caused_by": $body.error.caused_by, "root_cause": $body.error.root_cause, "reason": $body.error.reason/] - -You can use the same trick to see that `_source` is a `LinkedHashMap` -in the `_update` API: - -[source,js] ---------------------------------------------------------- -POST /hockey/player/1/_update -{ - "script": "Debug.explain(ctx._source)" -} ---------------------------------------------------------- -// CONSOLE -// TEST[continued s/_update/_update?error_trace=false/ catch:/painless_explain_error/] - -The response looks like: - -[source,js] ---------------------------------------------------------- -{ - "error" : { - "root_cause": ..., - "type": "illegal_argument_exception", - "reason": "failed to execute script", - "caused_by": { - "type": "script_exception", - "to_string": "{gp=[26, 82, 1], last=gaudreau, assists=[17, 46, 0], first=johnny, goals=[9, 27, 1]}", - "painless_class": "LinkedHashMap", - "java_class": "java.util.LinkedHashMap", - ... - } - }, - "status": 400 -} ---------------------------------------------------------- -// TESTRESPONSE[s/"root_cause": \.\.\./"root_cause": $body.error.root_cause/] -// TESTRESPONSE[s/\.\.\./"script_stack": $body.error.caused_by.script_stack, "script": $body.error.caused_by.script, "lang": $body.error.caused_by.lang, "caused_by": $body.error.caused_by.caused_by, "reason": $body.error.caused_by.reason/] -// TESTRESPONSE[s/"to_string": ".+"/"to_string": $body.error.caused_by.to_string/] - -Once you have a class you can go to <> to see a list of -available methods. diff --git a/docs/reference/modules/scripting/painless-syntax.asciidoc b/docs/reference/modules/scripting/painless-syntax.asciidoc deleted file mode 100644 index 15656d72f2ca8..0000000000000 --- a/docs/reference/modules/scripting/painless-syntax.asciidoc +++ /dev/null @@ -1,244 +0,0 @@ -[[modules-scripting-painless-syntax]] -=== Painless Syntax - -experimental[The Painless scripting language is new and is still marked as experimental. The syntax or API may be changed in the future in non-backwards compatible ways if required.] - -[float] -[[painless-types]] -=== Variable types - -Painless supports all of https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html[Java's types], -including array types, but adds some additional built-in types. - -[float] -[[painless-def]] -==== Def - -The dynamic type `def` serves as a placeholder for any other type. It adopts the behavior -of whatever runtime type it represents. - -[float] -[[painless-strings]] -==== String - -String constants can be declared with single quotes, to avoid escaping horrors with JSON: - -[source,painless] ---------------------------------------------------------- -def mystring = 'foo'; ---------------------------------------------------------- - -[float] -[[painless-arrays]] -==== Arrays - -Arrays can be subscripted starting from `0` for traditional array access or with -negative numbers to starting from the back of the array. So the following -returns `2`. - -[source,painless] ---------------------------------------------------------- -int[] x = new int[5]; -x[0]++; -x[-5]++; -return x[0]; ---------------------------------------------------------- - - -[float] -[[painless-lists]] -==== List - -Lists can be created explicitly (e.g. `new ArrayList()`) or initialized similar to Groovy: - -[source,painless] ---------------------------------------------------------- -def list = [1,2,3]; ---------------------------------------------------------- - -Lists can also be accessed similar to arrays. They support `.length` and -subscripts, including negative subscripts to read from the back of the list: - -[source,painless] ---------------------------------------------------------- -def list = [1,2,3]; -list[-1] = 5 -return list[0] ---------------------------------------------------------- - -[float] -[[painless-maps]] -==== Map - -Maps can be created explicitly (e.g. `new HashMap()`) or initialized similar to Groovy: - -[source,painless] ---------------------------------------------------------- -def person = ['name': 'Joe', 'age': 63]; ---------------------------------------------------------- - -Map keys can also be accessed as properties. - -[source,painless] ---------------------------------------------------------- -def person = ['name': 'Joe', 'age': 63]; -person.retired = true; -return person.name ---------------------------------------------------------- - -Map keys can also be accessed via subscript (for keys containing special characters): - -[source,painless] ---------------------------------------------------------- -return map['something-absurd!'] ---------------------------------------------------------- - -[float] -[[painless-pattern]] -==== Pattern - -Regular expression constants are directly supported: - -[source,painless] ---------------------------------------------------------- -Pattern p = /[aeiou]/ ---------------------------------------------------------- - -Patterns can only be created via this mechanism. This ensures fast performance, regular expressions -are always constants and compiled efficiently a single time. - -[float] -[[modules-scripting-painless-regex-flags]] -==== Pattern flags - -You can define flags on patterns in Painless by adding characters after the -trailing `/` like `/foo/i` or `/foo \w #comment/iUx`. Painless exposes all the -flags from -https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html[Java's Pattern class] -using these characters: - -[cols="<,<,<",options="header",] -|======================================================================= -| Character | Java Constant | Example -|`c` | CANON_EQ | `'å' ==~ /å/c` (open in hex editor to see) -|`i` | CASE_INSENSITIVE | `'A' ==~ /a/i` -|`l` | LITERAL | `'[a]' ==~ /[a]/l` -|`m` | MULTILINE | `'a\nb\nc' =~ /^b$/m` -|`s` | DOTALL (aka single line) | `'a\nb\nc' =~ /.b./s` -|`U` | UNICODE_CHARACTER_CLASS | `'Ɛ' ==~ /\\w/U` -|`u` | UNICODE_CASE | `'Ɛ' ==~ /ɛ/iu` -|`x` | COMMENTS (aka extended) | `'a' ==~ /a #comment/x` -|======================================================================= - -[float] -[[painless-deref]] -=== Dereferences - -Like lots of languages, Painless uses `.` to reference fields and call methods: - -[source,painless] ---------------------------------------------------------- -String foo = 'foo'; -TypeWithGetterOrPublicField bar = new TypeWithGetterOrPublicField() -return foo.length() + bar.x ---------------------------------------------------------- - -Like Groovy, Painless uses `?.` to perform null-safe references, with the -result being `null` if the left hand side is null: - -[source,painless] ---------------------------------------------------------- -String foo = null; -return foo?.length() // Returns null ---------------------------------------------------------- - -Unlike Groovy, Painless doesn't support writing to null values with this -operator: - -[source,painless] ---------------------------------------------------------- -TypeWithSetterOrPublicField foo = null; -foo?.x = 'bar' // Compile error ---------------------------------------------------------- - -[float] -[[painless-operators]] -=== Operators - -All of Java's https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html[operators] are -supported with the same precedence, promotion, and semantics. - -There are only a few minor differences and add-ons: - -* `==` behaves as Java's for numeric types, but for non-numeric types acts as https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-[`Object.equals()`] -* `===` and `!==` support exact reference comparison (e.g. `x === y`) -* `=~` true if a portion of the text matches a pattern (e.g. `x =~ /b/`) -* `==~` true if the entire text matches a pattern (e.g. `x ==~ /[Bb]ob/`) - -The `?:` (aka Elvis) operator coalesces null values. So `x ?: 0` is `0` if `x` -is `null` and whatever value `x` has otherwise. It is a convenient way to write -default values like `doc['x'].value ?: 0` which is 0 if `x` is not in the -document being processed. It can also work with null safe dereferences to -efficiently handle null in chains. For example, -`doc['foo.keyword'].value?.length() ?: 0` is 0 if the document being processed -doesn't have a `foo.keyword` field but is the length of that field if it does. -Lastly, `?:` is lazy so the right hand side is not evaluated at all if the left -hand side isn't null. - -NOTE: Unlike Groovy, Painless' ++?:++ operator only coalesces `null`, not `false` -or http://groovy-lang.org/semantics.html#Groovy-Truth[falsy] values. Strictly -speaking Painless' ++?:++ is more like Kotlin's ++?:++ than Groovy's ++?:++. - -NOTE: The result of `?.` and `?:` can't be assigned to primitives. So -`int[] someArray = null; int l = someArray?.length` and -`int s = params.size ?: 100` don't work. Do -`def someArray = null; def l = someArray?.length` and -`def s = params.size ?: 100` instead. - - -[float] -[[painless-control-flow]] -=== Control flow - -Java's https://docs.oracle.com/javase/tutorial/java/nutsandbolts/flow.html[control flow statements] are supported, with the exception -of the `switch` statement. - -In addition to Java's `enhanced for` loop, the `for in` syntax from groovy can also be used: - -[source,painless] ---------------------------------------------------------- -for (def item : list) { - ... -} ---------------------------------------------------------- - -[float] -[[painless-functions]] -=== Functions - -Functions can be declared at the beginning of the script, for example: - -[source,painless] ---------------------------------------------------------- -boolean isNegative(def x) { x < 0 } -... -if (isNegative(someVar)) { - ... -} ---------------------------------------------------------- - -[float] -[[painless-lambda-expressions]] -=== Lambda expressions -Lambda expressions and method references work the same as https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html[Java's]. - -[source,painless] ---------------------------------------------------------- -list.removeIf(item -> item == 2); -list.removeIf((int item) -> item == 2); -list.removeIf((int item) -> { item == 2 }); -list.sort((x, y) -> x - y); -list.sort(Integer::compare); ---------------------------------------------------------- - -Method references to functions within the script can be accomplished using `this`, e.g. `list.sort(this::mycompare)`. diff --git a/docs/reference/modules/scripting/painless.asciidoc b/docs/reference/modules/scripting/painless.asciidoc index 638391ee908d5..0993701033b4b 100644 --- a/docs/reference/modules/scripting/painless.asciidoc +++ b/docs/reference/modules/scripting/painless.asciidoc @@ -3,384 +3,7 @@ experimental[The Painless scripting language is new and is still marked as experimental. The syntax or API may be changed in the future in non-backwards compatible ways if required.] -_Painless_ is a simple, secure scripting language available in Elasticsearch -by default. It is designed specifically for use with Elasticsearch and can -safely be used with `inline` and `stored` scripting, which is enabled by -default. +include::../../../painless/painless-description.asciidoc[] -The Painless syntax is similar to http://groovy-lang.org/index.html[Groovy]. - -You can use Painless anywhere a script can be used in Elasticsearch. It is the -default if you don't set the `lang` parameter but if you want to be explicit you -can set the `lang` parameter to `painless`. - -[[painless-features]] -[float] -== Painless Features - -* Fast performance: https://benchmarks.elastic.co/index.html#search_qps_scripts[several times faster] than the alternatives. - -* Safety: Fine-grained whitelist with method call/field granularity. See -<> for a complete list of available classes and methods. - -* Optional typing: Variables and parameters can use explicit types or the dynamic `def` type. - -* Syntax: Extends Java's syntax with a subset of Groovy for ease of use. See the <>. - -* Optimizations: Designed specifically for Elasticsearch scripting. - -[[painless-examples]] -[float] -== Painless Examples - -To illustrate how Painless works, let's load some hockey stats into an Elasticsearch index: - -[source,js] ----------------------------------------------------------------- -PUT hockey/player/_bulk?refresh -{"index":{"_id":1}} -{"first":"johnny","last":"gaudreau","goals":[9,27,1],"assists":[17,46,0],"gp":[26,82,1],"born":"1993/08/13"} -{"index":{"_id":2}} -{"first":"sean","last":"monohan","goals":[7,54,26],"assists":[11,26,13],"gp":[26,82,82],"born":"1994/10/12"} -{"index":{"_id":3}} -{"first":"jiri","last":"hudler","goals":[5,34,36],"assists":[11,62,42],"gp":[24,80,79],"born":"1984/01/04"} -{"index":{"_id":4}} -{"first":"micheal","last":"frolik","goals":[4,6,15],"assists":[8,23,15],"gp":[26,82,82],"born":"1988/02/17"} -{"index":{"_id":5}} -{"first":"sam","last":"bennett","goals":[5,0,0],"assists":[8,1,0],"gp":[26,1,0],"born":"1996/06/20"} -{"index":{"_id":6}} -{"first":"dennis","last":"wideman","goals":[0,26,15],"assists":[11,30,24],"gp":[26,81,82],"born":"1983/03/20"} -{"index":{"_id":7}} -{"first":"david","last":"jones","goals":[7,19,5],"assists":[3,17,4],"gp":[26,45,34],"born":"1984/08/10"} -{"index":{"_id":8}} -{"first":"tj","last":"brodie","goals":[2,14,7],"assists":[8,42,30],"gp":[26,82,82],"born":"1990/06/07"} -{"index":{"_id":39}} -{"first":"mark","last":"giordano","goals":[6,30,15],"assists":[3,30,24],"gp":[26,60,63],"born":"1983/10/03"} -{"index":{"_id":10}} -{"first":"mikael","last":"backlund","goals":[3,15,13],"assists":[6,24,18],"gp":[26,82,82],"born":"1989/03/17"} -{"index":{"_id":11}} -{"first":"joe","last":"colborne","goals":[3,18,13],"assists":[6,20,24],"gp":[26,67,82],"born":"1990/01/30"} ----------------------------------------------------------------- -// CONSOLE -// TESTSETUP - -[float] -=== Accessing Doc Values from Painless - -Document values can be accessed from a `Map` named `doc`. - -For example, the following script calculates a player's total goals. This example uses a strongly typed `int` and a `for` loop. - -[source,js] ----------------------------------------------------------------- -GET hockey/_search -{ - "query": { - "function_score": { - "script_score": { - "script": { - "lang": "painless", - "inline": "int total = 0; for (int i = 0; i < doc['goals'].length; ++i) { total += doc['goals'][i]; } return total;" - } - } - } - } -} ----------------------------------------------------------------- -// CONSOLE - -Alternatively, you could do the same thing using a script field instead of a function score: - -[source,js] ----------------------------------------------------------------- -GET hockey/_search -{ - "query": { - "match_all": {} - }, - "script_fields": { - "total_goals": { - "script": { - "lang": "painless", - "inline": "int total = 0; for (int i = 0; i < doc['goals'].length; ++i) { total += doc['goals'][i]; } return total;" - } - } - } -} ----------------------------------------------------------------- -// CONSOLE - -The following example uses a Painless script to sort the players by their combined first and last names. The names are accessed using -`doc['first'].value` and `doc['last'].value`. - -[source,js] ----------------------------------------------------------------- -GET hockey/_search -{ - "query": { - "match_all": {} - }, - "sort": { - "_script": { - "type": "string", - "order": "asc", - "script": { - "lang": "painless", - "inline": "doc['first.keyword'].value + ' ' + doc['last.keyword'].value" - } - } - } -} ----------------------------------------------------------------- -// CONSOLE - -[float] -=== Updating Fields with Painless - -You can also easily update fields. You access the original source for a field as `ctx._source.`. - -First, let's look at the source data for a player by submitting the following request: - -[source,js] ----------------------------------------------------------------- -GET hockey/_search -{ - "stored_fields": [ - "_id", - "_source" - ], - "query": { - "term": { - "_id": 1 - } - } -} ----------------------------------------------------------------- -// CONSOLE - -To change player 1's last name to `hockey`, simply set `ctx._source.last` to the new value: - -[source,js] ----------------------------------------------------------------- -POST hockey/player/1/_update -{ - "script": { - "lang": "painless", - "inline": "ctx._source.last = params.last", - "params": { - "last": "hockey" - } - } -} ----------------------------------------------------------------- -// CONSOLE - -You can also add fields to a document. For example, this script adds a new field that contains -the player's nickname, _hockey_. - -[source,js] ----------------------------------------------------------------- -POST hockey/player/1/_update -{ - "script": { - "lang": "painless", - "inline": "ctx._source.last = params.last; ctx._source.nick = params.nick", - "params": { - "last": "gaudreau", - "nick": "hockey" - } - } -} ----------------------------------------------------------------- -// CONSOLE - -[float] -[[modules-scripting-painless-dates]] -=== Dates - -Date fields are exposed as -<>s -so they support methods like -<>, -and -<>. -To get milliseconds since epoch call -<>. -For example, the following returns every hockey player's birth year: - -[source,js] ----------------------------------------------------------------- -GET hockey/_search -{ - "script_fields": { - "birth_year": { - "script": { - "inline": "doc.born.value.year" - } - } - } -} ----------------------------------------------------------------- -// CONSOLE - -[float] -[[modules-scripting-painless-regex]] -=== Regular expressions - -NOTE: Regexes are disabled by default because they circumvent Painless's -protection against long running and memory hungry scripts. To make matters -worse even innocuous looking regexes can have staggering performance and stack -depth behavior. They remain an amazing powerful tool but are too scary to enable -by default. To enable them yourself set `script.painless.regex.enabled: true` in -`elasticsearch.yml`. We'd like very much to have a safe alternative -implementation that can be enabled by default so check this space for later -developments! - -Painless's native support for regular expressions has syntax constructs: - -* `/pattern/`: Pattern literals create patterns. This is the only way to create -a pattern in painless. The pattern inside the ++/++'s are just -http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html[Java regular expressions]. -See <> for more. -* `=~`: The find operator return a `boolean`, `true` if a subsequence of the -text matches, `false` otherwise. -* `==~`: The match operator returns a `boolean`, `true` if the text matches, -`false` if it doesn't. - -Using the find operator (`=~`) you can update all hockey players with "b" in -their last name: - -[source,js] ----------------------------------------------------------------- -POST hockey/player/_update_by_query -{ - "script": { - "lang": "painless", - "inline": "if (ctx._source.last =~ /b/) {ctx._source.last += \"matched\"} else {ctx.op = 'noop'}" - } -} ----------------------------------------------------------------- -// CONSOLE - -Using the match operator (`==~`) you can update all the hockey players who's -names start with a consonant and end with a vowel: - -[source,js] ----------------------------------------------------------------- -POST hockey/player/_update_by_query -{ - "script": { - "lang": "painless", - "inline": "if (ctx._source.last ==~ /[^aeiou].*[aeiou]/) {ctx._source.last += \"matched\"} else {ctx.op = 'noop'}" - } -} ----------------------------------------------------------------- -// CONSOLE - -You can use the `Pattern.matcher` directly to get a `Matcher` instance and -remove all of the vowels in all of their last names: - -[source,js] ----------------------------------------------------------------- -POST hockey/player/_update_by_query -{ - "script": { - "lang": "painless", - "inline": "ctx._source.last = /[aeiou]/.matcher(ctx._source.last).replaceAll('')" - } -} ----------------------------------------------------------------- -// CONSOLE - -`Matcher.replaceAll` is just a call to Java's `Matcher`'s -http://docs.oracle.com/javase/8/docs/api/java/util/regex/Matcher.html#replaceAll-java.lang.String-[replaceAll] -method so it supports `$1` and `\1` for replacements: - -[source,js] ----------------------------------------------------------------- -POST hockey/player/_update_by_query -{ - "script": { - "lang": "painless", - "inline": "ctx._source.last = /n([aeiou])/.matcher(ctx._source.last).replaceAll('$1')" - } -} ----------------------------------------------------------------- -// CONSOLE - -If you need more control over replacements you can call `replaceAll` on a -`CharSequence` with a `Function` that builds the replacement. -This does not support `$1` or `\1` to access replacements because you already -have a reference to the matcher and can get them with `m.group(1)`. - -IMPORTANT: Calling `Matcher.find` inside of the function that builds the -replacement is rude and will likely break the replacement process. - -This will make all of the vowels in the hockey player's last names upper case: - -[source,js] ----------------------------------------------------------------- -POST hockey/player/_update_by_query -{ - "script": { - "lang": "painless", - "inline": "ctx._source.last = ctx._source.last.replaceAll(/[aeiou]/, m -> m.group().toUpperCase(Locale.ROOT))" - } -} ----------------------------------------------------------------- -// CONSOLE - -Or you can use the `CharSequence.replaceFirst` to make the first vowel in their -last names upper case: - -[source,js] ----------------------------------------------------------------- -POST hockey/player/_update_by_query -{ - "script": { - "lang": "painless", - "inline": "ctx._source.last = ctx._source.last.replaceFirst(/[aeiou]/, m -> m.group().toUpperCase(Locale.ROOT))" - } -} ----------------------------------------------------------------- -// CONSOLE - - -Note: all of the `_update_by_query` examples above could really do with a -`query` to limit the data that they pull back. While you *could* use a -<> it wouldn't be as efficient as using any other query -because script queries aren't able to use the inverted index to limit the -documents that they have to check. - -[float] -[[modules-scripting-painless-dispatch]] -=== How painless dispatches functions - -Painless uses receiver, name, and https://en.wikipedia.org/wiki/Arity[arity] -for method dispatch. For example, `s.foo(a, b)` is resolved by first getting -the class of `s` and then looking up the method `foo` with two parameters. This -is different from Groovy which uses the -https://en.wikipedia.org/wiki/Multiple_dispatch[runtime types] of the -parameters and Java which uses the compile time types of the parameters. - -The consequence of this that Painless doesn't support overloaded methods like -Java, leading to some trouble when it whitelists classes from the Java -standard library. For example, in Java and Groovy, `Matcher` has two methods: -`group(int)` and `group(String)`. Painless can't whitelist both of them methods -because they have the same name and the same number of parameters. So instead it -has <> and -<>. - -We have a few justifications for this different way of dispatching methods: - -1. It makes operating on `def` types simpler and, presumably, faster. Using -receiver, name, and arity means when Painless sees a call on a `def` object it -can dispatch the appropriate method without having to do expensive comparisons -of the types of the parameters. The same is true for invocations with `def` -typed parameters. -2. It keeps things consistent. It would be genuinely weird for Painless to -behave like Groovy if any `def` typed parameters were involved and Java -otherwise. It'd be slow for it to behave like Groovy all the time. -3. It keeps Painless maintainable. Adding the Java or Groovy like method -dispatch *feels* like it'd add a ton of complexity which'd make maintenance and -other improvements much more difficult. +Ready to start scripting with Painless? See {painless}/painless-getting-started.html[Getting Started with Painless] in the guide to the +{painless}/index.html[Painless Scripting Language]. \ No newline at end of file diff --git a/docs/reference/painless-api-reference.asciidoc b/docs/reference/painless-api-reference.asciidoc deleted file mode 100644 index f729c52a1bc29..0000000000000 --- a/docs/reference/painless-api-reference.asciidoc +++ /dev/null @@ -1,17 +0,0 @@ -["appendix",id="painless-api-reference"] -= Painless API Reference - -<> has a strict whitelist for methods and -classes to make sure that all painless scripts are secure and fast. Most of -these methods are exposed directly from the JRE while others are part of -Elasticsearch or Painless itself. Below is a list of all available methods -grouped under the classes on which you can call them. Clicking on the method -name takes you to the documentation for the method. - -NOTE: Methods defined in the JRE also have a `(java 9)` link which can be used -to see the method's documentation in Java 9 while clicking on the method's name -goes to the Java 8 documentation. Usually these aren't different but it is -worth going to the version that matches the version of Java you are using to -run Elasticsearch just in case. - -include::painless-api-reference/index.asciidoc[] diff --git a/docs/reference/painless-api-reference/AbstractChronology.asciidoc b/docs/reference/painless-api-reference/AbstractChronology.asciidoc deleted file mode 100644 index 3cc90ee65cde2..0000000000000 --- a/docs/reference/painless-api-reference/AbstractChronology.asciidoc +++ /dev/null @@ -1,7 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-AbstractChronology]]++AbstractChronology++:: -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/AbstractCollection.asciidoc b/docs/reference/painless-api-reference/AbstractCollection.asciidoc deleted file mode 100644 index 313c327ac07cc..0000000000000 --- a/docs/reference/painless-api-reference/AbstractCollection.asciidoc +++ /dev/null @@ -1,7 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-AbstractCollection]]++AbstractCollection++:: -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/AbstractList.asciidoc b/docs/reference/painless-api-reference/AbstractList.asciidoc deleted file mode 100644 index 9ce7c9a683001..0000000000000 --- a/docs/reference/painless-api-reference/AbstractList.asciidoc +++ /dev/null @@ -1,7 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-AbstractList]]++AbstractList++:: -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/AbstractMap.SimpleEntry.asciidoc b/docs/reference/painless-api-reference/AbstractMap.SimpleEntry.asciidoc deleted file mode 100644 index 327939e009d5e..0000000000000 --- a/docs/reference/painless-api-reference/AbstractMap.SimpleEntry.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-AbstractMap-SimpleEntry]]++AbstractMap.SimpleEntry++:: -* ++[[painless-api-reference-AbstractMap-SimpleEntry-AbstractMap.SimpleEntry-1]]link:{java8-javadoc}/java/util/AbstractMap$SimpleEntry.html#AbstractMap.SimpleEntry%2Djava.util.Map$Entry%2D[AbstractMap.SimpleEntry](<>)++ (link:{java9-javadoc}/java/util/AbstractMap$SimpleEntry.html#AbstractMap.SimpleEntry%2Djava.util.Map$Entry%2D[java 9]) -* ++[[painless-api-reference-AbstractMap-SimpleEntry-AbstractMap.SimpleEntry-2]]link:{java8-javadoc}/java/util/AbstractMap$SimpleEntry.html#AbstractMap.SimpleEntry%2Djava.lang.Object%2Djava.lang.Object%2D[AbstractMap.SimpleEntry](def, def)++ (link:{java9-javadoc}/java/util/AbstractMap$SimpleEntry.html#AbstractMap.SimpleEntry%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/AbstractMap.SimpleImmutableEntry.asciidoc b/docs/reference/painless-api-reference/AbstractMap.SimpleImmutableEntry.asciidoc deleted file mode 100644 index 94a4b0c307aa2..0000000000000 --- a/docs/reference/painless-api-reference/AbstractMap.SimpleImmutableEntry.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-AbstractMap-SimpleImmutableEntry]]++AbstractMap.SimpleImmutableEntry++:: -* ++[[painless-api-reference-AbstractMap-SimpleImmutableEntry-AbstractMap.SimpleImmutableEntry-1]]link:{java8-javadoc}/java/util/AbstractMap$SimpleImmutableEntry.html#AbstractMap.SimpleImmutableEntry%2Djava.util.Map$Entry%2D[AbstractMap.SimpleImmutableEntry](<>)++ (link:{java9-javadoc}/java/util/AbstractMap$SimpleImmutableEntry.html#AbstractMap.SimpleImmutableEntry%2Djava.util.Map$Entry%2D[java 9]) -* ++[[painless-api-reference-AbstractMap-SimpleImmutableEntry-AbstractMap.SimpleImmutableEntry-2]]link:{java8-javadoc}/java/util/AbstractMap$SimpleImmutableEntry.html#AbstractMap.SimpleImmutableEntry%2Djava.lang.Object%2Djava.lang.Object%2D[AbstractMap.SimpleImmutableEntry](def, def)++ (link:{java9-javadoc}/java/util/AbstractMap$SimpleImmutableEntry.html#AbstractMap.SimpleImmutableEntry%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/AbstractMap.asciidoc b/docs/reference/painless-api-reference/AbstractMap.asciidoc deleted file mode 100644 index ea3be0d125a98..0000000000000 --- a/docs/reference/painless-api-reference/AbstractMap.asciidoc +++ /dev/null @@ -1,7 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-AbstractMap]]++AbstractMap++:: -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/AbstractQueue.asciidoc b/docs/reference/painless-api-reference/AbstractQueue.asciidoc deleted file mode 100644 index 37fc53bd73d65..0000000000000 --- a/docs/reference/painless-api-reference/AbstractQueue.asciidoc +++ /dev/null @@ -1,7 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-AbstractQueue]]++AbstractQueue++:: -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/AbstractSequentialList.asciidoc b/docs/reference/painless-api-reference/AbstractSequentialList.asciidoc deleted file mode 100644 index 822b203851648..0000000000000 --- a/docs/reference/painless-api-reference/AbstractSequentialList.asciidoc +++ /dev/null @@ -1,7 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-AbstractSequentialList]]++AbstractSequentialList++:: -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/AbstractSet.asciidoc b/docs/reference/painless-api-reference/AbstractSet.asciidoc deleted file mode 100644 index 7baa3a58f7995..0000000000000 --- a/docs/reference/painless-api-reference/AbstractSet.asciidoc +++ /dev/null @@ -1,7 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-AbstractSet]]++AbstractSet++:: -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Annotation.asciidoc b/docs/reference/painless-api-reference/Annotation.asciidoc deleted file mode 100644 index 33252543951b8..0000000000000 --- a/docs/reference/painless-api-reference/Annotation.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Annotation]]++Annotation++:: -* ++[[painless-api-reference-Annotation-Annotation-1]]link:{java8-javadoc}/java/text/Annotation.html#Annotation%2Djava.lang.Object%2D[Annotation](<>)++ (link:{java9-javadoc}/java/text/Annotation.html#Annotation%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Annotation-getValue-0]]def link:{java8-javadoc}/java/text/Annotation.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/text/Annotation.html#getValue%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Appendable.asciidoc b/docs/reference/painless-api-reference/Appendable.asciidoc deleted file mode 100644 index 61fcbaf5a6052..0000000000000 --- a/docs/reference/painless-api-reference/Appendable.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Appendable]]++Appendable++:: -* ++[[painless-api-reference-Appendable-append-3]]<> link:{java8-javadoc}/java/lang/Appendable.html#append%2Djava.lang.CharSequence%2Dint%2Dint%2D[append](<>, int, int)++ (link:{java9-javadoc}/java/lang/Appendable.html#append%2Djava.lang.CharSequence%2Dint%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ArithmeticException.asciidoc b/docs/reference/painless-api-reference/ArithmeticException.asciidoc deleted file mode 100644 index f486289320570..0000000000000 --- a/docs/reference/painless-api-reference/ArithmeticException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ArithmeticException]]++ArithmeticException++:: -* ++[[painless-api-reference-ArithmeticException-ArithmeticException-0]]link:{java8-javadoc}/java/lang/ArithmeticException.html#ArithmeticException%2D%2D[ArithmeticException]()++ (link:{java9-javadoc}/java/lang/ArithmeticException.html#ArithmeticException%2D%2D[java 9]) -* ++[[painless-api-reference-ArithmeticException-ArithmeticException-1]]link:{java8-javadoc}/java/lang/ArithmeticException.html#ArithmeticException%2Djava.lang.String%2D[ArithmeticException](<>)++ (link:{java9-javadoc}/java/lang/ArithmeticException.html#ArithmeticException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ArrayDeque.asciidoc b/docs/reference/painless-api-reference/ArrayDeque.asciidoc deleted file mode 100644 index 668a86185e690..0000000000000 --- a/docs/reference/painless-api-reference/ArrayDeque.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ArrayDeque]]++ArrayDeque++:: -* ++[[painless-api-reference-ArrayDeque-ArrayDeque-0]]link:{java8-javadoc}/java/util/ArrayDeque.html#ArrayDeque%2D%2D[ArrayDeque]()++ (link:{java9-javadoc}/java/util/ArrayDeque.html#ArrayDeque%2D%2D[java 9]) -* ++[[painless-api-reference-ArrayDeque-ArrayDeque-1]]link:{java8-javadoc}/java/util/ArrayDeque.html#ArrayDeque%2Djava.util.Collection%2D[ArrayDeque](<>)++ (link:{java9-javadoc}/java/util/ArrayDeque.html#ArrayDeque%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-ArrayDeque-clone-0]]<> link:{java8-javadoc}/java/util/ArrayDeque.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/ArrayDeque.html#clone%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ArrayIndexOutOfBoundsException.asciidoc b/docs/reference/painless-api-reference/ArrayIndexOutOfBoundsException.asciidoc deleted file mode 100644 index 49b13361163f4..0000000000000 --- a/docs/reference/painless-api-reference/ArrayIndexOutOfBoundsException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ArrayIndexOutOfBoundsException]]++ArrayIndexOutOfBoundsException++:: -* ++[[painless-api-reference-ArrayIndexOutOfBoundsException-ArrayIndexOutOfBoundsException-0]]link:{java8-javadoc}/java/lang/ArrayIndexOutOfBoundsException.html#ArrayIndexOutOfBoundsException%2D%2D[ArrayIndexOutOfBoundsException]()++ (link:{java9-javadoc}/java/lang/ArrayIndexOutOfBoundsException.html#ArrayIndexOutOfBoundsException%2D%2D[java 9]) -* ++[[painless-api-reference-ArrayIndexOutOfBoundsException-ArrayIndexOutOfBoundsException-1]]link:{java8-javadoc}/java/lang/ArrayIndexOutOfBoundsException.html#ArrayIndexOutOfBoundsException%2Djava.lang.String%2D[ArrayIndexOutOfBoundsException](<>)++ (link:{java9-javadoc}/java/lang/ArrayIndexOutOfBoundsException.html#ArrayIndexOutOfBoundsException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ArrayList.asciidoc b/docs/reference/painless-api-reference/ArrayList.asciidoc deleted file mode 100644 index 90386a3742cf7..0000000000000 --- a/docs/reference/painless-api-reference/ArrayList.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ArrayList]]++ArrayList++:: -* ++[[painless-api-reference-ArrayList-ArrayList-0]]link:{java8-javadoc}/java/util/ArrayList.html#ArrayList%2D%2D[ArrayList]()++ (link:{java9-javadoc}/java/util/ArrayList.html#ArrayList%2D%2D[java 9]) -* ++[[painless-api-reference-ArrayList-ArrayList-1]]link:{java8-javadoc}/java/util/ArrayList.html#ArrayList%2Djava.util.Collection%2D[ArrayList](<>)++ (link:{java9-javadoc}/java/util/ArrayList.html#ArrayList%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-ArrayList-clone-0]]def link:{java8-javadoc}/java/util/ArrayList.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/ArrayList.html#clone%2D%2D[java 9]) -* ++[[painless-api-reference-ArrayList-trimToSize-0]]void link:{java8-javadoc}/java/util/ArrayList.html#trimToSize%2D%2D[trimToSize]()++ (link:{java9-javadoc}/java/util/ArrayList.html#trimToSize%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ArrayStoreException.asciidoc b/docs/reference/painless-api-reference/ArrayStoreException.asciidoc deleted file mode 100644 index 29f7a536eadf9..0000000000000 --- a/docs/reference/painless-api-reference/ArrayStoreException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ArrayStoreException]]++ArrayStoreException++:: -* ++[[painless-api-reference-ArrayStoreException-ArrayStoreException-0]]link:{java8-javadoc}/java/lang/ArrayStoreException.html#ArrayStoreException%2D%2D[ArrayStoreException]()++ (link:{java9-javadoc}/java/lang/ArrayStoreException.html#ArrayStoreException%2D%2D[java 9]) -* ++[[painless-api-reference-ArrayStoreException-ArrayStoreException-1]]link:{java8-javadoc}/java/lang/ArrayStoreException.html#ArrayStoreException%2Djava.lang.String%2D[ArrayStoreException](<>)++ (link:{java9-javadoc}/java/lang/ArrayStoreException.html#ArrayStoreException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Arrays.asciidoc b/docs/reference/painless-api-reference/Arrays.asciidoc deleted file mode 100644 index 97598d64d5790..0000000000000 --- a/docs/reference/painless-api-reference/Arrays.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Arrays]]++Arrays++:: -* ++[[painless-api-reference-Arrays-asList-1]]static <> link:{java8-javadoc}/java/util/Arrays.html#asList%2Djava.lang.Object:A%2D[asList](<>[])++ (link:{java9-javadoc}/java/util/Arrays.html#asList%2Djava.lang.Object:A%2D[java 9]) -* ++[[painless-api-reference-Arrays-deepEquals-2]]static boolean link:{java8-javadoc}/java/util/Arrays.html#deepEquals%2Djava.lang.Object:A%2Djava.lang.Object:A%2D[deepEquals](<>[], <>[])++ (link:{java9-javadoc}/java/util/Arrays.html#deepEquals%2Djava.lang.Object:A%2Djava.lang.Object:A%2D[java 9]) -* ++[[painless-api-reference-Arrays-deepHashCode-1]]static int link:{java8-javadoc}/java/util/Arrays.html#deepHashCode%2Djava.lang.Object:A%2D[deepHashCode](<>[])++ (link:{java9-javadoc}/java/util/Arrays.html#deepHashCode%2Djava.lang.Object:A%2D[java 9]) -* ++[[painless-api-reference-Arrays-deepToString-1]]static <> link:{java8-javadoc}/java/util/Arrays.html#deepToString%2Djava.lang.Object:A%2D[deepToString](<>[])++ (link:{java9-javadoc}/java/util/Arrays.html#deepToString%2Djava.lang.Object:A%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/AttributedCharacterIterator.Attribute.asciidoc b/docs/reference/painless-api-reference/AttributedCharacterIterator.Attribute.asciidoc deleted file mode 100644 index 56ccfbe6aabc1..0000000000000 --- a/docs/reference/painless-api-reference/AttributedCharacterIterator.Attribute.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-AttributedCharacterIterator-Attribute]]++AttributedCharacterIterator.Attribute++:: -** [[painless-api-reference-AttributedCharacterIterator-Attribute-INPUT_METHOD_SEGMENT]]static <> link:{java8-javadoc}/java/text/AttributedCharacterIterator$Attribute.html#INPUT_METHOD_SEGMENT[INPUT_METHOD_SEGMENT] (link:{java9-javadoc}/java/text/AttributedCharacterIterator$Attribute.html#INPUT_METHOD_SEGMENT[java 9]) -** [[painless-api-reference-AttributedCharacterIterator-Attribute-LANGUAGE]]static <> link:{java8-javadoc}/java/text/AttributedCharacterIterator$Attribute.html#LANGUAGE[LANGUAGE] (link:{java9-javadoc}/java/text/AttributedCharacterIterator$Attribute.html#LANGUAGE[java 9]) -** [[painless-api-reference-AttributedCharacterIterator-Attribute-READING]]static <> link:{java8-javadoc}/java/text/AttributedCharacterIterator$Attribute.html#READING[READING] (link:{java9-javadoc}/java/text/AttributedCharacterIterator$Attribute.html#READING[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/AttributedCharacterIterator.asciidoc b/docs/reference/painless-api-reference/AttributedCharacterIterator.asciidoc deleted file mode 100644 index a71d3906fa167..0000000000000 --- a/docs/reference/painless-api-reference/AttributedCharacterIterator.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-AttributedCharacterIterator]]++AttributedCharacterIterator++:: -* ++[[painless-api-reference-AttributedCharacterIterator-getAllAttributeKeys-0]]<> link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getAllAttributeKeys%2D%2D[getAllAttributeKeys]()++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getAllAttributeKeys%2D%2D[java 9]) -* ++[[painless-api-reference-AttributedCharacterIterator-getAttribute-1]]def link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getAttribute%2Djava.text.AttributedCharacterIterator$Attribute%2D[getAttribute](<>)++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getAttribute%2Djava.text.AttributedCharacterIterator$Attribute%2D[java 9]) -* ++[[painless-api-reference-AttributedCharacterIterator-getAttributes-0]]<> link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getAttributes%2D%2D[getAttributes]()++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getAttributes%2D%2D[java 9]) -* ++[[painless-api-reference-AttributedCharacterIterator-getRunLimit-0]]int link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getRunLimit%2D%2D[getRunLimit]()++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getRunLimit%2D%2D[java 9]) -* ++[[painless-api-reference-AttributedCharacterIterator-getRunLimit-1]]int link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getRunLimit%2Djava.util.Set%2D[getRunLimit](<>)++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getRunLimit%2Djava.util.Set%2D[java 9]) -* ++[[painless-api-reference-AttributedCharacterIterator-getRunStart-0]]int link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getRunStart%2D%2D[getRunStart]()++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getRunStart%2D%2D[java 9]) -* ++[[painless-api-reference-AttributedCharacterIterator-getRunStart-1]]int link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getRunStart%2Djava.util.Set%2D[getRunStart](<>)++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getRunStart%2Djava.util.Set%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/AttributedString.asciidoc b/docs/reference/painless-api-reference/AttributedString.asciidoc deleted file mode 100644 index 0393838a73717..0000000000000 --- a/docs/reference/painless-api-reference/AttributedString.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-AttributedString]]++AttributedString++:: -* ++[[painless-api-reference-AttributedString-AttributedString-1]]link:{java8-javadoc}/java/text/AttributedString.html#AttributedString%2Djava.lang.String%2D[AttributedString](<>)++ (link:{java9-javadoc}/java/text/AttributedString.html#AttributedString%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-AttributedString-AttributedString-2]]link:{java8-javadoc}/java/text/AttributedString.html#AttributedString%2Djava.lang.String%2Djava.util.Map%2D[AttributedString](<>, <>)++ (link:{java9-javadoc}/java/text/AttributedString.html#AttributedString%2Djava.lang.String%2Djava.util.Map%2D[java 9]) -* ++[[painless-api-reference-AttributedString-addAttribute-2]]void link:{java8-javadoc}/java/text/AttributedString.html#addAttribute%2Djava.text.AttributedCharacterIterator$Attribute%2Djava.lang.Object%2D[addAttribute](<>, <>)++ (link:{java9-javadoc}/java/text/AttributedString.html#addAttribute%2Djava.text.AttributedCharacterIterator$Attribute%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-AttributedString-addAttribute-4]]void link:{java8-javadoc}/java/text/AttributedString.html#addAttribute%2Djava.text.AttributedCharacterIterator$Attribute%2Djava.lang.Object%2Dint%2Dint%2D[addAttribute](<>, <>, int, int)++ (link:{java9-javadoc}/java/text/AttributedString.html#addAttribute%2Djava.text.AttributedCharacterIterator$Attribute%2Djava.lang.Object%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-AttributedString-addAttributes-3]]void link:{java8-javadoc}/java/text/AttributedString.html#addAttributes%2Djava.util.Map%2Dint%2Dint%2D[addAttributes](<>, int, int)++ (link:{java9-javadoc}/java/text/AttributedString.html#addAttributes%2Djava.util.Map%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-AttributedString-getIterator-0]]<> link:{java8-javadoc}/java/text/AttributedString.html#getIterator%2D%2D[getIterator]()++ (link:{java9-javadoc}/java/text/AttributedString.html#getIterator%2D%2D[java 9]) -* ++[[painless-api-reference-AttributedString-getIterator-1]]<> link:{java8-javadoc}/java/text/AttributedString.html#getIterator%2Djava.text.AttributedCharacterIterator$Attribute:A%2D[getIterator](<>[])++ (link:{java9-javadoc}/java/text/AttributedString.html#getIterator%2Djava.text.AttributedCharacterIterator$Attribute:A%2D[java 9]) -* ++[[painless-api-reference-AttributedString-getIterator-3]]<> link:{java8-javadoc}/java/text/AttributedString.html#getIterator%2Djava.text.AttributedCharacterIterator$Attribute:A%2Dint%2Dint%2D[getIterator](<>[], int, int)++ (link:{java9-javadoc}/java/text/AttributedString.html#getIterator%2Djava.text.AttributedCharacterIterator$Attribute:A%2Dint%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Base64.Decoder.asciidoc b/docs/reference/painless-api-reference/Base64.Decoder.asciidoc deleted file mode 100644 index 3b3acd16e61ef..0000000000000 --- a/docs/reference/painless-api-reference/Base64.Decoder.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Base64-Decoder]]++Base64.Decoder++:: -* ++[[painless-api-reference-Base64-Decoder-decode-1]]byte[] link:{java8-javadoc}/java/util/Base64$Decoder.html#decode%2Djava.lang.String%2D[decode](<>)++ (link:{java9-javadoc}/java/util/Base64$Decoder.html#decode%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Base64-Decoder-decode-2]]int link:{java8-javadoc}/java/util/Base64$Decoder.html#decode%2Dbyte:A%2Dbyte:A%2D[decode](byte[], byte[])++ (link:{java9-javadoc}/java/util/Base64$Decoder.html#decode%2Dbyte:A%2Dbyte:A%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Base64.Encoder.asciidoc b/docs/reference/painless-api-reference/Base64.Encoder.asciidoc deleted file mode 100644 index bc632fc79950e..0000000000000 --- a/docs/reference/painless-api-reference/Base64.Encoder.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Base64-Encoder]]++Base64.Encoder++:: -* ++[[painless-api-reference-Base64-Encoder-encode-2]]int link:{java8-javadoc}/java/util/Base64$Encoder.html#encode%2Dbyte:A%2Dbyte:A%2D[encode](byte[], byte[])++ (link:{java9-javadoc}/java/util/Base64$Encoder.html#encode%2Dbyte:A%2Dbyte:A%2D[java 9]) -* ++[[painless-api-reference-Base64-Encoder-encodeToString-1]]<> link:{java8-javadoc}/java/util/Base64$Encoder.html#encodeToString%2Dbyte:A%2D[encodeToString](byte[])++ (link:{java9-javadoc}/java/util/Base64$Encoder.html#encodeToString%2Dbyte:A%2D[java 9]) -* ++[[painless-api-reference-Base64-Encoder-withoutPadding-0]]<> link:{java8-javadoc}/java/util/Base64$Encoder.html#withoutPadding%2D%2D[withoutPadding]()++ (link:{java9-javadoc}/java/util/Base64$Encoder.html#withoutPadding%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Base64.asciidoc b/docs/reference/painless-api-reference/Base64.asciidoc deleted file mode 100644 index 795f538d7d479..0000000000000 --- a/docs/reference/painless-api-reference/Base64.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Base64]]++Base64++:: -* ++[[painless-api-reference-Base64-getDecoder-0]]static <> link:{java8-javadoc}/java/util/Base64.html#getDecoder%2D%2D[getDecoder]()++ (link:{java9-javadoc}/java/util/Base64.html#getDecoder%2D%2D[java 9]) -* ++[[painless-api-reference-Base64-getEncoder-0]]static <> link:{java8-javadoc}/java/util/Base64.html#getEncoder%2D%2D[getEncoder]()++ (link:{java9-javadoc}/java/util/Base64.html#getEncoder%2D%2D[java 9]) -* ++[[painless-api-reference-Base64-getMimeDecoder-0]]static <> link:{java8-javadoc}/java/util/Base64.html#getMimeDecoder%2D%2D[getMimeDecoder]()++ (link:{java9-javadoc}/java/util/Base64.html#getMimeDecoder%2D%2D[java 9]) -* ++[[painless-api-reference-Base64-getMimeEncoder-0]]static <> link:{java8-javadoc}/java/util/Base64.html#getMimeEncoder%2D%2D[getMimeEncoder]()++ (link:{java9-javadoc}/java/util/Base64.html#getMimeEncoder%2D%2D[java 9]) -* ++[[painless-api-reference-Base64-getMimeEncoder-2]]static <> link:{java8-javadoc}/java/util/Base64.html#getMimeEncoder%2Dint%2Dbyte:A%2D[getMimeEncoder](int, byte[])++ (link:{java9-javadoc}/java/util/Base64.html#getMimeEncoder%2Dint%2Dbyte:A%2D[java 9]) -* ++[[painless-api-reference-Base64-getUrlDecoder-0]]static <> link:{java8-javadoc}/java/util/Base64.html#getUrlDecoder%2D%2D[getUrlDecoder]()++ (link:{java9-javadoc}/java/util/Base64.html#getUrlDecoder%2D%2D[java 9]) -* ++[[painless-api-reference-Base64-getUrlEncoder-0]]static <> link:{java8-javadoc}/java/util/Base64.html#getUrlEncoder%2D%2D[getUrlEncoder]()++ (link:{java9-javadoc}/java/util/Base64.html#getUrlEncoder%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/BaseStream.asciidoc b/docs/reference/painless-api-reference/BaseStream.asciidoc deleted file mode 100644 index 680e9fff71d79..0000000000000 --- a/docs/reference/painless-api-reference/BaseStream.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-BaseStream]]++BaseStream++:: -* ++[[painless-api-reference-BaseStream-close-0]]void link:{java8-javadoc}/java/util/stream/BaseStream.html#close%2D%2D[close]()++ (link:{java9-javadoc}/java/util/stream/BaseStream.html#close%2D%2D[java 9]) -* ++[[painless-api-reference-BaseStream-isParallel-0]]boolean link:{java8-javadoc}/java/util/stream/BaseStream.html#isParallel%2D%2D[isParallel]()++ (link:{java9-javadoc}/java/util/stream/BaseStream.html#isParallel%2D%2D[java 9]) -* ++[[painless-api-reference-BaseStream-iterator-0]]<> link:{java8-javadoc}/java/util/stream/BaseStream.html#iterator%2D%2D[iterator]()++ (link:{java9-javadoc}/java/util/stream/BaseStream.html#iterator%2D%2D[java 9]) -* ++[[painless-api-reference-BaseStream-sequential-0]]<> link:{java8-javadoc}/java/util/stream/BaseStream.html#sequential%2D%2D[sequential]()++ (link:{java9-javadoc}/java/util/stream/BaseStream.html#sequential%2D%2D[java 9]) -* ++[[painless-api-reference-BaseStream-spliterator-0]]<> link:{java8-javadoc}/java/util/stream/BaseStream.html#spliterator%2D%2D[spliterator]()++ (link:{java9-javadoc}/java/util/stream/BaseStream.html#spliterator%2D%2D[java 9]) -* ++[[painless-api-reference-BaseStream-unordered-0]]<> link:{java8-javadoc}/java/util/stream/BaseStream.html#unordered%2D%2D[unordered]()++ (link:{java9-javadoc}/java/util/stream/BaseStream.html#unordered%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/BiConsumer.asciidoc b/docs/reference/painless-api-reference/BiConsumer.asciidoc deleted file mode 100644 index a13bbdbd300e2..0000000000000 --- a/docs/reference/painless-api-reference/BiConsumer.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-BiConsumer]]++BiConsumer++:: -* ++[[painless-api-reference-BiConsumer-accept-2]]void link:{java8-javadoc}/java/util/function/BiConsumer.html#accept%2Djava.lang.Object%2Djava.lang.Object%2D[accept](def, def)++ (link:{java9-javadoc}/java/util/function/BiConsumer.html#accept%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-BiConsumer-andThen-1]]<> link:{java8-javadoc}/java/util/function/BiConsumer.html#andThen%2Djava.util.function.BiConsumer%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/BiConsumer.html#andThen%2Djava.util.function.BiConsumer%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/BiFunction.asciidoc b/docs/reference/painless-api-reference/BiFunction.asciidoc deleted file mode 100644 index 88b316300321e..0000000000000 --- a/docs/reference/painless-api-reference/BiFunction.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-BiFunction]]++BiFunction++:: -* ++[[painless-api-reference-BiFunction-andThen-1]]<> link:{java8-javadoc}/java/util/function/BiFunction.html#andThen%2Djava.util.function.Function%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/BiFunction.html#andThen%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-BiFunction-apply-2]]def link:{java8-javadoc}/java/util/function/BiFunction.html#apply%2Djava.lang.Object%2Djava.lang.Object%2D[apply](def, def)++ (link:{java9-javadoc}/java/util/function/BiFunction.html#apply%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/BiPredicate.asciidoc b/docs/reference/painless-api-reference/BiPredicate.asciidoc deleted file mode 100644 index 7961800814cc6..0000000000000 --- a/docs/reference/painless-api-reference/BiPredicate.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-BiPredicate]]++BiPredicate++:: -* ++[[painless-api-reference-BiPredicate-and-1]]<> link:{java8-javadoc}/java/util/function/BiPredicate.html#and%2Djava.util.function.BiPredicate%2D[and](<>)++ (link:{java9-javadoc}/java/util/function/BiPredicate.html#and%2Djava.util.function.BiPredicate%2D[java 9]) -* ++[[painless-api-reference-BiPredicate-negate-0]]<> link:{java8-javadoc}/java/util/function/BiPredicate.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/util/function/BiPredicate.html#negate%2D%2D[java 9]) -* ++[[painless-api-reference-BiPredicate-or-1]]<> link:{java8-javadoc}/java/util/function/BiPredicate.html#or%2Djava.util.function.BiPredicate%2D[or](<>)++ (link:{java9-javadoc}/java/util/function/BiPredicate.html#or%2Djava.util.function.BiPredicate%2D[java 9]) -* ++[[painless-api-reference-BiPredicate-test-2]]boolean link:{java8-javadoc}/java/util/function/BiPredicate.html#test%2Djava.lang.Object%2Djava.lang.Object%2D[test](def, def)++ (link:{java9-javadoc}/java/util/function/BiPredicate.html#test%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Bidi.asciidoc b/docs/reference/painless-api-reference/Bidi.asciidoc deleted file mode 100644 index cf6272dd49cf5..0000000000000 --- a/docs/reference/painless-api-reference/Bidi.asciidoc +++ /dev/null @@ -1,28 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Bidi]]++Bidi++:: -** [[painless-api-reference-Bidi-DIRECTION_DEFAULT_LEFT_TO_RIGHT]]static int link:{java8-javadoc}/java/text/Bidi.html#DIRECTION_DEFAULT_LEFT_TO_RIGHT[DIRECTION_DEFAULT_LEFT_TO_RIGHT] (link:{java9-javadoc}/java/text/Bidi.html#DIRECTION_DEFAULT_LEFT_TO_RIGHT[java 9]) -** [[painless-api-reference-Bidi-DIRECTION_DEFAULT_RIGHT_TO_LEFT]]static int link:{java8-javadoc}/java/text/Bidi.html#DIRECTION_DEFAULT_RIGHT_TO_LEFT[DIRECTION_DEFAULT_RIGHT_TO_LEFT] (link:{java9-javadoc}/java/text/Bidi.html#DIRECTION_DEFAULT_RIGHT_TO_LEFT[java 9]) -** [[painless-api-reference-Bidi-DIRECTION_LEFT_TO_RIGHT]]static int link:{java8-javadoc}/java/text/Bidi.html#DIRECTION_LEFT_TO_RIGHT[DIRECTION_LEFT_TO_RIGHT] (link:{java9-javadoc}/java/text/Bidi.html#DIRECTION_LEFT_TO_RIGHT[java 9]) -** [[painless-api-reference-Bidi-DIRECTION_RIGHT_TO_LEFT]]static int link:{java8-javadoc}/java/text/Bidi.html#DIRECTION_RIGHT_TO_LEFT[DIRECTION_RIGHT_TO_LEFT] (link:{java9-javadoc}/java/text/Bidi.html#DIRECTION_RIGHT_TO_LEFT[java 9]) -* ++[[painless-api-reference-Bidi-reorderVisually-5]]static void link:{java8-javadoc}/java/text/Bidi.html#reorderVisually%2Dbyte:A%2Dint%2Djava.lang.Object:A%2Dint%2Dint%2D[reorderVisually](byte[], int, <>[], int, int)++ (link:{java9-javadoc}/java/text/Bidi.html#reorderVisually%2Dbyte:A%2Dint%2Djava.lang.Object:A%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Bidi-requiresBidi-3]]static boolean link:{java8-javadoc}/java/text/Bidi.html#requiresBidi%2Dchar:A%2Dint%2Dint%2D[requiresBidi](char[], int, int)++ (link:{java9-javadoc}/java/text/Bidi.html#requiresBidi%2Dchar:A%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Bidi-Bidi-1]]link:{java8-javadoc}/java/text/Bidi.html#Bidi%2Djava.text.AttributedCharacterIterator%2D[Bidi](<>)++ (link:{java9-javadoc}/java/text/Bidi.html#Bidi%2Djava.text.AttributedCharacterIterator%2D[java 9]) -* ++[[painless-api-reference-Bidi-Bidi-2]]link:{java8-javadoc}/java/text/Bidi.html#Bidi%2Djava.lang.String%2Dint%2D[Bidi](<>, int)++ (link:{java9-javadoc}/java/text/Bidi.html#Bidi%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-Bidi-Bidi-6]]link:{java8-javadoc}/java/text/Bidi.html#Bidi%2Dchar:A%2Dint%2Dbyte:A%2Dint%2Dint%2Dint%2D[Bidi](char[], int, byte[], int, int, int)++ (link:{java9-javadoc}/java/text/Bidi.html#Bidi%2Dchar:A%2Dint%2Dbyte:A%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Bidi-baseIsLeftToRight-0]]boolean link:{java8-javadoc}/java/text/Bidi.html#baseIsLeftToRight%2D%2D[baseIsLeftToRight]()++ (link:{java9-javadoc}/java/text/Bidi.html#baseIsLeftToRight%2D%2D[java 9]) -* ++[[painless-api-reference-Bidi-createLineBidi-2]]<> link:{java8-javadoc}/java/text/Bidi.html#createLineBidi%2Dint%2Dint%2D[createLineBidi](int, int)++ (link:{java9-javadoc}/java/text/Bidi.html#createLineBidi%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Bidi-getBaseLevel-0]]int link:{java8-javadoc}/java/text/Bidi.html#getBaseLevel%2D%2D[getBaseLevel]()++ (link:{java9-javadoc}/java/text/Bidi.html#getBaseLevel%2D%2D[java 9]) -* ++[[painless-api-reference-Bidi-getLength-0]]int link:{java8-javadoc}/java/text/Bidi.html#getLength%2D%2D[getLength]()++ (link:{java9-javadoc}/java/text/Bidi.html#getLength%2D%2D[java 9]) -* ++[[painless-api-reference-Bidi-getLevelAt-1]]int link:{java8-javadoc}/java/text/Bidi.html#getLevelAt%2Dint%2D[getLevelAt](int)++ (link:{java9-javadoc}/java/text/Bidi.html#getLevelAt%2Dint%2D[java 9]) -* ++[[painless-api-reference-Bidi-getRunCount-0]]int link:{java8-javadoc}/java/text/Bidi.html#getRunCount%2D%2D[getRunCount]()++ (link:{java9-javadoc}/java/text/Bidi.html#getRunCount%2D%2D[java 9]) -* ++[[painless-api-reference-Bidi-getRunLevel-1]]int link:{java8-javadoc}/java/text/Bidi.html#getRunLevel%2Dint%2D[getRunLevel](int)++ (link:{java9-javadoc}/java/text/Bidi.html#getRunLevel%2Dint%2D[java 9]) -* ++[[painless-api-reference-Bidi-getRunLimit-1]]int link:{java8-javadoc}/java/text/Bidi.html#getRunLimit%2Dint%2D[getRunLimit](int)++ (link:{java9-javadoc}/java/text/Bidi.html#getRunLimit%2Dint%2D[java 9]) -* ++[[painless-api-reference-Bidi-getRunStart-1]]int link:{java8-javadoc}/java/text/Bidi.html#getRunStart%2Dint%2D[getRunStart](int)++ (link:{java9-javadoc}/java/text/Bidi.html#getRunStart%2Dint%2D[java 9]) -* ++[[painless-api-reference-Bidi-isLeftToRight-0]]boolean link:{java8-javadoc}/java/text/Bidi.html#isLeftToRight%2D%2D[isLeftToRight]()++ (link:{java9-javadoc}/java/text/Bidi.html#isLeftToRight%2D%2D[java 9]) -* ++[[painless-api-reference-Bidi-isMixed-0]]boolean link:{java8-javadoc}/java/text/Bidi.html#isMixed%2D%2D[isMixed]()++ (link:{java9-javadoc}/java/text/Bidi.html#isMixed%2D%2D[java 9]) -* ++[[painless-api-reference-Bidi-isRightToLeft-0]]boolean link:{java8-javadoc}/java/text/Bidi.html#isRightToLeft%2D%2D[isRightToLeft]()++ (link:{java9-javadoc}/java/text/Bidi.html#isRightToLeft%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/BigDecimal.asciidoc b/docs/reference/painless-api-reference/BigDecimal.asciidoc deleted file mode 100644 index ab8a78f35c15b..0000000000000 --- a/docs/reference/painless-api-reference/BigDecimal.asciidoc +++ /dev/null @@ -1,57 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-BigDecimal]]++BigDecimal++:: -** [[painless-api-reference-BigDecimal-ONE]]static <> link:{java8-javadoc}/java/math/BigDecimal.html#ONE[ONE] (link:{java9-javadoc}/java/math/BigDecimal.html#ONE[java 9]) -** [[painless-api-reference-BigDecimal-TEN]]static <> link:{java8-javadoc}/java/math/BigDecimal.html#TEN[TEN] (link:{java9-javadoc}/java/math/BigDecimal.html#TEN[java 9]) -** [[painless-api-reference-BigDecimal-ZERO]]static <> link:{java8-javadoc}/java/math/BigDecimal.html#ZERO[ZERO] (link:{java9-javadoc}/java/math/BigDecimal.html#ZERO[java 9]) -* ++[[painless-api-reference-BigDecimal-valueOf-1]]static <> link:{java8-javadoc}/java/math/BigDecimal.html#valueOf%2Ddouble%2D[valueOf](double)++ (link:{java9-javadoc}/java/math/BigDecimal.html#valueOf%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-BigDecimal-1]]link:{java8-javadoc}/java/math/BigDecimal.html#BigDecimal%2Djava.lang.String%2D[BigDecimal](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#BigDecimal%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-BigDecimal-2]]link:{java8-javadoc}/java/math/BigDecimal.html#BigDecimal%2Djava.lang.String%2Djava.math.MathContext%2D[BigDecimal](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#BigDecimal%2Djava.lang.String%2Djava.math.MathContext%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-abs-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#abs%2D%2D[abs]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#abs%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-abs-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#abs%2Djava.math.MathContext%2D[abs](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#abs%2Djava.math.MathContext%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-add-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#add%2Djava.math.BigDecimal%2D[add](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#add%2Djava.math.BigDecimal%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-add-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#add%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[add](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#add%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-byteValueExact-0]]byte link:{java8-javadoc}/java/math/BigDecimal.html#byteValueExact%2D%2D[byteValueExact]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#byteValueExact%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-compareTo-1]]int link:{java8-javadoc}/java/math/BigDecimal.html#compareTo%2Djava.math.BigDecimal%2D[compareTo](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#compareTo%2Djava.math.BigDecimal%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-divide-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#divide%2Djava.math.BigDecimal%2D[divide](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#divide%2Djava.math.BigDecimal%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-divide-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#divide%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[divide](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#divide%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-divideAndRemainder-1]]<>[] link:{java8-javadoc}/java/math/BigDecimal.html#divideAndRemainder%2Djava.math.BigDecimal%2D[divideAndRemainder](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#divideAndRemainder%2Djava.math.BigDecimal%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-divideAndRemainder-2]]<>[] link:{java8-javadoc}/java/math/BigDecimal.html#divideAndRemainder%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[divideAndRemainder](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#divideAndRemainder%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-divideToIntegralValue-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#divideToIntegralValue%2Djava.math.BigDecimal%2D[divideToIntegralValue](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#divideToIntegralValue%2Djava.math.BigDecimal%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-divideToIntegralValue-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#divideToIntegralValue%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[divideToIntegralValue](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#divideToIntegralValue%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-intValueExact-0]]int link:{java8-javadoc}/java/math/BigDecimal.html#intValueExact%2D%2D[intValueExact]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#intValueExact%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-longValueExact-0]]long link:{java8-javadoc}/java/math/BigDecimal.html#longValueExact%2D%2D[longValueExact]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#longValueExact%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-max-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#max%2Djava.math.BigDecimal%2D[max](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#max%2Djava.math.BigDecimal%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-min-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#min%2Djava.math.BigDecimal%2D[min](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#min%2Djava.math.BigDecimal%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-movePointLeft-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#movePointLeft%2Dint%2D[movePointLeft](int)++ (link:{java9-javadoc}/java/math/BigDecimal.html#movePointLeft%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-movePointRight-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#movePointRight%2Dint%2D[movePointRight](int)++ (link:{java9-javadoc}/java/math/BigDecimal.html#movePointRight%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-multiply-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#multiply%2Djava.math.BigDecimal%2D[multiply](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#multiply%2Djava.math.BigDecimal%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-multiply-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#multiply%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[multiply](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#multiply%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-negate-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#negate%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-negate-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#negate%2Djava.math.MathContext%2D[negate](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#negate%2Djava.math.MathContext%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-plus-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#plus%2D%2D[plus]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#plus%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-plus-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#plus%2Djava.math.MathContext%2D[plus](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#plus%2Djava.math.MathContext%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-pow-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#pow%2Dint%2D[pow](int)++ (link:{java9-javadoc}/java/math/BigDecimal.html#pow%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-pow-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#pow%2Dint%2Djava.math.MathContext%2D[pow](int, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#pow%2Dint%2Djava.math.MathContext%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-precision-0]]int link:{java8-javadoc}/java/math/BigDecimal.html#precision%2D%2D[precision]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#precision%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-remainder-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#remainder%2Djava.math.BigDecimal%2D[remainder](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#remainder%2Djava.math.BigDecimal%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-remainder-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#remainder%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[remainder](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#remainder%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-round-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#round%2Djava.math.MathContext%2D[round](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#round%2Djava.math.MathContext%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-scale-0]]int link:{java8-javadoc}/java/math/BigDecimal.html#scale%2D%2D[scale]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#scale%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-scaleByPowerOfTen-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#scaleByPowerOfTen%2Dint%2D[scaleByPowerOfTen](int)++ (link:{java9-javadoc}/java/math/BigDecimal.html#scaleByPowerOfTen%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-setScale-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#setScale%2Dint%2D[setScale](int)++ (link:{java9-javadoc}/java/math/BigDecimal.html#setScale%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-setScale-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#setScale%2Dint%2Djava.math.RoundingMode%2D[setScale](int, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#setScale%2Dint%2Djava.math.RoundingMode%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-shortValueExact-0]]short link:{java8-javadoc}/java/math/BigDecimal.html#shortValueExact%2D%2D[shortValueExact]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#shortValueExact%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-signum-0]]int link:{java8-javadoc}/java/math/BigDecimal.html#signum%2D%2D[signum]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#signum%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-stripTrailingZeros-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#stripTrailingZeros%2D%2D[stripTrailingZeros]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#stripTrailingZeros%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-subtract-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#subtract%2Djava.math.BigDecimal%2D[subtract](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#subtract%2Djava.math.BigDecimal%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-subtract-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#subtract%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[subtract](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#subtract%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-toBigInteger-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#toBigInteger%2D%2D[toBigInteger]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#toBigInteger%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-toBigIntegerExact-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#toBigIntegerExact%2D%2D[toBigIntegerExact]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#toBigIntegerExact%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-toEngineeringString-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#toEngineeringString%2D%2D[toEngineeringString]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#toEngineeringString%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-toPlainString-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#toPlainString%2D%2D[toPlainString]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#toPlainString%2D%2D[java 9]) -* ++[[painless-api-reference-BigDecimal-ulp-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#ulp%2D%2D[ulp]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#ulp%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/BigInteger.asciidoc b/docs/reference/painless-api-reference/BigInteger.asciidoc deleted file mode 100644 index 796355cadd3b7..0000000000000 --- a/docs/reference/painless-api-reference/BigInteger.asciidoc +++ /dev/null @@ -1,50 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-BigInteger]]++BigInteger++:: -** [[painless-api-reference-BigInteger-ONE]]static <> link:{java8-javadoc}/java/math/BigInteger.html#ONE[ONE] (link:{java9-javadoc}/java/math/BigInteger.html#ONE[java 9]) -** [[painless-api-reference-BigInteger-TEN]]static <> link:{java8-javadoc}/java/math/BigInteger.html#TEN[TEN] (link:{java9-javadoc}/java/math/BigInteger.html#TEN[java 9]) -** [[painless-api-reference-BigInteger-ZERO]]static <> link:{java8-javadoc}/java/math/BigInteger.html#ZERO[ZERO] (link:{java9-javadoc}/java/math/BigInteger.html#ZERO[java 9]) -* ++[[painless-api-reference-BigInteger-valueOf-1]]static <> link:{java8-javadoc}/java/math/BigInteger.html#valueOf%2Dlong%2D[valueOf](long)++ (link:{java9-javadoc}/java/math/BigInteger.html#valueOf%2Dlong%2D[java 9]) -* ++[[painless-api-reference-BigInteger-BigInteger-1]]link:{java8-javadoc}/java/math/BigInteger.html#BigInteger%2Djava.lang.String%2D[BigInteger](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#BigInteger%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-BigInteger-BigInteger-2]]link:{java8-javadoc}/java/math/BigInteger.html#BigInteger%2Djava.lang.String%2Dint%2D[BigInteger](<>, int)++ (link:{java9-javadoc}/java/math/BigInteger.html#BigInteger%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigInteger-abs-0]]<> link:{java8-javadoc}/java/math/BigInteger.html#abs%2D%2D[abs]()++ (link:{java9-javadoc}/java/math/BigInteger.html#abs%2D%2D[java 9]) -* ++[[painless-api-reference-BigInteger-add-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#add%2Djava.math.BigInteger%2D[add](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#add%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-and-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#and%2Djava.math.BigInteger%2D[and](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#and%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-andNot-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#andNot%2Djava.math.BigInteger%2D[andNot](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#andNot%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-bitCount-0]]int link:{java8-javadoc}/java/math/BigInteger.html#bitCount%2D%2D[bitCount]()++ (link:{java9-javadoc}/java/math/BigInteger.html#bitCount%2D%2D[java 9]) -* ++[[painless-api-reference-BigInteger-bitLength-0]]int link:{java8-javadoc}/java/math/BigInteger.html#bitLength%2D%2D[bitLength]()++ (link:{java9-javadoc}/java/math/BigInteger.html#bitLength%2D%2D[java 9]) -* ++[[painless-api-reference-BigInteger-byteValueExact-0]]byte link:{java8-javadoc}/java/math/BigInteger.html#byteValueExact%2D%2D[byteValueExact]()++ (link:{java9-javadoc}/java/math/BigInteger.html#byteValueExact%2D%2D[java 9]) -* ++[[painless-api-reference-BigInteger-clearBit-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#clearBit%2Dint%2D[clearBit](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#clearBit%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigInteger-compareTo-1]]int link:{java8-javadoc}/java/math/BigInteger.html#compareTo%2Djava.math.BigInteger%2D[compareTo](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#compareTo%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-divide-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#divide%2Djava.math.BigInteger%2D[divide](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#divide%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-divideAndRemainder-1]]<>[] link:{java8-javadoc}/java/math/BigInteger.html#divideAndRemainder%2Djava.math.BigInteger%2D[divideAndRemainder](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#divideAndRemainder%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-flipBit-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#flipBit%2Dint%2D[flipBit](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#flipBit%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigInteger-gcd-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#gcd%2Djava.math.BigInteger%2D[gcd](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#gcd%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-getLowestSetBit-0]]int link:{java8-javadoc}/java/math/BigInteger.html#getLowestSetBit%2D%2D[getLowestSetBit]()++ (link:{java9-javadoc}/java/math/BigInteger.html#getLowestSetBit%2D%2D[java 9]) -* ++[[painless-api-reference-BigInteger-intValueExact-0]]int link:{java8-javadoc}/java/math/BigInteger.html#intValueExact%2D%2D[intValueExact]()++ (link:{java9-javadoc}/java/math/BigInteger.html#intValueExact%2D%2D[java 9]) -* ++[[painless-api-reference-BigInteger-longValueExact-0]]long link:{java8-javadoc}/java/math/BigInteger.html#longValueExact%2D%2D[longValueExact]()++ (link:{java9-javadoc}/java/math/BigInteger.html#longValueExact%2D%2D[java 9]) -* ++[[painless-api-reference-BigInteger-max-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#max%2Djava.math.BigInteger%2D[max](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#max%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-min-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#min%2Djava.math.BigInteger%2D[min](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#min%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-mod-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#mod%2Djava.math.BigInteger%2D[mod](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#mod%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-modInverse-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#modInverse%2Djava.math.BigInteger%2D[modInverse](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#modInverse%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-modPow-2]]<> link:{java8-javadoc}/java/math/BigInteger.html#modPow%2Djava.math.BigInteger%2Djava.math.BigInteger%2D[modPow](<>, <>)++ (link:{java9-javadoc}/java/math/BigInteger.html#modPow%2Djava.math.BigInteger%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-multiply-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#multiply%2Djava.math.BigInteger%2D[multiply](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#multiply%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-negate-0]]<> link:{java8-javadoc}/java/math/BigInteger.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/math/BigInteger.html#negate%2D%2D[java 9]) -* ++[[painless-api-reference-BigInteger-not-0]]<> link:{java8-javadoc}/java/math/BigInteger.html#not%2D%2D[not]()++ (link:{java9-javadoc}/java/math/BigInteger.html#not%2D%2D[java 9]) -* ++[[painless-api-reference-BigInteger-or-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#or%2Djava.math.BigInteger%2D[or](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#or%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-pow-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#pow%2Dint%2D[pow](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#pow%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigInteger-remainder-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#remainder%2Djava.math.BigInteger%2D[remainder](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#remainder%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-setBit-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#setBit%2Dint%2D[setBit](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#setBit%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigInteger-shiftLeft-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#shiftLeft%2Dint%2D[shiftLeft](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#shiftLeft%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigInteger-shiftRight-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#shiftRight%2Dint%2D[shiftRight](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#shiftRight%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigInteger-shortValueExact-0]]short link:{java8-javadoc}/java/math/BigInteger.html#shortValueExact%2D%2D[shortValueExact]()++ (link:{java9-javadoc}/java/math/BigInteger.html#shortValueExact%2D%2D[java 9]) -* ++[[painless-api-reference-BigInteger-signum-0]]int link:{java8-javadoc}/java/math/BigInteger.html#signum%2D%2D[signum]()++ (link:{java9-javadoc}/java/math/BigInteger.html#signum%2D%2D[java 9]) -* ++[[painless-api-reference-BigInteger-subtract-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#subtract%2Djava.math.BigInteger%2D[subtract](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#subtract%2Djava.math.BigInteger%2D[java 9]) -* ++[[painless-api-reference-BigInteger-testBit-1]]boolean link:{java8-javadoc}/java/math/BigInteger.html#testBit%2Dint%2D[testBit](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#testBit%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigInteger-toByteArray-0]]byte[] link:{java8-javadoc}/java/math/BigInteger.html#toByteArray%2D%2D[toByteArray]()++ (link:{java9-javadoc}/java/math/BigInteger.html#toByteArray%2D%2D[java 9]) -* ++[[painless-api-reference-BigInteger-toString-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#toString%2Dint%2D[toString](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#toString%2Dint%2D[java 9]) -* ++[[painless-api-reference-BigInteger-xor-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#xor%2Djava.math.BigInteger%2D[xor](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#xor%2Djava.math.BigInteger%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/BinaryOperator.asciidoc b/docs/reference/painless-api-reference/BinaryOperator.asciidoc deleted file mode 100644 index c7e53be47f720..0000000000000 --- a/docs/reference/painless-api-reference/BinaryOperator.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-BinaryOperator]]++BinaryOperator++:: -* ++[[painless-api-reference-BinaryOperator-maxBy-1]]static <> link:{java8-javadoc}/java/util/function/BinaryOperator.html#maxBy%2Djava.util.Comparator%2D[maxBy](<>)++ (link:{java9-javadoc}/java/util/function/BinaryOperator.html#maxBy%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-BinaryOperator-minBy-1]]static <> link:{java8-javadoc}/java/util/function/BinaryOperator.html#minBy%2Djava.util.Comparator%2D[minBy](<>)++ (link:{java9-javadoc}/java/util/function/BinaryOperator.html#minBy%2Djava.util.Comparator%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/BitSet.asciidoc b/docs/reference/painless-api-reference/BitSet.asciidoc deleted file mode 100644 index 158f64675d93e..0000000000000 --- a/docs/reference/painless-api-reference/BitSet.asciidoc +++ /dev/null @@ -1,34 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-BitSet]]++BitSet++:: -* ++[[painless-api-reference-BitSet-valueOf-1]]static <> link:{java8-javadoc}/java/util/BitSet.html#valueOf%2Dlong:A%2D[valueOf](long[])++ (link:{java9-javadoc}/java/util/BitSet.html#valueOf%2Dlong:A%2D[java 9]) -* ++[[painless-api-reference-BitSet-BitSet-0]]link:{java8-javadoc}/java/util/BitSet.html#BitSet%2D%2D[BitSet]()++ (link:{java9-javadoc}/java/util/BitSet.html#BitSet%2D%2D[java 9]) -* ++[[painless-api-reference-BitSet-BitSet-1]]link:{java8-javadoc}/java/util/BitSet.html#BitSet%2Dint%2D[BitSet](int)++ (link:{java9-javadoc}/java/util/BitSet.html#BitSet%2Dint%2D[java 9]) -* ++[[painless-api-reference-BitSet-and-1]]void link:{java8-javadoc}/java/util/BitSet.html#and%2Djava.util.BitSet%2D[and](<>)++ (link:{java9-javadoc}/java/util/BitSet.html#and%2Djava.util.BitSet%2D[java 9]) -* ++[[painless-api-reference-BitSet-andNot-1]]void link:{java8-javadoc}/java/util/BitSet.html#andNot%2Djava.util.BitSet%2D[andNot](<>)++ (link:{java9-javadoc}/java/util/BitSet.html#andNot%2Djava.util.BitSet%2D[java 9]) -* ++[[painless-api-reference-BitSet-cardinality-0]]int link:{java8-javadoc}/java/util/BitSet.html#cardinality%2D%2D[cardinality]()++ (link:{java9-javadoc}/java/util/BitSet.html#cardinality%2D%2D[java 9]) -* ++[[painless-api-reference-BitSet-clear-0]]void link:{java8-javadoc}/java/util/BitSet.html#clear%2D%2D[clear]()++ (link:{java9-javadoc}/java/util/BitSet.html#clear%2D%2D[java 9]) -* ++[[painless-api-reference-BitSet-clear-1]]void link:{java8-javadoc}/java/util/BitSet.html#clear%2Dint%2D[clear](int)++ (link:{java9-javadoc}/java/util/BitSet.html#clear%2Dint%2D[java 9]) -* ++[[painless-api-reference-BitSet-clear-2]]void link:{java8-javadoc}/java/util/BitSet.html#clear%2Dint%2Dint%2D[clear](int, int)++ (link:{java9-javadoc}/java/util/BitSet.html#clear%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-BitSet-clone-0]]def link:{java8-javadoc}/java/util/BitSet.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/BitSet.html#clone%2D%2D[java 9]) -* ++[[painless-api-reference-BitSet-flip-1]]void link:{java8-javadoc}/java/util/BitSet.html#flip%2Dint%2D[flip](int)++ (link:{java9-javadoc}/java/util/BitSet.html#flip%2Dint%2D[java 9]) -* ++[[painless-api-reference-BitSet-flip-2]]void link:{java8-javadoc}/java/util/BitSet.html#flip%2Dint%2Dint%2D[flip](int, int)++ (link:{java9-javadoc}/java/util/BitSet.html#flip%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-BitSet-intersects-1]]boolean link:{java8-javadoc}/java/util/BitSet.html#intersects%2Djava.util.BitSet%2D[intersects](<>)++ (link:{java9-javadoc}/java/util/BitSet.html#intersects%2Djava.util.BitSet%2D[java 9]) -* ++[[painless-api-reference-BitSet-isEmpty-0]]boolean link:{java8-javadoc}/java/util/BitSet.html#isEmpty%2D%2D[isEmpty]()++ (link:{java9-javadoc}/java/util/BitSet.html#isEmpty%2D%2D[java 9]) -* ++[[painless-api-reference-BitSet-length-0]]int link:{java8-javadoc}/java/util/BitSet.html#length%2D%2D[length]()++ (link:{java9-javadoc}/java/util/BitSet.html#length%2D%2D[java 9]) -* ++[[painless-api-reference-BitSet-nextClearBit-1]]int link:{java8-javadoc}/java/util/BitSet.html#nextClearBit%2Dint%2D[nextClearBit](int)++ (link:{java9-javadoc}/java/util/BitSet.html#nextClearBit%2Dint%2D[java 9]) -* ++[[painless-api-reference-BitSet-nextSetBit-1]]int link:{java8-javadoc}/java/util/BitSet.html#nextSetBit%2Dint%2D[nextSetBit](int)++ (link:{java9-javadoc}/java/util/BitSet.html#nextSetBit%2Dint%2D[java 9]) -* ++[[painless-api-reference-BitSet-or-1]]void link:{java8-javadoc}/java/util/BitSet.html#or%2Djava.util.BitSet%2D[or](<>)++ (link:{java9-javadoc}/java/util/BitSet.html#or%2Djava.util.BitSet%2D[java 9]) -* ++[[painless-api-reference-BitSet-previousClearBit-1]]int link:{java8-javadoc}/java/util/BitSet.html#previousClearBit%2Dint%2D[previousClearBit](int)++ (link:{java9-javadoc}/java/util/BitSet.html#previousClearBit%2Dint%2D[java 9]) -* ++[[painless-api-reference-BitSet-previousSetBit-1]]int link:{java8-javadoc}/java/util/BitSet.html#previousSetBit%2Dint%2D[previousSetBit](int)++ (link:{java9-javadoc}/java/util/BitSet.html#previousSetBit%2Dint%2D[java 9]) -* ++[[painless-api-reference-BitSet-set-1]]void link:{java8-javadoc}/java/util/BitSet.html#set%2Dint%2D[set](int)++ (link:{java9-javadoc}/java/util/BitSet.html#set%2Dint%2D[java 9]) -* ++[[painless-api-reference-BitSet-set-2]]void link:{java8-javadoc}/java/util/BitSet.html#set%2Dint%2Dint%2D[set](int, int)++ (link:{java9-javadoc}/java/util/BitSet.html#set%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-BitSet-set-3]]void link:{java8-javadoc}/java/util/BitSet.html#set%2Dint%2Dint%2Dboolean%2D[set](int, int, boolean)++ (link:{java9-javadoc}/java/util/BitSet.html#set%2Dint%2Dint%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-BitSet-size-0]]int link:{java8-javadoc}/java/util/BitSet.html#size%2D%2D[size]()++ (link:{java9-javadoc}/java/util/BitSet.html#size%2D%2D[java 9]) -* ++[[painless-api-reference-BitSet-toByteArray-0]]byte[] link:{java8-javadoc}/java/util/BitSet.html#toByteArray%2D%2D[toByteArray]()++ (link:{java9-javadoc}/java/util/BitSet.html#toByteArray%2D%2D[java 9]) -* ++[[painless-api-reference-BitSet-toLongArray-0]]long[] link:{java8-javadoc}/java/util/BitSet.html#toLongArray%2D%2D[toLongArray]()++ (link:{java9-javadoc}/java/util/BitSet.html#toLongArray%2D%2D[java 9]) -* ++[[painless-api-reference-BitSet-xor-1]]void link:{java8-javadoc}/java/util/BitSet.html#xor%2Djava.util.BitSet%2D[xor](<>)++ (link:{java9-javadoc}/java/util/BitSet.html#xor%2Djava.util.BitSet%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Boolean.asciidoc b/docs/reference/painless-api-reference/Boolean.asciidoc deleted file mode 100644 index 47000e58fa828..0000000000000 --- a/docs/reference/painless-api-reference/Boolean.asciidoc +++ /dev/null @@ -1,19 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Boolean]]++Boolean++:: -** [[painless-api-reference-Boolean-FALSE]]static <> link:{java8-javadoc}/java/lang/Boolean.html#FALSE[FALSE] (link:{java9-javadoc}/java/lang/Boolean.html#FALSE[java 9]) -** [[painless-api-reference-Boolean-TRUE]]static <> link:{java8-javadoc}/java/lang/Boolean.html#TRUE[TRUE] (link:{java9-javadoc}/java/lang/Boolean.html#TRUE[java 9]) -* ++[[painless-api-reference-Boolean-compare-2]]static int link:{java8-javadoc}/java/lang/Boolean.html#compare%2Dboolean%2Dboolean%2D[compare](boolean, boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#compare%2Dboolean%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-Boolean-hashCode-1]]static int link:{java8-javadoc}/java/lang/Boolean.html#hashCode%2Dboolean%2D[hashCode](boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#hashCode%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-Boolean-logicalAnd-2]]static boolean link:{java8-javadoc}/java/lang/Boolean.html#logicalAnd%2Dboolean%2Dboolean%2D[logicalAnd](boolean, boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#logicalAnd%2Dboolean%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-Boolean-logicalOr-2]]static boolean link:{java8-javadoc}/java/lang/Boolean.html#logicalOr%2Dboolean%2Dboolean%2D[logicalOr](boolean, boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#logicalOr%2Dboolean%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-Boolean-logicalXor-2]]static boolean link:{java8-javadoc}/java/lang/Boolean.html#logicalXor%2Dboolean%2Dboolean%2D[logicalXor](boolean, boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#logicalXor%2Dboolean%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-Boolean-parseBoolean-1]]static boolean link:{java8-javadoc}/java/lang/Boolean.html#parseBoolean%2Djava.lang.String%2D[parseBoolean](<>)++ (link:{java9-javadoc}/java/lang/Boolean.html#parseBoolean%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Boolean-toString-1]]static <> link:{java8-javadoc}/java/lang/Boolean.html#toString%2Dboolean%2D[toString](boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#toString%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-Boolean-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Boolean.html#valueOf%2Dboolean%2D[valueOf](boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#valueOf%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-Boolean-booleanValue-0]]boolean link:{java8-javadoc}/java/lang/Boolean.html#booleanValue%2D%2D[booleanValue]()++ (link:{java9-javadoc}/java/lang/Boolean.html#booleanValue%2D%2D[java 9]) -* ++[[painless-api-reference-Boolean-compareTo-1]]int link:{java8-javadoc}/java/lang/Boolean.html#compareTo%2Djava.lang.Boolean%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Boolean.html#compareTo%2Djava.lang.Boolean%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/BooleanSupplier.asciidoc b/docs/reference/painless-api-reference/BooleanSupplier.asciidoc deleted file mode 100644 index 9a34d1caf2d99..0000000000000 --- a/docs/reference/painless-api-reference/BooleanSupplier.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-BooleanSupplier]]++BooleanSupplier++:: -* ++[[painless-api-reference-BooleanSupplier-getAsBoolean-0]]boolean link:{java8-javadoc}/java/util/function/BooleanSupplier.html#getAsBoolean%2D%2D[getAsBoolean]()++ (link:{java9-javadoc}/java/util/function/BooleanSupplier.html#getAsBoolean%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/BreakIterator.asciidoc b/docs/reference/painless-api-reference/BreakIterator.asciidoc deleted file mode 100644 index 0e830859c457f..0000000000000 --- a/docs/reference/painless-api-reference/BreakIterator.asciidoc +++ /dev/null @@ -1,29 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-BreakIterator]]++BreakIterator++:: -** [[painless-api-reference-BreakIterator-DONE]]static int link:{java8-javadoc}/java/text/BreakIterator.html#DONE[DONE] (link:{java9-javadoc}/java/text/BreakIterator.html#DONE[java 9]) -* ++[[painless-api-reference-BreakIterator-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/text/BreakIterator.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#getAvailableLocales%2D%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-getCharacterInstance-0]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getCharacterInstance%2D%2D[getCharacterInstance]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#getCharacterInstance%2D%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-getCharacterInstance-1]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getCharacterInstance%2Djava.util.Locale%2D[getCharacterInstance](<>)++ (link:{java9-javadoc}/java/text/BreakIterator.html#getCharacterInstance%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-getLineInstance-0]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getLineInstance%2D%2D[getLineInstance]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#getLineInstance%2D%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-getLineInstance-1]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getLineInstance%2Djava.util.Locale%2D[getLineInstance](<>)++ (link:{java9-javadoc}/java/text/BreakIterator.html#getLineInstance%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-getSentenceInstance-0]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getSentenceInstance%2D%2D[getSentenceInstance]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#getSentenceInstance%2D%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-getSentenceInstance-1]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getSentenceInstance%2Djava.util.Locale%2D[getSentenceInstance](<>)++ (link:{java9-javadoc}/java/text/BreakIterator.html#getSentenceInstance%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-getWordInstance-0]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getWordInstance%2D%2D[getWordInstance]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#getWordInstance%2D%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-getWordInstance-1]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getWordInstance%2Djava.util.Locale%2D[getWordInstance](<>)++ (link:{java9-javadoc}/java/text/BreakIterator.html#getWordInstance%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-clone-0]]def link:{java8-javadoc}/java/text/BreakIterator.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#clone%2D%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-current-0]]int link:{java8-javadoc}/java/text/BreakIterator.html#current%2D%2D[current]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#current%2D%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-first-0]]int link:{java8-javadoc}/java/text/BreakIterator.html#first%2D%2D[first]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#first%2D%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-following-1]]int link:{java8-javadoc}/java/text/BreakIterator.html#following%2Dint%2D[following](int)++ (link:{java9-javadoc}/java/text/BreakIterator.html#following%2Dint%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-getText-0]]<> link:{java8-javadoc}/java/text/BreakIterator.html#getText%2D%2D[getText]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#getText%2D%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-isBoundary-1]]boolean link:{java8-javadoc}/java/text/BreakIterator.html#isBoundary%2Dint%2D[isBoundary](int)++ (link:{java9-javadoc}/java/text/BreakIterator.html#isBoundary%2Dint%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-last-0]]int link:{java8-javadoc}/java/text/BreakIterator.html#last%2D%2D[last]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#last%2D%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-next-0]]int link:{java8-javadoc}/java/text/BreakIterator.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#next%2D%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-next-1]]int link:{java8-javadoc}/java/text/BreakIterator.html#next%2Dint%2D[next](int)++ (link:{java9-javadoc}/java/text/BreakIterator.html#next%2Dint%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-preceding-1]]int link:{java8-javadoc}/java/text/BreakIterator.html#preceding%2Dint%2D[preceding](int)++ (link:{java9-javadoc}/java/text/BreakIterator.html#preceding%2Dint%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-previous-0]]int link:{java8-javadoc}/java/text/BreakIterator.html#previous%2D%2D[previous]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#previous%2D%2D[java 9]) -* ++[[painless-api-reference-BreakIterator-setText-1]]void link:{java8-javadoc}/java/text/BreakIterator.html#setText%2Djava.lang.String%2D[setText](<>)++ (link:{java9-javadoc}/java/text/BreakIterator.html#setText%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Byte.asciidoc b/docs/reference/painless-api-reference/Byte.asciidoc deleted file mode 100644 index 2fc42b6362540..0000000000000 --- a/docs/reference/painless-api-reference/Byte.asciidoc +++ /dev/null @@ -1,22 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Byte]]++Byte++:: -** [[painless-api-reference-Byte-BYTES]]static int link:{java8-javadoc}/java/lang/Byte.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Byte.html#BYTES[java 9]) -** [[painless-api-reference-Byte-MAX_VALUE]]static byte link:{java8-javadoc}/java/lang/Byte.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Byte.html#MAX_VALUE[java 9]) -** [[painless-api-reference-Byte-MIN_VALUE]]static byte link:{java8-javadoc}/java/lang/Byte.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Byte.html#MIN_VALUE[java 9]) -** [[painless-api-reference-Byte-SIZE]]static int link:{java8-javadoc}/java/lang/Byte.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Byte.html#SIZE[java 9]) -* ++[[painless-api-reference-Byte-compare-2]]static int link:{java8-javadoc}/java/lang/Byte.html#compare%2Dbyte%2Dbyte%2D[compare](byte, byte)++ (link:{java9-javadoc}/java/lang/Byte.html#compare%2Dbyte%2Dbyte%2D[java 9]) -* ++[[painless-api-reference-Byte-decode-1]]static <> link:{java8-javadoc}/java/lang/Byte.html#decode%2Djava.lang.String%2D[decode](<>)++ (link:{java9-javadoc}/java/lang/Byte.html#decode%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Byte-hashCode-1]]static int link:{java8-javadoc}/java/lang/Byte.html#hashCode%2Dbyte%2D[hashCode](byte)++ (link:{java9-javadoc}/java/lang/Byte.html#hashCode%2Dbyte%2D[java 9]) -* ++[[painless-api-reference-Byte-parseByte-1]]static byte link:{java8-javadoc}/java/lang/Byte.html#parseByte%2Djava.lang.String%2D[parseByte](<>)++ (link:{java9-javadoc}/java/lang/Byte.html#parseByte%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Byte-parseByte-2]]static byte link:{java8-javadoc}/java/lang/Byte.html#parseByte%2Djava.lang.String%2Dint%2D[parseByte](<>, int)++ (link:{java9-javadoc}/java/lang/Byte.html#parseByte%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-Byte-toString-1]]static <> link:{java8-javadoc}/java/lang/Byte.html#toString%2Dbyte%2D[toString](byte)++ (link:{java9-javadoc}/java/lang/Byte.html#toString%2Dbyte%2D[java 9]) -* ++[[painless-api-reference-Byte-toUnsignedInt-1]]static int link:{java8-javadoc}/java/lang/Byte.html#toUnsignedInt%2Dbyte%2D[toUnsignedInt](byte)++ (link:{java9-javadoc}/java/lang/Byte.html#toUnsignedInt%2Dbyte%2D[java 9]) -* ++[[painless-api-reference-Byte-toUnsignedLong-1]]static long link:{java8-javadoc}/java/lang/Byte.html#toUnsignedLong%2Dbyte%2D[toUnsignedLong](byte)++ (link:{java9-javadoc}/java/lang/Byte.html#toUnsignedLong%2Dbyte%2D[java 9]) -* ++[[painless-api-reference-Byte-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Byte.html#valueOf%2Dbyte%2D[valueOf](byte)++ (link:{java9-javadoc}/java/lang/Byte.html#valueOf%2Dbyte%2D[java 9]) -* ++[[painless-api-reference-Byte-valueOf-2]]static <> link:{java8-javadoc}/java/lang/Byte.html#valueOf%2Djava.lang.String%2Dint%2D[valueOf](<>, int)++ (link:{java9-javadoc}/java/lang/Byte.html#valueOf%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-Byte-compareTo-1]]int link:{java8-javadoc}/java/lang/Byte.html#compareTo%2Djava.lang.Byte%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Byte.html#compareTo%2Djava.lang.Byte%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/BytesRef.asciidoc b/docs/reference/painless-api-reference/BytesRef.asciidoc deleted file mode 100644 index 6114a5625fcae..0000000000000 --- a/docs/reference/painless-api-reference/BytesRef.asciidoc +++ /dev/null @@ -1,12 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-BytesRef]]++BytesRef++:: -** [[painless-api-reference-BytesRef-bytes]]byte[] link:{lucene-core-javadoc}/org/apache/lucene/util/BytesRef.html#bytes[bytes] -** [[painless-api-reference-BytesRef-length]]int link:{lucene-core-javadoc}/org/apache/lucene/util/BytesRef.html#length[length] -** [[painless-api-reference-BytesRef-offset]]int link:{lucene-core-javadoc}/org/apache/lucene/util/BytesRef.html#offset[offset] -* ++[[painless-api-reference-BytesRef-bytesEquals-1]]boolean link:{lucene-core-javadoc}/org/apache/lucene/util/BytesRef.html#bytesEquals%2Dorg.apache.lucene.util.BytesRef%2D[bytesEquals](<>)++ -* ++[[painless-api-reference-BytesRef-utf8ToString-0]]<> link:{lucene-core-javadoc}/org/apache/lucene/util/BytesRef.html#utf8ToString%2D%2D[utf8ToString]()++ -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Calendar.Builder.asciidoc b/docs/reference/painless-api-reference/Calendar.Builder.asciidoc deleted file mode 100644 index 2fd4cafa03637..0000000000000 --- a/docs/reference/painless-api-reference/Calendar.Builder.asciidoc +++ /dev/null @@ -1,21 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Calendar-Builder]]++Calendar.Builder++:: -* ++[[painless-api-reference-Calendar-Builder-Calendar.Builder-0]]link:{java8-javadoc}/java/util/Calendar$Builder.html#Calendar.Builder%2D%2D[Calendar.Builder]()++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#Calendar.Builder%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-Builder-build-0]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#build%2D%2D[build]()++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#build%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-Builder-set-2]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#set%2Dint%2Dint%2D[set](int, int)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#set%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-Builder-setCalendarType-1]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setCalendarType%2Djava.lang.String%2D[setCalendarType](<>)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setCalendarType%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Calendar-Builder-setDate-3]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setDate%2Dint%2Dint%2Dint%2D[setDate](int, int, int)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setDate%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-Builder-setFields-1]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setFields%2Dint:A%2D[setFields](int[])++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setFields%2Dint:A%2D[java 9]) -* ++[[painless-api-reference-Calendar-Builder-setInstant-1]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setInstant%2Dlong%2D[setInstant](long)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setInstant%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Calendar-Builder-setLenient-1]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setLenient%2Dboolean%2D[setLenient](boolean)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setLenient%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-Calendar-Builder-setLocale-1]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setLocale%2Djava.util.Locale%2D[setLocale](<>)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setLocale%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Calendar-Builder-setTimeOfDay-3]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setTimeOfDay%2Dint%2Dint%2Dint%2D[setTimeOfDay](int, int, int)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setTimeOfDay%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-Builder-setTimeOfDay-4]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setTimeOfDay%2Dint%2Dint%2Dint%2Dint%2D[setTimeOfDay](int, int, int, int)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setTimeOfDay%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-Builder-setTimeZone-1]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setTimeZone%2Djava.util.TimeZone%2D[setTimeZone](<>)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setTimeZone%2Djava.util.TimeZone%2D[java 9]) -* ++[[painless-api-reference-Calendar-Builder-setWeekDate-3]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setWeekDate%2Dint%2Dint%2Dint%2D[setWeekDate](int, int, int)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setWeekDate%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-Builder-setWeekDefinition-2]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setWeekDefinition%2Dint%2Dint%2D[setWeekDefinition](int, int)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setWeekDefinition%2Dint%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Calendar.asciidoc b/docs/reference/painless-api-reference/Calendar.asciidoc deleted file mode 100644 index e1a489d8c87d1..0000000000000 --- a/docs/reference/painless-api-reference/Calendar.asciidoc +++ /dev/null @@ -1,102 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Calendar]]++Calendar++:: -** [[painless-api-reference-Calendar-ALL_STYLES]]static int link:{java8-javadoc}/java/util/Calendar.html#ALL_STYLES[ALL_STYLES] (link:{java9-javadoc}/java/util/Calendar.html#ALL_STYLES[java 9]) -** [[painless-api-reference-Calendar-AM]]static int link:{java8-javadoc}/java/util/Calendar.html#AM[AM] (link:{java9-javadoc}/java/util/Calendar.html#AM[java 9]) -** [[painless-api-reference-Calendar-AM_PM]]static int link:{java8-javadoc}/java/util/Calendar.html#AM_PM[AM_PM] (link:{java9-javadoc}/java/util/Calendar.html#AM_PM[java 9]) -** [[painless-api-reference-Calendar-APRIL]]static int link:{java8-javadoc}/java/util/Calendar.html#APRIL[APRIL] (link:{java9-javadoc}/java/util/Calendar.html#APRIL[java 9]) -** [[painless-api-reference-Calendar-AUGUST]]static int link:{java8-javadoc}/java/util/Calendar.html#AUGUST[AUGUST] (link:{java9-javadoc}/java/util/Calendar.html#AUGUST[java 9]) -** [[painless-api-reference-Calendar-DATE]]static int link:{java8-javadoc}/java/util/Calendar.html#DATE[DATE] (link:{java9-javadoc}/java/util/Calendar.html#DATE[java 9]) -** [[painless-api-reference-Calendar-DAY_OF_MONTH]]static int link:{java8-javadoc}/java/util/Calendar.html#DAY_OF_MONTH[DAY_OF_MONTH] (link:{java9-javadoc}/java/util/Calendar.html#DAY_OF_MONTH[java 9]) -** [[painless-api-reference-Calendar-DAY_OF_WEEK]]static int link:{java8-javadoc}/java/util/Calendar.html#DAY_OF_WEEK[DAY_OF_WEEK] (link:{java9-javadoc}/java/util/Calendar.html#DAY_OF_WEEK[java 9]) -** [[painless-api-reference-Calendar-DAY_OF_WEEK_IN_MONTH]]static int link:{java8-javadoc}/java/util/Calendar.html#DAY_OF_WEEK_IN_MONTH[DAY_OF_WEEK_IN_MONTH] (link:{java9-javadoc}/java/util/Calendar.html#DAY_OF_WEEK_IN_MONTH[java 9]) -** [[painless-api-reference-Calendar-DAY_OF_YEAR]]static int link:{java8-javadoc}/java/util/Calendar.html#DAY_OF_YEAR[DAY_OF_YEAR] (link:{java9-javadoc}/java/util/Calendar.html#DAY_OF_YEAR[java 9]) -** [[painless-api-reference-Calendar-DECEMBER]]static int link:{java8-javadoc}/java/util/Calendar.html#DECEMBER[DECEMBER] (link:{java9-javadoc}/java/util/Calendar.html#DECEMBER[java 9]) -** [[painless-api-reference-Calendar-DST_OFFSET]]static int link:{java8-javadoc}/java/util/Calendar.html#DST_OFFSET[DST_OFFSET] (link:{java9-javadoc}/java/util/Calendar.html#DST_OFFSET[java 9]) -** [[painless-api-reference-Calendar-ERA]]static int link:{java8-javadoc}/java/util/Calendar.html#ERA[ERA] (link:{java9-javadoc}/java/util/Calendar.html#ERA[java 9]) -** [[painless-api-reference-Calendar-FEBRUARY]]static int link:{java8-javadoc}/java/util/Calendar.html#FEBRUARY[FEBRUARY] (link:{java9-javadoc}/java/util/Calendar.html#FEBRUARY[java 9]) -** [[painless-api-reference-Calendar-FIELD_COUNT]]static int link:{java8-javadoc}/java/util/Calendar.html#FIELD_COUNT[FIELD_COUNT] (link:{java9-javadoc}/java/util/Calendar.html#FIELD_COUNT[java 9]) -** [[painless-api-reference-Calendar-FRIDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#FRIDAY[FRIDAY] (link:{java9-javadoc}/java/util/Calendar.html#FRIDAY[java 9]) -** [[painless-api-reference-Calendar-HOUR]]static int link:{java8-javadoc}/java/util/Calendar.html#HOUR[HOUR] (link:{java9-javadoc}/java/util/Calendar.html#HOUR[java 9]) -** [[painless-api-reference-Calendar-HOUR_OF_DAY]]static int link:{java8-javadoc}/java/util/Calendar.html#HOUR_OF_DAY[HOUR_OF_DAY] (link:{java9-javadoc}/java/util/Calendar.html#HOUR_OF_DAY[java 9]) -** [[painless-api-reference-Calendar-JANUARY]]static int link:{java8-javadoc}/java/util/Calendar.html#JANUARY[JANUARY] (link:{java9-javadoc}/java/util/Calendar.html#JANUARY[java 9]) -** [[painless-api-reference-Calendar-JULY]]static int link:{java8-javadoc}/java/util/Calendar.html#JULY[JULY] (link:{java9-javadoc}/java/util/Calendar.html#JULY[java 9]) -** [[painless-api-reference-Calendar-JUNE]]static int link:{java8-javadoc}/java/util/Calendar.html#JUNE[JUNE] (link:{java9-javadoc}/java/util/Calendar.html#JUNE[java 9]) -** [[painless-api-reference-Calendar-LONG]]static int link:{java8-javadoc}/java/util/Calendar.html#LONG[LONG] (link:{java9-javadoc}/java/util/Calendar.html#LONG[java 9]) -** [[painless-api-reference-Calendar-LONG_FORMAT]]static int link:{java8-javadoc}/java/util/Calendar.html#LONG_FORMAT[LONG_FORMAT] (link:{java9-javadoc}/java/util/Calendar.html#LONG_FORMAT[java 9]) -** [[painless-api-reference-Calendar-LONG_STANDALONE]]static int link:{java8-javadoc}/java/util/Calendar.html#LONG_STANDALONE[LONG_STANDALONE] (link:{java9-javadoc}/java/util/Calendar.html#LONG_STANDALONE[java 9]) -** [[painless-api-reference-Calendar-MARCH]]static int link:{java8-javadoc}/java/util/Calendar.html#MARCH[MARCH] (link:{java9-javadoc}/java/util/Calendar.html#MARCH[java 9]) -** [[painless-api-reference-Calendar-MAY]]static int link:{java8-javadoc}/java/util/Calendar.html#MAY[MAY] (link:{java9-javadoc}/java/util/Calendar.html#MAY[java 9]) -** [[painless-api-reference-Calendar-MILLISECOND]]static int link:{java8-javadoc}/java/util/Calendar.html#MILLISECOND[MILLISECOND] (link:{java9-javadoc}/java/util/Calendar.html#MILLISECOND[java 9]) -** [[painless-api-reference-Calendar-MINUTE]]static int link:{java8-javadoc}/java/util/Calendar.html#MINUTE[MINUTE] (link:{java9-javadoc}/java/util/Calendar.html#MINUTE[java 9]) -** [[painless-api-reference-Calendar-MONDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#MONDAY[MONDAY] (link:{java9-javadoc}/java/util/Calendar.html#MONDAY[java 9]) -** [[painless-api-reference-Calendar-MONTH]]static int link:{java8-javadoc}/java/util/Calendar.html#MONTH[MONTH] (link:{java9-javadoc}/java/util/Calendar.html#MONTH[java 9]) -** [[painless-api-reference-Calendar-NARROW_FORMAT]]static int link:{java8-javadoc}/java/util/Calendar.html#NARROW_FORMAT[NARROW_FORMAT] (link:{java9-javadoc}/java/util/Calendar.html#NARROW_FORMAT[java 9]) -** [[painless-api-reference-Calendar-NARROW_STANDALONE]]static int link:{java8-javadoc}/java/util/Calendar.html#NARROW_STANDALONE[NARROW_STANDALONE] (link:{java9-javadoc}/java/util/Calendar.html#NARROW_STANDALONE[java 9]) -** [[painless-api-reference-Calendar-NOVEMBER]]static int link:{java8-javadoc}/java/util/Calendar.html#NOVEMBER[NOVEMBER] (link:{java9-javadoc}/java/util/Calendar.html#NOVEMBER[java 9]) -** [[painless-api-reference-Calendar-OCTOBER]]static int link:{java8-javadoc}/java/util/Calendar.html#OCTOBER[OCTOBER] (link:{java9-javadoc}/java/util/Calendar.html#OCTOBER[java 9]) -** [[painless-api-reference-Calendar-PM]]static int link:{java8-javadoc}/java/util/Calendar.html#PM[PM] (link:{java9-javadoc}/java/util/Calendar.html#PM[java 9]) -** [[painless-api-reference-Calendar-SATURDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#SATURDAY[SATURDAY] (link:{java9-javadoc}/java/util/Calendar.html#SATURDAY[java 9]) -** [[painless-api-reference-Calendar-SECOND]]static int link:{java8-javadoc}/java/util/Calendar.html#SECOND[SECOND] (link:{java9-javadoc}/java/util/Calendar.html#SECOND[java 9]) -** [[painless-api-reference-Calendar-SEPTEMBER]]static int link:{java8-javadoc}/java/util/Calendar.html#SEPTEMBER[SEPTEMBER] (link:{java9-javadoc}/java/util/Calendar.html#SEPTEMBER[java 9]) -** [[painless-api-reference-Calendar-SHORT]]static int link:{java8-javadoc}/java/util/Calendar.html#SHORT[SHORT] (link:{java9-javadoc}/java/util/Calendar.html#SHORT[java 9]) -** [[painless-api-reference-Calendar-SHORT_FORMAT]]static int link:{java8-javadoc}/java/util/Calendar.html#SHORT_FORMAT[SHORT_FORMAT] (link:{java9-javadoc}/java/util/Calendar.html#SHORT_FORMAT[java 9]) -** [[painless-api-reference-Calendar-SHORT_STANDALONE]]static int link:{java8-javadoc}/java/util/Calendar.html#SHORT_STANDALONE[SHORT_STANDALONE] (link:{java9-javadoc}/java/util/Calendar.html#SHORT_STANDALONE[java 9]) -** [[painless-api-reference-Calendar-SUNDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#SUNDAY[SUNDAY] (link:{java9-javadoc}/java/util/Calendar.html#SUNDAY[java 9]) -** [[painless-api-reference-Calendar-THURSDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#THURSDAY[THURSDAY] (link:{java9-javadoc}/java/util/Calendar.html#THURSDAY[java 9]) -** [[painless-api-reference-Calendar-TUESDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#TUESDAY[TUESDAY] (link:{java9-javadoc}/java/util/Calendar.html#TUESDAY[java 9]) -** [[painless-api-reference-Calendar-UNDECIMBER]]static int link:{java8-javadoc}/java/util/Calendar.html#UNDECIMBER[UNDECIMBER] (link:{java9-javadoc}/java/util/Calendar.html#UNDECIMBER[java 9]) -** [[painless-api-reference-Calendar-WEDNESDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#WEDNESDAY[WEDNESDAY] (link:{java9-javadoc}/java/util/Calendar.html#WEDNESDAY[java 9]) -** [[painless-api-reference-Calendar-WEEK_OF_MONTH]]static int link:{java8-javadoc}/java/util/Calendar.html#WEEK_OF_MONTH[WEEK_OF_MONTH] (link:{java9-javadoc}/java/util/Calendar.html#WEEK_OF_MONTH[java 9]) -** [[painless-api-reference-Calendar-WEEK_OF_YEAR]]static int link:{java8-javadoc}/java/util/Calendar.html#WEEK_OF_YEAR[WEEK_OF_YEAR] (link:{java9-javadoc}/java/util/Calendar.html#WEEK_OF_YEAR[java 9]) -** [[painless-api-reference-Calendar-YEAR]]static int link:{java8-javadoc}/java/util/Calendar.html#YEAR[YEAR] (link:{java9-javadoc}/java/util/Calendar.html#YEAR[java 9]) -** [[painless-api-reference-Calendar-ZONE_OFFSET]]static int link:{java8-javadoc}/java/util/Calendar.html#ZONE_OFFSET[ZONE_OFFSET] (link:{java9-javadoc}/java/util/Calendar.html#ZONE_OFFSET[java 9]) -* ++[[painless-api-reference-Calendar-getAvailableCalendarTypes-0]]static <> link:{java8-javadoc}/java/util/Calendar.html#getAvailableCalendarTypes%2D%2D[getAvailableCalendarTypes]()++ (link:{java9-javadoc}/java/util/Calendar.html#getAvailableCalendarTypes%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/util/Calendar.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/util/Calendar.html#getAvailableLocales%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-getInstance-0]]static <> link:{java8-javadoc}/java/util/Calendar.html#getInstance%2D%2D[getInstance]()++ (link:{java9-javadoc}/java/util/Calendar.html#getInstance%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-getInstance-1]]static <> link:{java8-javadoc}/java/util/Calendar.html#getInstance%2Djava.util.TimeZone%2D[getInstance](<>)++ (link:{java9-javadoc}/java/util/Calendar.html#getInstance%2Djava.util.TimeZone%2D[java 9]) -* ++[[painless-api-reference-Calendar-getInstance-2]]static <> link:{java8-javadoc}/java/util/Calendar.html#getInstance%2Djava.util.TimeZone%2Djava.util.Locale%2D[getInstance](<>, <>)++ (link:{java9-javadoc}/java/util/Calendar.html#getInstance%2Djava.util.TimeZone%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Calendar-add-2]]void link:{java8-javadoc}/java/util/Calendar.html#add%2Dint%2Dint%2D[add](int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#add%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-after-1]]boolean link:{java8-javadoc}/java/util/Calendar.html#after%2Djava.lang.Object%2D[after](<>)++ (link:{java9-javadoc}/java/util/Calendar.html#after%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Calendar-before-1]]boolean link:{java8-javadoc}/java/util/Calendar.html#before%2Djava.lang.Object%2D[before](<>)++ (link:{java9-javadoc}/java/util/Calendar.html#before%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Calendar-clear-0]]void link:{java8-javadoc}/java/util/Calendar.html#clear%2D%2D[clear]()++ (link:{java9-javadoc}/java/util/Calendar.html#clear%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-clear-1]]void link:{java8-javadoc}/java/util/Calendar.html#clear%2Dint%2D[clear](int)++ (link:{java9-javadoc}/java/util/Calendar.html#clear%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-clone-0]]def link:{java8-javadoc}/java/util/Calendar.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/Calendar.html#clone%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-compareTo-1]]int link:{java8-javadoc}/java/util/Calendar.html#compareTo%2Djava.util.Calendar%2D[compareTo](<>)++ (link:{java9-javadoc}/java/util/Calendar.html#compareTo%2Djava.util.Calendar%2D[java 9]) -* ++[[painless-api-reference-Calendar-get-1]]int link:{java8-javadoc}/java/util/Calendar.html#get%2Dint%2D[get](int)++ (link:{java9-javadoc}/java/util/Calendar.html#get%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-getActualMaximum-1]]int link:{java8-javadoc}/java/util/Calendar.html#getActualMaximum%2Dint%2D[getActualMaximum](int)++ (link:{java9-javadoc}/java/util/Calendar.html#getActualMaximum%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-getActualMinimum-1]]int link:{java8-javadoc}/java/util/Calendar.html#getActualMinimum%2Dint%2D[getActualMinimum](int)++ (link:{java9-javadoc}/java/util/Calendar.html#getActualMinimum%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-getCalendarType-0]]<> link:{java8-javadoc}/java/util/Calendar.html#getCalendarType%2D%2D[getCalendarType]()++ (link:{java9-javadoc}/java/util/Calendar.html#getCalendarType%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-getDisplayName-3]]<> link:{java8-javadoc}/java/util/Calendar.html#getDisplayName%2Dint%2Dint%2Djava.util.Locale%2D[getDisplayName](int, int, <>)++ (link:{java9-javadoc}/java/util/Calendar.html#getDisplayName%2Dint%2Dint%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Calendar-getDisplayNames-3]]<> link:{java8-javadoc}/java/util/Calendar.html#getDisplayNames%2Dint%2Dint%2Djava.util.Locale%2D[getDisplayNames](int, int, <>)++ (link:{java9-javadoc}/java/util/Calendar.html#getDisplayNames%2Dint%2Dint%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Calendar-getFirstDayOfWeek-0]]int link:{java8-javadoc}/java/util/Calendar.html#getFirstDayOfWeek%2D%2D[getFirstDayOfWeek]()++ (link:{java9-javadoc}/java/util/Calendar.html#getFirstDayOfWeek%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-getGreatestMinimum-1]]int link:{java8-javadoc}/java/util/Calendar.html#getGreatestMinimum%2Dint%2D[getGreatestMinimum](int)++ (link:{java9-javadoc}/java/util/Calendar.html#getGreatestMinimum%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-getLeastMaximum-1]]int link:{java8-javadoc}/java/util/Calendar.html#getLeastMaximum%2Dint%2D[getLeastMaximum](int)++ (link:{java9-javadoc}/java/util/Calendar.html#getLeastMaximum%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-getMaximum-1]]int link:{java8-javadoc}/java/util/Calendar.html#getMaximum%2Dint%2D[getMaximum](int)++ (link:{java9-javadoc}/java/util/Calendar.html#getMaximum%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-getMinimalDaysInFirstWeek-0]]int link:{java8-javadoc}/java/util/Calendar.html#getMinimalDaysInFirstWeek%2D%2D[getMinimalDaysInFirstWeek]()++ (link:{java9-javadoc}/java/util/Calendar.html#getMinimalDaysInFirstWeek%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-getMinimum-1]]int link:{java8-javadoc}/java/util/Calendar.html#getMinimum%2Dint%2D[getMinimum](int)++ (link:{java9-javadoc}/java/util/Calendar.html#getMinimum%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-getTime-0]]<> link:{java8-javadoc}/java/util/Calendar.html#getTime%2D%2D[getTime]()++ (link:{java9-javadoc}/java/util/Calendar.html#getTime%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-getTimeInMillis-0]]long link:{java8-javadoc}/java/util/Calendar.html#getTimeInMillis%2D%2D[getTimeInMillis]()++ (link:{java9-javadoc}/java/util/Calendar.html#getTimeInMillis%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-getTimeZone-0]]<> link:{java8-javadoc}/java/util/Calendar.html#getTimeZone%2D%2D[getTimeZone]()++ (link:{java9-javadoc}/java/util/Calendar.html#getTimeZone%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-getWeekYear-0]]int link:{java8-javadoc}/java/util/Calendar.html#getWeekYear%2D%2D[getWeekYear]()++ (link:{java9-javadoc}/java/util/Calendar.html#getWeekYear%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-getWeeksInWeekYear-0]]int link:{java8-javadoc}/java/util/Calendar.html#getWeeksInWeekYear%2D%2D[getWeeksInWeekYear]()++ (link:{java9-javadoc}/java/util/Calendar.html#getWeeksInWeekYear%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-isLenient-0]]boolean link:{java8-javadoc}/java/util/Calendar.html#isLenient%2D%2D[isLenient]()++ (link:{java9-javadoc}/java/util/Calendar.html#isLenient%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-isSet-1]]boolean link:{java8-javadoc}/java/util/Calendar.html#isSet%2Dint%2D[isSet](int)++ (link:{java9-javadoc}/java/util/Calendar.html#isSet%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-isWeekDateSupported-0]]boolean link:{java8-javadoc}/java/util/Calendar.html#isWeekDateSupported%2D%2D[isWeekDateSupported]()++ (link:{java9-javadoc}/java/util/Calendar.html#isWeekDateSupported%2D%2D[java 9]) -* ++[[painless-api-reference-Calendar-roll-2]]void link:{java8-javadoc}/java/util/Calendar.html#roll%2Dint%2Dint%2D[roll](int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#roll%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-set-2]]void link:{java8-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2D[set](int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-set-3]]void link:{java8-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2Dint%2D[set](int, int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-set-5]]void link:{java8-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2Dint%2Dint%2Dint%2D[set](int, int, int, int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-set-6]]void link:{java8-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[set](int, int, int, int, int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-setFirstDayOfWeek-1]]void link:{java8-javadoc}/java/util/Calendar.html#setFirstDayOfWeek%2Dint%2D[setFirstDayOfWeek](int)++ (link:{java9-javadoc}/java/util/Calendar.html#setFirstDayOfWeek%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-setLenient-1]]void link:{java8-javadoc}/java/util/Calendar.html#setLenient%2Dboolean%2D[setLenient](boolean)++ (link:{java9-javadoc}/java/util/Calendar.html#setLenient%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-Calendar-setMinimalDaysInFirstWeek-1]]void link:{java8-javadoc}/java/util/Calendar.html#setMinimalDaysInFirstWeek%2Dint%2D[setMinimalDaysInFirstWeek](int)++ (link:{java9-javadoc}/java/util/Calendar.html#setMinimalDaysInFirstWeek%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-setTime-1]]void link:{java8-javadoc}/java/util/Calendar.html#setTime%2Djava.util.Date%2D[setTime](<>)++ (link:{java9-javadoc}/java/util/Calendar.html#setTime%2Djava.util.Date%2D[java 9]) -* ++[[painless-api-reference-Calendar-setTimeInMillis-1]]void link:{java8-javadoc}/java/util/Calendar.html#setTimeInMillis%2Dlong%2D[setTimeInMillis](long)++ (link:{java9-javadoc}/java/util/Calendar.html#setTimeInMillis%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Calendar-setTimeZone-1]]void link:{java8-javadoc}/java/util/Calendar.html#setTimeZone%2Djava.util.TimeZone%2D[setTimeZone](<>)++ (link:{java9-javadoc}/java/util/Calendar.html#setTimeZone%2Djava.util.TimeZone%2D[java 9]) -* ++[[painless-api-reference-Calendar-setWeekDate-3]]void link:{java8-javadoc}/java/util/Calendar.html#setWeekDate%2Dint%2Dint%2Dint%2D[setWeekDate](int, int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#setWeekDate%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Calendar-toInstant-0]]<> link:{java8-javadoc}/java/util/Calendar.html#toInstant%2D%2D[toInstant]()++ (link:{java9-javadoc}/java/util/Calendar.html#toInstant%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/CharSequence.asciidoc b/docs/reference/painless-api-reference/CharSequence.asciidoc deleted file mode 100644 index 2618a22881e02..0000000000000 --- a/docs/reference/painless-api-reference/CharSequence.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-CharSequence]]++CharSequence++:: -* ++[[painless-api-reference-CharSequence-charAt-1]]char link:{java8-javadoc}/java/lang/CharSequence.html#charAt%2Dint%2D[charAt](int)++ (link:{java9-javadoc}/java/lang/CharSequence.html#charAt%2Dint%2D[java 9]) -* ++[[painless-api-reference-CharSequence-chars-0]]<> link:{java8-javadoc}/java/lang/CharSequence.html#chars%2D%2D[chars]()++ (link:{java9-javadoc}/java/lang/CharSequence.html#chars%2D%2D[java 9]) -* ++[[painless-api-reference-CharSequence-codePoints-0]]<> link:{java8-javadoc}/java/lang/CharSequence.html#codePoints%2D%2D[codePoints]()++ (link:{java9-javadoc}/java/lang/CharSequence.html#codePoints%2D%2D[java 9]) -* ++[[painless-api-reference-CharSequence-length-0]]int link:{java8-javadoc}/java/lang/CharSequence.html#length%2D%2D[length]()++ (link:{java9-javadoc}/java/lang/CharSequence.html#length%2D%2D[java 9]) -* ++[[painless-api-reference-CharSequence-replaceAll-2]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#replaceAll%2Djava.lang.CharSequence%2Djava.util.regex.Pattern%2Djava.util.function.Function%2D[replaceAll](<>, <>)++ -* ++[[painless-api-reference-CharSequence-replaceFirst-2]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#replaceFirst%2Djava.lang.CharSequence%2Djava.util.regex.Pattern%2Djava.util.function.Function%2D[replaceFirst](<>, <>)++ -* ++[[painless-api-reference-CharSequence-subSequence-2]]<> link:{java8-javadoc}/java/lang/CharSequence.html#subSequence%2Dint%2Dint%2D[subSequence](int, int)++ (link:{java9-javadoc}/java/lang/CharSequence.html#subSequence%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-CharSequence-toString-0]]<> link:{java8-javadoc}/java/lang/CharSequence.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/lang/CharSequence.html#toString%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Character.Subset.asciidoc b/docs/reference/painless-api-reference/Character.Subset.asciidoc deleted file mode 100644 index e8dff322e6e27..0000000000000 --- a/docs/reference/painless-api-reference/Character.Subset.asciidoc +++ /dev/null @@ -1,7 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Character-Subset]]++Character.Subset++:: -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Character.UnicodeBlock.asciidoc b/docs/reference/painless-api-reference/Character.UnicodeBlock.asciidoc deleted file mode 100644 index b06cc70397a23..0000000000000 --- a/docs/reference/painless-api-reference/Character.UnicodeBlock.asciidoc +++ /dev/null @@ -1,229 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Character-UnicodeBlock]]++Character.UnicodeBlock++:: -** [[painless-api-reference-Character-UnicodeBlock-AEGEAN_NUMBERS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#AEGEAN_NUMBERS[AEGEAN_NUMBERS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#AEGEAN_NUMBERS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ALCHEMICAL_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ALCHEMICAL_SYMBOLS[ALCHEMICAL_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ALCHEMICAL_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ALPHABETIC_PRESENTATION_FORMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ALPHABETIC_PRESENTATION_FORMS[ALPHABETIC_PRESENTATION_FORMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ALPHABETIC_PRESENTATION_FORMS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ANCIENT_GREEK_MUSICAL_NOTATION]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ANCIENT_GREEK_MUSICAL_NOTATION[ANCIENT_GREEK_MUSICAL_NOTATION] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ANCIENT_GREEK_MUSICAL_NOTATION[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ANCIENT_GREEK_NUMBERS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ANCIENT_GREEK_NUMBERS[ANCIENT_GREEK_NUMBERS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ANCIENT_GREEK_NUMBERS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ANCIENT_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ANCIENT_SYMBOLS[ANCIENT_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ANCIENT_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ARABIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC[ARABIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ARABIC_EXTENDED_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_EXTENDED_A[ARABIC_EXTENDED_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_EXTENDED_A[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS[ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ARABIC_PRESENTATION_FORMS_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_PRESENTATION_FORMS_A[ARABIC_PRESENTATION_FORMS_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_PRESENTATION_FORMS_A[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ARABIC_PRESENTATION_FORMS_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_PRESENTATION_FORMS_B[ARABIC_PRESENTATION_FORMS_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_PRESENTATION_FORMS_B[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ARABIC_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_SUPPLEMENT[ARABIC_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ARMENIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARMENIAN[ARMENIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARMENIAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ARROWS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARROWS[ARROWS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARROWS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-AVESTAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#AVESTAN[AVESTAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#AVESTAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BALINESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BALINESE[BALINESE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BALINESE[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BAMUM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BAMUM[BAMUM] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BAMUM[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BAMUM_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BAMUM_SUPPLEMENT[BAMUM_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BAMUM_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BASIC_LATIN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BASIC_LATIN[BASIC_LATIN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BASIC_LATIN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BATAK]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BATAK[BATAK] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BATAK[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BENGALI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BENGALI[BENGALI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BENGALI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BLOCK_ELEMENTS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BLOCK_ELEMENTS[BLOCK_ELEMENTS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BLOCK_ELEMENTS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BOPOMOFO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BOPOMOFO[BOPOMOFO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BOPOMOFO[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BOPOMOFO_EXTENDED]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BOPOMOFO_EXTENDED[BOPOMOFO_EXTENDED] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BOPOMOFO_EXTENDED[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BOX_DRAWING]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BOX_DRAWING[BOX_DRAWING] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BOX_DRAWING[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BRAHMI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BRAHMI[BRAHMI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BRAHMI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BRAILLE_PATTERNS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BRAILLE_PATTERNS[BRAILLE_PATTERNS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BRAILLE_PATTERNS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BUGINESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BUGINESE[BUGINESE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BUGINESE[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BUHID]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BUHID[BUHID] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BUHID[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-BYZANTINE_MUSICAL_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BYZANTINE_MUSICAL_SYMBOLS[BYZANTINE_MUSICAL_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BYZANTINE_MUSICAL_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CARIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CARIAN[CARIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CARIAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CHAKMA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CHAKMA[CHAKMA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CHAKMA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CHAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CHAM[CHAM] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CHAM[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CHEROKEE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CHEROKEE[CHEROKEE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CHEROKEE[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CJK_COMPATIBILITY]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY[CJK_COMPATIBILITY] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CJK_COMPATIBILITY_FORMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY_FORMS[CJK_COMPATIBILITY_FORMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY_FORMS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CJK_COMPATIBILITY_IDEOGRAPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY_IDEOGRAPHS[CJK_COMPATIBILITY_IDEOGRAPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY_IDEOGRAPHS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT[CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CJK_RADICALS_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_RADICALS_SUPPLEMENT[CJK_RADICALS_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_RADICALS_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CJK_STROKES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_STROKES[CJK_STROKES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_STROKES[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CJK_SYMBOLS_AND_PUNCTUATION]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_SYMBOLS_AND_PUNCTUATION[CJK_SYMBOLS_AND_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_SYMBOLS_AND_PUNCTUATION[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CJK_UNIFIED_IDEOGRAPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS[CJK_UNIFIED_IDEOGRAPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A[CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B[CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C[CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D[CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-COMBINING_DIACRITICAL_MARKS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_DIACRITICAL_MARKS[COMBINING_DIACRITICAL_MARKS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_DIACRITICAL_MARKS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-COMBINING_DIACRITICAL_MARKS_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_DIACRITICAL_MARKS_SUPPLEMENT[COMBINING_DIACRITICAL_MARKS_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_DIACRITICAL_MARKS_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-COMBINING_HALF_MARKS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_HALF_MARKS[COMBINING_HALF_MARKS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_HALF_MARKS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-COMBINING_MARKS_FOR_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_MARKS_FOR_SYMBOLS[COMBINING_MARKS_FOR_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_MARKS_FOR_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-COMMON_INDIC_NUMBER_FORMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COMMON_INDIC_NUMBER_FORMS[COMMON_INDIC_NUMBER_FORMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COMMON_INDIC_NUMBER_FORMS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CONTROL_PICTURES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CONTROL_PICTURES[CONTROL_PICTURES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CONTROL_PICTURES[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-COPTIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COPTIC[COPTIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COPTIC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-COUNTING_ROD_NUMERALS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COUNTING_ROD_NUMERALS[COUNTING_ROD_NUMERALS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COUNTING_ROD_NUMERALS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CUNEIFORM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CUNEIFORM[CUNEIFORM] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CUNEIFORM[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CUNEIFORM_NUMBERS_AND_PUNCTUATION]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CUNEIFORM_NUMBERS_AND_PUNCTUATION[CUNEIFORM_NUMBERS_AND_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CUNEIFORM_NUMBERS_AND_PUNCTUATION[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CURRENCY_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CURRENCY_SYMBOLS[CURRENCY_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CURRENCY_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CYPRIOT_SYLLABARY]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CYPRIOT_SYLLABARY[CYPRIOT_SYLLABARY] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CYPRIOT_SYLLABARY[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CYRILLIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC[CYRILLIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CYRILLIC_EXTENDED_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC_EXTENDED_A[CYRILLIC_EXTENDED_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC_EXTENDED_A[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CYRILLIC_EXTENDED_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC_EXTENDED_B[CYRILLIC_EXTENDED_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC_EXTENDED_B[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-CYRILLIC_SUPPLEMENTARY]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC_SUPPLEMENTARY[CYRILLIC_SUPPLEMENTARY] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC_SUPPLEMENTARY[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-DESERET]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#DESERET[DESERET] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#DESERET[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-DEVANAGARI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#DEVANAGARI[DEVANAGARI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#DEVANAGARI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-DEVANAGARI_EXTENDED]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#DEVANAGARI_EXTENDED[DEVANAGARI_EXTENDED] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#DEVANAGARI_EXTENDED[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-DINGBATS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#DINGBATS[DINGBATS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#DINGBATS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-DOMINO_TILES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#DOMINO_TILES[DOMINO_TILES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#DOMINO_TILES[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-EGYPTIAN_HIEROGLYPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#EGYPTIAN_HIEROGLYPHS[EGYPTIAN_HIEROGLYPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#EGYPTIAN_HIEROGLYPHS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-EMOTICONS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#EMOTICONS[EMOTICONS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#EMOTICONS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ENCLOSED_ALPHANUMERICS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_ALPHANUMERICS[ENCLOSED_ALPHANUMERICS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_ALPHANUMERICS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ENCLOSED_ALPHANUMERIC_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_ALPHANUMERIC_SUPPLEMENT[ENCLOSED_ALPHANUMERIC_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_ALPHANUMERIC_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ENCLOSED_CJK_LETTERS_AND_MONTHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_CJK_LETTERS_AND_MONTHS[ENCLOSED_CJK_LETTERS_AND_MONTHS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_CJK_LETTERS_AND_MONTHS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ENCLOSED_IDEOGRAPHIC_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_IDEOGRAPHIC_SUPPLEMENT[ENCLOSED_IDEOGRAPHIC_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_IDEOGRAPHIC_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ETHIOPIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC[ETHIOPIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ETHIOPIC_EXTENDED]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC_EXTENDED[ETHIOPIC_EXTENDED] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC_EXTENDED[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ETHIOPIC_EXTENDED_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC_EXTENDED_A[ETHIOPIC_EXTENDED_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC_EXTENDED_A[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ETHIOPIC_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC_SUPPLEMENT[ETHIOPIC_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-GENERAL_PUNCTUATION]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GENERAL_PUNCTUATION[GENERAL_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GENERAL_PUNCTUATION[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-GEOMETRIC_SHAPES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GEOMETRIC_SHAPES[GEOMETRIC_SHAPES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GEOMETRIC_SHAPES[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-GEORGIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GEORGIAN[GEORGIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GEORGIAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-GEORGIAN_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GEORGIAN_SUPPLEMENT[GEORGIAN_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GEORGIAN_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-GLAGOLITIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GLAGOLITIC[GLAGOLITIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GLAGOLITIC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-GOTHIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GOTHIC[GOTHIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GOTHIC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-GREEK]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GREEK[GREEK] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GREEK[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-GREEK_EXTENDED]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GREEK_EXTENDED[GREEK_EXTENDED] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GREEK_EXTENDED[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-GUJARATI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GUJARATI[GUJARATI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GUJARATI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-GURMUKHI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GURMUKHI[GURMUKHI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GURMUKHI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-HALFWIDTH_AND_FULLWIDTH_FORMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HALFWIDTH_AND_FULLWIDTH_FORMS[HALFWIDTH_AND_FULLWIDTH_FORMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HALFWIDTH_AND_FULLWIDTH_FORMS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-HANGUL_COMPATIBILITY_JAMO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_COMPATIBILITY_JAMO[HANGUL_COMPATIBILITY_JAMO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_COMPATIBILITY_JAMO[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-HANGUL_JAMO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_JAMO[HANGUL_JAMO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_JAMO[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-HANGUL_JAMO_EXTENDED_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_JAMO_EXTENDED_A[HANGUL_JAMO_EXTENDED_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_JAMO_EXTENDED_A[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-HANGUL_JAMO_EXTENDED_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_JAMO_EXTENDED_B[HANGUL_JAMO_EXTENDED_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_JAMO_EXTENDED_B[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-HANGUL_SYLLABLES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_SYLLABLES[HANGUL_SYLLABLES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_SYLLABLES[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-HANUNOO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HANUNOO[HANUNOO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HANUNOO[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-HEBREW]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HEBREW[HEBREW] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HEBREW[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-HIGH_PRIVATE_USE_SURROGATES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HIGH_PRIVATE_USE_SURROGATES[HIGH_PRIVATE_USE_SURROGATES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HIGH_PRIVATE_USE_SURROGATES[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-HIGH_SURROGATES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HIGH_SURROGATES[HIGH_SURROGATES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HIGH_SURROGATES[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-HIRAGANA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HIRAGANA[HIRAGANA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HIRAGANA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-IDEOGRAPHIC_DESCRIPTION_CHARACTERS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#IDEOGRAPHIC_DESCRIPTION_CHARACTERS[IDEOGRAPHIC_DESCRIPTION_CHARACTERS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#IDEOGRAPHIC_DESCRIPTION_CHARACTERS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-IMPERIAL_ARAMAIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#IMPERIAL_ARAMAIC[IMPERIAL_ARAMAIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#IMPERIAL_ARAMAIC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-INSCRIPTIONAL_PAHLAVI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#INSCRIPTIONAL_PAHLAVI[INSCRIPTIONAL_PAHLAVI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#INSCRIPTIONAL_PAHLAVI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-INSCRIPTIONAL_PARTHIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#INSCRIPTIONAL_PARTHIAN[INSCRIPTIONAL_PARTHIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#INSCRIPTIONAL_PARTHIAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-IPA_EXTENSIONS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#IPA_EXTENSIONS[IPA_EXTENSIONS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#IPA_EXTENSIONS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-JAVANESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#JAVANESE[JAVANESE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#JAVANESE[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-KAITHI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KAITHI[KAITHI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KAITHI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-KANA_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KANA_SUPPLEMENT[KANA_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KANA_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-KANBUN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KANBUN[KANBUN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KANBUN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-KANGXI_RADICALS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KANGXI_RADICALS[KANGXI_RADICALS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KANGXI_RADICALS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-KANNADA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KANNADA[KANNADA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KANNADA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-KATAKANA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KATAKANA[KATAKANA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KATAKANA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-KATAKANA_PHONETIC_EXTENSIONS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KATAKANA_PHONETIC_EXTENSIONS[KATAKANA_PHONETIC_EXTENSIONS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KATAKANA_PHONETIC_EXTENSIONS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-KAYAH_LI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KAYAH_LI[KAYAH_LI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KAYAH_LI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-KHAROSHTHI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KHAROSHTHI[KHAROSHTHI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KHAROSHTHI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-KHMER]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KHMER[KHMER] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KHMER[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-KHMER_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KHMER_SYMBOLS[KHMER_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KHMER_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LAO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LAO[LAO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LAO[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LATIN_1_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_1_SUPPLEMENT[LATIN_1_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_1_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LATIN_EXTENDED_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_A[LATIN_EXTENDED_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_A[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LATIN_EXTENDED_ADDITIONAL]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_ADDITIONAL[LATIN_EXTENDED_ADDITIONAL] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_ADDITIONAL[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LATIN_EXTENDED_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_B[LATIN_EXTENDED_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_B[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LATIN_EXTENDED_C]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_C[LATIN_EXTENDED_C] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_C[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LATIN_EXTENDED_D]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_D[LATIN_EXTENDED_D] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_D[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LEPCHA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LEPCHA[LEPCHA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LEPCHA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LETTERLIKE_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LETTERLIKE_SYMBOLS[LETTERLIKE_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LETTERLIKE_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LIMBU]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LIMBU[LIMBU] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LIMBU[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LINEAR_B_IDEOGRAMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LINEAR_B_IDEOGRAMS[LINEAR_B_IDEOGRAMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LINEAR_B_IDEOGRAMS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LINEAR_B_SYLLABARY]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LINEAR_B_SYLLABARY[LINEAR_B_SYLLABARY] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LINEAR_B_SYLLABARY[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LISU]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LISU[LISU] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LISU[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LOW_SURROGATES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LOW_SURROGATES[LOW_SURROGATES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LOW_SURROGATES[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LYCIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LYCIAN[LYCIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LYCIAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-LYDIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LYDIAN[LYDIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LYDIAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MAHJONG_TILES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MAHJONG_TILES[MAHJONG_TILES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MAHJONG_TILES[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MALAYALAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MALAYALAM[MALAYALAM] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MALAYALAM[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MANDAIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MANDAIC[MANDAIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MANDAIC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MATHEMATICAL_ALPHANUMERIC_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MATHEMATICAL_ALPHANUMERIC_SYMBOLS[MATHEMATICAL_ALPHANUMERIC_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MATHEMATICAL_ALPHANUMERIC_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MATHEMATICAL_OPERATORS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MATHEMATICAL_OPERATORS[MATHEMATICAL_OPERATORS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MATHEMATICAL_OPERATORS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MEETEI_MAYEK]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MEETEI_MAYEK[MEETEI_MAYEK] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MEETEI_MAYEK[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MEETEI_MAYEK_EXTENSIONS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MEETEI_MAYEK_EXTENSIONS[MEETEI_MAYEK_EXTENSIONS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MEETEI_MAYEK_EXTENSIONS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MEROITIC_CURSIVE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MEROITIC_CURSIVE[MEROITIC_CURSIVE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MEROITIC_CURSIVE[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MEROITIC_HIEROGLYPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MEROITIC_HIEROGLYPHS[MEROITIC_HIEROGLYPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MEROITIC_HIEROGLYPHS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MIAO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MIAO[MIAO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MIAO[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A[MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B[MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MISCELLANEOUS_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_SYMBOLS[MISCELLANEOUS_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MISCELLANEOUS_SYMBOLS_AND_ARROWS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_SYMBOLS_AND_ARROWS[MISCELLANEOUS_SYMBOLS_AND_ARROWS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_SYMBOLS_AND_ARROWS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS[MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MISCELLANEOUS_TECHNICAL]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_TECHNICAL[MISCELLANEOUS_TECHNICAL] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_TECHNICAL[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MODIFIER_TONE_LETTERS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MODIFIER_TONE_LETTERS[MODIFIER_TONE_LETTERS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MODIFIER_TONE_LETTERS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MONGOLIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MONGOLIAN[MONGOLIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MONGOLIAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MUSICAL_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MUSICAL_SYMBOLS[MUSICAL_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MUSICAL_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MYANMAR]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MYANMAR[MYANMAR] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MYANMAR[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-MYANMAR_EXTENDED_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MYANMAR_EXTENDED_A[MYANMAR_EXTENDED_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MYANMAR_EXTENDED_A[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-NEW_TAI_LUE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#NEW_TAI_LUE[NEW_TAI_LUE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#NEW_TAI_LUE[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-NKO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#NKO[NKO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#NKO[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-NUMBER_FORMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#NUMBER_FORMS[NUMBER_FORMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#NUMBER_FORMS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-OGHAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OGHAM[OGHAM] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OGHAM[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-OLD_ITALIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_ITALIC[OLD_ITALIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_ITALIC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-OLD_PERSIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_PERSIAN[OLD_PERSIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_PERSIAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-OLD_SOUTH_ARABIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_SOUTH_ARABIAN[OLD_SOUTH_ARABIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_SOUTH_ARABIAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-OLD_TURKIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_TURKIC[OLD_TURKIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_TURKIC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-OL_CHIKI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OL_CHIKI[OL_CHIKI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OL_CHIKI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-OPTICAL_CHARACTER_RECOGNITION]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OPTICAL_CHARACTER_RECOGNITION[OPTICAL_CHARACTER_RECOGNITION] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OPTICAL_CHARACTER_RECOGNITION[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-ORIYA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ORIYA[ORIYA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ORIYA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-OSMANYA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OSMANYA[OSMANYA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OSMANYA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-PHAGS_PA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PHAGS_PA[PHAGS_PA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PHAGS_PA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-PHAISTOS_DISC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PHAISTOS_DISC[PHAISTOS_DISC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PHAISTOS_DISC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-PHOENICIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PHOENICIAN[PHOENICIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PHOENICIAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-PHONETIC_EXTENSIONS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PHONETIC_EXTENSIONS[PHONETIC_EXTENSIONS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PHONETIC_EXTENSIONS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-PHONETIC_EXTENSIONS_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PHONETIC_EXTENSIONS_SUPPLEMENT[PHONETIC_EXTENSIONS_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PHONETIC_EXTENSIONS_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-PLAYING_CARDS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PLAYING_CARDS[PLAYING_CARDS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PLAYING_CARDS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-PRIVATE_USE_AREA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PRIVATE_USE_AREA[PRIVATE_USE_AREA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PRIVATE_USE_AREA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-REJANG]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#REJANG[REJANG] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#REJANG[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-RUMI_NUMERAL_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#RUMI_NUMERAL_SYMBOLS[RUMI_NUMERAL_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#RUMI_NUMERAL_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-RUNIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#RUNIC[RUNIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#RUNIC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SAMARITAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SAMARITAN[SAMARITAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SAMARITAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SAURASHTRA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SAURASHTRA[SAURASHTRA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SAURASHTRA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SHARADA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SHARADA[SHARADA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SHARADA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SHAVIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SHAVIAN[SHAVIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SHAVIAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SINHALA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SINHALA[SINHALA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SINHALA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SMALL_FORM_VARIANTS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SMALL_FORM_VARIANTS[SMALL_FORM_VARIANTS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SMALL_FORM_VARIANTS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SORA_SOMPENG]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SORA_SOMPENG[SORA_SOMPENG] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SORA_SOMPENG[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SPACING_MODIFIER_LETTERS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SPACING_MODIFIER_LETTERS[SPACING_MODIFIER_LETTERS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SPACING_MODIFIER_LETTERS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SPECIALS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SPECIALS[SPECIALS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SPECIALS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SUNDANESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUNDANESE[SUNDANESE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUNDANESE[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SUNDANESE_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUNDANESE_SUPPLEMENT[SUNDANESE_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUNDANESE_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SUPERSCRIPTS_AND_SUBSCRIPTS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPERSCRIPTS_AND_SUBSCRIPTS[SUPERSCRIPTS_AND_SUBSCRIPTS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPERSCRIPTS_AND_SUBSCRIPTS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SUPPLEMENTAL_ARROWS_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_ARROWS_A[SUPPLEMENTAL_ARROWS_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_ARROWS_A[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SUPPLEMENTAL_ARROWS_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_ARROWS_B[SUPPLEMENTAL_ARROWS_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_ARROWS_B[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SUPPLEMENTAL_MATHEMATICAL_OPERATORS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_MATHEMATICAL_OPERATORS[SUPPLEMENTAL_MATHEMATICAL_OPERATORS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_MATHEMATICAL_OPERATORS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SUPPLEMENTAL_PUNCTUATION]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_PUNCTUATION[SUPPLEMENTAL_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_PUNCTUATION[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SUPPLEMENTARY_PRIVATE_USE_AREA_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTARY_PRIVATE_USE_AREA_A[SUPPLEMENTARY_PRIVATE_USE_AREA_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTARY_PRIVATE_USE_AREA_A[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SUPPLEMENTARY_PRIVATE_USE_AREA_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTARY_PRIVATE_USE_AREA_B[SUPPLEMENTARY_PRIVATE_USE_AREA_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTARY_PRIVATE_USE_AREA_B[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SYLOTI_NAGRI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SYLOTI_NAGRI[SYLOTI_NAGRI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SYLOTI_NAGRI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-SYRIAC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SYRIAC[SYRIAC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SYRIAC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-TAGALOG]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAGALOG[TAGALOG] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAGALOG[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-TAGBANWA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAGBANWA[TAGBANWA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAGBANWA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-TAGS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAGS[TAGS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAGS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-TAI_LE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_LE[TAI_LE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_LE[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-TAI_THAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_THAM[TAI_THAM] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_THAM[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-TAI_VIET]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_VIET[TAI_VIET] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_VIET[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-TAI_XUAN_JING_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_XUAN_JING_SYMBOLS[TAI_XUAN_JING_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_XUAN_JING_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-TAKRI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAKRI[TAKRI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAKRI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-TAMIL]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAMIL[TAMIL] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAMIL[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-TELUGU]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TELUGU[TELUGU] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TELUGU[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-THAANA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#THAANA[THAANA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#THAANA[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-THAI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#THAI[THAI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#THAI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-TIBETAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TIBETAN[TIBETAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TIBETAN[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-TIFINAGH]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TIFINAGH[TIFINAGH] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TIFINAGH[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-TRANSPORT_AND_MAP_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TRANSPORT_AND_MAP_SYMBOLS[TRANSPORT_AND_MAP_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TRANSPORT_AND_MAP_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-UGARITIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#UGARITIC[UGARITIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#UGARITIC[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS[UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED[UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-VAI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#VAI[VAI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#VAI[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-VARIATION_SELECTORS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#VARIATION_SELECTORS[VARIATION_SELECTORS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#VARIATION_SELECTORS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-VARIATION_SELECTORS_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#VARIATION_SELECTORS_SUPPLEMENT[VARIATION_SELECTORS_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#VARIATION_SELECTORS_SUPPLEMENT[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-VEDIC_EXTENSIONS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#VEDIC_EXTENSIONS[VEDIC_EXTENSIONS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#VEDIC_EXTENSIONS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-VERTICAL_FORMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#VERTICAL_FORMS[VERTICAL_FORMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#VERTICAL_FORMS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-YIJING_HEXAGRAM_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#YIJING_HEXAGRAM_SYMBOLS[YIJING_HEXAGRAM_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#YIJING_HEXAGRAM_SYMBOLS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-YI_RADICALS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#YI_RADICALS[YI_RADICALS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#YI_RADICALS[java 9]) -** [[painless-api-reference-Character-UnicodeBlock-YI_SYLLABLES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#YI_SYLLABLES[YI_SYLLABLES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#YI_SYLLABLES[java 9]) -* ++[[painless-api-reference-Character-UnicodeBlock-forName-1]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#forName%2Djava.lang.String%2D[forName](<>)++ (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#forName%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Character-UnicodeBlock-of-1]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#of%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Character.UnicodeScript.asciidoc b/docs/reference/painless-api-reference/Character.UnicodeScript.asciidoc deleted file mode 100644 index cc189b1ee1e2f..0000000000000 --- a/docs/reference/painless-api-reference/Character.UnicodeScript.asciidoc +++ /dev/null @@ -1,114 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Character-UnicodeScript]]++Character.UnicodeScript++:: -** [[painless-api-reference-Character-UnicodeScript-ARABIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#ARABIC[ARABIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#ARABIC[java 9]) -** [[painless-api-reference-Character-UnicodeScript-ARMENIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#ARMENIAN[ARMENIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#ARMENIAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-AVESTAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#AVESTAN[AVESTAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#AVESTAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-BALINESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BALINESE[BALINESE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BALINESE[java 9]) -** [[painless-api-reference-Character-UnicodeScript-BAMUM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BAMUM[BAMUM] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BAMUM[java 9]) -** [[painless-api-reference-Character-UnicodeScript-BATAK]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BATAK[BATAK] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BATAK[java 9]) -** [[painless-api-reference-Character-UnicodeScript-BENGALI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BENGALI[BENGALI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BENGALI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-BOPOMOFO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BOPOMOFO[BOPOMOFO] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BOPOMOFO[java 9]) -** [[painless-api-reference-Character-UnicodeScript-BRAHMI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BRAHMI[BRAHMI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BRAHMI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-BRAILLE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BRAILLE[BRAILLE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BRAILLE[java 9]) -** [[painless-api-reference-Character-UnicodeScript-BUGINESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BUGINESE[BUGINESE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BUGINESE[java 9]) -** [[painless-api-reference-Character-UnicodeScript-BUHID]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BUHID[BUHID] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BUHID[java 9]) -** [[painless-api-reference-Character-UnicodeScript-CANADIAN_ABORIGINAL]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CANADIAN_ABORIGINAL[CANADIAN_ABORIGINAL] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CANADIAN_ABORIGINAL[java 9]) -** [[painless-api-reference-Character-UnicodeScript-CARIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CARIAN[CARIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CARIAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-CHAKMA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CHAKMA[CHAKMA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CHAKMA[java 9]) -** [[painless-api-reference-Character-UnicodeScript-CHAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CHAM[CHAM] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CHAM[java 9]) -** [[painless-api-reference-Character-UnicodeScript-CHEROKEE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CHEROKEE[CHEROKEE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CHEROKEE[java 9]) -** [[painless-api-reference-Character-UnicodeScript-COMMON]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#COMMON[COMMON] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#COMMON[java 9]) -** [[painless-api-reference-Character-UnicodeScript-COPTIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#COPTIC[COPTIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#COPTIC[java 9]) -** [[painless-api-reference-Character-UnicodeScript-CUNEIFORM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CUNEIFORM[CUNEIFORM] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CUNEIFORM[java 9]) -** [[painless-api-reference-Character-UnicodeScript-CYPRIOT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CYPRIOT[CYPRIOT] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CYPRIOT[java 9]) -** [[painless-api-reference-Character-UnicodeScript-CYRILLIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CYRILLIC[CYRILLIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CYRILLIC[java 9]) -** [[painless-api-reference-Character-UnicodeScript-DESERET]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#DESERET[DESERET] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#DESERET[java 9]) -** [[painless-api-reference-Character-UnicodeScript-DEVANAGARI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#DEVANAGARI[DEVANAGARI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#DEVANAGARI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-EGYPTIAN_HIEROGLYPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#EGYPTIAN_HIEROGLYPHS[EGYPTIAN_HIEROGLYPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#EGYPTIAN_HIEROGLYPHS[java 9]) -** [[painless-api-reference-Character-UnicodeScript-ETHIOPIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#ETHIOPIC[ETHIOPIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#ETHIOPIC[java 9]) -** [[painless-api-reference-Character-UnicodeScript-GEORGIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#GEORGIAN[GEORGIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#GEORGIAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-GLAGOLITIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#GLAGOLITIC[GLAGOLITIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#GLAGOLITIC[java 9]) -** [[painless-api-reference-Character-UnicodeScript-GOTHIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#GOTHIC[GOTHIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#GOTHIC[java 9]) -** [[painless-api-reference-Character-UnicodeScript-GREEK]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#GREEK[GREEK] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#GREEK[java 9]) -** [[painless-api-reference-Character-UnicodeScript-GUJARATI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#GUJARATI[GUJARATI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#GUJARATI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-GURMUKHI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#GURMUKHI[GURMUKHI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#GURMUKHI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-HAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#HAN[HAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#HAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-HANGUL]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#HANGUL[HANGUL] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#HANGUL[java 9]) -** [[painless-api-reference-Character-UnicodeScript-HANUNOO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#HANUNOO[HANUNOO] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#HANUNOO[java 9]) -** [[painless-api-reference-Character-UnicodeScript-HEBREW]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#HEBREW[HEBREW] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#HEBREW[java 9]) -** [[painless-api-reference-Character-UnicodeScript-HIRAGANA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#HIRAGANA[HIRAGANA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#HIRAGANA[java 9]) -** [[painless-api-reference-Character-UnicodeScript-IMPERIAL_ARAMAIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#IMPERIAL_ARAMAIC[IMPERIAL_ARAMAIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#IMPERIAL_ARAMAIC[java 9]) -** [[painless-api-reference-Character-UnicodeScript-INHERITED]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#INHERITED[INHERITED] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#INHERITED[java 9]) -** [[painless-api-reference-Character-UnicodeScript-INSCRIPTIONAL_PAHLAVI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#INSCRIPTIONAL_PAHLAVI[INSCRIPTIONAL_PAHLAVI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#INSCRIPTIONAL_PAHLAVI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-INSCRIPTIONAL_PARTHIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#INSCRIPTIONAL_PARTHIAN[INSCRIPTIONAL_PARTHIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#INSCRIPTIONAL_PARTHIAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-JAVANESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#JAVANESE[JAVANESE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#JAVANESE[java 9]) -** [[painless-api-reference-Character-UnicodeScript-KAITHI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#KAITHI[KAITHI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#KAITHI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-KANNADA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#KANNADA[KANNADA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#KANNADA[java 9]) -** [[painless-api-reference-Character-UnicodeScript-KATAKANA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#KATAKANA[KATAKANA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#KATAKANA[java 9]) -** [[painless-api-reference-Character-UnicodeScript-KAYAH_LI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#KAYAH_LI[KAYAH_LI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#KAYAH_LI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-KHAROSHTHI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#KHAROSHTHI[KHAROSHTHI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#KHAROSHTHI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-KHMER]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#KHMER[KHMER] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#KHMER[java 9]) -** [[painless-api-reference-Character-UnicodeScript-LAO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LAO[LAO] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LAO[java 9]) -** [[painless-api-reference-Character-UnicodeScript-LATIN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LATIN[LATIN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LATIN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-LEPCHA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LEPCHA[LEPCHA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LEPCHA[java 9]) -** [[painless-api-reference-Character-UnicodeScript-LIMBU]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LIMBU[LIMBU] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LIMBU[java 9]) -** [[painless-api-reference-Character-UnicodeScript-LINEAR_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LINEAR_B[LINEAR_B] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LINEAR_B[java 9]) -** [[painless-api-reference-Character-UnicodeScript-LISU]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LISU[LISU] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LISU[java 9]) -** [[painless-api-reference-Character-UnicodeScript-LYCIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LYCIAN[LYCIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LYCIAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-LYDIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LYDIAN[LYDIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LYDIAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-MALAYALAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MALAYALAM[MALAYALAM] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MALAYALAM[java 9]) -** [[painless-api-reference-Character-UnicodeScript-MANDAIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MANDAIC[MANDAIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MANDAIC[java 9]) -** [[painless-api-reference-Character-UnicodeScript-MEETEI_MAYEK]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MEETEI_MAYEK[MEETEI_MAYEK] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MEETEI_MAYEK[java 9]) -** [[painless-api-reference-Character-UnicodeScript-MEROITIC_CURSIVE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MEROITIC_CURSIVE[MEROITIC_CURSIVE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MEROITIC_CURSIVE[java 9]) -** [[painless-api-reference-Character-UnicodeScript-MEROITIC_HIEROGLYPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MEROITIC_HIEROGLYPHS[MEROITIC_HIEROGLYPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MEROITIC_HIEROGLYPHS[java 9]) -** [[painless-api-reference-Character-UnicodeScript-MIAO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MIAO[MIAO] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MIAO[java 9]) -** [[painless-api-reference-Character-UnicodeScript-MONGOLIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MONGOLIAN[MONGOLIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MONGOLIAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-MYANMAR]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MYANMAR[MYANMAR] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MYANMAR[java 9]) -** [[painless-api-reference-Character-UnicodeScript-NEW_TAI_LUE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#NEW_TAI_LUE[NEW_TAI_LUE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#NEW_TAI_LUE[java 9]) -** [[painless-api-reference-Character-UnicodeScript-NKO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#NKO[NKO] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#NKO[java 9]) -** [[painless-api-reference-Character-UnicodeScript-OGHAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OGHAM[OGHAM] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OGHAM[java 9]) -** [[painless-api-reference-Character-UnicodeScript-OLD_ITALIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OLD_ITALIC[OLD_ITALIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OLD_ITALIC[java 9]) -** [[painless-api-reference-Character-UnicodeScript-OLD_PERSIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OLD_PERSIAN[OLD_PERSIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OLD_PERSIAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-OLD_SOUTH_ARABIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OLD_SOUTH_ARABIAN[OLD_SOUTH_ARABIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OLD_SOUTH_ARABIAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-OLD_TURKIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OLD_TURKIC[OLD_TURKIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OLD_TURKIC[java 9]) -** [[painless-api-reference-Character-UnicodeScript-OL_CHIKI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OL_CHIKI[OL_CHIKI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OL_CHIKI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-ORIYA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#ORIYA[ORIYA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#ORIYA[java 9]) -** [[painless-api-reference-Character-UnicodeScript-OSMANYA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OSMANYA[OSMANYA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OSMANYA[java 9]) -** [[painless-api-reference-Character-UnicodeScript-PHAGS_PA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#PHAGS_PA[PHAGS_PA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#PHAGS_PA[java 9]) -** [[painless-api-reference-Character-UnicodeScript-PHOENICIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#PHOENICIAN[PHOENICIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#PHOENICIAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-REJANG]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#REJANG[REJANG] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#REJANG[java 9]) -** [[painless-api-reference-Character-UnicodeScript-RUNIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#RUNIC[RUNIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#RUNIC[java 9]) -** [[painless-api-reference-Character-UnicodeScript-SAMARITAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SAMARITAN[SAMARITAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SAMARITAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-SAURASHTRA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SAURASHTRA[SAURASHTRA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SAURASHTRA[java 9]) -** [[painless-api-reference-Character-UnicodeScript-SHARADA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SHARADA[SHARADA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SHARADA[java 9]) -** [[painless-api-reference-Character-UnicodeScript-SHAVIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SHAVIAN[SHAVIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SHAVIAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-SINHALA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SINHALA[SINHALA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SINHALA[java 9]) -** [[painless-api-reference-Character-UnicodeScript-SORA_SOMPENG]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SORA_SOMPENG[SORA_SOMPENG] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SORA_SOMPENG[java 9]) -** [[painless-api-reference-Character-UnicodeScript-SUNDANESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SUNDANESE[SUNDANESE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SUNDANESE[java 9]) -** [[painless-api-reference-Character-UnicodeScript-SYLOTI_NAGRI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SYLOTI_NAGRI[SYLOTI_NAGRI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SYLOTI_NAGRI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-SYRIAC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SYRIAC[SYRIAC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SYRIAC[java 9]) -** [[painless-api-reference-Character-UnicodeScript-TAGALOG]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAGALOG[TAGALOG] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAGALOG[java 9]) -** [[painless-api-reference-Character-UnicodeScript-TAGBANWA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAGBANWA[TAGBANWA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAGBANWA[java 9]) -** [[painless-api-reference-Character-UnicodeScript-TAI_LE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAI_LE[TAI_LE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAI_LE[java 9]) -** [[painless-api-reference-Character-UnicodeScript-TAI_THAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAI_THAM[TAI_THAM] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAI_THAM[java 9]) -** [[painless-api-reference-Character-UnicodeScript-TAI_VIET]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAI_VIET[TAI_VIET] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAI_VIET[java 9]) -** [[painless-api-reference-Character-UnicodeScript-TAKRI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAKRI[TAKRI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAKRI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-TAMIL]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAMIL[TAMIL] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAMIL[java 9]) -** [[painless-api-reference-Character-UnicodeScript-TELUGU]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TELUGU[TELUGU] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TELUGU[java 9]) -** [[painless-api-reference-Character-UnicodeScript-THAANA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#THAANA[THAANA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#THAANA[java 9]) -** [[painless-api-reference-Character-UnicodeScript-THAI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#THAI[THAI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#THAI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-TIBETAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TIBETAN[TIBETAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TIBETAN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-TIFINAGH]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TIFINAGH[TIFINAGH] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TIFINAGH[java 9]) -** [[painless-api-reference-Character-UnicodeScript-UGARITIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#UGARITIC[UGARITIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#UGARITIC[java 9]) -** [[painless-api-reference-Character-UnicodeScript-UNKNOWN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#UNKNOWN[UNKNOWN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#UNKNOWN[java 9]) -** [[painless-api-reference-Character-UnicodeScript-VAI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#VAI[VAI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#VAI[java 9]) -** [[painless-api-reference-Character-UnicodeScript-YI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#YI[YI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#YI[java 9]) -* ++[[painless-api-reference-Character-UnicodeScript-forName-1]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#forName%2Djava.lang.String%2D[forName](<>)++ (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#forName%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Character-UnicodeScript-of-1]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#of%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-UnicodeScript-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Character-UnicodeScript-values-0]]static <>[] link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#values%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Character.asciidoc b/docs/reference/painless-api-reference/Character.asciidoc deleted file mode 100644 index 929a4ea3c5ec1..0000000000000 --- a/docs/reference/painless-api-reference/Character.asciidoc +++ /dev/null @@ -1,125 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Character]]++Character++:: -** [[painless-api-reference-Character-BYTES]]static int link:{java8-javadoc}/java/lang/Character.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Character.html#BYTES[java 9]) -** [[painless-api-reference-Character-COMBINING_SPACING_MARK]]static byte link:{java8-javadoc}/java/lang/Character.html#COMBINING_SPACING_MARK[COMBINING_SPACING_MARK] (link:{java9-javadoc}/java/lang/Character.html#COMBINING_SPACING_MARK[java 9]) -** [[painless-api-reference-Character-CONNECTOR_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#CONNECTOR_PUNCTUATION[CONNECTOR_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#CONNECTOR_PUNCTUATION[java 9]) -** [[painless-api-reference-Character-CONTROL]]static byte link:{java8-javadoc}/java/lang/Character.html#CONTROL[CONTROL] (link:{java9-javadoc}/java/lang/Character.html#CONTROL[java 9]) -** [[painless-api-reference-Character-CURRENCY_SYMBOL]]static byte link:{java8-javadoc}/java/lang/Character.html#CURRENCY_SYMBOL[CURRENCY_SYMBOL] (link:{java9-javadoc}/java/lang/Character.html#CURRENCY_SYMBOL[java 9]) -** [[painless-api-reference-Character-DASH_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#DASH_PUNCTUATION[DASH_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#DASH_PUNCTUATION[java 9]) -** [[painless-api-reference-Character-DECIMAL_DIGIT_NUMBER]]static byte link:{java8-javadoc}/java/lang/Character.html#DECIMAL_DIGIT_NUMBER[DECIMAL_DIGIT_NUMBER] (link:{java9-javadoc}/java/lang/Character.html#DECIMAL_DIGIT_NUMBER[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_ARABIC_NUMBER]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_ARABIC_NUMBER[DIRECTIONALITY_ARABIC_NUMBER] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_ARABIC_NUMBER[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_BOUNDARY_NEUTRAL]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_BOUNDARY_NEUTRAL[DIRECTIONALITY_BOUNDARY_NEUTRAL] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_BOUNDARY_NEUTRAL[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_COMMON_NUMBER_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_COMMON_NUMBER_SEPARATOR[DIRECTIONALITY_COMMON_NUMBER_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_COMMON_NUMBER_SEPARATOR[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_EUROPEAN_NUMBER]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_EUROPEAN_NUMBER[DIRECTIONALITY_EUROPEAN_NUMBER] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_EUROPEAN_NUMBER[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR[DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR[DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_LEFT_TO_RIGHT]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_LEFT_TO_RIGHT[DIRECTIONALITY_LEFT_TO_RIGHT] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_LEFT_TO_RIGHT[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING[DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE[DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_NONSPACING_MARK]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_NONSPACING_MARK[DIRECTIONALITY_NONSPACING_MARK] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_NONSPACING_MARK[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_OTHER_NEUTRALS]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_OTHER_NEUTRALS[DIRECTIONALITY_OTHER_NEUTRALS] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_OTHER_NEUTRALS[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_PARAGRAPH_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_PARAGRAPH_SEPARATOR[DIRECTIONALITY_PARAGRAPH_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_PARAGRAPH_SEPARATOR[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_POP_DIRECTIONAL_FORMAT]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_POP_DIRECTIONAL_FORMAT[DIRECTIONALITY_POP_DIRECTIONAL_FORMAT] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_POP_DIRECTIONAL_FORMAT[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_RIGHT_TO_LEFT]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT[DIRECTIONALITY_RIGHT_TO_LEFT] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC[DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING[DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE[DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_SEGMENT_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_SEGMENT_SEPARATOR[DIRECTIONALITY_SEGMENT_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_SEGMENT_SEPARATOR[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_UNDEFINED]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_UNDEFINED[DIRECTIONALITY_UNDEFINED] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_UNDEFINED[java 9]) -** [[painless-api-reference-Character-DIRECTIONALITY_WHITESPACE]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_WHITESPACE[DIRECTIONALITY_WHITESPACE] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_WHITESPACE[java 9]) -** [[painless-api-reference-Character-ENCLOSING_MARK]]static byte link:{java8-javadoc}/java/lang/Character.html#ENCLOSING_MARK[ENCLOSING_MARK] (link:{java9-javadoc}/java/lang/Character.html#ENCLOSING_MARK[java 9]) -** [[painless-api-reference-Character-END_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#END_PUNCTUATION[END_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#END_PUNCTUATION[java 9]) -** [[painless-api-reference-Character-FINAL_QUOTE_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#FINAL_QUOTE_PUNCTUATION[FINAL_QUOTE_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#FINAL_QUOTE_PUNCTUATION[java 9]) -** [[painless-api-reference-Character-FORMAT]]static byte link:{java8-javadoc}/java/lang/Character.html#FORMAT[FORMAT] (link:{java9-javadoc}/java/lang/Character.html#FORMAT[java 9]) -** [[painless-api-reference-Character-INITIAL_QUOTE_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#INITIAL_QUOTE_PUNCTUATION[INITIAL_QUOTE_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#INITIAL_QUOTE_PUNCTUATION[java 9]) -** [[painless-api-reference-Character-LETTER_NUMBER]]static byte link:{java8-javadoc}/java/lang/Character.html#LETTER_NUMBER[LETTER_NUMBER] (link:{java9-javadoc}/java/lang/Character.html#LETTER_NUMBER[java 9]) -** [[painless-api-reference-Character-LINE_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#LINE_SEPARATOR[LINE_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#LINE_SEPARATOR[java 9]) -** [[painless-api-reference-Character-LOWERCASE_LETTER]]static byte link:{java8-javadoc}/java/lang/Character.html#LOWERCASE_LETTER[LOWERCASE_LETTER] (link:{java9-javadoc}/java/lang/Character.html#LOWERCASE_LETTER[java 9]) -** [[painless-api-reference-Character-MATH_SYMBOL]]static byte link:{java8-javadoc}/java/lang/Character.html#MATH_SYMBOL[MATH_SYMBOL] (link:{java9-javadoc}/java/lang/Character.html#MATH_SYMBOL[java 9]) -** [[painless-api-reference-Character-MAX_CODE_POINT]]static int link:{java8-javadoc}/java/lang/Character.html#MAX_CODE_POINT[MAX_CODE_POINT] (link:{java9-javadoc}/java/lang/Character.html#MAX_CODE_POINT[java 9]) -** [[painless-api-reference-Character-MAX_HIGH_SURROGATE]]static char link:{java8-javadoc}/java/lang/Character.html#MAX_HIGH_SURROGATE[MAX_HIGH_SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#MAX_HIGH_SURROGATE[java 9]) -** [[painless-api-reference-Character-MAX_LOW_SURROGATE]]static char link:{java8-javadoc}/java/lang/Character.html#MAX_LOW_SURROGATE[MAX_LOW_SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#MAX_LOW_SURROGATE[java 9]) -** [[painless-api-reference-Character-MAX_RADIX]]static int link:{java8-javadoc}/java/lang/Character.html#MAX_RADIX[MAX_RADIX] (link:{java9-javadoc}/java/lang/Character.html#MAX_RADIX[java 9]) -** [[painless-api-reference-Character-MAX_SURROGATE]]static char link:{java8-javadoc}/java/lang/Character.html#MAX_SURROGATE[MAX_SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#MAX_SURROGATE[java 9]) -** [[painless-api-reference-Character-MAX_VALUE]]static char link:{java8-javadoc}/java/lang/Character.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Character.html#MAX_VALUE[java 9]) -** [[painless-api-reference-Character-MIN_CODE_POINT]]static char link:{java8-javadoc}/java/lang/Character.html#MIN_CODE_POINT[MIN_CODE_POINT] (link:{java9-javadoc}/java/lang/Character.html#MIN_CODE_POINT[java 9]) -** [[painless-api-reference-Character-MIN_HIGH_SURROGATE]]static char link:{java8-javadoc}/java/lang/Character.html#MIN_HIGH_SURROGATE[MIN_HIGH_SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#MIN_HIGH_SURROGATE[java 9]) -** [[painless-api-reference-Character-MIN_LOW_SURROGATE]]static char link:{java8-javadoc}/java/lang/Character.html#MIN_LOW_SURROGATE[MIN_LOW_SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#MIN_LOW_SURROGATE[java 9]) -** [[painless-api-reference-Character-MIN_RADIX]]static int link:{java8-javadoc}/java/lang/Character.html#MIN_RADIX[MIN_RADIX] (link:{java9-javadoc}/java/lang/Character.html#MIN_RADIX[java 9]) -** [[painless-api-reference-Character-MIN_SUPPLEMENTARY_CODE_POINT]]static int link:{java8-javadoc}/java/lang/Character.html#MIN_SUPPLEMENTARY_CODE_POINT[MIN_SUPPLEMENTARY_CODE_POINT] (link:{java9-javadoc}/java/lang/Character.html#MIN_SUPPLEMENTARY_CODE_POINT[java 9]) -** [[painless-api-reference-Character-MIN_SURROGATE]]static char link:{java8-javadoc}/java/lang/Character.html#MIN_SURROGATE[MIN_SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#MIN_SURROGATE[java 9]) -** [[painless-api-reference-Character-MIN_VALUE]]static char link:{java8-javadoc}/java/lang/Character.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Character.html#MIN_VALUE[java 9]) -** [[painless-api-reference-Character-MODIFIER_LETTER]]static byte link:{java8-javadoc}/java/lang/Character.html#MODIFIER_LETTER[MODIFIER_LETTER] (link:{java9-javadoc}/java/lang/Character.html#MODIFIER_LETTER[java 9]) -** [[painless-api-reference-Character-MODIFIER_SYMBOL]]static byte link:{java8-javadoc}/java/lang/Character.html#MODIFIER_SYMBOL[MODIFIER_SYMBOL] (link:{java9-javadoc}/java/lang/Character.html#MODIFIER_SYMBOL[java 9]) -** [[painless-api-reference-Character-NON_SPACING_MARK]]static byte link:{java8-javadoc}/java/lang/Character.html#NON_SPACING_MARK[NON_SPACING_MARK] (link:{java9-javadoc}/java/lang/Character.html#NON_SPACING_MARK[java 9]) -** [[painless-api-reference-Character-OTHER_LETTER]]static byte link:{java8-javadoc}/java/lang/Character.html#OTHER_LETTER[OTHER_LETTER] (link:{java9-javadoc}/java/lang/Character.html#OTHER_LETTER[java 9]) -** [[painless-api-reference-Character-OTHER_NUMBER]]static byte link:{java8-javadoc}/java/lang/Character.html#OTHER_NUMBER[OTHER_NUMBER] (link:{java9-javadoc}/java/lang/Character.html#OTHER_NUMBER[java 9]) -** [[painless-api-reference-Character-OTHER_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#OTHER_PUNCTUATION[OTHER_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#OTHER_PUNCTUATION[java 9]) -** [[painless-api-reference-Character-OTHER_SYMBOL]]static byte link:{java8-javadoc}/java/lang/Character.html#OTHER_SYMBOL[OTHER_SYMBOL] (link:{java9-javadoc}/java/lang/Character.html#OTHER_SYMBOL[java 9]) -** [[painless-api-reference-Character-PARAGRAPH_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#PARAGRAPH_SEPARATOR[PARAGRAPH_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#PARAGRAPH_SEPARATOR[java 9]) -** [[painless-api-reference-Character-PRIVATE_USE]]static byte link:{java8-javadoc}/java/lang/Character.html#PRIVATE_USE[PRIVATE_USE] (link:{java9-javadoc}/java/lang/Character.html#PRIVATE_USE[java 9]) -** [[painless-api-reference-Character-SIZE]]static int link:{java8-javadoc}/java/lang/Character.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Character.html#SIZE[java 9]) -** [[painless-api-reference-Character-SPACE_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#SPACE_SEPARATOR[SPACE_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#SPACE_SEPARATOR[java 9]) -** [[painless-api-reference-Character-START_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#START_PUNCTUATION[START_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#START_PUNCTUATION[java 9]) -** [[painless-api-reference-Character-SURROGATE]]static byte link:{java8-javadoc}/java/lang/Character.html#SURROGATE[SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#SURROGATE[java 9]) -** [[painless-api-reference-Character-TITLECASE_LETTER]]static byte link:{java8-javadoc}/java/lang/Character.html#TITLECASE_LETTER[TITLECASE_LETTER] (link:{java9-javadoc}/java/lang/Character.html#TITLECASE_LETTER[java 9]) -** [[painless-api-reference-Character-UNASSIGNED]]static byte link:{java8-javadoc}/java/lang/Character.html#UNASSIGNED[UNASSIGNED] (link:{java9-javadoc}/java/lang/Character.html#UNASSIGNED[java 9]) -** [[painless-api-reference-Character-UPPERCASE_LETTER]]static byte link:{java8-javadoc}/java/lang/Character.html#UPPERCASE_LETTER[UPPERCASE_LETTER] (link:{java9-javadoc}/java/lang/Character.html#UPPERCASE_LETTER[java 9]) -* ++[[painless-api-reference-Character-charCount-1]]static int link:{java8-javadoc}/java/lang/Character.html#charCount%2Dint%2D[charCount](int)++ (link:{java9-javadoc}/java/lang/Character.html#charCount%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-codePointAt-2]]static int link:{java8-javadoc}/java/lang/Character.html#codePointAt%2Djava.lang.CharSequence%2Dint%2D[codePointAt](<>, int)++ (link:{java9-javadoc}/java/lang/Character.html#codePointAt%2Djava.lang.CharSequence%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-codePointAt-3]]static int link:{java8-javadoc}/java/lang/Character.html#codePointAt%2Dchar:A%2Dint%2Dint%2D[codePointAt](char[], int, int)++ (link:{java9-javadoc}/java/lang/Character.html#codePointAt%2Dchar:A%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-codePointBefore-2]]static int link:{java8-javadoc}/java/lang/Character.html#codePointBefore%2Djava.lang.CharSequence%2Dint%2D[codePointBefore](<>, int)++ (link:{java9-javadoc}/java/lang/Character.html#codePointBefore%2Djava.lang.CharSequence%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-codePointBefore-3]]static int link:{java8-javadoc}/java/lang/Character.html#codePointBefore%2Dchar:A%2Dint%2Dint%2D[codePointBefore](char[], int, int)++ (link:{java9-javadoc}/java/lang/Character.html#codePointBefore%2Dchar:A%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-codePointCount-3]]static int link:{java8-javadoc}/java/lang/Character.html#codePointCount%2Djava.lang.CharSequence%2Dint%2Dint%2D[codePointCount](<>, int, int)++ (link:{java9-javadoc}/java/lang/Character.html#codePointCount%2Djava.lang.CharSequence%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-compare-2]]static int link:{java8-javadoc}/java/lang/Character.html#compare%2Dchar%2Dchar%2D[compare](char, char)++ (link:{java9-javadoc}/java/lang/Character.html#compare%2Dchar%2Dchar%2D[java 9]) -* ++[[painless-api-reference-Character-digit-2]]static int link:{java8-javadoc}/java/lang/Character.html#digit%2Dint%2Dint%2D[digit](int, int)++ (link:{java9-javadoc}/java/lang/Character.html#digit%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-forDigit-2]]static char link:{java8-javadoc}/java/lang/Character.html#forDigit%2Dint%2Dint%2D[forDigit](int, int)++ (link:{java9-javadoc}/java/lang/Character.html#forDigit%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-getDirectionality-1]]static byte link:{java8-javadoc}/java/lang/Character.html#getDirectionality%2Dint%2D[getDirectionality](int)++ (link:{java9-javadoc}/java/lang/Character.html#getDirectionality%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-getName-1]]static <> link:{java8-javadoc}/java/lang/Character.html#getName%2Dint%2D[getName](int)++ (link:{java9-javadoc}/java/lang/Character.html#getName%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-getNumericValue-1]]static int link:{java8-javadoc}/java/lang/Character.html#getNumericValue%2Dint%2D[getNumericValue](int)++ (link:{java9-javadoc}/java/lang/Character.html#getNumericValue%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-getType-1]]static int link:{java8-javadoc}/java/lang/Character.html#getType%2Dint%2D[getType](int)++ (link:{java9-javadoc}/java/lang/Character.html#getType%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-hashCode-1]]static int link:{java8-javadoc}/java/lang/Character.html#hashCode%2Dchar%2D[hashCode](char)++ (link:{java9-javadoc}/java/lang/Character.html#hashCode%2Dchar%2D[java 9]) -* ++[[painless-api-reference-Character-highSurrogate-1]]static char link:{java8-javadoc}/java/lang/Character.html#highSurrogate%2Dint%2D[highSurrogate](int)++ (link:{java9-javadoc}/java/lang/Character.html#highSurrogate%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isAlphabetic-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isAlphabetic%2Dint%2D[isAlphabetic](int)++ (link:{java9-javadoc}/java/lang/Character.html#isAlphabetic%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isBmpCodePoint-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isBmpCodePoint%2Dint%2D[isBmpCodePoint](int)++ (link:{java9-javadoc}/java/lang/Character.html#isBmpCodePoint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isDefined-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isDefined%2Dint%2D[isDefined](int)++ (link:{java9-javadoc}/java/lang/Character.html#isDefined%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isDigit-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isDigit%2Dint%2D[isDigit](int)++ (link:{java9-javadoc}/java/lang/Character.html#isDigit%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isHighSurrogate-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isHighSurrogate%2Dchar%2D[isHighSurrogate](char)++ (link:{java9-javadoc}/java/lang/Character.html#isHighSurrogate%2Dchar%2D[java 9]) -* ++[[painless-api-reference-Character-isISOControl-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isISOControl%2Dint%2D[isISOControl](int)++ (link:{java9-javadoc}/java/lang/Character.html#isISOControl%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isIdentifierIgnorable-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isIdentifierIgnorable%2Dint%2D[isIdentifierIgnorable](int)++ (link:{java9-javadoc}/java/lang/Character.html#isIdentifierIgnorable%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isIdeographic-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isIdeographic%2Dint%2D[isIdeographic](int)++ (link:{java9-javadoc}/java/lang/Character.html#isIdeographic%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isJavaIdentifierPart-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isJavaIdentifierPart%2Dint%2D[isJavaIdentifierPart](int)++ (link:{java9-javadoc}/java/lang/Character.html#isJavaIdentifierPart%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isJavaIdentifierStart-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isJavaIdentifierStart%2Dint%2D[isJavaIdentifierStart](int)++ (link:{java9-javadoc}/java/lang/Character.html#isJavaIdentifierStart%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isLetter-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isLetter%2Dint%2D[isLetter](int)++ (link:{java9-javadoc}/java/lang/Character.html#isLetter%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isLetterOrDigit-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isLetterOrDigit%2Dint%2D[isLetterOrDigit](int)++ (link:{java9-javadoc}/java/lang/Character.html#isLetterOrDigit%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isLowerCase-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isLowerCase%2Dint%2D[isLowerCase](int)++ (link:{java9-javadoc}/java/lang/Character.html#isLowerCase%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isMirrored-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isMirrored%2Dint%2D[isMirrored](int)++ (link:{java9-javadoc}/java/lang/Character.html#isMirrored%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isSpaceChar-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isSpaceChar%2Dint%2D[isSpaceChar](int)++ (link:{java9-javadoc}/java/lang/Character.html#isSpaceChar%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isSupplementaryCodePoint-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isSupplementaryCodePoint%2Dint%2D[isSupplementaryCodePoint](int)++ (link:{java9-javadoc}/java/lang/Character.html#isSupplementaryCodePoint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isSurrogate-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isSurrogate%2Dchar%2D[isSurrogate](char)++ (link:{java9-javadoc}/java/lang/Character.html#isSurrogate%2Dchar%2D[java 9]) -* ++[[painless-api-reference-Character-isSurrogatePair-2]]static boolean link:{java8-javadoc}/java/lang/Character.html#isSurrogatePair%2Dchar%2Dchar%2D[isSurrogatePair](char, char)++ (link:{java9-javadoc}/java/lang/Character.html#isSurrogatePair%2Dchar%2Dchar%2D[java 9]) -* ++[[painless-api-reference-Character-isTitleCase-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isTitleCase%2Dint%2D[isTitleCase](int)++ (link:{java9-javadoc}/java/lang/Character.html#isTitleCase%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isUnicodeIdentifierPart-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isUnicodeIdentifierPart%2Dint%2D[isUnicodeIdentifierPart](int)++ (link:{java9-javadoc}/java/lang/Character.html#isUnicodeIdentifierPart%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isUnicodeIdentifierStart-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isUnicodeIdentifierStart%2Dint%2D[isUnicodeIdentifierStart](int)++ (link:{java9-javadoc}/java/lang/Character.html#isUnicodeIdentifierStart%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isUpperCase-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isUpperCase%2Dint%2D[isUpperCase](int)++ (link:{java9-javadoc}/java/lang/Character.html#isUpperCase%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isValidCodePoint-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isValidCodePoint%2Dint%2D[isValidCodePoint](int)++ (link:{java9-javadoc}/java/lang/Character.html#isValidCodePoint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-isWhitespace-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isWhitespace%2Dint%2D[isWhitespace](int)++ (link:{java9-javadoc}/java/lang/Character.html#isWhitespace%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-lowSurrogate-1]]static char link:{java8-javadoc}/java/lang/Character.html#lowSurrogate%2Dint%2D[lowSurrogate](int)++ (link:{java9-javadoc}/java/lang/Character.html#lowSurrogate%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-offsetByCodePoints-3]]static int link:{java8-javadoc}/java/lang/Character.html#offsetByCodePoints%2Djava.lang.CharSequence%2Dint%2Dint%2D[offsetByCodePoints](<>, int, int)++ (link:{java9-javadoc}/java/lang/Character.html#offsetByCodePoints%2Djava.lang.CharSequence%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-offsetByCodePoints-5]]static int link:{java8-javadoc}/java/lang/Character.html#offsetByCodePoints%2Dchar:A%2Dint%2Dint%2Dint%2Dint%2D[offsetByCodePoints](char[], int, int, int, int)++ (link:{java9-javadoc}/java/lang/Character.html#offsetByCodePoints%2Dchar:A%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-reverseBytes-1]]static char link:{java8-javadoc}/java/lang/Character.html#reverseBytes%2Dchar%2D[reverseBytes](char)++ (link:{java9-javadoc}/java/lang/Character.html#reverseBytes%2Dchar%2D[java 9]) -* ++[[painless-api-reference-Character-toChars-1]]static char[] link:{java8-javadoc}/java/lang/Character.html#toChars%2Dint%2D[toChars](int)++ (link:{java9-javadoc}/java/lang/Character.html#toChars%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-toChars-3]]static int link:{java8-javadoc}/java/lang/Character.html#toChars%2Dint%2Dchar:A%2Dint%2D[toChars](int, char[], int)++ (link:{java9-javadoc}/java/lang/Character.html#toChars%2Dint%2Dchar:A%2Dint%2D[java 9]) -* ++[[painless-api-reference-Character-toCodePoint-2]]static int link:{java8-javadoc}/java/lang/Character.html#toCodePoint%2Dchar%2Dchar%2D[toCodePoint](char, char)++ (link:{java9-javadoc}/java/lang/Character.html#toCodePoint%2Dchar%2Dchar%2D[java 9]) -* ++[[painless-api-reference-Character-toLowerCase-1]]static char link:{java8-javadoc}/java/lang/Character.html#toLowerCase%2Dchar%2D[toLowerCase](char)++ (link:{java9-javadoc}/java/lang/Character.html#toLowerCase%2Dchar%2D[java 9]) -* ++[[painless-api-reference-Character-toString-1]]static <> link:{java8-javadoc}/java/lang/Character.html#toString%2Dchar%2D[toString](char)++ (link:{java9-javadoc}/java/lang/Character.html#toString%2Dchar%2D[java 9]) -* ++[[painless-api-reference-Character-toTitleCase-1]]static char link:{java8-javadoc}/java/lang/Character.html#toTitleCase%2Dchar%2D[toTitleCase](char)++ (link:{java9-javadoc}/java/lang/Character.html#toTitleCase%2Dchar%2D[java 9]) -* ++[[painless-api-reference-Character-toUpperCase-1]]static char link:{java8-javadoc}/java/lang/Character.html#toUpperCase%2Dchar%2D[toUpperCase](char)++ (link:{java9-javadoc}/java/lang/Character.html#toUpperCase%2Dchar%2D[java 9]) -* ++[[painless-api-reference-Character-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Character.html#valueOf%2Dchar%2D[valueOf](char)++ (link:{java9-javadoc}/java/lang/Character.html#valueOf%2Dchar%2D[java 9]) -* ++[[painless-api-reference-Character-charValue-0]]char link:{java8-javadoc}/java/lang/Character.html#charValue%2D%2D[charValue]()++ (link:{java9-javadoc}/java/lang/Character.html#charValue%2D%2D[java 9]) -* ++[[painless-api-reference-Character-compareTo-1]]int link:{java8-javadoc}/java/lang/Character.html#compareTo%2Djava.lang.Character%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Character.html#compareTo%2Djava.lang.Character%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/CharacterIterator.asciidoc b/docs/reference/painless-api-reference/CharacterIterator.asciidoc deleted file mode 100644 index 1303e2d47396a..0000000000000 --- a/docs/reference/painless-api-reference/CharacterIterator.asciidoc +++ /dev/null @@ -1,18 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-CharacterIterator]]++CharacterIterator++:: -** [[painless-api-reference-CharacterIterator-DONE]]static char link:{java8-javadoc}/java/text/CharacterIterator.html#DONE[DONE] (link:{java9-javadoc}/java/text/CharacterIterator.html#DONE[java 9]) -* ++[[painless-api-reference-CharacterIterator-clone-0]]def link:{java8-javadoc}/java/text/CharacterIterator.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#clone%2D%2D[java 9]) -* ++[[painless-api-reference-CharacterIterator-current-0]]char link:{java8-javadoc}/java/text/CharacterIterator.html#current%2D%2D[current]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#current%2D%2D[java 9]) -* ++[[painless-api-reference-CharacterIterator-first-0]]char link:{java8-javadoc}/java/text/CharacterIterator.html#first%2D%2D[first]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#first%2D%2D[java 9]) -* ++[[painless-api-reference-CharacterIterator-getBeginIndex-0]]int link:{java8-javadoc}/java/text/CharacterIterator.html#getBeginIndex%2D%2D[getBeginIndex]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#getBeginIndex%2D%2D[java 9]) -* ++[[painless-api-reference-CharacterIterator-getEndIndex-0]]int link:{java8-javadoc}/java/text/CharacterIterator.html#getEndIndex%2D%2D[getEndIndex]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#getEndIndex%2D%2D[java 9]) -* ++[[painless-api-reference-CharacterIterator-getIndex-0]]int link:{java8-javadoc}/java/text/CharacterIterator.html#getIndex%2D%2D[getIndex]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#getIndex%2D%2D[java 9]) -* ++[[painless-api-reference-CharacterIterator-last-0]]char link:{java8-javadoc}/java/text/CharacterIterator.html#last%2D%2D[last]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#last%2D%2D[java 9]) -* ++[[painless-api-reference-CharacterIterator-next-0]]char link:{java8-javadoc}/java/text/CharacterIterator.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#next%2D%2D[java 9]) -* ++[[painless-api-reference-CharacterIterator-previous-0]]char link:{java8-javadoc}/java/text/CharacterIterator.html#previous%2D%2D[previous]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#previous%2D%2D[java 9]) -* ++[[painless-api-reference-CharacterIterator-setIndex-1]]char link:{java8-javadoc}/java/text/CharacterIterator.html#setIndex%2Dint%2D[setIndex](int)++ (link:{java9-javadoc}/java/text/CharacterIterator.html#setIndex%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ChoiceFormat.asciidoc b/docs/reference/painless-api-reference/ChoiceFormat.asciidoc deleted file mode 100644 index 426120a194f73..0000000000000 --- a/docs/reference/painless-api-reference/ChoiceFormat.asciidoc +++ /dev/null @@ -1,17 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ChoiceFormat]]++ChoiceFormat++:: -* ++[[painless-api-reference-ChoiceFormat-nextDouble-1]]static double link:{java8-javadoc}/java/text/ChoiceFormat.html#nextDouble%2Ddouble%2D[nextDouble](double)++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#nextDouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-ChoiceFormat-nextDouble-2]]static double link:{java8-javadoc}/java/text/ChoiceFormat.html#nextDouble%2Ddouble%2Dboolean%2D[nextDouble](double, boolean)++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#nextDouble%2Ddouble%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-ChoiceFormat-previousDouble-1]]static double link:{java8-javadoc}/java/text/ChoiceFormat.html#previousDouble%2Ddouble%2D[previousDouble](double)++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#previousDouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-ChoiceFormat-ChoiceFormat-1]]link:{java8-javadoc}/java/text/ChoiceFormat.html#ChoiceFormat%2Djava.lang.String%2D[ChoiceFormat](<>)++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#ChoiceFormat%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-ChoiceFormat-ChoiceFormat-2]]link:{java8-javadoc}/java/text/ChoiceFormat.html#ChoiceFormat%2Ddouble:A%2Djava.lang.String:A%2D[ChoiceFormat](double[], <>[])++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#ChoiceFormat%2Ddouble:A%2Djava.lang.String:A%2D[java 9]) -* ++[[painless-api-reference-ChoiceFormat-applyPattern-1]]void link:{java8-javadoc}/java/text/ChoiceFormat.html#applyPattern%2Djava.lang.String%2D[applyPattern](<>)++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#applyPattern%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-ChoiceFormat-getFormats-0]]def[] link:{java8-javadoc}/java/text/ChoiceFormat.html#getFormats%2D%2D[getFormats]()++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#getFormats%2D%2D[java 9]) -* ++[[painless-api-reference-ChoiceFormat-getLimits-0]]double[] link:{java8-javadoc}/java/text/ChoiceFormat.html#getLimits%2D%2D[getLimits]()++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#getLimits%2D%2D[java 9]) -* ++[[painless-api-reference-ChoiceFormat-setChoices-2]]void link:{java8-javadoc}/java/text/ChoiceFormat.html#setChoices%2Ddouble:A%2Djava.lang.String:A%2D[setChoices](double[], <>[])++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#setChoices%2Ddouble:A%2Djava.lang.String:A%2D[java 9]) -* ++[[painless-api-reference-ChoiceFormat-toPattern-0]]<> link:{java8-javadoc}/java/text/ChoiceFormat.html#toPattern%2D%2D[toPattern]()++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#toPattern%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ChronoField.asciidoc b/docs/reference/painless-api-reference/ChronoField.asciidoc deleted file mode 100644 index 33143a52eaf63..0000000000000 --- a/docs/reference/painless-api-reference/ChronoField.asciidoc +++ /dev/null @@ -1,41 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ChronoField]]++ChronoField++:: -** [[painless-api-reference-ChronoField-ALIGNED_DAY_OF_WEEK_IN_MONTH]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_DAY_OF_WEEK_IN_MONTH[ALIGNED_DAY_OF_WEEK_IN_MONTH] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_DAY_OF_WEEK_IN_MONTH[java 9]) -** [[painless-api-reference-ChronoField-ALIGNED_DAY_OF_WEEK_IN_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_DAY_OF_WEEK_IN_YEAR[ALIGNED_DAY_OF_WEEK_IN_YEAR] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_DAY_OF_WEEK_IN_YEAR[java 9]) -** [[painless-api-reference-ChronoField-ALIGNED_WEEK_OF_MONTH]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_WEEK_OF_MONTH[ALIGNED_WEEK_OF_MONTH] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_WEEK_OF_MONTH[java 9]) -** [[painless-api-reference-ChronoField-ALIGNED_WEEK_OF_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_WEEK_OF_YEAR[ALIGNED_WEEK_OF_YEAR] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_WEEK_OF_YEAR[java 9]) -** [[painless-api-reference-ChronoField-AMPM_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#AMPM_OF_DAY[AMPM_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#AMPM_OF_DAY[java 9]) -** [[painless-api-reference-ChronoField-CLOCK_HOUR_OF_AMPM]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#CLOCK_HOUR_OF_AMPM[CLOCK_HOUR_OF_AMPM] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#CLOCK_HOUR_OF_AMPM[java 9]) -** [[painless-api-reference-ChronoField-CLOCK_HOUR_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#CLOCK_HOUR_OF_DAY[CLOCK_HOUR_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#CLOCK_HOUR_OF_DAY[java 9]) -** [[painless-api-reference-ChronoField-DAY_OF_MONTH]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#DAY_OF_MONTH[DAY_OF_MONTH] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#DAY_OF_MONTH[java 9]) -** [[painless-api-reference-ChronoField-DAY_OF_WEEK]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#DAY_OF_WEEK[DAY_OF_WEEK] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#DAY_OF_WEEK[java 9]) -** [[painless-api-reference-ChronoField-DAY_OF_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#DAY_OF_YEAR[DAY_OF_YEAR] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#DAY_OF_YEAR[java 9]) -** [[painless-api-reference-ChronoField-EPOCH_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#EPOCH_DAY[EPOCH_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#EPOCH_DAY[java 9]) -** [[painless-api-reference-ChronoField-ERA]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#ERA[ERA] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#ERA[java 9]) -** [[painless-api-reference-ChronoField-HOUR_OF_AMPM]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#HOUR_OF_AMPM[HOUR_OF_AMPM] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#HOUR_OF_AMPM[java 9]) -** [[painless-api-reference-ChronoField-HOUR_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#HOUR_OF_DAY[HOUR_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#HOUR_OF_DAY[java 9]) -** [[painless-api-reference-ChronoField-INSTANT_SECONDS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#INSTANT_SECONDS[INSTANT_SECONDS] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#INSTANT_SECONDS[java 9]) -** [[painless-api-reference-ChronoField-MICRO_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MICRO_OF_DAY[MICRO_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MICRO_OF_DAY[java 9]) -** [[painless-api-reference-ChronoField-MICRO_OF_SECOND]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MICRO_OF_SECOND[MICRO_OF_SECOND] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MICRO_OF_SECOND[java 9]) -** [[painless-api-reference-ChronoField-MILLI_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MILLI_OF_DAY[MILLI_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MILLI_OF_DAY[java 9]) -** [[painless-api-reference-ChronoField-MILLI_OF_SECOND]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MILLI_OF_SECOND[MILLI_OF_SECOND] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MILLI_OF_SECOND[java 9]) -** [[painless-api-reference-ChronoField-MINUTE_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MINUTE_OF_DAY[MINUTE_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MINUTE_OF_DAY[java 9]) -** [[painless-api-reference-ChronoField-MINUTE_OF_HOUR]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MINUTE_OF_HOUR[MINUTE_OF_HOUR] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MINUTE_OF_HOUR[java 9]) -** [[painless-api-reference-ChronoField-MONTH_OF_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MONTH_OF_YEAR[MONTH_OF_YEAR] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MONTH_OF_YEAR[java 9]) -** [[painless-api-reference-ChronoField-NANO_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#NANO_OF_DAY[NANO_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#NANO_OF_DAY[java 9]) -** [[painless-api-reference-ChronoField-NANO_OF_SECOND]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#NANO_OF_SECOND[NANO_OF_SECOND] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#NANO_OF_SECOND[java 9]) -** [[painless-api-reference-ChronoField-OFFSET_SECONDS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#OFFSET_SECONDS[OFFSET_SECONDS] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#OFFSET_SECONDS[java 9]) -** [[painless-api-reference-ChronoField-PROLEPTIC_MONTH]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#PROLEPTIC_MONTH[PROLEPTIC_MONTH] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#PROLEPTIC_MONTH[java 9]) -** [[painless-api-reference-ChronoField-SECOND_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#SECOND_OF_DAY[SECOND_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#SECOND_OF_DAY[java 9]) -** [[painless-api-reference-ChronoField-SECOND_OF_MINUTE]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#SECOND_OF_MINUTE[SECOND_OF_MINUTE] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#SECOND_OF_MINUTE[java 9]) -** [[painless-api-reference-ChronoField-YEAR]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#YEAR[YEAR] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#YEAR[java 9]) -** [[painless-api-reference-ChronoField-YEAR_OF_ERA]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#YEAR_OF_ERA[YEAR_OF_ERA] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#YEAR_OF_ERA[java 9]) -* ++[[painless-api-reference-ChronoField-valueOf-1]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/temporal/ChronoField.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-ChronoField-values-0]]static <>[] link:{java8-javadoc}/java/time/temporal/ChronoField.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/temporal/ChronoField.html#values%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoField-checkValidIntValue-1]]int link:{java8-javadoc}/java/time/temporal/ChronoField.html#checkValidIntValue%2Dlong%2D[checkValidIntValue](long)++ (link:{java9-javadoc}/java/time/temporal/ChronoField.html#checkValidIntValue%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ChronoField-checkValidValue-1]]long link:{java8-javadoc}/java/time/temporal/ChronoField.html#checkValidValue%2Dlong%2D[checkValidValue](long)++ (link:{java9-javadoc}/java/time/temporal/ChronoField.html#checkValidValue%2Dlong%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ChronoLocalDate.asciidoc b/docs/reference/painless-api-reference/ChronoLocalDate.asciidoc deleted file mode 100644 index 2c483c27bd8d9..0000000000000 --- a/docs/reference/painless-api-reference/ChronoLocalDate.asciidoc +++ /dev/null @@ -1,31 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ChronoLocalDate]]++ChronoLocalDate++:: -* ++[[painless-api-reference-ChronoLocalDate-from-1]]static <> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-timeLineOrder-0]]static <> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#timeLineOrder%2D%2D[timeLineOrder]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#timeLineOrder%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-atTime-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#atTime%2Djava.time.LocalTime%2D[atTime](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#atTime%2Djava.time.LocalTime%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-compareTo-1]]int link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#compareTo%2Djava.time.chrono.ChronoLocalDate%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#compareTo%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-equals-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#equals%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-format-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#getChronology%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-getEra-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#getEra%2D%2D[getEra]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#getEra%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-hashCode-0]]int link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#hashCode%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-isAfter-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#isAfter%2Djava.time.chrono.ChronoLocalDate%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#isAfter%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-isBefore-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#isBefore%2Djava.time.chrono.ChronoLocalDate%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#isBefore%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-isEqual-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#isEqual%2Djava.time.chrono.ChronoLocalDate%2D[isEqual](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#isEqual%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-isLeapYear-0]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#isLeapYear%2D%2D[isLeapYear]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#isLeapYear%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-lengthOfMonth-0]]int link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#lengthOfMonth%2D%2D[lengthOfMonth]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#lengthOfMonth%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-lengthOfYear-0]]int link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#lengthOfYear%2D%2D[lengthOfYear]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#lengthOfYear%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-minus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-minus-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-plus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-plus-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-toEpochDay-0]]long link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#toEpochDay%2D%2D[toEpochDay]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#toEpochDay%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-toString-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#toString%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-until-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#until%2Djava.time.chrono.ChronoLocalDate%2D[until](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#until%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-with-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDate-with-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ChronoLocalDateTime.asciidoc b/docs/reference/painless-api-reference/ChronoLocalDateTime.asciidoc deleted file mode 100644 index 7b49712ee0985..0000000000000 --- a/docs/reference/painless-api-reference/ChronoLocalDateTime.asciidoc +++ /dev/null @@ -1,29 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ChronoLocalDateTime]]++ChronoLocalDateTime++:: -* ++[[painless-api-reference-ChronoLocalDateTime-from-1]]static <> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-timeLineOrder-0]]static <> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#timeLineOrder%2D%2D[timeLineOrder]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#timeLineOrder%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-atZone-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#atZone%2Djava.time.ZoneId%2D[atZone](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#atZone%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-compareTo-1]]int link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#compareTo%2Djava.time.chrono.ChronoLocalDateTime%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#compareTo%2Djava.time.chrono.ChronoLocalDateTime%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-equals-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#equals%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-format-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#getChronology%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-hashCode-0]]int link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#hashCode%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-isAfter-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#isAfter%2Djava.time.chrono.ChronoLocalDateTime%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#isAfter%2Djava.time.chrono.ChronoLocalDateTime%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-isBefore-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#isBefore%2Djava.time.chrono.ChronoLocalDateTime%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#isBefore%2Djava.time.chrono.ChronoLocalDateTime%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-isEqual-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#isEqual%2Djava.time.chrono.ChronoLocalDateTime%2D[isEqual](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#isEqual%2Djava.time.chrono.ChronoLocalDateTime%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-minus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-minus-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-plus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-plus-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-toEpochSecond-1]]long link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toEpochSecond%2Djava.time.ZoneOffset%2D[toEpochSecond](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toEpochSecond%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-toInstant-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toInstant%2Djava.time.ZoneOffset%2D[toInstant](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toInstant%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-toLocalDate-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toLocalDate%2D%2D[toLocalDate]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toLocalDate%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-toLocalTime-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toLocalTime%2D%2D[toLocalTime]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toLocalTime%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-toString-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toString%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-with-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-ChronoLocalDateTime-with-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ChronoPeriod.asciidoc b/docs/reference/painless-api-reference/ChronoPeriod.asciidoc deleted file mode 100644 index 1b15df31c7667..0000000000000 --- a/docs/reference/painless-api-reference/ChronoPeriod.asciidoc +++ /dev/null @@ -1,20 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ChronoPeriod]]++ChronoPeriod++:: -* ++[[painless-api-reference-ChronoPeriod-between-2]]static <> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#between%2Djava.time.chrono.ChronoLocalDate%2Djava.time.chrono.ChronoLocalDate%2D[between](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#between%2Djava.time.chrono.ChronoLocalDate%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) -* ++[[painless-api-reference-ChronoPeriod-equals-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#equals%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-ChronoPeriod-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#getChronology%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoPeriod-getUnits-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#getUnits%2D%2D[getUnits]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#getUnits%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoPeriod-hashCode-0]]int link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#hashCode%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoPeriod-isNegative-0]]boolean link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#isNegative%2D%2D[isNegative]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#isNegative%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoPeriod-isZero-0]]boolean link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#isZero%2D%2D[isZero]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#isZero%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoPeriod-minus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-ChronoPeriod-multipliedBy-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#multipliedBy%2Dint%2D[multipliedBy](int)++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#multipliedBy%2Dint%2D[java 9]) -* ++[[painless-api-reference-ChronoPeriod-negated-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#negated%2D%2D[negated]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#negated%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoPeriod-normalized-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#normalized%2D%2D[normalized]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#normalized%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoPeriod-plus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-ChronoPeriod-toString-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#toString%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ChronoUnit.asciidoc b/docs/reference/painless-api-reference/ChronoUnit.asciidoc deleted file mode 100644 index 297fbf61835f9..0000000000000 --- a/docs/reference/painless-api-reference/ChronoUnit.asciidoc +++ /dev/null @@ -1,25 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ChronoUnit]]++ChronoUnit++:: -** [[painless-api-reference-ChronoUnit-CENTURIES]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#CENTURIES[CENTURIES] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#CENTURIES[java 9]) -** [[painless-api-reference-ChronoUnit-DAYS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#DAYS[DAYS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#DAYS[java 9]) -** [[painless-api-reference-ChronoUnit-DECADES]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#DECADES[DECADES] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#DECADES[java 9]) -** [[painless-api-reference-ChronoUnit-ERAS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#ERAS[ERAS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#ERAS[java 9]) -** [[painless-api-reference-ChronoUnit-FOREVER]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#FOREVER[FOREVER] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#FOREVER[java 9]) -** [[painless-api-reference-ChronoUnit-HALF_DAYS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#HALF_DAYS[HALF_DAYS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#HALF_DAYS[java 9]) -** [[painless-api-reference-ChronoUnit-HOURS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#HOURS[HOURS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#HOURS[java 9]) -** [[painless-api-reference-ChronoUnit-MICROS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#MICROS[MICROS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#MICROS[java 9]) -** [[painless-api-reference-ChronoUnit-MILLENNIA]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#MILLENNIA[MILLENNIA] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#MILLENNIA[java 9]) -** [[painless-api-reference-ChronoUnit-MILLIS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#MILLIS[MILLIS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#MILLIS[java 9]) -** [[painless-api-reference-ChronoUnit-MINUTES]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#MINUTES[MINUTES] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#MINUTES[java 9]) -** [[painless-api-reference-ChronoUnit-MONTHS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#MONTHS[MONTHS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#MONTHS[java 9]) -** [[painless-api-reference-ChronoUnit-NANOS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#NANOS[NANOS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#NANOS[java 9]) -** [[painless-api-reference-ChronoUnit-SECONDS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#SECONDS[SECONDS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#SECONDS[java 9]) -** [[painless-api-reference-ChronoUnit-WEEKS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#WEEKS[WEEKS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#WEEKS[java 9]) -** [[painless-api-reference-ChronoUnit-YEARS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#YEARS[YEARS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#YEARS[java 9]) -* ++[[painless-api-reference-ChronoUnit-valueOf-1]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-ChronoUnit-values-0]]static <>[] link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#values%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ChronoZonedDateTime.asciidoc b/docs/reference/painless-api-reference/ChronoZonedDateTime.asciidoc deleted file mode 100644 index 0684b8b541e02..0000000000000 --- a/docs/reference/painless-api-reference/ChronoZonedDateTime.asciidoc +++ /dev/null @@ -1,35 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ChronoZonedDateTime]]++ChronoZonedDateTime++:: -* ++[[painless-api-reference-ChronoZonedDateTime-from-1]]static <> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-timeLineOrder-0]]static <> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#timeLineOrder%2D%2D[timeLineOrder]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#timeLineOrder%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-compareTo-1]]int link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#compareTo%2Djava.time.chrono.ChronoZonedDateTime%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#compareTo%2Djava.time.chrono.ChronoZonedDateTime%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-equals-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#equals%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-format-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#getChronology%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-getOffset-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#getOffset%2D%2D[getOffset]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#getOffset%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-getZone-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#getZone%2D%2D[getZone]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#getZone%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-hashCode-0]]int link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#hashCode%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-isAfter-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#isAfter%2Djava.time.chrono.ChronoZonedDateTime%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#isAfter%2Djava.time.chrono.ChronoZonedDateTime%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-isBefore-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#isBefore%2Djava.time.chrono.ChronoZonedDateTime%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#isBefore%2Djava.time.chrono.ChronoZonedDateTime%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-isEqual-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#isEqual%2Djava.time.chrono.ChronoZonedDateTime%2D[isEqual](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#isEqual%2Djava.time.chrono.ChronoZonedDateTime%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-minus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-minus-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-plus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-plus-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-toEpochSecond-0]]long link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toEpochSecond%2D%2D[toEpochSecond]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toEpochSecond%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-toInstant-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toInstant%2D%2D[toInstant]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toInstant%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-toLocalDate-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toLocalDate%2D%2D[toLocalDate]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toLocalDate%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-toLocalDateTime-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toLocalDateTime%2D%2D[toLocalDateTime]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toLocalDateTime%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-toLocalTime-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toLocalTime%2D%2D[toLocalTime]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toLocalTime%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-toString-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toString%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-with-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-with-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-withEarlierOffsetAtOverlap-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withEarlierOffsetAtOverlap%2D%2D[withEarlierOffsetAtOverlap]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withEarlierOffsetAtOverlap%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-withLaterOffsetAtOverlap-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withLaterOffsetAtOverlap%2D%2D[withLaterOffsetAtOverlap]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withLaterOffsetAtOverlap%2D%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-withZoneSameInstant-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withZoneSameInstant%2Djava.time.ZoneId%2D[withZoneSameInstant](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withZoneSameInstant%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-ChronoZonedDateTime-withZoneSameLocal-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withZoneSameLocal%2Djava.time.ZoneId%2D[withZoneSameLocal](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withZoneSameLocal%2Djava.time.ZoneId%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Chronology.asciidoc b/docs/reference/painless-api-reference/Chronology.asciidoc deleted file mode 100644 index 42b395d930a40..0000000000000 --- a/docs/reference/painless-api-reference/Chronology.asciidoc +++ /dev/null @@ -1,33 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Chronology]]++Chronology++:: -* ++[[painless-api-reference-Chronology-from-1]]static <> link:{java8-javadoc}/java/time/chrono/Chronology.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-Chronology-getAvailableChronologies-0]]static <> link:{java8-javadoc}/java/time/chrono/Chronology.html#getAvailableChronologies%2D%2D[getAvailableChronologies]()++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#getAvailableChronologies%2D%2D[java 9]) -* ++[[painless-api-reference-Chronology-of-1]]static <> link:{java8-javadoc}/java/time/chrono/Chronology.html#of%2Djava.lang.String%2D[of](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#of%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Chronology-ofLocale-1]]static <> link:{java8-javadoc}/java/time/chrono/Chronology.html#ofLocale%2Djava.util.Locale%2D[ofLocale](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#ofLocale%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Chronology-compareTo-1]]int link:{java8-javadoc}/java/time/chrono/Chronology.html#compareTo%2Djava.time.chrono.Chronology%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#compareTo%2Djava.time.chrono.Chronology%2D[java 9]) -* ++[[painless-api-reference-Chronology-date-1]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[date](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-Chronology-date-3]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#date%2Dint%2Dint%2Dint%2D[date](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#date%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Chronology-date-4]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[date](<>, int, int, int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Chronology-dateEpochDay-1]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#dateEpochDay%2Dlong%2D[dateEpochDay](long)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#dateEpochDay%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Chronology-dateYearDay-2]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#dateYearDay%2Dint%2Dint%2D[dateYearDay](int, int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#dateYearDay%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Chronology-dateYearDay-3]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[dateYearDay](<>, int, int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Chronology-equals-1]]boolean link:{java8-javadoc}/java/time/chrono/Chronology.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#equals%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Chronology-eraOf-1]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#eraOf%2Dint%2D[eraOf](int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#eraOf%2Dint%2D[java 9]) -* ++[[painless-api-reference-Chronology-eras-0]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#eras%2D%2D[eras]()++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#eras%2D%2D[java 9]) -* ++[[painless-api-reference-Chronology-getCalendarType-0]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#getCalendarType%2D%2D[getCalendarType]()++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#getCalendarType%2D%2D[java 9]) -* ++[[painless-api-reference-Chronology-getDisplayName-2]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[getDisplayName](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Chronology-getId-0]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#getId%2D%2D[getId]()++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#getId%2D%2D[java 9]) -* ++[[painless-api-reference-Chronology-hashCode-0]]int link:{java8-javadoc}/java/time/chrono/Chronology.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#hashCode%2D%2D[java 9]) -* ++[[painless-api-reference-Chronology-isLeapYear-1]]boolean link:{java8-javadoc}/java/time/chrono/Chronology.html#isLeapYear%2Dlong%2D[isLeapYear](long)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#isLeapYear%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Chronology-localDateTime-1]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#localDateTime%2Djava.time.temporal.TemporalAccessor%2D[localDateTime](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#localDateTime%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-Chronology-period-3]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#period%2Dint%2Dint%2Dint%2D[period](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#period%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Chronology-prolepticYear-2]]int link:{java8-javadoc}/java/time/chrono/Chronology.html#prolepticYear%2Djava.time.chrono.Era%2Dint%2D[prolepticYear](<>, int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#prolepticYear%2Djava.time.chrono.Era%2Dint%2D[java 9]) -* ++[[painless-api-reference-Chronology-range-1]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#range%2Djava.time.temporal.ChronoField%2D[range](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#range%2Djava.time.temporal.ChronoField%2D[java 9]) -* ++[[painless-api-reference-Chronology-resolveDate-2]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[resolveDate](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[java 9]) -* ++[[painless-api-reference-Chronology-toString-0]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#toString%2D%2D[java 9]) -* ++[[painless-api-reference-Chronology-zonedDateTime-1]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#zonedDateTime%2Djava.time.temporal.TemporalAccessor%2D[zonedDateTime](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#zonedDateTime%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-Chronology-zonedDateTime-2]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#zonedDateTime%2Djava.time.Instant%2Djava.time.ZoneId%2D[zonedDateTime](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#zonedDateTime%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) diff --git a/docs/reference/painless-api-reference/ClassCastException.asciidoc b/docs/reference/painless-api-reference/ClassCastException.asciidoc deleted file mode 100644 index f837c66f9db11..0000000000000 --- a/docs/reference/painless-api-reference/ClassCastException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ClassCastException]]++ClassCastException++:: -* ++[[painless-api-reference-ClassCastException-ClassCastException-0]]link:{java8-javadoc}/java/lang/ClassCastException.html#ClassCastException%2D%2D[ClassCastException]()++ (link:{java9-javadoc}/java/lang/ClassCastException.html#ClassCastException%2D%2D[java 9]) -* ++[[painless-api-reference-ClassCastException-ClassCastException-1]]link:{java8-javadoc}/java/lang/ClassCastException.html#ClassCastException%2Djava.lang.String%2D[ClassCastException](<>)++ (link:{java9-javadoc}/java/lang/ClassCastException.html#ClassCastException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ClassNotFoundException.asciidoc b/docs/reference/painless-api-reference/ClassNotFoundException.asciidoc deleted file mode 100644 index f4d0d63aa0605..0000000000000 --- a/docs/reference/painless-api-reference/ClassNotFoundException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ClassNotFoundException]]++ClassNotFoundException++:: -* ++[[painless-api-reference-ClassNotFoundException-ClassNotFoundException-0]]link:{java8-javadoc}/java/lang/ClassNotFoundException.html#ClassNotFoundException%2D%2D[ClassNotFoundException]()++ (link:{java9-javadoc}/java/lang/ClassNotFoundException.html#ClassNotFoundException%2D%2D[java 9]) -* ++[[painless-api-reference-ClassNotFoundException-ClassNotFoundException-1]]link:{java8-javadoc}/java/lang/ClassNotFoundException.html#ClassNotFoundException%2Djava.lang.String%2D[ClassNotFoundException](<>)++ (link:{java9-javadoc}/java/lang/ClassNotFoundException.html#ClassNotFoundException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Clock.asciidoc b/docs/reference/painless-api-reference/Clock.asciidoc deleted file mode 100644 index c40a09a2fe92a..0000000000000 --- a/docs/reference/painless-api-reference/Clock.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Clock]]++Clock++:: -* ++[[painless-api-reference-Clock-fixed-2]]static <> link:{java8-javadoc}/java/time/Clock.html#fixed%2Djava.time.Instant%2Djava.time.ZoneId%2D[fixed](<>, <>)++ (link:{java9-javadoc}/java/time/Clock.html#fixed%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-Clock-offset-2]]static <> link:{java8-javadoc}/java/time/Clock.html#offset%2Djava.time.Clock%2Djava.time.Duration%2D[offset](<>, <>)++ (link:{java9-javadoc}/java/time/Clock.html#offset%2Djava.time.Clock%2Djava.time.Duration%2D[java 9]) -* ++[[painless-api-reference-Clock-tick-2]]static <> link:{java8-javadoc}/java/time/Clock.html#tick%2Djava.time.Clock%2Djava.time.Duration%2D[tick](<>, <>)++ (link:{java9-javadoc}/java/time/Clock.html#tick%2Djava.time.Clock%2Djava.time.Duration%2D[java 9]) -* ++[[painless-api-reference-Clock-getZone-0]]<> link:{java8-javadoc}/java/time/Clock.html#getZone%2D%2D[getZone]()++ (link:{java9-javadoc}/java/time/Clock.html#getZone%2D%2D[java 9]) -* ++[[painless-api-reference-Clock-instant-0]]<> link:{java8-javadoc}/java/time/Clock.html#instant%2D%2D[instant]()++ (link:{java9-javadoc}/java/time/Clock.html#instant%2D%2D[java 9]) -* ++[[painless-api-reference-Clock-millis-0]]long link:{java8-javadoc}/java/time/Clock.html#millis%2D%2D[millis]()++ (link:{java9-javadoc}/java/time/Clock.html#millis%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/CloneNotSupportedException.asciidoc b/docs/reference/painless-api-reference/CloneNotSupportedException.asciidoc deleted file mode 100644 index deeb87d599b5c..0000000000000 --- a/docs/reference/painless-api-reference/CloneNotSupportedException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-CloneNotSupportedException]]++CloneNotSupportedException++:: -* ++[[painless-api-reference-CloneNotSupportedException-CloneNotSupportedException-0]]link:{java8-javadoc}/java/lang/CloneNotSupportedException.html#CloneNotSupportedException%2D%2D[CloneNotSupportedException]()++ (link:{java9-javadoc}/java/lang/CloneNotSupportedException.html#CloneNotSupportedException%2D%2D[java 9]) -* ++[[painless-api-reference-CloneNotSupportedException-CloneNotSupportedException-1]]link:{java8-javadoc}/java/lang/CloneNotSupportedException.html#CloneNotSupportedException%2Djava.lang.String%2D[CloneNotSupportedException](<>)++ (link:{java9-javadoc}/java/lang/CloneNotSupportedException.html#CloneNotSupportedException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/CollationElementIterator.asciidoc b/docs/reference/painless-api-reference/CollationElementIterator.asciidoc deleted file mode 100644 index f277709123d8a..0000000000000 --- a/docs/reference/painless-api-reference/CollationElementIterator.asciidoc +++ /dev/null @@ -1,18 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-CollationElementIterator]]++CollationElementIterator++:: -** [[painless-api-reference-CollationElementIterator-NULLORDER]]static int link:{java8-javadoc}/java/text/CollationElementIterator.html#NULLORDER[NULLORDER] (link:{java9-javadoc}/java/text/CollationElementIterator.html#NULLORDER[java 9]) -* ++[[painless-api-reference-CollationElementIterator-primaryOrder-1]]static int link:{java8-javadoc}/java/text/CollationElementIterator.html#primaryOrder%2Dint%2D[primaryOrder](int)++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#primaryOrder%2Dint%2D[java 9]) -* ++[[painless-api-reference-CollationElementIterator-secondaryOrder-1]]static short link:{java8-javadoc}/java/text/CollationElementIterator.html#secondaryOrder%2Dint%2D[secondaryOrder](int)++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#secondaryOrder%2Dint%2D[java 9]) -* ++[[painless-api-reference-CollationElementIterator-tertiaryOrder-1]]static short link:{java8-javadoc}/java/text/CollationElementIterator.html#tertiaryOrder%2Dint%2D[tertiaryOrder](int)++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#tertiaryOrder%2Dint%2D[java 9]) -* ++[[painless-api-reference-CollationElementIterator-getMaxExpansion-1]]int link:{java8-javadoc}/java/text/CollationElementIterator.html#getMaxExpansion%2Dint%2D[getMaxExpansion](int)++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#getMaxExpansion%2Dint%2D[java 9]) -* ++[[painless-api-reference-CollationElementIterator-getOffset-0]]int link:{java8-javadoc}/java/text/CollationElementIterator.html#getOffset%2D%2D[getOffset]()++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#getOffset%2D%2D[java 9]) -* ++[[painless-api-reference-CollationElementIterator-next-0]]int link:{java8-javadoc}/java/text/CollationElementIterator.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#next%2D%2D[java 9]) -* ++[[painless-api-reference-CollationElementIterator-previous-0]]int link:{java8-javadoc}/java/text/CollationElementIterator.html#previous%2D%2D[previous]()++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#previous%2D%2D[java 9]) -* ++[[painless-api-reference-CollationElementIterator-reset-0]]void link:{java8-javadoc}/java/text/CollationElementIterator.html#reset%2D%2D[reset]()++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#reset%2D%2D[java 9]) -* ++[[painless-api-reference-CollationElementIterator-setOffset-1]]void link:{java8-javadoc}/java/text/CollationElementIterator.html#setOffset%2Dint%2D[setOffset](int)++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#setOffset%2Dint%2D[java 9]) -* ++[[painless-api-reference-CollationElementIterator-setText-1]]void link:{java8-javadoc}/java/text/CollationElementIterator.html#setText%2Djava.lang.String%2D[setText](<>)++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#setText%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/CollationKey.asciidoc b/docs/reference/painless-api-reference/CollationKey.asciidoc deleted file mode 100644 index adb6d29cf81bc..0000000000000 --- a/docs/reference/painless-api-reference/CollationKey.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-CollationKey]]++CollationKey++:: -* ++[[painless-api-reference-CollationKey-compareTo-1]]int link:{java8-javadoc}/java/text/CollationKey.html#compareTo%2Djava.text.CollationKey%2D[compareTo](<>)++ (link:{java9-javadoc}/java/text/CollationKey.html#compareTo%2Djava.text.CollationKey%2D[java 9]) -* ++[[painless-api-reference-CollationKey-getSourceString-0]]<> link:{java8-javadoc}/java/text/CollationKey.html#getSourceString%2D%2D[getSourceString]()++ (link:{java9-javadoc}/java/text/CollationKey.html#getSourceString%2D%2D[java 9]) -* ++[[painless-api-reference-CollationKey-toByteArray-0]]byte[] link:{java8-javadoc}/java/text/CollationKey.html#toByteArray%2D%2D[toByteArray]()++ (link:{java9-javadoc}/java/text/CollationKey.html#toByteArray%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Collator.asciidoc b/docs/reference/painless-api-reference/Collator.asciidoc deleted file mode 100644 index a14f29d986851..0000000000000 --- a/docs/reference/painless-api-reference/Collator.asciidoc +++ /dev/null @@ -1,24 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Collator]]++Collator++:: -** [[painless-api-reference-Collator-CANONICAL_DECOMPOSITION]]static int link:{java8-javadoc}/java/text/Collator.html#CANONICAL_DECOMPOSITION[CANONICAL_DECOMPOSITION] (link:{java9-javadoc}/java/text/Collator.html#CANONICAL_DECOMPOSITION[java 9]) -** [[painless-api-reference-Collator-FULL_DECOMPOSITION]]static int link:{java8-javadoc}/java/text/Collator.html#FULL_DECOMPOSITION[FULL_DECOMPOSITION] (link:{java9-javadoc}/java/text/Collator.html#FULL_DECOMPOSITION[java 9]) -** [[painless-api-reference-Collator-IDENTICAL]]static int link:{java8-javadoc}/java/text/Collator.html#IDENTICAL[IDENTICAL] (link:{java9-javadoc}/java/text/Collator.html#IDENTICAL[java 9]) -** [[painless-api-reference-Collator-NO_DECOMPOSITION]]static int link:{java8-javadoc}/java/text/Collator.html#NO_DECOMPOSITION[NO_DECOMPOSITION] (link:{java9-javadoc}/java/text/Collator.html#NO_DECOMPOSITION[java 9]) -** [[painless-api-reference-Collator-PRIMARY]]static int link:{java8-javadoc}/java/text/Collator.html#PRIMARY[PRIMARY] (link:{java9-javadoc}/java/text/Collator.html#PRIMARY[java 9]) -** [[painless-api-reference-Collator-SECONDARY]]static int link:{java8-javadoc}/java/text/Collator.html#SECONDARY[SECONDARY] (link:{java9-javadoc}/java/text/Collator.html#SECONDARY[java 9]) -** [[painless-api-reference-Collator-TERTIARY]]static int link:{java8-javadoc}/java/text/Collator.html#TERTIARY[TERTIARY] (link:{java9-javadoc}/java/text/Collator.html#TERTIARY[java 9]) -* ++[[painless-api-reference-Collator-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/text/Collator.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/text/Collator.html#getAvailableLocales%2D%2D[java 9]) -* ++[[painless-api-reference-Collator-getInstance-0]]static <> link:{java8-javadoc}/java/text/Collator.html#getInstance%2D%2D[getInstance]()++ (link:{java9-javadoc}/java/text/Collator.html#getInstance%2D%2D[java 9]) -* ++[[painless-api-reference-Collator-getInstance-1]]static <> link:{java8-javadoc}/java/text/Collator.html#getInstance%2Djava.util.Locale%2D[getInstance](<>)++ (link:{java9-javadoc}/java/text/Collator.html#getInstance%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Collator-clone-0]]def link:{java8-javadoc}/java/text/Collator.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/text/Collator.html#clone%2D%2D[java 9]) -* ++[[painless-api-reference-Collator-equals-2]]boolean link:{java8-javadoc}/java/text/Collator.html#equals%2Djava.lang.String%2Djava.lang.String%2D[equals](<>, <>)++ (link:{java9-javadoc}/java/text/Collator.html#equals%2Djava.lang.String%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Collator-getCollationKey-1]]<> link:{java8-javadoc}/java/text/Collator.html#getCollationKey%2Djava.lang.String%2D[getCollationKey](<>)++ (link:{java9-javadoc}/java/text/Collator.html#getCollationKey%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Collator-getDecomposition-0]]int link:{java8-javadoc}/java/text/Collator.html#getDecomposition%2D%2D[getDecomposition]()++ (link:{java9-javadoc}/java/text/Collator.html#getDecomposition%2D%2D[java 9]) -* ++[[painless-api-reference-Collator-getStrength-0]]int link:{java8-javadoc}/java/text/Collator.html#getStrength%2D%2D[getStrength]()++ (link:{java9-javadoc}/java/text/Collator.html#getStrength%2D%2D[java 9]) -* ++[[painless-api-reference-Collator-setDecomposition-1]]void link:{java8-javadoc}/java/text/Collator.html#setDecomposition%2Dint%2D[setDecomposition](int)++ (link:{java9-javadoc}/java/text/Collator.html#setDecomposition%2Dint%2D[java 9]) -* ++[[painless-api-reference-Collator-setStrength-1]]void link:{java8-javadoc}/java/text/Collator.html#setStrength%2Dint%2D[setStrength](int)++ (link:{java9-javadoc}/java/text/Collator.html#setStrength%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Collection.asciidoc b/docs/reference/painless-api-reference/Collection.asciidoc deleted file mode 100644 index e8faba23a6109..0000000000000 --- a/docs/reference/painless-api-reference/Collection.asciidoc +++ /dev/null @@ -1,28 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Collection]]++Collection++:: -* ++[[painless-api-reference-Collection-add-1]]boolean link:{java8-javadoc}/java/util/Collection.html#add%2Djava.lang.Object%2D[add](def)++ (link:{java9-javadoc}/java/util/Collection.html#add%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Collection-addAll-1]]boolean link:{java8-javadoc}/java/util/Collection.html#addAll%2Djava.util.Collection%2D[addAll](<>)++ (link:{java9-javadoc}/java/util/Collection.html#addAll%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Collection-clear-0]]void link:{java8-javadoc}/java/util/Collection.html#clear%2D%2D[clear]()++ (link:{java9-javadoc}/java/util/Collection.html#clear%2D%2D[java 9]) -* ++[[painless-api-reference-Collection-collect-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#collect%2Djava.util.Collection%2Djava.util.function.Function%2D[collect](<>)++ -* ++[[painless-api-reference-Collection-collect-2]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#collect%2Djava.util.Collection%2Djava.util.Collection%2Djava.util.function.Function%2D[collect](<>, <>)++ -* ++[[painless-api-reference-Collection-contains-1]]boolean link:{java8-javadoc}/java/util/Collection.html#contains%2Djava.lang.Object%2D[contains](def)++ (link:{java9-javadoc}/java/util/Collection.html#contains%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Collection-containsAll-1]]boolean link:{java8-javadoc}/java/util/Collection.html#containsAll%2Djava.util.Collection%2D[containsAll](<>)++ (link:{java9-javadoc}/java/util/Collection.html#containsAll%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Collection-find-1]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#find%2Djava.util.Collection%2Djava.util.function.Predicate%2D[find](<>)++ -* ++[[painless-api-reference-Collection-findAll-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findAll%2Djava.util.Collection%2Djava.util.function.Predicate%2D[findAll](<>)++ -* ++[[painless-api-reference-Collection-findResult-1]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findResult%2Djava.util.Collection%2Djava.util.function.Function%2D[findResult](<>)++ -* ++[[painless-api-reference-Collection-findResult-2]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findResult%2Djava.util.Collection%2Djava.lang.Object%2Djava.util.function.Function%2D[findResult](def, <>)++ -* ++[[painless-api-reference-Collection-isEmpty-0]]boolean link:{java8-javadoc}/java/util/Collection.html#isEmpty%2D%2D[isEmpty]()++ (link:{java9-javadoc}/java/util/Collection.html#isEmpty%2D%2D[java 9]) -* ++[[painless-api-reference-Collection-removeAll-1]]boolean link:{java8-javadoc}/java/util/Collection.html#removeAll%2Djava.util.Collection%2D[removeAll](<>)++ (link:{java9-javadoc}/java/util/Collection.html#removeAll%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Collection-removeIf-1]]boolean link:{java8-javadoc}/java/util/Collection.html#removeIf%2Djava.util.function.Predicate%2D[removeIf](<>)++ (link:{java9-javadoc}/java/util/Collection.html#removeIf%2Djava.util.function.Predicate%2D[java 9]) -* ++[[painless-api-reference-Collection-retainAll-1]]boolean link:{java8-javadoc}/java/util/Collection.html#retainAll%2Djava.util.Collection%2D[retainAll](<>)++ (link:{java9-javadoc}/java/util/Collection.html#retainAll%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Collection-size-0]]int link:{java8-javadoc}/java/util/Collection.html#size%2D%2D[size]()++ (link:{java9-javadoc}/java/util/Collection.html#size%2D%2D[java 9]) -* ++[[painless-api-reference-Collection-split-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#split%2Djava.util.Collection%2Djava.util.function.Predicate%2D[split](<>)++ -* ++[[painless-api-reference-Collection-spliterator-0]]<> link:{java8-javadoc}/java/util/Collection.html#spliterator%2D%2D[spliterator]()++ (link:{java9-javadoc}/java/util/Collection.html#spliterator%2D%2D[java 9]) -* ++[[painless-api-reference-Collection-stream-0]]<> link:{java8-javadoc}/java/util/Collection.html#stream%2D%2D[stream]()++ (link:{java9-javadoc}/java/util/Collection.html#stream%2D%2D[java 9]) -* ++[[painless-api-reference-Collection-toArray-0]]def[] link:{java8-javadoc}/java/util/Collection.html#toArray%2D%2D[toArray]()++ (link:{java9-javadoc}/java/util/Collection.html#toArray%2D%2D[java 9]) -* ++[[painless-api-reference-Collection-toArray-1]]def[] link:{java8-javadoc}/java/util/Collection.html#toArray%2Djava.lang.Object:A%2D[toArray](def[])++ (link:{java9-javadoc}/java/util/Collection.html#toArray%2Djava.lang.Object:A%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Collections.asciidoc b/docs/reference/painless-api-reference/Collections.asciidoc deleted file mode 100644 index db6d87e85210e..0000000000000 --- a/docs/reference/painless-api-reference/Collections.asciidoc +++ /dev/null @@ -1,59 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Collections]]++Collections++:: -** [[painless-api-reference-Collections-EMPTY_LIST]]static <> link:{java8-javadoc}/java/util/Collections.html#EMPTY_LIST[EMPTY_LIST] (link:{java9-javadoc}/java/util/Collections.html#EMPTY_LIST[java 9]) -** [[painless-api-reference-Collections-EMPTY_MAP]]static <> link:{java8-javadoc}/java/util/Collections.html#EMPTY_MAP[EMPTY_MAP] (link:{java9-javadoc}/java/util/Collections.html#EMPTY_MAP[java 9]) -** [[painless-api-reference-Collections-EMPTY_SET]]static <> link:{java8-javadoc}/java/util/Collections.html#EMPTY_SET[EMPTY_SET] (link:{java9-javadoc}/java/util/Collections.html#EMPTY_SET[java 9]) -* ++[[painless-api-reference-Collections-addAll-2]]static boolean link:{java8-javadoc}/java/util/Collections.html#addAll%2Djava.util.Collection%2Djava.lang.Object:A%2D[addAll](<>, def[])++ (link:{java9-javadoc}/java/util/Collections.html#addAll%2Djava.util.Collection%2Djava.lang.Object:A%2D[java 9]) -* ++[[painless-api-reference-Collections-asLifoQueue-1]]static <> link:{java8-javadoc}/java/util/Collections.html#asLifoQueue%2Djava.util.Deque%2D[asLifoQueue](<>)++ (link:{java9-javadoc}/java/util/Collections.html#asLifoQueue%2Djava.util.Deque%2D[java 9]) -* ++[[painless-api-reference-Collections-binarySearch-2]]static int link:{java8-javadoc}/java/util/Collections.html#binarySearch%2Djava.util.List%2Djava.lang.Object%2D[binarySearch](<>, def)++ (link:{java9-javadoc}/java/util/Collections.html#binarySearch%2Djava.util.List%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Collections-binarySearch-3]]static int link:{java8-javadoc}/java/util/Collections.html#binarySearch%2Djava.util.List%2Djava.lang.Object%2Djava.util.Comparator%2D[binarySearch](<>, def, <>)++ (link:{java9-javadoc}/java/util/Collections.html#binarySearch%2Djava.util.List%2Djava.lang.Object%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Collections-copy-2]]static void link:{java8-javadoc}/java/util/Collections.html#copy%2Djava.util.List%2Djava.util.List%2D[copy](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#copy%2Djava.util.List%2Djava.util.List%2D[java 9]) -* ++[[painless-api-reference-Collections-disjoint-2]]static boolean link:{java8-javadoc}/java/util/Collections.html#disjoint%2Djava.util.Collection%2Djava.util.Collection%2D[disjoint](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#disjoint%2Djava.util.Collection%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Collections-emptyEnumeration-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyEnumeration%2D%2D[emptyEnumeration]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyEnumeration%2D%2D[java 9]) -* ++[[painless-api-reference-Collections-emptyIterator-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyIterator%2D%2D[emptyIterator]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyIterator%2D%2D[java 9]) -* ++[[painless-api-reference-Collections-emptyList-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyList%2D%2D[emptyList]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyList%2D%2D[java 9]) -* ++[[painless-api-reference-Collections-emptyListIterator-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyListIterator%2D%2D[emptyListIterator]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyListIterator%2D%2D[java 9]) -* ++[[painless-api-reference-Collections-emptyMap-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyMap%2D%2D[emptyMap]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyMap%2D%2D[java 9]) -* ++[[painless-api-reference-Collections-emptyNavigableMap-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyNavigableMap%2D%2D[emptyNavigableMap]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyNavigableMap%2D%2D[java 9]) -* ++[[painless-api-reference-Collections-emptyNavigableSet-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyNavigableSet%2D%2D[emptyNavigableSet]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyNavigableSet%2D%2D[java 9]) -* ++[[painless-api-reference-Collections-emptySet-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptySet%2D%2D[emptySet]()++ (link:{java9-javadoc}/java/util/Collections.html#emptySet%2D%2D[java 9]) -* ++[[painless-api-reference-Collections-emptySortedMap-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptySortedMap%2D%2D[emptySortedMap]()++ (link:{java9-javadoc}/java/util/Collections.html#emptySortedMap%2D%2D[java 9]) -* ++[[painless-api-reference-Collections-emptySortedSet-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptySortedSet%2D%2D[emptySortedSet]()++ (link:{java9-javadoc}/java/util/Collections.html#emptySortedSet%2D%2D[java 9]) -* ++[[painless-api-reference-Collections-enumeration-1]]static <> link:{java8-javadoc}/java/util/Collections.html#enumeration%2Djava.util.Collection%2D[enumeration](<>)++ (link:{java9-javadoc}/java/util/Collections.html#enumeration%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Collections-fill-2]]static void link:{java8-javadoc}/java/util/Collections.html#fill%2Djava.util.List%2Djava.lang.Object%2D[fill](<>, def)++ (link:{java9-javadoc}/java/util/Collections.html#fill%2Djava.util.List%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Collections-frequency-2]]static int link:{java8-javadoc}/java/util/Collections.html#frequency%2Djava.util.Collection%2Djava.lang.Object%2D[frequency](<>, def)++ (link:{java9-javadoc}/java/util/Collections.html#frequency%2Djava.util.Collection%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Collections-indexOfSubList-2]]static int link:{java8-javadoc}/java/util/Collections.html#indexOfSubList%2Djava.util.List%2Djava.util.List%2D[indexOfSubList](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#indexOfSubList%2Djava.util.List%2Djava.util.List%2D[java 9]) -* ++[[painless-api-reference-Collections-lastIndexOfSubList-2]]static int link:{java8-javadoc}/java/util/Collections.html#lastIndexOfSubList%2Djava.util.List%2Djava.util.List%2D[lastIndexOfSubList](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#lastIndexOfSubList%2Djava.util.List%2Djava.util.List%2D[java 9]) -* ++[[painless-api-reference-Collections-list-1]]static <> link:{java8-javadoc}/java/util/Collections.html#list%2Djava.util.Enumeration%2D[list](<>)++ (link:{java9-javadoc}/java/util/Collections.html#list%2Djava.util.Enumeration%2D[java 9]) -* ++[[painless-api-reference-Collections-max-1]]static def link:{java8-javadoc}/java/util/Collections.html#max%2Djava.util.Collection%2D[max](<>)++ (link:{java9-javadoc}/java/util/Collections.html#max%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Collections-max-2]]static def link:{java8-javadoc}/java/util/Collections.html#max%2Djava.util.Collection%2Djava.util.Comparator%2D[max](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#max%2Djava.util.Collection%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Collections-min-1]]static def link:{java8-javadoc}/java/util/Collections.html#min%2Djava.util.Collection%2D[min](<>)++ (link:{java9-javadoc}/java/util/Collections.html#min%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Collections-min-2]]static def link:{java8-javadoc}/java/util/Collections.html#min%2Djava.util.Collection%2Djava.util.Comparator%2D[min](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#min%2Djava.util.Collection%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Collections-nCopies-2]]static <> link:{java8-javadoc}/java/util/Collections.html#nCopies%2Dint%2Djava.lang.Object%2D[nCopies](int, def)++ (link:{java9-javadoc}/java/util/Collections.html#nCopies%2Dint%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Collections-newSetFromMap-1]]static <> link:{java8-javadoc}/java/util/Collections.html#newSetFromMap%2Djava.util.Map%2D[newSetFromMap](<>)++ (link:{java9-javadoc}/java/util/Collections.html#newSetFromMap%2Djava.util.Map%2D[java 9]) -* ++[[painless-api-reference-Collections-replaceAll-3]]static boolean link:{java8-javadoc}/java/util/Collections.html#replaceAll%2Djava.util.List%2Djava.lang.Object%2Djava.lang.Object%2D[replaceAll](<>, def, def)++ (link:{java9-javadoc}/java/util/Collections.html#replaceAll%2Djava.util.List%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Collections-reverse-1]]static void link:{java8-javadoc}/java/util/Collections.html#reverse%2Djava.util.List%2D[reverse](<>)++ (link:{java9-javadoc}/java/util/Collections.html#reverse%2Djava.util.List%2D[java 9]) -* ++[[painless-api-reference-Collections-reverseOrder-0]]static <> link:{java8-javadoc}/java/util/Collections.html#reverseOrder%2D%2D[reverseOrder]()++ (link:{java9-javadoc}/java/util/Collections.html#reverseOrder%2D%2D[java 9]) -* ++[[painless-api-reference-Collections-reverseOrder-1]]static <> link:{java8-javadoc}/java/util/Collections.html#reverseOrder%2Djava.util.Comparator%2D[reverseOrder](<>)++ (link:{java9-javadoc}/java/util/Collections.html#reverseOrder%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Collections-rotate-2]]static void link:{java8-javadoc}/java/util/Collections.html#rotate%2Djava.util.List%2Dint%2D[rotate](<>, int)++ (link:{java9-javadoc}/java/util/Collections.html#rotate%2Djava.util.List%2Dint%2D[java 9]) -* ++[[painless-api-reference-Collections-shuffle-1]]static void link:{java8-javadoc}/java/util/Collections.html#shuffle%2Djava.util.List%2D[shuffle](<>)++ (link:{java9-javadoc}/java/util/Collections.html#shuffle%2Djava.util.List%2D[java 9]) -* ++[[painless-api-reference-Collections-shuffle-2]]static void link:{java8-javadoc}/java/util/Collections.html#shuffle%2Djava.util.List%2Djava.util.Random%2D[shuffle](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#shuffle%2Djava.util.List%2Djava.util.Random%2D[java 9]) -* ++[[painless-api-reference-Collections-singleton-1]]static <> link:{java8-javadoc}/java/util/Collections.html#singleton%2Djava.lang.Object%2D[singleton](def)++ (link:{java9-javadoc}/java/util/Collections.html#singleton%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Collections-singletonList-1]]static <> link:{java8-javadoc}/java/util/Collections.html#singletonList%2Djava.lang.Object%2D[singletonList](def)++ (link:{java9-javadoc}/java/util/Collections.html#singletonList%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Collections-singletonMap-2]]static <> link:{java8-javadoc}/java/util/Collections.html#singletonMap%2Djava.lang.Object%2Djava.lang.Object%2D[singletonMap](def, def)++ (link:{java9-javadoc}/java/util/Collections.html#singletonMap%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Collections-sort-1]]static void link:{java8-javadoc}/java/util/Collections.html#sort%2Djava.util.List%2D[sort](<>)++ (link:{java9-javadoc}/java/util/Collections.html#sort%2Djava.util.List%2D[java 9]) -* ++[[painless-api-reference-Collections-sort-2]]static void link:{java8-javadoc}/java/util/Collections.html#sort%2Djava.util.List%2Djava.util.Comparator%2D[sort](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#sort%2Djava.util.List%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Collections-swap-3]]static void link:{java8-javadoc}/java/util/Collections.html#swap%2Djava.util.List%2Dint%2Dint%2D[swap](<>, int, int)++ (link:{java9-javadoc}/java/util/Collections.html#swap%2Djava.util.List%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Collections-unmodifiableCollection-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableCollection%2Djava.util.Collection%2D[unmodifiableCollection](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableCollection%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Collections-unmodifiableList-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableList%2Djava.util.List%2D[unmodifiableList](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableList%2Djava.util.List%2D[java 9]) -* ++[[painless-api-reference-Collections-unmodifiableMap-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableMap%2Djava.util.Map%2D[unmodifiableMap](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableMap%2Djava.util.Map%2D[java 9]) -* ++[[painless-api-reference-Collections-unmodifiableNavigableMap-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableNavigableMap%2Djava.util.NavigableMap%2D[unmodifiableNavigableMap](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableNavigableMap%2Djava.util.NavigableMap%2D[java 9]) -* ++[[painless-api-reference-Collections-unmodifiableNavigableSet-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableNavigableSet%2Djava.util.NavigableSet%2D[unmodifiableNavigableSet](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableNavigableSet%2Djava.util.NavigableSet%2D[java 9]) -* ++[[painless-api-reference-Collections-unmodifiableSet-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableSet%2Djava.util.Set%2D[unmodifiableSet](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableSet%2Djava.util.Set%2D[java 9]) -* ++[[painless-api-reference-Collections-unmodifiableSortedMap-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableSortedMap%2Djava.util.SortedMap%2D[unmodifiableSortedMap](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableSortedMap%2Djava.util.SortedMap%2D[java 9]) -* ++[[painless-api-reference-Collections-unmodifiableSortedSet-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableSortedSet%2Djava.util.SortedSet%2D[unmodifiableSortedSet](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableSortedSet%2Djava.util.SortedSet%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Collector.Characteristics.asciidoc b/docs/reference/painless-api-reference/Collector.Characteristics.asciidoc deleted file mode 100644 index 17c6bec94751c..0000000000000 --- a/docs/reference/painless-api-reference/Collector.Characteristics.asciidoc +++ /dev/null @@ -1,12 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Collector-Characteristics]]++Collector.Characteristics++:: -** [[painless-api-reference-Collector-Characteristics-CONCURRENT]]static <> link:{java8-javadoc}/java/util/stream/Collector$Characteristics.html#CONCURRENT[CONCURRENT] (link:{java9-javadoc}/java/util/stream/Collector$Characteristics.html#CONCURRENT[java 9]) -** [[painless-api-reference-Collector-Characteristics-IDENTITY_FINISH]]static <> link:{java8-javadoc}/java/util/stream/Collector$Characteristics.html#IDENTITY_FINISH[IDENTITY_FINISH] (link:{java9-javadoc}/java/util/stream/Collector$Characteristics.html#IDENTITY_FINISH[java 9]) -** [[painless-api-reference-Collector-Characteristics-UNORDERED]]static <> link:{java8-javadoc}/java/util/stream/Collector$Characteristics.html#UNORDERED[UNORDERED] (link:{java9-javadoc}/java/util/stream/Collector$Characteristics.html#UNORDERED[java 9]) -* ++[[painless-api-reference-Collector-Characteristics-valueOf-1]]static <> link:{java8-javadoc}/java/util/stream/Collector$Characteristics.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/util/stream/Collector$Characteristics.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Collector-Characteristics-values-0]]static <>[] link:{java8-javadoc}/java/util/stream/Collector$Characteristics.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/util/stream/Collector$Characteristics.html#values%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Collector.asciidoc b/docs/reference/painless-api-reference/Collector.asciidoc deleted file mode 100644 index 1d1b92f1e0911..0000000000000 --- a/docs/reference/painless-api-reference/Collector.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Collector]]++Collector++:: -* ++[[painless-api-reference-Collector-of-4]]static <> link:{java8-javadoc}/java/util/stream/Collector.html#of%2Djava.util.function.Supplier%2Djava.util.function.BiConsumer%2Djava.util.function.BinaryOperator%2Djava.util.stream.Collector$Characteristics:A%2D[of](<>, <>, <>, <>[])++ (link:{java9-javadoc}/java/util/stream/Collector.html#of%2Djava.util.function.Supplier%2Djava.util.function.BiConsumer%2Djava.util.function.BinaryOperator%2Djava.util.stream.Collector$Characteristics:A%2D[java 9]) -* ++[[painless-api-reference-Collector-of-5]]static <> link:{java8-javadoc}/java/util/stream/Collector.html#of%2Djava.util.function.Supplier%2Djava.util.function.BiConsumer%2Djava.util.function.BinaryOperator%2Djava.util.function.Function%2Djava.util.stream.Collector$Characteristics:A%2D[of](<>, <>, <>, <>, <>[])++ (link:{java9-javadoc}/java/util/stream/Collector.html#of%2Djava.util.function.Supplier%2Djava.util.function.BiConsumer%2Djava.util.function.BinaryOperator%2Djava.util.function.Function%2Djava.util.stream.Collector$Characteristics:A%2D[java 9]) -* ++[[painless-api-reference-Collector-accumulator-0]]<> link:{java8-javadoc}/java/util/stream/Collector.html#accumulator%2D%2D[accumulator]()++ (link:{java9-javadoc}/java/util/stream/Collector.html#accumulator%2D%2D[java 9]) -* ++[[painless-api-reference-Collector-characteristics-0]]<> link:{java8-javadoc}/java/util/stream/Collector.html#characteristics%2D%2D[characteristics]()++ (link:{java9-javadoc}/java/util/stream/Collector.html#characteristics%2D%2D[java 9]) -* ++[[painless-api-reference-Collector-combiner-0]]<> link:{java8-javadoc}/java/util/stream/Collector.html#combiner%2D%2D[combiner]()++ (link:{java9-javadoc}/java/util/stream/Collector.html#combiner%2D%2D[java 9]) -* ++[[painless-api-reference-Collector-finisher-0]]<> link:{java8-javadoc}/java/util/stream/Collector.html#finisher%2D%2D[finisher]()++ (link:{java9-javadoc}/java/util/stream/Collector.html#finisher%2D%2D[java 9]) -* ++[[painless-api-reference-Collector-supplier-0]]<> link:{java8-javadoc}/java/util/stream/Collector.html#supplier%2D%2D[supplier]()++ (link:{java9-javadoc}/java/util/stream/Collector.html#supplier%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Collectors.asciidoc b/docs/reference/painless-api-reference/Collectors.asciidoc deleted file mode 100644 index ef4d0199386e1..0000000000000 --- a/docs/reference/painless-api-reference/Collectors.asciidoc +++ /dev/null @@ -1,38 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Collectors]]++Collectors++:: -* ++[[painless-api-reference-Collectors-averagingDouble-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#averagingDouble%2Djava.util.function.ToDoubleFunction%2D[averagingDouble](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#averagingDouble%2Djava.util.function.ToDoubleFunction%2D[java 9]) -* ++[[painless-api-reference-Collectors-averagingInt-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#averagingInt%2Djava.util.function.ToIntFunction%2D[averagingInt](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#averagingInt%2Djava.util.function.ToIntFunction%2D[java 9]) -* ++[[painless-api-reference-Collectors-averagingLong-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#averagingLong%2Djava.util.function.ToLongFunction%2D[averagingLong](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#averagingLong%2Djava.util.function.ToLongFunction%2D[java 9]) -* ++[[painless-api-reference-Collectors-collectingAndThen-2]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#collectingAndThen%2Djava.util.stream.Collector%2Djava.util.function.Function%2D[collectingAndThen](<>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#collectingAndThen%2Djava.util.stream.Collector%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-Collectors-counting-0]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#counting%2D%2D[counting]()++ (link:{java9-javadoc}/java/util/stream/Collectors.html#counting%2D%2D[java 9]) -* ++[[painless-api-reference-Collectors-groupingBy-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#groupingBy%2Djava.util.function.Function%2D[groupingBy](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#groupingBy%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-Collectors-groupingBy-2]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#groupingBy%2Djava.util.function.Function%2Djava.util.stream.Collector%2D[groupingBy](<>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#groupingBy%2Djava.util.function.Function%2Djava.util.stream.Collector%2D[java 9]) -* ++[[painless-api-reference-Collectors-groupingBy-3]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#groupingBy%2Djava.util.function.Function%2Djava.util.function.Supplier%2Djava.util.stream.Collector%2D[groupingBy](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#groupingBy%2Djava.util.function.Function%2Djava.util.function.Supplier%2Djava.util.stream.Collector%2D[java 9]) -* ++[[painless-api-reference-Collectors-joining-0]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#joining%2D%2D[joining]()++ (link:{java9-javadoc}/java/util/stream/Collectors.html#joining%2D%2D[java 9]) -* ++[[painless-api-reference-Collectors-joining-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#joining%2Djava.lang.CharSequence%2D[joining](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#joining%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-Collectors-joining-3]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#joining%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2D[joining](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#joining%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-Collectors-mapping-2]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#mapping%2Djava.util.function.Function%2Djava.util.stream.Collector%2D[mapping](<>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#mapping%2Djava.util.function.Function%2Djava.util.stream.Collector%2D[java 9]) -* ++[[painless-api-reference-Collectors-maxBy-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#maxBy%2Djava.util.Comparator%2D[maxBy](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#maxBy%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Collectors-minBy-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#minBy%2Djava.util.Comparator%2D[minBy](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#minBy%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Collectors-partitioningBy-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#partitioningBy%2Djava.util.function.Predicate%2D[partitioningBy](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#partitioningBy%2Djava.util.function.Predicate%2D[java 9]) -* ++[[painless-api-reference-Collectors-partitioningBy-2]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#partitioningBy%2Djava.util.function.Predicate%2Djava.util.stream.Collector%2D[partitioningBy](<>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#partitioningBy%2Djava.util.function.Predicate%2Djava.util.stream.Collector%2D[java 9]) -* ++[[painless-api-reference-Collectors-reducing-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#reducing%2Djava.util.function.BinaryOperator%2D[reducing](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#reducing%2Djava.util.function.BinaryOperator%2D[java 9]) -* ++[[painless-api-reference-Collectors-reducing-2]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#reducing%2Djava.lang.Object%2Djava.util.function.BinaryOperator%2D[reducing](def, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#reducing%2Djava.lang.Object%2Djava.util.function.BinaryOperator%2D[java 9]) -* ++[[painless-api-reference-Collectors-reducing-3]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#reducing%2Djava.lang.Object%2Djava.util.function.Function%2Djava.util.function.BinaryOperator%2D[reducing](def, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#reducing%2Djava.lang.Object%2Djava.util.function.Function%2Djava.util.function.BinaryOperator%2D[java 9]) -* ++[[painless-api-reference-Collectors-summarizingDouble-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#summarizingDouble%2Djava.util.function.ToDoubleFunction%2D[summarizingDouble](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#summarizingDouble%2Djava.util.function.ToDoubleFunction%2D[java 9]) -* ++[[painless-api-reference-Collectors-summarizingInt-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#summarizingInt%2Djava.util.function.ToIntFunction%2D[summarizingInt](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#summarizingInt%2Djava.util.function.ToIntFunction%2D[java 9]) -* ++[[painless-api-reference-Collectors-summarizingLong-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#summarizingLong%2Djava.util.function.ToLongFunction%2D[summarizingLong](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#summarizingLong%2Djava.util.function.ToLongFunction%2D[java 9]) -* ++[[painless-api-reference-Collectors-summingDouble-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#summingDouble%2Djava.util.function.ToDoubleFunction%2D[summingDouble](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#summingDouble%2Djava.util.function.ToDoubleFunction%2D[java 9]) -* ++[[painless-api-reference-Collectors-summingInt-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#summingInt%2Djava.util.function.ToIntFunction%2D[summingInt](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#summingInt%2Djava.util.function.ToIntFunction%2D[java 9]) -* ++[[painless-api-reference-Collectors-summingLong-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#summingLong%2Djava.util.function.ToLongFunction%2D[summingLong](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#summingLong%2Djava.util.function.ToLongFunction%2D[java 9]) -* ++[[painless-api-reference-Collectors-toCollection-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#toCollection%2Djava.util.function.Supplier%2D[toCollection](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#toCollection%2Djava.util.function.Supplier%2D[java 9]) -* ++[[painless-api-reference-Collectors-toList-0]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#toList%2D%2D[toList]()++ (link:{java9-javadoc}/java/util/stream/Collectors.html#toList%2D%2D[java 9]) -* ++[[painless-api-reference-Collectors-toMap-2]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#toMap%2Djava.util.function.Function%2Djava.util.function.Function%2D[toMap](<>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#toMap%2Djava.util.function.Function%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-Collectors-toMap-3]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#toMap%2Djava.util.function.Function%2Djava.util.function.Function%2Djava.util.function.BinaryOperator%2D[toMap](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#toMap%2Djava.util.function.Function%2Djava.util.function.Function%2Djava.util.function.BinaryOperator%2D[java 9]) -* ++[[painless-api-reference-Collectors-toMap-4]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#toMap%2Djava.util.function.Function%2Djava.util.function.Function%2Djava.util.function.BinaryOperator%2Djava.util.function.Supplier%2D[toMap](<>, <>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#toMap%2Djava.util.function.Function%2Djava.util.function.Function%2Djava.util.function.BinaryOperator%2Djava.util.function.Supplier%2D[java 9]) -* ++[[painless-api-reference-Collectors-toSet-0]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#toSet%2D%2D[toSet]()++ (link:{java9-javadoc}/java/util/stream/Collectors.html#toSet%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Comparable.asciidoc b/docs/reference/painless-api-reference/Comparable.asciidoc deleted file mode 100644 index 1550d93b51144..0000000000000 --- a/docs/reference/painless-api-reference/Comparable.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Comparable]]++Comparable++:: -* ++[[painless-api-reference-Comparable-compareTo-1]]int link:{java8-javadoc}/java/lang/Comparable.html#compareTo%2Djava.lang.Object%2D[compareTo](def)++ (link:{java9-javadoc}/java/lang/Comparable.html#compareTo%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Comparator.asciidoc b/docs/reference/painless-api-reference/Comparator.asciidoc deleted file mode 100644 index 25545376942cc..0000000000000 --- a/docs/reference/painless-api-reference/Comparator.asciidoc +++ /dev/null @@ -1,24 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Comparator]]++Comparator++:: -* ++[[painless-api-reference-Comparator-comparing-1]]static <> link:{java8-javadoc}/java/util/Comparator.html#comparing%2Djava.util.function.Function%2D[comparing](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#comparing%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-Comparator-comparing-2]]static <> link:{java8-javadoc}/java/util/Comparator.html#comparing%2Djava.util.function.Function%2Djava.util.Comparator%2D[comparing](<>, <>)++ (link:{java9-javadoc}/java/util/Comparator.html#comparing%2Djava.util.function.Function%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Comparator-comparingDouble-1]]static <> link:{java8-javadoc}/java/util/Comparator.html#comparingDouble%2Djava.util.function.ToDoubleFunction%2D[comparingDouble](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#comparingDouble%2Djava.util.function.ToDoubleFunction%2D[java 9]) -* ++[[painless-api-reference-Comparator-comparingInt-1]]static <> link:{java8-javadoc}/java/util/Comparator.html#comparingInt%2Djava.util.function.ToIntFunction%2D[comparingInt](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#comparingInt%2Djava.util.function.ToIntFunction%2D[java 9]) -* ++[[painless-api-reference-Comparator-comparingLong-1]]static <> link:{java8-javadoc}/java/util/Comparator.html#comparingLong%2Djava.util.function.ToLongFunction%2D[comparingLong](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#comparingLong%2Djava.util.function.ToLongFunction%2D[java 9]) -* ++[[painless-api-reference-Comparator-naturalOrder-0]]static <> link:{java8-javadoc}/java/util/Comparator.html#naturalOrder%2D%2D[naturalOrder]()++ (link:{java9-javadoc}/java/util/Comparator.html#naturalOrder%2D%2D[java 9]) -* ++[[painless-api-reference-Comparator-nullsFirst-1]]static <> link:{java8-javadoc}/java/util/Comparator.html#nullsFirst%2Djava.util.Comparator%2D[nullsFirst](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#nullsFirst%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Comparator-nullsLast-1]]static <> link:{java8-javadoc}/java/util/Comparator.html#nullsLast%2Djava.util.Comparator%2D[nullsLast](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#nullsLast%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Comparator-reverseOrder-0]]static <> link:{java8-javadoc}/java/util/Comparator.html#reverseOrder%2D%2D[reverseOrder]()++ (link:{java9-javadoc}/java/util/Comparator.html#reverseOrder%2D%2D[java 9]) -* ++[[painless-api-reference-Comparator-compare-2]]int link:{java8-javadoc}/java/util/Comparator.html#compare%2Djava.lang.Object%2Djava.lang.Object%2D[compare](def, def)++ (link:{java9-javadoc}/java/util/Comparator.html#compare%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Comparator-equals-1]]boolean link:{java8-javadoc}/java/util/Comparator.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#equals%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Comparator-reversed-0]]<> link:{java8-javadoc}/java/util/Comparator.html#reversed%2D%2D[reversed]()++ (link:{java9-javadoc}/java/util/Comparator.html#reversed%2D%2D[java 9]) -* ++[[painless-api-reference-Comparator-thenComparing-1]]<> link:{java8-javadoc}/java/util/Comparator.html#thenComparing%2Djava.util.Comparator%2D[thenComparing](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#thenComparing%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Comparator-thenComparing-2]]<> link:{java8-javadoc}/java/util/Comparator.html#thenComparing%2Djava.util.function.Function%2Djava.util.Comparator%2D[thenComparing](<>, <>)++ (link:{java9-javadoc}/java/util/Comparator.html#thenComparing%2Djava.util.function.Function%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Comparator-thenComparingDouble-1]]<> link:{java8-javadoc}/java/util/Comparator.html#thenComparingDouble%2Djava.util.function.ToDoubleFunction%2D[thenComparingDouble](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#thenComparingDouble%2Djava.util.function.ToDoubleFunction%2D[java 9]) -* ++[[painless-api-reference-Comparator-thenComparingInt-1]]<> link:{java8-javadoc}/java/util/Comparator.html#thenComparingInt%2Djava.util.function.ToIntFunction%2D[thenComparingInt](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#thenComparingInt%2Djava.util.function.ToIntFunction%2D[java 9]) -* ++[[painless-api-reference-Comparator-thenComparingLong-1]]<> link:{java8-javadoc}/java/util/Comparator.html#thenComparingLong%2Djava.util.function.ToLongFunction%2D[thenComparingLong](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#thenComparingLong%2Djava.util.function.ToLongFunction%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ConcurrentModificationException.asciidoc b/docs/reference/painless-api-reference/ConcurrentModificationException.asciidoc deleted file mode 100644 index b33eefee1e5ad..0000000000000 --- a/docs/reference/painless-api-reference/ConcurrentModificationException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ConcurrentModificationException]]++ConcurrentModificationException++:: -* ++[[painless-api-reference-ConcurrentModificationException-ConcurrentModificationException-0]]link:{java8-javadoc}/java/util/ConcurrentModificationException.html#ConcurrentModificationException%2D%2D[ConcurrentModificationException]()++ (link:{java9-javadoc}/java/util/ConcurrentModificationException.html#ConcurrentModificationException%2D%2D[java 9]) -* ++[[painless-api-reference-ConcurrentModificationException-ConcurrentModificationException-1]]link:{java8-javadoc}/java/util/ConcurrentModificationException.html#ConcurrentModificationException%2Djava.lang.String%2D[ConcurrentModificationException](<>)++ (link:{java9-javadoc}/java/util/ConcurrentModificationException.html#ConcurrentModificationException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Consumer.asciidoc b/docs/reference/painless-api-reference/Consumer.asciidoc deleted file mode 100644 index a93f47e1a1a7c..0000000000000 --- a/docs/reference/painless-api-reference/Consumer.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Consumer]]++Consumer++:: -* ++[[painless-api-reference-Consumer-accept-1]]void link:{java8-javadoc}/java/util/function/Consumer.html#accept%2Djava.lang.Object%2D[accept](def)++ (link:{java9-javadoc}/java/util/function/Consumer.html#accept%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Consumer-andThen-1]]<> link:{java8-javadoc}/java/util/function/Consumer.html#andThen%2Djava.util.function.Consumer%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/Consumer.html#andThen%2Djava.util.function.Consumer%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Currency.asciidoc b/docs/reference/painless-api-reference/Currency.asciidoc deleted file mode 100644 index 12e172e01eeea..0000000000000 --- a/docs/reference/painless-api-reference/Currency.asciidoc +++ /dev/null @@ -1,16 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Currency]]++Currency++:: -* ++[[painless-api-reference-Currency-getAvailableCurrencies-0]]static <> link:{java8-javadoc}/java/util/Currency.html#getAvailableCurrencies%2D%2D[getAvailableCurrencies]()++ (link:{java9-javadoc}/java/util/Currency.html#getAvailableCurrencies%2D%2D[java 9]) -* ++[[painless-api-reference-Currency-getInstance-1]]static <> link:{java8-javadoc}/java/util/Currency.html#getInstance%2Djava.lang.String%2D[getInstance](<>)++ (link:{java9-javadoc}/java/util/Currency.html#getInstance%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Currency-getCurrencyCode-0]]<> link:{java8-javadoc}/java/util/Currency.html#getCurrencyCode%2D%2D[getCurrencyCode]()++ (link:{java9-javadoc}/java/util/Currency.html#getCurrencyCode%2D%2D[java 9]) -* ++[[painless-api-reference-Currency-getDefaultFractionDigits-0]]int link:{java8-javadoc}/java/util/Currency.html#getDefaultFractionDigits%2D%2D[getDefaultFractionDigits]()++ (link:{java9-javadoc}/java/util/Currency.html#getDefaultFractionDigits%2D%2D[java 9]) -* ++[[painless-api-reference-Currency-getDisplayName-0]]<> link:{java8-javadoc}/java/util/Currency.html#getDisplayName%2D%2D[getDisplayName]()++ (link:{java9-javadoc}/java/util/Currency.html#getDisplayName%2D%2D[java 9]) -* ++[[painless-api-reference-Currency-getDisplayName-1]]<> link:{java8-javadoc}/java/util/Currency.html#getDisplayName%2Djava.util.Locale%2D[getDisplayName](<>)++ (link:{java9-javadoc}/java/util/Currency.html#getDisplayName%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Currency-getNumericCode-0]]int link:{java8-javadoc}/java/util/Currency.html#getNumericCode%2D%2D[getNumericCode]()++ (link:{java9-javadoc}/java/util/Currency.html#getNumericCode%2D%2D[java 9]) -* ++[[painless-api-reference-Currency-getSymbol-0]]<> link:{java8-javadoc}/java/util/Currency.html#getSymbol%2D%2D[getSymbol]()++ (link:{java9-javadoc}/java/util/Currency.html#getSymbol%2D%2D[java 9]) -* ++[[painless-api-reference-Currency-getSymbol-1]]<> link:{java8-javadoc}/java/util/Currency.html#getSymbol%2Djava.util.Locale%2D[getSymbol](<>)++ (link:{java9-javadoc}/java/util/Currency.html#getSymbol%2Djava.util.Locale%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Date.asciidoc b/docs/reference/painless-api-reference/Date.asciidoc deleted file mode 100644 index 5b83f70a0eaae..0000000000000 --- a/docs/reference/painless-api-reference/Date.asciidoc +++ /dev/null @@ -1,16 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Date]]++Date++:: -* ++[[painless-api-reference-Date-from-1]]static <> link:{java8-javadoc}/java/util/Date.html#from%2Djava.time.Instant%2D[from](<>)++ (link:{java9-javadoc}/java/util/Date.html#from%2Djava.time.Instant%2D[java 9]) -* ++[[painless-api-reference-Date-Date-0]]link:{java8-javadoc}/java/util/Date.html#Date%2D%2D[Date]()++ (link:{java9-javadoc}/java/util/Date.html#Date%2D%2D[java 9]) -* ++[[painless-api-reference-Date-Date-1]]link:{java8-javadoc}/java/util/Date.html#Date%2Dlong%2D[Date](long)++ (link:{java9-javadoc}/java/util/Date.html#Date%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Date-after-1]]boolean link:{java8-javadoc}/java/util/Date.html#after%2Djava.util.Date%2D[after](<>)++ (link:{java9-javadoc}/java/util/Date.html#after%2Djava.util.Date%2D[java 9]) -* ++[[painless-api-reference-Date-before-1]]boolean link:{java8-javadoc}/java/util/Date.html#before%2Djava.util.Date%2D[before](<>)++ (link:{java9-javadoc}/java/util/Date.html#before%2Djava.util.Date%2D[java 9]) -* ++[[painless-api-reference-Date-clone-0]]def link:{java8-javadoc}/java/util/Date.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/Date.html#clone%2D%2D[java 9]) -* ++[[painless-api-reference-Date-compareTo-1]]int link:{java8-javadoc}/java/util/Date.html#compareTo%2Djava.util.Date%2D[compareTo](<>)++ (link:{java9-javadoc}/java/util/Date.html#compareTo%2Djava.util.Date%2D[java 9]) -* ++[[painless-api-reference-Date-getTime-0]]long link:{java8-javadoc}/java/util/Date.html#getTime%2D%2D[getTime]()++ (link:{java9-javadoc}/java/util/Date.html#getTime%2D%2D[java 9]) -* ++[[painless-api-reference-Date-setTime-1]]void link:{java8-javadoc}/java/util/Date.html#setTime%2Dlong%2D[setTime](long)++ (link:{java9-javadoc}/java/util/Date.html#setTime%2Dlong%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DateFormat.Field.asciidoc b/docs/reference/painless-api-reference/DateFormat.Field.asciidoc deleted file mode 100644 index b9ad2cda2926c..0000000000000 --- a/docs/reference/painless-api-reference/DateFormat.Field.asciidoc +++ /dev/null @@ -1,27 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DateFormat-Field]]++DateFormat.Field++:: -** [[painless-api-reference-DateFormat-Field-AM_PM]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#AM_PM[AM_PM] (link:{java9-javadoc}/java/text/DateFormat$Field.html#AM_PM[java 9]) -** [[painless-api-reference-DateFormat-Field-DAY_OF_MONTH]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#DAY_OF_MONTH[DAY_OF_MONTH] (link:{java9-javadoc}/java/text/DateFormat$Field.html#DAY_OF_MONTH[java 9]) -** [[painless-api-reference-DateFormat-Field-DAY_OF_WEEK]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#DAY_OF_WEEK[DAY_OF_WEEK] (link:{java9-javadoc}/java/text/DateFormat$Field.html#DAY_OF_WEEK[java 9]) -** [[painless-api-reference-DateFormat-Field-DAY_OF_WEEK_IN_MONTH]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#DAY_OF_WEEK_IN_MONTH[DAY_OF_WEEK_IN_MONTH] (link:{java9-javadoc}/java/text/DateFormat$Field.html#DAY_OF_WEEK_IN_MONTH[java 9]) -** [[painless-api-reference-DateFormat-Field-DAY_OF_YEAR]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#DAY_OF_YEAR[DAY_OF_YEAR] (link:{java9-javadoc}/java/text/DateFormat$Field.html#DAY_OF_YEAR[java 9]) -** [[painless-api-reference-DateFormat-Field-ERA]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#ERA[ERA] (link:{java9-javadoc}/java/text/DateFormat$Field.html#ERA[java 9]) -** [[painless-api-reference-DateFormat-Field-HOUR0]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#HOUR0[HOUR0] (link:{java9-javadoc}/java/text/DateFormat$Field.html#HOUR0[java 9]) -** [[painless-api-reference-DateFormat-Field-HOUR1]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#HOUR1[HOUR1] (link:{java9-javadoc}/java/text/DateFormat$Field.html#HOUR1[java 9]) -** [[painless-api-reference-DateFormat-Field-HOUR_OF_DAY0]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#HOUR_OF_DAY0[HOUR_OF_DAY0] (link:{java9-javadoc}/java/text/DateFormat$Field.html#HOUR_OF_DAY0[java 9]) -** [[painless-api-reference-DateFormat-Field-HOUR_OF_DAY1]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#HOUR_OF_DAY1[HOUR_OF_DAY1] (link:{java9-javadoc}/java/text/DateFormat$Field.html#HOUR_OF_DAY1[java 9]) -** [[painless-api-reference-DateFormat-Field-MILLISECOND]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#MILLISECOND[MILLISECOND] (link:{java9-javadoc}/java/text/DateFormat$Field.html#MILLISECOND[java 9]) -** [[painless-api-reference-DateFormat-Field-MINUTE]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#MINUTE[MINUTE] (link:{java9-javadoc}/java/text/DateFormat$Field.html#MINUTE[java 9]) -** [[painless-api-reference-DateFormat-Field-MONTH]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#MONTH[MONTH] (link:{java9-javadoc}/java/text/DateFormat$Field.html#MONTH[java 9]) -** [[painless-api-reference-DateFormat-Field-SECOND]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#SECOND[SECOND] (link:{java9-javadoc}/java/text/DateFormat$Field.html#SECOND[java 9]) -** [[painless-api-reference-DateFormat-Field-TIME_ZONE]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#TIME_ZONE[TIME_ZONE] (link:{java9-javadoc}/java/text/DateFormat$Field.html#TIME_ZONE[java 9]) -** [[painless-api-reference-DateFormat-Field-WEEK_OF_MONTH]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#WEEK_OF_MONTH[WEEK_OF_MONTH] (link:{java9-javadoc}/java/text/DateFormat$Field.html#WEEK_OF_MONTH[java 9]) -** [[painless-api-reference-DateFormat-Field-WEEK_OF_YEAR]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#WEEK_OF_YEAR[WEEK_OF_YEAR] (link:{java9-javadoc}/java/text/DateFormat$Field.html#WEEK_OF_YEAR[java 9]) -** [[painless-api-reference-DateFormat-Field-YEAR]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#YEAR[YEAR] (link:{java9-javadoc}/java/text/DateFormat$Field.html#YEAR[java 9]) -* ++[[painless-api-reference-DateFormat-Field-ofCalendarField-1]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#ofCalendarField%2Dint%2D[ofCalendarField](int)++ (link:{java9-javadoc}/java/text/DateFormat$Field.html#ofCalendarField%2Dint%2D[java 9]) -* ++[[painless-api-reference-DateFormat-Field-getCalendarField-0]]int link:{java8-javadoc}/java/text/DateFormat$Field.html#getCalendarField%2D%2D[getCalendarField]()++ (link:{java9-javadoc}/java/text/DateFormat$Field.html#getCalendarField%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DateFormat.asciidoc b/docs/reference/painless-api-reference/DateFormat.asciidoc deleted file mode 100644 index 8bb4fdf2eff71..0000000000000 --- a/docs/reference/painless-api-reference/DateFormat.asciidoc +++ /dev/null @@ -1,51 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DateFormat]]++DateFormat++:: -** [[painless-api-reference-DateFormat-AM_PM_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#AM_PM_FIELD[AM_PM_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#AM_PM_FIELD[java 9]) -** [[painless-api-reference-DateFormat-DATE_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#DATE_FIELD[DATE_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#DATE_FIELD[java 9]) -** [[painless-api-reference-DateFormat-DAY_OF_WEEK_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#DAY_OF_WEEK_FIELD[DAY_OF_WEEK_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#DAY_OF_WEEK_FIELD[java 9]) -** [[painless-api-reference-DateFormat-DAY_OF_WEEK_IN_MONTH_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#DAY_OF_WEEK_IN_MONTH_FIELD[DAY_OF_WEEK_IN_MONTH_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#DAY_OF_WEEK_IN_MONTH_FIELD[java 9]) -** [[painless-api-reference-DateFormat-DAY_OF_YEAR_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#DAY_OF_YEAR_FIELD[DAY_OF_YEAR_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#DAY_OF_YEAR_FIELD[java 9]) -** [[painless-api-reference-DateFormat-DEFAULT]]static int link:{java8-javadoc}/java/text/DateFormat.html#DEFAULT[DEFAULT] (link:{java9-javadoc}/java/text/DateFormat.html#DEFAULT[java 9]) -** [[painless-api-reference-DateFormat-ERA_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#ERA_FIELD[ERA_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#ERA_FIELD[java 9]) -** [[painless-api-reference-DateFormat-FULL]]static int link:{java8-javadoc}/java/text/DateFormat.html#FULL[FULL] (link:{java9-javadoc}/java/text/DateFormat.html#FULL[java 9]) -** [[painless-api-reference-DateFormat-HOUR0_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#HOUR0_FIELD[HOUR0_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#HOUR0_FIELD[java 9]) -** [[painless-api-reference-DateFormat-HOUR1_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#HOUR1_FIELD[HOUR1_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#HOUR1_FIELD[java 9]) -** [[painless-api-reference-DateFormat-HOUR_OF_DAY0_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#HOUR_OF_DAY0_FIELD[HOUR_OF_DAY0_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#HOUR_OF_DAY0_FIELD[java 9]) -** [[painless-api-reference-DateFormat-HOUR_OF_DAY1_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#HOUR_OF_DAY1_FIELD[HOUR_OF_DAY1_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#HOUR_OF_DAY1_FIELD[java 9]) -** [[painless-api-reference-DateFormat-LONG]]static int link:{java8-javadoc}/java/text/DateFormat.html#LONG[LONG] (link:{java9-javadoc}/java/text/DateFormat.html#LONG[java 9]) -** [[painless-api-reference-DateFormat-MEDIUM]]static int link:{java8-javadoc}/java/text/DateFormat.html#MEDIUM[MEDIUM] (link:{java9-javadoc}/java/text/DateFormat.html#MEDIUM[java 9]) -** [[painless-api-reference-DateFormat-MILLISECOND_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#MILLISECOND_FIELD[MILLISECOND_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#MILLISECOND_FIELD[java 9]) -** [[painless-api-reference-DateFormat-MINUTE_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#MINUTE_FIELD[MINUTE_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#MINUTE_FIELD[java 9]) -** [[painless-api-reference-DateFormat-MONTH_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#MONTH_FIELD[MONTH_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#MONTH_FIELD[java 9]) -** [[painless-api-reference-DateFormat-SECOND_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#SECOND_FIELD[SECOND_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#SECOND_FIELD[java 9]) -** [[painless-api-reference-DateFormat-SHORT]]static int link:{java8-javadoc}/java/text/DateFormat.html#SHORT[SHORT] (link:{java9-javadoc}/java/text/DateFormat.html#SHORT[java 9]) -** [[painless-api-reference-DateFormat-TIMEZONE_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#TIMEZONE_FIELD[TIMEZONE_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#TIMEZONE_FIELD[java 9]) -** [[painless-api-reference-DateFormat-WEEK_OF_MONTH_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#WEEK_OF_MONTH_FIELD[WEEK_OF_MONTH_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#WEEK_OF_MONTH_FIELD[java 9]) -** [[painless-api-reference-DateFormat-WEEK_OF_YEAR_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#WEEK_OF_YEAR_FIELD[WEEK_OF_YEAR_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#WEEK_OF_YEAR_FIELD[java 9]) -** [[painless-api-reference-DateFormat-YEAR_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#YEAR_FIELD[YEAR_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#YEAR_FIELD[java 9]) -* ++[[painless-api-reference-DateFormat-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/text/DateFormat.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getAvailableLocales%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormat-getDateInstance-0]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getDateInstance%2D%2D[getDateInstance]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getDateInstance%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormat-getDateInstance-1]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getDateInstance%2Dint%2D[getDateInstance](int)++ (link:{java9-javadoc}/java/text/DateFormat.html#getDateInstance%2Dint%2D[java 9]) -* ++[[painless-api-reference-DateFormat-getDateInstance-2]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getDateInstance%2Dint%2Djava.util.Locale%2D[getDateInstance](int, <>)++ (link:{java9-javadoc}/java/text/DateFormat.html#getDateInstance%2Dint%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-DateFormat-getDateTimeInstance-0]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getDateTimeInstance%2D%2D[getDateTimeInstance]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getDateTimeInstance%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormat-getDateTimeInstance-2]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getDateTimeInstance%2Dint%2Dint%2D[getDateTimeInstance](int, int)++ (link:{java9-javadoc}/java/text/DateFormat.html#getDateTimeInstance%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-DateFormat-getDateTimeInstance-3]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getDateTimeInstance%2Dint%2Dint%2Djava.util.Locale%2D[getDateTimeInstance](int, int, <>)++ (link:{java9-javadoc}/java/text/DateFormat.html#getDateTimeInstance%2Dint%2Dint%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-DateFormat-getInstance-0]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getInstance%2D%2D[getInstance]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getInstance%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormat-getTimeInstance-0]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getTimeInstance%2D%2D[getTimeInstance]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getTimeInstance%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormat-getTimeInstance-1]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getTimeInstance%2Dint%2D[getTimeInstance](int)++ (link:{java9-javadoc}/java/text/DateFormat.html#getTimeInstance%2Dint%2D[java 9]) -* ++[[painless-api-reference-DateFormat-getTimeInstance-2]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getTimeInstance%2Dint%2Djava.util.Locale%2D[getTimeInstance](int, <>)++ (link:{java9-javadoc}/java/text/DateFormat.html#getTimeInstance%2Dint%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-DateFormat-getCalendar-0]]<> link:{java8-javadoc}/java/text/DateFormat.html#getCalendar%2D%2D[getCalendar]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getCalendar%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormat-getNumberFormat-0]]<> link:{java8-javadoc}/java/text/DateFormat.html#getNumberFormat%2D%2D[getNumberFormat]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getNumberFormat%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormat-getTimeZone-0]]<> link:{java8-javadoc}/java/text/DateFormat.html#getTimeZone%2D%2D[getTimeZone]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getTimeZone%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormat-isLenient-0]]boolean link:{java8-javadoc}/java/text/DateFormat.html#isLenient%2D%2D[isLenient]()++ (link:{java9-javadoc}/java/text/DateFormat.html#isLenient%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormat-parse-1]]<> link:{java8-javadoc}/java/text/DateFormat.html#parse%2Djava.lang.String%2D[parse](<>)++ (link:{java9-javadoc}/java/text/DateFormat.html#parse%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DateFormat-parse-2]]<> link:{java8-javadoc}/java/text/DateFormat.html#parse%2Djava.lang.String%2Djava.text.ParsePosition%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/text/DateFormat.html#parse%2Djava.lang.String%2Djava.text.ParsePosition%2D[java 9]) -* ++[[painless-api-reference-DateFormat-setCalendar-1]]void link:{java8-javadoc}/java/text/DateFormat.html#setCalendar%2Djava.util.Calendar%2D[setCalendar](<>)++ (link:{java9-javadoc}/java/text/DateFormat.html#setCalendar%2Djava.util.Calendar%2D[java 9]) -* ++[[painless-api-reference-DateFormat-setLenient-1]]void link:{java8-javadoc}/java/text/DateFormat.html#setLenient%2Dboolean%2D[setLenient](boolean)++ (link:{java9-javadoc}/java/text/DateFormat.html#setLenient%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-DateFormat-setNumberFormat-1]]void link:{java8-javadoc}/java/text/DateFormat.html#setNumberFormat%2Djava.text.NumberFormat%2D[setNumberFormat](<>)++ (link:{java9-javadoc}/java/text/DateFormat.html#setNumberFormat%2Djava.text.NumberFormat%2D[java 9]) -* ++[[painless-api-reference-DateFormat-setTimeZone-1]]void link:{java8-javadoc}/java/text/DateFormat.html#setTimeZone%2Djava.util.TimeZone%2D[setTimeZone](<>)++ (link:{java9-javadoc}/java/text/DateFormat.html#setTimeZone%2Djava.util.TimeZone%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/DateFormatSymbols.asciidoc b/docs/reference/painless-api-reference/DateFormatSymbols.asciidoc deleted file mode 100644 index 31cf39bbc4586..0000000000000 --- a/docs/reference/painless-api-reference/DateFormatSymbols.asciidoc +++ /dev/null @@ -1,30 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DateFormatSymbols]]++DateFormatSymbols++:: -* ++[[painless-api-reference-DateFormatSymbols-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getAvailableLocales%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-getInstance-0]]static <> link:{java8-javadoc}/java/text/DateFormatSymbols.html#getInstance%2D%2D[getInstance]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getInstance%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-getInstance-1]]static <> link:{java8-javadoc}/java/text/DateFormatSymbols.html#getInstance%2Djava.util.Locale%2D[getInstance](<>)++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getInstance%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-DateFormatSymbols-0]]link:{java8-javadoc}/java/text/DateFormatSymbols.html#DateFormatSymbols%2D%2D[DateFormatSymbols]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#DateFormatSymbols%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-DateFormatSymbols-1]]link:{java8-javadoc}/java/text/DateFormatSymbols.html#DateFormatSymbols%2Djava.util.Locale%2D[DateFormatSymbols](<>)++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#DateFormatSymbols%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-clone-0]]def link:{java8-javadoc}/java/text/DateFormatSymbols.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#clone%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-getAmPmStrings-0]]<>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getAmPmStrings%2D%2D[getAmPmStrings]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getAmPmStrings%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-getEras-0]]<>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getEras%2D%2D[getEras]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getEras%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-getLocalPatternChars-0]]<> link:{java8-javadoc}/java/text/DateFormatSymbols.html#getLocalPatternChars%2D%2D[getLocalPatternChars]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getLocalPatternChars%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-getMonths-0]]<>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getMonths%2D%2D[getMonths]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getMonths%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-getShortMonths-0]]<>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getShortMonths%2D%2D[getShortMonths]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getShortMonths%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-getShortWeekdays-0]]<>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getShortWeekdays%2D%2D[getShortWeekdays]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getShortWeekdays%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-getWeekdays-0]]<>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getWeekdays%2D%2D[getWeekdays]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getWeekdays%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-getZoneStrings-0]]<>[][] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getZoneStrings%2D%2D[getZoneStrings]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getZoneStrings%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-hashCode-0]]int link:{java8-javadoc}/java/text/DateFormatSymbols.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#hashCode%2D%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-setAmPmStrings-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setAmPmStrings%2Djava.lang.String:A%2D[setAmPmStrings](<>[])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setAmPmStrings%2Djava.lang.String:A%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-setEras-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setEras%2Djava.lang.String:A%2D[setEras](<>[])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setEras%2Djava.lang.String:A%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-setLocalPatternChars-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setLocalPatternChars%2Djava.lang.String%2D[setLocalPatternChars](<>)++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setLocalPatternChars%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-setMonths-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setMonths%2Djava.lang.String:A%2D[setMonths](<>[])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setMonths%2Djava.lang.String:A%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-setShortMonths-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setShortMonths%2Djava.lang.String:A%2D[setShortMonths](<>[])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setShortMonths%2Djava.lang.String:A%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-setShortWeekdays-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setShortWeekdays%2Djava.lang.String:A%2D[setShortWeekdays](<>[])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setShortWeekdays%2Djava.lang.String:A%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-setWeekdays-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setWeekdays%2Djava.lang.String:A%2D[setWeekdays](<>[])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setWeekdays%2Djava.lang.String:A%2D[java 9]) -* ++[[painless-api-reference-DateFormatSymbols-setZoneStrings-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setZoneStrings%2Djava.lang.String:A%2D[setZoneStrings](<>[][])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setZoneStrings%2Djava.lang.String:A%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DateTimeException.asciidoc b/docs/reference/painless-api-reference/DateTimeException.asciidoc deleted file mode 100644 index 5a9953c5bd582..0000000000000 --- a/docs/reference/painless-api-reference/DateTimeException.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DateTimeException]]++DateTimeException++:: -* ++[[painless-api-reference-DateTimeException-DateTimeException-1]]link:{java8-javadoc}/java/time/DateTimeException.html#DateTimeException%2Djava.lang.String%2D[DateTimeException](<>)++ (link:{java9-javadoc}/java/time/DateTimeException.html#DateTimeException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/DateTimeFormatter.asciidoc b/docs/reference/painless-api-reference/DateTimeFormatter.asciidoc deleted file mode 100644 index ad104dfe5d6d2..0000000000000 --- a/docs/reference/painless-api-reference/DateTimeFormatter.asciidoc +++ /dev/null @@ -1,50 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DateTimeFormatter]]++DateTimeFormatter++:: -** [[painless-api-reference-DateTimeFormatter-BASIC_ISO_DATE]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#BASIC_ISO_DATE[BASIC_ISO_DATE] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#BASIC_ISO_DATE[java 9]) -** [[painless-api-reference-DateTimeFormatter-ISO_DATE]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_DATE[ISO_DATE] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_DATE[java 9]) -** [[painless-api-reference-DateTimeFormatter-ISO_DATE_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_DATE_TIME[ISO_DATE_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_DATE_TIME[java 9]) -** [[painless-api-reference-DateTimeFormatter-ISO_INSTANT]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_INSTANT[ISO_INSTANT] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_INSTANT[java 9]) -** [[painless-api-reference-DateTimeFormatter-ISO_LOCAL_DATE]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_LOCAL_DATE[ISO_LOCAL_DATE] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_LOCAL_DATE[java 9]) -** [[painless-api-reference-DateTimeFormatter-ISO_LOCAL_DATE_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_LOCAL_DATE_TIME[ISO_LOCAL_DATE_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_LOCAL_DATE_TIME[java 9]) -** [[painless-api-reference-DateTimeFormatter-ISO_LOCAL_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_LOCAL_TIME[ISO_LOCAL_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_LOCAL_TIME[java 9]) -** [[painless-api-reference-DateTimeFormatter-ISO_OFFSET_DATE]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE[ISO_OFFSET_DATE] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE[java 9]) -** [[painless-api-reference-DateTimeFormatter-ISO_OFFSET_DATE_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE_TIME[ISO_OFFSET_DATE_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE_TIME[java 9]) -** [[painless-api-reference-DateTimeFormatter-ISO_OFFSET_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_OFFSET_TIME[ISO_OFFSET_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_OFFSET_TIME[java 9]) -** [[painless-api-reference-DateTimeFormatter-ISO_ORDINAL_DATE]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_ORDINAL_DATE[ISO_ORDINAL_DATE] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_ORDINAL_DATE[java 9]) -** [[painless-api-reference-DateTimeFormatter-ISO_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_TIME[ISO_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_TIME[java 9]) -** [[painless-api-reference-DateTimeFormatter-ISO_WEEK_DATE]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_WEEK_DATE[ISO_WEEK_DATE] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_WEEK_DATE[java 9]) -** [[painless-api-reference-DateTimeFormatter-ISO_ZONED_DATE_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_ZONED_DATE_TIME[ISO_ZONED_DATE_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_ZONED_DATE_TIME[java 9]) -** [[painless-api-reference-DateTimeFormatter-RFC_1123_DATE_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#RFC_1123_DATE_TIME[RFC_1123_DATE_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#RFC_1123_DATE_TIME[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-ofLocalizedDate-1]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedDate%2Djava.time.format.FormatStyle%2D[ofLocalizedDate](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedDate%2Djava.time.format.FormatStyle%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-ofLocalizedDateTime-1]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedDateTime%2Djava.time.format.FormatStyle%2D[ofLocalizedDateTime](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedDateTime%2Djava.time.format.FormatStyle%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-ofLocalizedDateTime-2]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedDateTime%2Djava.time.format.FormatStyle%2Djava.time.format.FormatStyle%2D[ofLocalizedDateTime](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedDateTime%2Djava.time.format.FormatStyle%2Djava.time.format.FormatStyle%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-ofLocalizedTime-1]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedTime%2Djava.time.format.FormatStyle%2D[ofLocalizedTime](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedTime%2Djava.time.format.FormatStyle%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-ofPattern-1]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ofPattern%2Djava.lang.String%2D[ofPattern](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ofPattern%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-ofPattern-2]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ofPattern%2Djava.lang.String%2Djava.util.Locale%2D[ofPattern](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ofPattern%2Djava.lang.String%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-parsedExcessDays-0]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#parsedExcessDays%2D%2D[parsedExcessDays]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#parsedExcessDays%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-parsedLeapSecond-0]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#parsedLeapSecond%2D%2D[parsedLeapSecond]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#parsedLeapSecond%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-format-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#format%2Djava.time.temporal.TemporalAccessor%2D[format](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#format%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-formatTo-2]]void link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#formatTo%2Djava.time.temporal.TemporalAccessor%2Djava.lang.Appendable%2D[formatTo](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#formatTo%2Djava.time.temporal.TemporalAccessor%2Djava.lang.Appendable%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-getChronology-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#getChronology%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-getDecimalStyle-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#getDecimalStyle%2D%2D[getDecimalStyle]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#getDecimalStyle%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-getLocale-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#getLocale%2D%2D[getLocale]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#getLocale%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-getResolverFields-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#getResolverFields%2D%2D[getResolverFields]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#getResolverFields%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-getResolverStyle-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#getResolverStyle%2D%2D[getResolverStyle]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#getResolverStyle%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-getZone-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#getZone%2D%2D[getZone]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#getZone%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-parse-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#parse%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-parse-2]]def link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#parse%2Djava.lang.CharSequence%2Djava.time.temporal.TemporalQuery%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#parse%2Djava.lang.CharSequence%2Djava.time.temporal.TemporalQuery%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-parseBest-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#parseBest%2Djava.lang.CharSequence%2Djava.time.temporal.TemporalQuery:A%2D[parseBest](<>, <>[])++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#parseBest%2Djava.lang.CharSequence%2Djava.time.temporal.TemporalQuery:A%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-parseUnresolved-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#parseUnresolved%2Djava.lang.CharSequence%2Djava.text.ParsePosition%2D[parseUnresolved](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#parseUnresolved%2Djava.lang.CharSequence%2Djava.text.ParsePosition%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-toFormat-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#toFormat%2D%2D[toFormat]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#toFormat%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-toFormat-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#toFormat%2Djava.time.temporal.TemporalQuery%2D[toFormat](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#toFormat%2Djava.time.temporal.TemporalQuery%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-withChronology-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#withChronology%2Djava.time.chrono.Chronology%2D[withChronology](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#withChronology%2Djava.time.chrono.Chronology%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-withDecimalStyle-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#withDecimalStyle%2Djava.time.format.DecimalStyle%2D[withDecimalStyle](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#withDecimalStyle%2Djava.time.format.DecimalStyle%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-withLocale-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#withLocale%2Djava.util.Locale%2D[withLocale](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#withLocale%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-withResolverFields-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#withResolverFields%2Djava.util.Set%2D[withResolverFields](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#withResolverFields%2Djava.util.Set%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-withResolverStyle-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#withResolverStyle%2Djava.time.format.ResolverStyle%2D[withResolverStyle](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#withResolverStyle%2Djava.time.format.ResolverStyle%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatter-withZone-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#withZone%2Djava.time.ZoneId%2D[withZone](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#withZone%2Djava.time.ZoneId%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DateTimeFormatterBuilder.asciidoc b/docs/reference/painless-api-reference/DateTimeFormatterBuilder.asciidoc deleted file mode 100644 index cc352560f41a9..0000000000000 --- a/docs/reference/painless-api-reference/DateTimeFormatterBuilder.asciidoc +++ /dev/null @@ -1,44 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DateTimeFormatterBuilder]]++DateTimeFormatterBuilder++:: -* ++[[painless-api-reference-DateTimeFormatterBuilder-getLocalizedDateTimePattern-4]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#getLocalizedDateTimePattern%2Djava.time.format.FormatStyle%2Djava.time.format.FormatStyle%2Djava.time.chrono.Chronology%2Djava.util.Locale%2D[getLocalizedDateTimePattern](<>, <>, <>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#getLocalizedDateTimePattern%2Djava.time.format.FormatStyle%2Djava.time.format.FormatStyle%2Djava.time.chrono.Chronology%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-DateTimeFormatterBuilder-0]]link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#DateTimeFormatterBuilder%2D%2D[DateTimeFormatterBuilder]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#DateTimeFormatterBuilder%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-append-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#append%2Djava.time.format.DateTimeFormatter%2D[append](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#append%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendChronologyId-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendChronologyId%2D%2D[appendChronologyId]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendChronologyId%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendChronologyText-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendChronologyText%2Djava.time.format.TextStyle%2D[appendChronologyText](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendChronologyText%2Djava.time.format.TextStyle%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendFraction-4]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendFraction%2Djava.time.temporal.TemporalField%2Dint%2Dint%2Dboolean%2D[appendFraction](<>, int, int, boolean)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendFraction%2Djava.time.temporal.TemporalField%2Dint%2Dint%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendInstant-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendInstant%2D%2D[appendInstant]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendInstant%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendInstant-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendInstant%2Dint%2D[appendInstant](int)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendInstant%2Dint%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendLiteral-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendLiteral%2Djava.lang.String%2D[appendLiteral](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendLiteral%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendLocalized-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendLocalized%2Djava.time.format.FormatStyle%2Djava.time.format.FormatStyle%2D[appendLocalized](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendLocalized%2Djava.time.format.FormatStyle%2Djava.time.format.FormatStyle%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendLocalizedOffset-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendLocalizedOffset%2Djava.time.format.TextStyle%2D[appendLocalizedOffset](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendLocalizedOffset%2Djava.time.format.TextStyle%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendOffset-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendOffset%2Djava.lang.String%2Djava.lang.String%2D[appendOffset](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendOffset%2Djava.lang.String%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendOffsetId-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendOffsetId%2D%2D[appendOffsetId]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendOffsetId%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendOptional-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendOptional%2Djava.time.format.DateTimeFormatter%2D[appendOptional](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendOptional%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendPattern-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendPattern%2Djava.lang.String%2D[appendPattern](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendPattern%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendText-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendText%2Djava.time.temporal.TemporalField%2D[appendText](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendText%2Djava.time.temporal.TemporalField%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendText-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendText%2Djava.time.temporal.TemporalField%2Djava.time.format.TextStyle%2D[appendText](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendText%2Djava.time.temporal.TemporalField%2Djava.time.format.TextStyle%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendValue-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValue%2Djava.time.temporal.TemporalField%2D[appendValue](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValue%2Djava.time.temporal.TemporalField%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendValue-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValue%2Djava.time.temporal.TemporalField%2Dint%2D[appendValue](<>, int)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValue%2Djava.time.temporal.TemporalField%2Dint%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendValue-4]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValue%2Djava.time.temporal.TemporalField%2Dint%2Dint%2Djava.time.format.SignStyle%2D[appendValue](<>, int, int, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValue%2Djava.time.temporal.TemporalField%2Dint%2Dint%2Djava.time.format.SignStyle%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendValueReduced-4]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValueReduced%2Djava.time.temporal.TemporalField%2Dint%2Dint%2Dint%2D[appendValueReduced](<>, int, int, int)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValueReduced%2Djava.time.temporal.TemporalField%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendZoneId-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneId%2D%2D[appendZoneId]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneId%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendZoneOrOffsetId-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneOrOffsetId%2D%2D[appendZoneOrOffsetId]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneOrOffsetId%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendZoneRegionId-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneRegionId%2D%2D[appendZoneRegionId]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneRegionId%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendZoneText-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneText%2Djava.time.format.TextStyle%2D[appendZoneText](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneText%2Djava.time.format.TextStyle%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-appendZoneText-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneText%2Djava.time.format.TextStyle%2Djava.util.Set%2D[appendZoneText](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneText%2Djava.time.format.TextStyle%2Djava.util.Set%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-optionalEnd-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#optionalEnd%2D%2D[optionalEnd]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#optionalEnd%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-optionalStart-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#optionalStart%2D%2D[optionalStart]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#optionalStart%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-padNext-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#padNext%2Dint%2D[padNext](int)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#padNext%2Dint%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-padNext-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#padNext%2Dint%2Dchar%2D[padNext](int, char)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#padNext%2Dint%2Dchar%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-parseCaseInsensitive-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseCaseInsensitive%2D%2D[parseCaseInsensitive]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseCaseInsensitive%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-parseCaseSensitive-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseCaseSensitive%2D%2D[parseCaseSensitive]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseCaseSensitive%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-parseDefaulting-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseDefaulting%2Djava.time.temporal.TemporalField%2Dlong%2D[parseDefaulting](<>, long)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseDefaulting%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-parseLenient-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseLenient%2D%2D[parseLenient]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseLenient%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-parseStrict-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseStrict%2D%2D[parseStrict]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseStrict%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-toFormatter-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#toFormatter%2D%2D[toFormatter]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#toFormatter%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeFormatterBuilder-toFormatter-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#toFormatter%2Djava.util.Locale%2D[toFormatter](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#toFormatter%2Djava.util.Locale%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DateTimeParseException.asciidoc b/docs/reference/painless-api-reference/DateTimeParseException.asciidoc deleted file mode 100644 index 8a9626b64052a..0000000000000 --- a/docs/reference/painless-api-reference/DateTimeParseException.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DateTimeParseException]]++DateTimeParseException++:: -* ++[[painless-api-reference-DateTimeParseException-DateTimeParseException-3]]link:{java8-javadoc}/java/time/format/DateTimeParseException.html#DateTimeParseException%2Djava.lang.String%2Djava.lang.CharSequence%2Dint%2D[DateTimeParseException](<>, <>, int)++ (link:{java9-javadoc}/java/time/format/DateTimeParseException.html#DateTimeParseException%2Djava.lang.String%2Djava.lang.CharSequence%2Dint%2D[java 9]) -* ++[[painless-api-reference-DateTimeParseException-getErrorIndex-0]]int link:{java8-javadoc}/java/time/format/DateTimeParseException.html#getErrorIndex%2D%2D[getErrorIndex]()++ (link:{java9-javadoc}/java/time/format/DateTimeParseException.html#getErrorIndex%2D%2D[java 9]) -* ++[[painless-api-reference-DateTimeParseException-getParsedString-0]]<> link:{java8-javadoc}/java/time/format/DateTimeParseException.html#getParsedString%2D%2D[getParsedString]()++ (link:{java9-javadoc}/java/time/format/DateTimeParseException.html#getParsedString%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/DayOfWeek.asciidoc b/docs/reference/painless-api-reference/DayOfWeek.asciidoc deleted file mode 100644 index fd847f3d019ab..0000000000000 --- a/docs/reference/painless-api-reference/DayOfWeek.asciidoc +++ /dev/null @@ -1,22 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DayOfWeek]]++DayOfWeek++:: -** [[painless-api-reference-DayOfWeek-FRIDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#FRIDAY[FRIDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#FRIDAY[java 9]) -** [[painless-api-reference-DayOfWeek-MONDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#MONDAY[MONDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#MONDAY[java 9]) -** [[painless-api-reference-DayOfWeek-SATURDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#SATURDAY[SATURDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#SATURDAY[java 9]) -** [[painless-api-reference-DayOfWeek-SUNDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#SUNDAY[SUNDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#SUNDAY[java 9]) -** [[painless-api-reference-DayOfWeek-THURSDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#THURSDAY[THURSDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#THURSDAY[java 9]) -** [[painless-api-reference-DayOfWeek-TUESDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#TUESDAY[TUESDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#TUESDAY[java 9]) -** [[painless-api-reference-DayOfWeek-WEDNESDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#WEDNESDAY[WEDNESDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#WEDNESDAY[java 9]) -* ++[[painless-api-reference-DayOfWeek-from-1]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/DayOfWeek.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-DayOfWeek-of-1]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/DayOfWeek.html#of%2Dint%2D[java 9]) -* ++[[painless-api-reference-DayOfWeek-valueOf-1]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/DayOfWeek.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DayOfWeek-values-0]]static <>[] link:{java8-javadoc}/java/time/DayOfWeek.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/DayOfWeek.html#values%2D%2D[java 9]) -* ++[[painless-api-reference-DayOfWeek-getDisplayName-2]]<> link:{java8-javadoc}/java/time/DayOfWeek.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[getDisplayName](<>, <>)++ (link:{java9-javadoc}/java/time/DayOfWeek.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-DayOfWeek-getValue-0]]int link:{java8-javadoc}/java/time/DayOfWeek.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/DayOfWeek.html#getValue%2D%2D[java 9]) -* ++[[painless-api-reference-DayOfWeek-minus-1]]<> link:{java8-javadoc}/java/time/DayOfWeek.html#minus%2Dlong%2D[minus](long)++ (link:{java9-javadoc}/java/time/DayOfWeek.html#minus%2Dlong%2D[java 9]) -* ++[[painless-api-reference-DayOfWeek-plus-1]]<> link:{java8-javadoc}/java/time/DayOfWeek.html#plus%2Dlong%2D[plus](long)++ (link:{java9-javadoc}/java/time/DayOfWeek.html#plus%2Dlong%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Debug.asciidoc b/docs/reference/painless-api-reference/Debug.asciidoc deleted file mode 100644 index 8c64ed6bcc4ad..0000000000000 --- a/docs/reference/painless-api-reference/Debug.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Debug]]++Debug++:: -* ++[[painless-api-reference-Debug-explain-1]]static void link:{painless-javadoc}/org/elasticsearch/painless/api/Debug.html#explain%2Djava.lang.Object%2D[explain](<>)++ -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DecimalFormat.asciidoc b/docs/reference/painless-api-reference/DecimalFormat.asciidoc deleted file mode 100644 index 96768b77273f8..0000000000000 --- a/docs/reference/painless-api-reference/DecimalFormat.asciidoc +++ /dev/null @@ -1,32 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DecimalFormat]]++DecimalFormat++:: -* ++[[painless-api-reference-DecimalFormat-DecimalFormat-0]]link:{java8-javadoc}/java/text/DecimalFormat.html#DecimalFormat%2D%2D[DecimalFormat]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#DecimalFormat%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-DecimalFormat-1]]link:{java8-javadoc}/java/text/DecimalFormat.html#DecimalFormat%2Djava.lang.String%2D[DecimalFormat](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#DecimalFormat%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-DecimalFormat-2]]link:{java8-javadoc}/java/text/DecimalFormat.html#DecimalFormat%2Djava.lang.String%2Djava.text.DecimalFormatSymbols%2D[DecimalFormat](<>, <>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#DecimalFormat%2Djava.lang.String%2Djava.text.DecimalFormatSymbols%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-applyLocalizedPattern-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#applyLocalizedPattern%2Djava.lang.String%2D[applyLocalizedPattern](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#applyLocalizedPattern%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-applyPattern-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#applyPattern%2Djava.lang.String%2D[applyPattern](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#applyPattern%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-getDecimalFormatSymbols-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#getDecimalFormatSymbols%2D%2D[getDecimalFormatSymbols]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getDecimalFormatSymbols%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-getGroupingSize-0]]int link:{java8-javadoc}/java/text/DecimalFormat.html#getGroupingSize%2D%2D[getGroupingSize]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getGroupingSize%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-getMultiplier-0]]int link:{java8-javadoc}/java/text/DecimalFormat.html#getMultiplier%2D%2D[getMultiplier]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getMultiplier%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-getNegativePrefix-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#getNegativePrefix%2D%2D[getNegativePrefix]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getNegativePrefix%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-getNegativeSuffix-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#getNegativeSuffix%2D%2D[getNegativeSuffix]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getNegativeSuffix%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-getPositivePrefix-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#getPositivePrefix%2D%2D[getPositivePrefix]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getPositivePrefix%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-getPositiveSuffix-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#getPositiveSuffix%2D%2D[getPositiveSuffix]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getPositiveSuffix%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-isDecimalSeparatorAlwaysShown-0]]boolean link:{java8-javadoc}/java/text/DecimalFormat.html#isDecimalSeparatorAlwaysShown%2D%2D[isDecimalSeparatorAlwaysShown]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#isDecimalSeparatorAlwaysShown%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-isParseBigDecimal-0]]boolean link:{java8-javadoc}/java/text/DecimalFormat.html#isParseBigDecimal%2D%2D[isParseBigDecimal]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#isParseBigDecimal%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-setDecimalFormatSymbols-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setDecimalFormatSymbols%2Djava.text.DecimalFormatSymbols%2D[setDecimalFormatSymbols](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setDecimalFormatSymbols%2Djava.text.DecimalFormatSymbols%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-setDecimalSeparatorAlwaysShown-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setDecimalSeparatorAlwaysShown%2Dboolean%2D[setDecimalSeparatorAlwaysShown](boolean)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setDecimalSeparatorAlwaysShown%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-setGroupingSize-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setGroupingSize%2Dint%2D[setGroupingSize](int)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setGroupingSize%2Dint%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-setMultiplier-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setMultiplier%2Dint%2D[setMultiplier](int)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setMultiplier%2Dint%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-setNegativePrefix-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setNegativePrefix%2Djava.lang.String%2D[setNegativePrefix](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setNegativePrefix%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-setNegativeSuffix-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setNegativeSuffix%2Djava.lang.String%2D[setNegativeSuffix](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setNegativeSuffix%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-setParseBigDecimal-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setParseBigDecimal%2Dboolean%2D[setParseBigDecimal](boolean)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setParseBigDecimal%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-setPositivePrefix-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setPositivePrefix%2Djava.lang.String%2D[setPositivePrefix](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setPositivePrefix%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-setPositiveSuffix-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setPositiveSuffix%2Djava.lang.String%2D[setPositiveSuffix](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setPositiveSuffix%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-toLocalizedPattern-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#toLocalizedPattern%2D%2D[toLocalizedPattern]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#toLocalizedPattern%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormat-toPattern-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#toPattern%2D%2D[toPattern]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#toPattern%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/DecimalFormatSymbols.asciidoc b/docs/reference/painless-api-reference/DecimalFormatSymbols.asciidoc deleted file mode 100644 index 37f6fc090d105..0000000000000 --- a/docs/reference/painless-api-reference/DecimalFormatSymbols.asciidoc +++ /dev/null @@ -1,43 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DecimalFormatSymbols]]++DecimalFormatSymbols++:: -* ++[[painless-api-reference-DecimalFormatSymbols-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getAvailableLocales%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getInstance-0]]static <> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getInstance%2D%2D[getInstance]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getInstance%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getInstance-1]]static <> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getInstance%2Djava.util.Locale%2D[getInstance](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getInstance%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-DecimalFormatSymbols-0]]link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#DecimalFormatSymbols%2D%2D[DecimalFormatSymbols]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#DecimalFormatSymbols%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-DecimalFormatSymbols-1]]link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#DecimalFormatSymbols%2Djava.util.Locale%2D[DecimalFormatSymbols](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#DecimalFormatSymbols%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-clone-0]]def link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#clone%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getCurrency-0]]<> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getCurrency%2D%2D[getCurrency]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getCurrency%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getCurrencySymbol-0]]<> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getCurrencySymbol%2D%2D[getCurrencySymbol]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getCurrencySymbol%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getDecimalSeparator-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getDecimalSeparator%2D%2D[getDecimalSeparator]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getDecimalSeparator%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getDigit-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getDigit%2D%2D[getDigit]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getDigit%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getExponentSeparator-0]]<> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getExponentSeparator%2D%2D[getExponentSeparator]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getExponentSeparator%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getGroupingSeparator-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getGroupingSeparator%2D%2D[getGroupingSeparator]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getGroupingSeparator%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getInfinity-0]]<> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getInfinity%2D%2D[getInfinity]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getInfinity%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getInternationalCurrencySymbol-0]]<> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getInternationalCurrencySymbol%2D%2D[getInternationalCurrencySymbol]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getInternationalCurrencySymbol%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getMinusSign-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getMinusSign%2D%2D[getMinusSign]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getMinusSign%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getMonetaryDecimalSeparator-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getMonetaryDecimalSeparator%2D%2D[getMonetaryDecimalSeparator]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getMonetaryDecimalSeparator%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getNaN-0]]<> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getNaN%2D%2D[getNaN]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getNaN%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getPatternSeparator-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getPatternSeparator%2D%2D[getPatternSeparator]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getPatternSeparator%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getPerMill-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getPerMill%2D%2D[getPerMill]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getPerMill%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getPercent-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getPercent%2D%2D[getPercent]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getPercent%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-getZeroDigit-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getZeroDigit%2D%2D[getZeroDigit]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getZeroDigit%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setCurrency-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setCurrency%2Djava.util.Currency%2D[setCurrency](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setCurrency%2Djava.util.Currency%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setCurrencySymbol-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setCurrencySymbol%2Djava.lang.String%2D[setCurrencySymbol](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setCurrencySymbol%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setDecimalSeparator-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setDecimalSeparator%2Dchar%2D[setDecimalSeparator](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setDecimalSeparator%2Dchar%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setDigit-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setDigit%2Dchar%2D[setDigit](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setDigit%2Dchar%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setExponentSeparator-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setExponentSeparator%2Djava.lang.String%2D[setExponentSeparator](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setExponentSeparator%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setGroupingSeparator-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setGroupingSeparator%2Dchar%2D[setGroupingSeparator](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setGroupingSeparator%2Dchar%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setInfinity-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setInfinity%2Djava.lang.String%2D[setInfinity](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setInfinity%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setInternationalCurrencySymbol-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setInternationalCurrencySymbol%2Djava.lang.String%2D[setInternationalCurrencySymbol](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setInternationalCurrencySymbol%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setMinusSign-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setMinusSign%2Dchar%2D[setMinusSign](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setMinusSign%2Dchar%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setMonetaryDecimalSeparator-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setMonetaryDecimalSeparator%2Dchar%2D[setMonetaryDecimalSeparator](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setMonetaryDecimalSeparator%2Dchar%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setNaN-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setNaN%2Djava.lang.String%2D[setNaN](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setNaN%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setPatternSeparator-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setPatternSeparator%2Dchar%2D[setPatternSeparator](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setPatternSeparator%2Dchar%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setPerMill-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setPerMill%2Dchar%2D[setPerMill](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setPerMill%2Dchar%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setPercent-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setPercent%2Dchar%2D[setPercent](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setPercent%2Dchar%2D[java 9]) -* ++[[painless-api-reference-DecimalFormatSymbols-setZeroDigit-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setZeroDigit%2Dchar%2D[setZeroDigit](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setZeroDigit%2Dchar%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DecimalStyle.asciidoc b/docs/reference/painless-api-reference/DecimalStyle.asciidoc deleted file mode 100644 index 8dac3a7ae2440..0000000000000 --- a/docs/reference/painless-api-reference/DecimalStyle.asciidoc +++ /dev/null @@ -1,19 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DecimalStyle]]++DecimalStyle++:: -** [[painless-api-reference-DecimalStyle-STANDARD]]static <> link:{java8-javadoc}/java/time/format/DecimalStyle.html#STANDARD[STANDARD] (link:{java9-javadoc}/java/time/format/DecimalStyle.html#STANDARD[java 9]) -* ++[[painless-api-reference-DecimalStyle-getAvailableLocales-0]]static <> link:{java8-javadoc}/java/time/format/DecimalStyle.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#getAvailableLocales%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalStyle-of-1]]static <> link:{java8-javadoc}/java/time/format/DecimalStyle.html#of%2Djava.util.Locale%2D[of](<>)++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#of%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-DecimalStyle-ofDefaultLocale-0]]static <> link:{java8-javadoc}/java/time/format/DecimalStyle.html#ofDefaultLocale%2D%2D[ofDefaultLocale]()++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#ofDefaultLocale%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalStyle-getDecimalSeparator-0]]char link:{java8-javadoc}/java/time/format/DecimalStyle.html#getDecimalSeparator%2D%2D[getDecimalSeparator]()++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#getDecimalSeparator%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalStyle-getNegativeSign-0]]char link:{java8-javadoc}/java/time/format/DecimalStyle.html#getNegativeSign%2D%2D[getNegativeSign]()++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#getNegativeSign%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalStyle-getPositiveSign-0]]char link:{java8-javadoc}/java/time/format/DecimalStyle.html#getPositiveSign%2D%2D[getPositiveSign]()++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#getPositiveSign%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalStyle-getZeroDigit-0]]char link:{java8-javadoc}/java/time/format/DecimalStyle.html#getZeroDigit%2D%2D[getZeroDigit]()++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#getZeroDigit%2D%2D[java 9]) -* ++[[painless-api-reference-DecimalStyle-withDecimalSeparator-1]]<> link:{java8-javadoc}/java/time/format/DecimalStyle.html#withDecimalSeparator%2Dchar%2D[withDecimalSeparator](char)++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#withDecimalSeparator%2Dchar%2D[java 9]) -* ++[[painless-api-reference-DecimalStyle-withNegativeSign-1]]<> link:{java8-javadoc}/java/time/format/DecimalStyle.html#withNegativeSign%2Dchar%2D[withNegativeSign](char)++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#withNegativeSign%2Dchar%2D[java 9]) -* ++[[painless-api-reference-DecimalStyle-withPositiveSign-1]]<> link:{java8-javadoc}/java/time/format/DecimalStyle.html#withPositiveSign%2Dchar%2D[withPositiveSign](char)++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#withPositiveSign%2Dchar%2D[java 9]) -* ++[[painless-api-reference-DecimalStyle-withZeroDigit-1]]<> link:{java8-javadoc}/java/time/format/DecimalStyle.html#withZeroDigit%2Dchar%2D[withZeroDigit](char)++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#withZeroDigit%2Dchar%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Deque.asciidoc b/docs/reference/painless-api-reference/Deque.asciidoc deleted file mode 100644 index 4dc6b5c06fc84..0000000000000 --- a/docs/reference/painless-api-reference/Deque.asciidoc +++ /dev/null @@ -1,25 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Deque]]++Deque++:: -* ++[[painless-api-reference-Deque-addFirst-1]]void link:{java8-javadoc}/java/util/Deque.html#addFirst%2Djava.lang.Object%2D[addFirst](def)++ (link:{java9-javadoc}/java/util/Deque.html#addFirst%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Deque-addLast-1]]void link:{java8-javadoc}/java/util/Deque.html#addLast%2Djava.lang.Object%2D[addLast](def)++ (link:{java9-javadoc}/java/util/Deque.html#addLast%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Deque-descendingIterator-0]]<> link:{java8-javadoc}/java/util/Deque.html#descendingIterator%2D%2D[descendingIterator]()++ (link:{java9-javadoc}/java/util/Deque.html#descendingIterator%2D%2D[java 9]) -* ++[[painless-api-reference-Deque-getFirst-0]]def link:{java8-javadoc}/java/util/Deque.html#getFirst%2D%2D[getFirst]()++ (link:{java9-javadoc}/java/util/Deque.html#getFirst%2D%2D[java 9]) -* ++[[painless-api-reference-Deque-getLast-0]]def link:{java8-javadoc}/java/util/Deque.html#getLast%2D%2D[getLast]()++ (link:{java9-javadoc}/java/util/Deque.html#getLast%2D%2D[java 9]) -* ++[[painless-api-reference-Deque-offerFirst-1]]boolean link:{java8-javadoc}/java/util/Deque.html#offerFirst%2Djava.lang.Object%2D[offerFirst](def)++ (link:{java9-javadoc}/java/util/Deque.html#offerFirst%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Deque-offerLast-1]]boolean link:{java8-javadoc}/java/util/Deque.html#offerLast%2Djava.lang.Object%2D[offerLast](def)++ (link:{java9-javadoc}/java/util/Deque.html#offerLast%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Deque-peekFirst-0]]def link:{java8-javadoc}/java/util/Deque.html#peekFirst%2D%2D[peekFirst]()++ (link:{java9-javadoc}/java/util/Deque.html#peekFirst%2D%2D[java 9]) -* ++[[painless-api-reference-Deque-peekLast-0]]def link:{java8-javadoc}/java/util/Deque.html#peekLast%2D%2D[peekLast]()++ (link:{java9-javadoc}/java/util/Deque.html#peekLast%2D%2D[java 9]) -* ++[[painless-api-reference-Deque-pollFirst-0]]def link:{java8-javadoc}/java/util/Deque.html#pollFirst%2D%2D[pollFirst]()++ (link:{java9-javadoc}/java/util/Deque.html#pollFirst%2D%2D[java 9]) -* ++[[painless-api-reference-Deque-pollLast-0]]def link:{java8-javadoc}/java/util/Deque.html#pollLast%2D%2D[pollLast]()++ (link:{java9-javadoc}/java/util/Deque.html#pollLast%2D%2D[java 9]) -* ++[[painless-api-reference-Deque-pop-0]]def link:{java8-javadoc}/java/util/Deque.html#pop%2D%2D[pop]()++ (link:{java9-javadoc}/java/util/Deque.html#pop%2D%2D[java 9]) -* ++[[painless-api-reference-Deque-push-1]]void link:{java8-javadoc}/java/util/Deque.html#push%2Djava.lang.Object%2D[push](def)++ (link:{java9-javadoc}/java/util/Deque.html#push%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Deque-remove-1]]boolean link:{java8-javadoc}/java/util/Deque.html#remove%2Djava.lang.Object%2D[remove](def)++ (link:{java9-javadoc}/java/util/Deque.html#remove%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Deque-removeFirst-0]]def link:{java8-javadoc}/java/util/Deque.html#removeFirst%2D%2D[removeFirst]()++ (link:{java9-javadoc}/java/util/Deque.html#removeFirst%2D%2D[java 9]) -* ++[[painless-api-reference-Deque-removeFirstOccurrence-1]]boolean link:{java8-javadoc}/java/util/Deque.html#removeFirstOccurrence%2Djava.lang.Object%2D[removeFirstOccurrence](def)++ (link:{java9-javadoc}/java/util/Deque.html#removeFirstOccurrence%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Deque-removeLast-0]]def link:{java8-javadoc}/java/util/Deque.html#removeLast%2D%2D[removeLast]()++ (link:{java9-javadoc}/java/util/Deque.html#removeLast%2D%2D[java 9]) -* ++[[painless-api-reference-Deque-removeLastOccurrence-1]]boolean link:{java8-javadoc}/java/util/Deque.html#removeLastOccurrence%2Djava.lang.Object%2D[removeLastOccurrence](def)++ (link:{java9-javadoc}/java/util/Deque.html#removeLastOccurrence%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Dictionary.asciidoc b/docs/reference/painless-api-reference/Dictionary.asciidoc deleted file mode 100644 index bfa8f2c495e19..0000000000000 --- a/docs/reference/painless-api-reference/Dictionary.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Dictionary]]++Dictionary++:: -* ++[[painless-api-reference-Dictionary-elements-0]]<> link:{java8-javadoc}/java/util/Dictionary.html#elements%2D%2D[elements]()++ (link:{java9-javadoc}/java/util/Dictionary.html#elements%2D%2D[java 9]) -* ++[[painless-api-reference-Dictionary-get-1]]def link:{java8-javadoc}/java/util/Dictionary.html#get%2Djava.lang.Object%2D[get](def)++ (link:{java9-javadoc}/java/util/Dictionary.html#get%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Dictionary-isEmpty-0]]boolean link:{java8-javadoc}/java/util/Dictionary.html#isEmpty%2D%2D[isEmpty]()++ (link:{java9-javadoc}/java/util/Dictionary.html#isEmpty%2D%2D[java 9]) -* ++[[painless-api-reference-Dictionary-keys-0]]<> link:{java8-javadoc}/java/util/Dictionary.html#keys%2D%2D[keys]()++ (link:{java9-javadoc}/java/util/Dictionary.html#keys%2D%2D[java 9]) -* ++[[painless-api-reference-Dictionary-put-2]]def link:{java8-javadoc}/java/util/Dictionary.html#put%2Djava.lang.Object%2Djava.lang.Object%2D[put](def, def)++ (link:{java9-javadoc}/java/util/Dictionary.html#put%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Dictionary-remove-1]]def link:{java8-javadoc}/java/util/Dictionary.html#remove%2Djava.lang.Object%2D[remove](def)++ (link:{java9-javadoc}/java/util/Dictionary.html#remove%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Dictionary-size-0]]int link:{java8-javadoc}/java/util/Dictionary.html#size%2D%2D[size]()++ (link:{java9-javadoc}/java/util/Dictionary.html#size%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Double.asciidoc b/docs/reference/painless-api-reference/Double.asciidoc deleted file mode 100644 index 50d1d8c217cba..0000000000000 --- a/docs/reference/painless-api-reference/Double.asciidoc +++ /dev/null @@ -1,35 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Double]]++Double++:: -** [[painless-api-reference-Double-BYTES]]static int link:{java8-javadoc}/java/lang/Double.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Double.html#BYTES[java 9]) -** [[painless-api-reference-Double-MAX_EXPONENT]]static int link:{java8-javadoc}/java/lang/Double.html#MAX_EXPONENT[MAX_EXPONENT] (link:{java9-javadoc}/java/lang/Double.html#MAX_EXPONENT[java 9]) -** [[painless-api-reference-Double-MAX_VALUE]]static double link:{java8-javadoc}/java/lang/Double.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Double.html#MAX_VALUE[java 9]) -** [[painless-api-reference-Double-MIN_EXPONENT]]static int link:{java8-javadoc}/java/lang/Double.html#MIN_EXPONENT[MIN_EXPONENT] (link:{java9-javadoc}/java/lang/Double.html#MIN_EXPONENT[java 9]) -** [[painless-api-reference-Double-MIN_NORMAL]]static double link:{java8-javadoc}/java/lang/Double.html#MIN_NORMAL[MIN_NORMAL] (link:{java9-javadoc}/java/lang/Double.html#MIN_NORMAL[java 9]) -** [[painless-api-reference-Double-MIN_VALUE]]static double link:{java8-javadoc}/java/lang/Double.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Double.html#MIN_VALUE[java 9]) -** [[painless-api-reference-Double-NEGATIVE_INFINITY]]static double link:{java8-javadoc}/java/lang/Double.html#NEGATIVE_INFINITY[NEGATIVE_INFINITY] (link:{java9-javadoc}/java/lang/Double.html#NEGATIVE_INFINITY[java 9]) -** [[painless-api-reference-Double-NaN]]static double link:{java8-javadoc}/java/lang/Double.html#NaN[NaN] (link:{java9-javadoc}/java/lang/Double.html#NaN[java 9]) -** [[painless-api-reference-Double-POSITIVE_INFINITY]]static double link:{java8-javadoc}/java/lang/Double.html#POSITIVE_INFINITY[POSITIVE_INFINITY] (link:{java9-javadoc}/java/lang/Double.html#POSITIVE_INFINITY[java 9]) -** [[painless-api-reference-Double-SIZE]]static int link:{java8-javadoc}/java/lang/Double.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Double.html#SIZE[java 9]) -* ++[[painless-api-reference-Double-compare-2]]static int link:{java8-javadoc}/java/lang/Double.html#compare%2Ddouble%2Ddouble%2D[compare](double, double)++ (link:{java9-javadoc}/java/lang/Double.html#compare%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Double-doubleToLongBits-1]]static long link:{java8-javadoc}/java/lang/Double.html#doubleToLongBits%2Ddouble%2D[doubleToLongBits](double)++ (link:{java9-javadoc}/java/lang/Double.html#doubleToLongBits%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Double-doubleToRawLongBits-1]]static long link:{java8-javadoc}/java/lang/Double.html#doubleToRawLongBits%2Ddouble%2D[doubleToRawLongBits](double)++ (link:{java9-javadoc}/java/lang/Double.html#doubleToRawLongBits%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Double-hashCode-1]]static int link:{java8-javadoc}/java/lang/Double.html#hashCode%2Ddouble%2D[hashCode](double)++ (link:{java9-javadoc}/java/lang/Double.html#hashCode%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Double-isFinite-1]]static boolean link:{java8-javadoc}/java/lang/Double.html#isFinite%2Ddouble%2D[isFinite](double)++ (link:{java9-javadoc}/java/lang/Double.html#isFinite%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Double-isInfinite-1]]static boolean link:{java8-javadoc}/java/lang/Double.html#isInfinite%2Ddouble%2D[isInfinite](double)++ (link:{java9-javadoc}/java/lang/Double.html#isInfinite%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Double-isNaN-1]]static boolean link:{java8-javadoc}/java/lang/Double.html#isNaN%2Ddouble%2D[isNaN](double)++ (link:{java9-javadoc}/java/lang/Double.html#isNaN%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Double-longBitsToDouble-1]]static double link:{java8-javadoc}/java/lang/Double.html#longBitsToDouble%2Dlong%2D[longBitsToDouble](long)++ (link:{java9-javadoc}/java/lang/Double.html#longBitsToDouble%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Double-max-2]]static double link:{java8-javadoc}/java/lang/Double.html#max%2Ddouble%2Ddouble%2D[max](double, double)++ (link:{java9-javadoc}/java/lang/Double.html#max%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Double-min-2]]static double link:{java8-javadoc}/java/lang/Double.html#min%2Ddouble%2Ddouble%2D[min](double, double)++ (link:{java9-javadoc}/java/lang/Double.html#min%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Double-parseDouble-1]]static double link:{java8-javadoc}/java/lang/Double.html#parseDouble%2Djava.lang.String%2D[parseDouble](<>)++ (link:{java9-javadoc}/java/lang/Double.html#parseDouble%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Double-sum-2]]static double link:{java8-javadoc}/java/lang/Double.html#sum%2Ddouble%2Ddouble%2D[sum](double, double)++ (link:{java9-javadoc}/java/lang/Double.html#sum%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Double-toHexString-1]]static <> link:{java8-javadoc}/java/lang/Double.html#toHexString%2Ddouble%2D[toHexString](double)++ (link:{java9-javadoc}/java/lang/Double.html#toHexString%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Double-toString-1]]static <> link:{java8-javadoc}/java/lang/Double.html#toString%2Ddouble%2D[toString](double)++ (link:{java9-javadoc}/java/lang/Double.html#toString%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Double-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Double.html#valueOf%2Ddouble%2D[valueOf](double)++ (link:{java9-javadoc}/java/lang/Double.html#valueOf%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Double-compareTo-1]]int link:{java8-javadoc}/java/lang/Double.html#compareTo%2Djava.lang.Double%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Double.html#compareTo%2Djava.lang.Double%2D[java 9]) -* ++[[painless-api-reference-Double-isInfinite-0]]boolean link:{java8-javadoc}/java/lang/Double.html#isInfinite%2D%2D[isInfinite]()++ (link:{java9-javadoc}/java/lang/Double.html#isInfinite%2D%2D[java 9]) -* ++[[painless-api-reference-Double-isNaN-0]]boolean link:{java8-javadoc}/java/lang/Double.html#isNaN%2D%2D[isNaN]()++ (link:{java9-javadoc}/java/lang/Double.html#isNaN%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/DoubleBinaryOperator.asciidoc b/docs/reference/painless-api-reference/DoubleBinaryOperator.asciidoc deleted file mode 100644 index deec8c5337c81..0000000000000 --- a/docs/reference/painless-api-reference/DoubleBinaryOperator.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DoubleBinaryOperator]]++DoubleBinaryOperator++:: -* ++[[painless-api-reference-DoubleBinaryOperator-applyAsDouble-2]]double link:{java8-javadoc}/java/util/function/DoubleBinaryOperator.html#applyAsDouble%2Ddouble%2Ddouble%2D[applyAsDouble](double, double)++ (link:{java9-javadoc}/java/util/function/DoubleBinaryOperator.html#applyAsDouble%2Ddouble%2Ddouble%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DoubleConsumer.asciidoc b/docs/reference/painless-api-reference/DoubleConsumer.asciidoc deleted file mode 100644 index 3c078a4d4fb1c..0000000000000 --- a/docs/reference/painless-api-reference/DoubleConsumer.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DoubleConsumer]]++DoubleConsumer++:: -* ++[[painless-api-reference-DoubleConsumer-accept-1]]void link:{java8-javadoc}/java/util/function/DoubleConsumer.html#accept%2Ddouble%2D[accept](double)++ (link:{java9-javadoc}/java/util/function/DoubleConsumer.html#accept%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-DoubleConsumer-andThen-1]]<> link:{java8-javadoc}/java/util/function/DoubleConsumer.html#andThen%2Djava.util.function.DoubleConsumer%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/DoubleConsumer.html#andThen%2Djava.util.function.DoubleConsumer%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DoubleFunction.asciidoc b/docs/reference/painless-api-reference/DoubleFunction.asciidoc deleted file mode 100644 index 082a70001328e..0000000000000 --- a/docs/reference/painless-api-reference/DoubleFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DoubleFunction]]++DoubleFunction++:: -* ++[[painless-api-reference-DoubleFunction-apply-1]]def link:{java8-javadoc}/java/util/function/DoubleFunction.html#apply%2Ddouble%2D[apply](double)++ (link:{java9-javadoc}/java/util/function/DoubleFunction.html#apply%2Ddouble%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DoublePredicate.asciidoc b/docs/reference/painless-api-reference/DoublePredicate.asciidoc deleted file mode 100644 index 48cfd3c59296c..0000000000000 --- a/docs/reference/painless-api-reference/DoublePredicate.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DoublePredicate]]++DoublePredicate++:: -* ++[[painless-api-reference-DoublePredicate-and-1]]<> link:{java8-javadoc}/java/util/function/DoublePredicate.html#and%2Djava.util.function.DoublePredicate%2D[and](<>)++ (link:{java9-javadoc}/java/util/function/DoublePredicate.html#and%2Djava.util.function.DoublePredicate%2D[java 9]) -* ++[[painless-api-reference-DoublePredicate-negate-0]]<> link:{java8-javadoc}/java/util/function/DoublePredicate.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/util/function/DoublePredicate.html#negate%2D%2D[java 9]) -* ++[[painless-api-reference-DoublePredicate-or-1]]<> link:{java8-javadoc}/java/util/function/DoublePredicate.html#or%2Djava.util.function.DoublePredicate%2D[or](<>)++ (link:{java9-javadoc}/java/util/function/DoublePredicate.html#or%2Djava.util.function.DoublePredicate%2D[java 9]) -* ++[[painless-api-reference-DoublePredicate-test-1]]boolean link:{java8-javadoc}/java/util/function/DoublePredicate.html#test%2Ddouble%2D[test](double)++ (link:{java9-javadoc}/java/util/function/DoublePredicate.html#test%2Ddouble%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DoubleStream.Builder.asciidoc b/docs/reference/painless-api-reference/DoubleStream.Builder.asciidoc deleted file mode 100644 index 278a3f99a9ab9..0000000000000 --- a/docs/reference/painless-api-reference/DoubleStream.Builder.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DoubleStream-Builder]]++DoubleStream.Builder++:: -* ++[[painless-api-reference-DoubleStream-Builder-add-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream$Builder.html#add%2Ddouble%2D[add](double)++ (link:{java9-javadoc}/java/util/stream/DoubleStream$Builder.html#add%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-Builder-build-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream$Builder.html#build%2D%2D[build]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream$Builder.html#build%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/DoubleStream.asciidoc b/docs/reference/painless-api-reference/DoubleStream.asciidoc deleted file mode 100644 index d3f517e29e40b..0000000000000 --- a/docs/reference/painless-api-reference/DoubleStream.asciidoc +++ /dev/null @@ -1,43 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DoubleStream]]++DoubleStream++:: -* ++[[painless-api-reference-DoubleStream-builder-0]]static <> link:{java8-javadoc}/java/util/stream/DoubleStream.html#builder%2D%2D[builder]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#builder%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-concat-2]]static <> link:{java8-javadoc}/java/util/stream/DoubleStream.html#concat%2Djava.util.stream.DoubleStream%2Djava.util.stream.DoubleStream%2D[concat](<>, <>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#concat%2Djava.util.stream.DoubleStream%2Djava.util.stream.DoubleStream%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-empty-0]]static <> link:{java8-javadoc}/java/util/stream/DoubleStream.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#empty%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-of-1]]static <> link:{java8-javadoc}/java/util/stream/DoubleStream.html#of%2Ddouble:A%2D[of](double[])++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#of%2Ddouble:A%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-allMatch-1]]boolean link:{java8-javadoc}/java/util/stream/DoubleStream.html#allMatch%2Djava.util.function.DoublePredicate%2D[allMatch](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#allMatch%2Djava.util.function.DoublePredicate%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-anyMatch-1]]boolean link:{java8-javadoc}/java/util/stream/DoubleStream.html#anyMatch%2Djava.util.function.DoublePredicate%2D[anyMatch](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#anyMatch%2Djava.util.function.DoublePredicate%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-average-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#average%2D%2D[average]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#average%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-boxed-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#boxed%2D%2D[boxed]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#boxed%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-collect-3]]def link:{java8-javadoc}/java/util/stream/DoubleStream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.ObjDoubleConsumer%2Djava.util.function.BiConsumer%2D[collect](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.ObjDoubleConsumer%2Djava.util.function.BiConsumer%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-count-0]]long link:{java8-javadoc}/java/util/stream/DoubleStream.html#count%2D%2D[count]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#count%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-distinct-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#distinct%2D%2D[distinct]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#distinct%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-filter-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#filter%2Djava.util.function.DoublePredicate%2D[filter](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#filter%2Djava.util.function.DoublePredicate%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-findAny-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#findAny%2D%2D[findAny]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#findAny%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-findFirst-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#findFirst%2D%2D[findFirst]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#findFirst%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-flatMap-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#flatMap%2Djava.util.function.DoubleFunction%2D[flatMap](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#flatMap%2Djava.util.function.DoubleFunction%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-forEach-1]]void link:{java8-javadoc}/java/util/stream/DoubleStream.html#forEach%2Djava.util.function.DoubleConsumer%2D[forEach](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#forEach%2Djava.util.function.DoubleConsumer%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-forEachOrdered-1]]void link:{java8-javadoc}/java/util/stream/DoubleStream.html#forEachOrdered%2Djava.util.function.DoubleConsumer%2D[forEachOrdered](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#forEachOrdered%2Djava.util.function.DoubleConsumer%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-iterator-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#iterator%2D%2D[iterator]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#iterator%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-limit-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#limit%2Dlong%2D[limit](long)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#limit%2Dlong%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-map-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#map%2Djava.util.function.DoubleUnaryOperator%2D[map](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#map%2Djava.util.function.DoubleUnaryOperator%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-mapToInt-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#mapToInt%2Djava.util.function.DoubleToIntFunction%2D[mapToInt](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#mapToInt%2Djava.util.function.DoubleToIntFunction%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-mapToLong-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#mapToLong%2Djava.util.function.DoubleToLongFunction%2D[mapToLong](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#mapToLong%2Djava.util.function.DoubleToLongFunction%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-mapToObj-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#mapToObj%2Djava.util.function.DoubleFunction%2D[mapToObj](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#mapToObj%2Djava.util.function.DoubleFunction%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-max-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#max%2D%2D[max]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#max%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-min-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#min%2D%2D[min]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#min%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-noneMatch-1]]boolean link:{java8-javadoc}/java/util/stream/DoubleStream.html#noneMatch%2Djava.util.function.DoublePredicate%2D[noneMatch](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#noneMatch%2Djava.util.function.DoublePredicate%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-peek-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#peek%2Djava.util.function.DoubleConsumer%2D[peek](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#peek%2Djava.util.function.DoubleConsumer%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-reduce-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#reduce%2Djava.util.function.DoubleBinaryOperator%2D[reduce](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#reduce%2Djava.util.function.DoubleBinaryOperator%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-reduce-2]]double link:{java8-javadoc}/java/util/stream/DoubleStream.html#reduce%2Ddouble%2Djava.util.function.DoubleBinaryOperator%2D[reduce](double, <>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#reduce%2Ddouble%2Djava.util.function.DoubleBinaryOperator%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-sequential-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#sequential%2D%2D[sequential]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#sequential%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-skip-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#skip%2Dlong%2D[skip](long)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#skip%2Dlong%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-sorted-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#sorted%2D%2D[sorted]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#sorted%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-spliterator-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#spliterator%2D%2D[spliterator]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#spliterator%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-sum-0]]double link:{java8-javadoc}/java/util/stream/DoubleStream.html#sum%2D%2D[sum]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#sum%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-summaryStatistics-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#summaryStatistics%2D%2D[summaryStatistics]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#summaryStatistics%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleStream-toArray-0]]double[] link:{java8-javadoc}/java/util/stream/DoubleStream.html#toArray%2D%2D[toArray]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#toArray%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/DoubleSummaryStatistics.asciidoc b/docs/reference/painless-api-reference/DoubleSummaryStatistics.asciidoc deleted file mode 100644 index 976eb98440410..0000000000000 --- a/docs/reference/painless-api-reference/DoubleSummaryStatistics.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DoubleSummaryStatistics]]++DoubleSummaryStatistics++:: -* ++[[painless-api-reference-DoubleSummaryStatistics-DoubleSummaryStatistics-0]]link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#DoubleSummaryStatistics%2D%2D[DoubleSummaryStatistics]()++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#DoubleSummaryStatistics%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleSummaryStatistics-combine-1]]void link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#combine%2Djava.util.DoubleSummaryStatistics%2D[combine](<>)++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#combine%2Djava.util.DoubleSummaryStatistics%2D[java 9]) -* ++[[painless-api-reference-DoubleSummaryStatistics-getAverage-0]]double link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#getAverage%2D%2D[getAverage]()++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#getAverage%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleSummaryStatistics-getCount-0]]long link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#getCount%2D%2D[getCount]()++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#getCount%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleSummaryStatistics-getMax-0]]double link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#getMax%2D%2D[getMax]()++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#getMax%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleSummaryStatistics-getMin-0]]double link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#getMin%2D%2D[getMin]()++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#getMin%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleSummaryStatistics-getSum-0]]double link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#getSum%2D%2D[getSum]()++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#getSum%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/DoubleSupplier.asciidoc b/docs/reference/painless-api-reference/DoubleSupplier.asciidoc deleted file mode 100644 index 1de478ab50f95..0000000000000 --- a/docs/reference/painless-api-reference/DoubleSupplier.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DoubleSupplier]]++DoubleSupplier++:: -* ++[[painless-api-reference-DoubleSupplier-getAsDouble-0]]double link:{java8-javadoc}/java/util/function/DoubleSupplier.html#getAsDouble%2D%2D[getAsDouble]()++ (link:{java9-javadoc}/java/util/function/DoubleSupplier.html#getAsDouble%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DoubleToIntFunction.asciidoc b/docs/reference/painless-api-reference/DoubleToIntFunction.asciidoc deleted file mode 100644 index 9448a88faf35a..0000000000000 --- a/docs/reference/painless-api-reference/DoubleToIntFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DoubleToIntFunction]]++DoubleToIntFunction++:: -* ++[[painless-api-reference-DoubleToIntFunction-applyAsInt-1]]int link:{java8-javadoc}/java/util/function/DoubleToIntFunction.html#applyAsInt%2Ddouble%2D[applyAsInt](double)++ (link:{java9-javadoc}/java/util/function/DoubleToIntFunction.html#applyAsInt%2Ddouble%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DoubleToLongFunction.asciidoc b/docs/reference/painless-api-reference/DoubleToLongFunction.asciidoc deleted file mode 100644 index 62a3f841b3391..0000000000000 --- a/docs/reference/painless-api-reference/DoubleToLongFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DoubleToLongFunction]]++DoubleToLongFunction++:: -* ++[[painless-api-reference-DoubleToLongFunction-applyAsLong-1]]long link:{java8-javadoc}/java/util/function/DoubleToLongFunction.html#applyAsLong%2Ddouble%2D[applyAsLong](double)++ (link:{java9-javadoc}/java/util/function/DoubleToLongFunction.html#applyAsLong%2Ddouble%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DoubleUnaryOperator.asciidoc b/docs/reference/painless-api-reference/DoubleUnaryOperator.asciidoc deleted file mode 100644 index a78b8ac5d0b1c..0000000000000 --- a/docs/reference/painless-api-reference/DoubleUnaryOperator.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DoubleUnaryOperator]]++DoubleUnaryOperator++:: -* ++[[painless-api-reference-DoubleUnaryOperator-identity-0]]static <> link:{java8-javadoc}/java/util/function/DoubleUnaryOperator.html#identity%2D%2D[identity]()++ (link:{java9-javadoc}/java/util/function/DoubleUnaryOperator.html#identity%2D%2D[java 9]) -* ++[[painless-api-reference-DoubleUnaryOperator-andThen-1]]<> link:{java8-javadoc}/java/util/function/DoubleUnaryOperator.html#andThen%2Djava.util.function.DoubleUnaryOperator%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/DoubleUnaryOperator.html#andThen%2Djava.util.function.DoubleUnaryOperator%2D[java 9]) -* ++[[painless-api-reference-DoubleUnaryOperator-applyAsDouble-1]]double link:{java8-javadoc}/java/util/function/DoubleUnaryOperator.html#applyAsDouble%2Ddouble%2D[applyAsDouble](double)++ (link:{java9-javadoc}/java/util/function/DoubleUnaryOperator.html#applyAsDouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-DoubleUnaryOperator-compose-1]]<> link:{java8-javadoc}/java/util/function/DoubleUnaryOperator.html#compose%2Djava.util.function.DoubleUnaryOperator%2D[compose](<>)++ (link:{java9-javadoc}/java/util/function/DoubleUnaryOperator.html#compose%2Djava.util.function.DoubleUnaryOperator%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/DuplicateFormatFlagsException.asciidoc b/docs/reference/painless-api-reference/DuplicateFormatFlagsException.asciidoc deleted file mode 100644 index f26103fdc198f..0000000000000 --- a/docs/reference/painless-api-reference/DuplicateFormatFlagsException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-DuplicateFormatFlagsException]]++DuplicateFormatFlagsException++:: -* ++[[painless-api-reference-DuplicateFormatFlagsException-DuplicateFormatFlagsException-1]]link:{java8-javadoc}/java/util/DuplicateFormatFlagsException.html#DuplicateFormatFlagsException%2Djava.lang.String%2D[DuplicateFormatFlagsException](<>)++ (link:{java9-javadoc}/java/util/DuplicateFormatFlagsException.html#DuplicateFormatFlagsException%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-DuplicateFormatFlagsException-getFlags-0]]<> link:{java8-javadoc}/java/util/DuplicateFormatFlagsException.html#getFlags%2D%2D[getFlags]()++ (link:{java9-javadoc}/java/util/DuplicateFormatFlagsException.html#getFlags%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Duration.asciidoc b/docs/reference/painless-api-reference/Duration.asciidoc deleted file mode 100644 index ef9ecf720a0f2..0000000000000 --- a/docs/reference/painless-api-reference/Duration.asciidoc +++ /dev/null @@ -1,51 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Duration]]++Duration++:: -** [[painless-api-reference-Duration-ZERO]]static <> link:{java8-javadoc}/java/time/Duration.html#ZERO[ZERO] (link:{java9-javadoc}/java/time/Duration.html#ZERO[java 9]) -* ++[[painless-api-reference-Duration-between-2]]static <> link:{java8-javadoc}/java/time/Duration.html#between%2Djava.time.temporal.Temporal%2Djava.time.temporal.Temporal%2D[between](<>, <>)++ (link:{java9-javadoc}/java/time/Duration.html#between%2Djava.time.temporal.Temporal%2Djava.time.temporal.Temporal%2D[java 9]) -* ++[[painless-api-reference-Duration-from-1]]static <> link:{java8-javadoc}/java/time/Duration.html#from%2Djava.time.temporal.TemporalAmount%2D[from](<>)++ (link:{java9-javadoc}/java/time/Duration.html#from%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-Duration-of-2]]static <> link:{java8-javadoc}/java/time/Duration.html#of%2Dlong%2Djava.time.temporal.TemporalUnit%2D[of](long, <>)++ (link:{java9-javadoc}/java/time/Duration.html#of%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-Duration-ofDays-1]]static <> link:{java8-javadoc}/java/time/Duration.html#ofDays%2Dlong%2D[ofDays](long)++ (link:{java9-javadoc}/java/time/Duration.html#ofDays%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-ofHours-1]]static <> link:{java8-javadoc}/java/time/Duration.html#ofHours%2Dlong%2D[ofHours](long)++ (link:{java9-javadoc}/java/time/Duration.html#ofHours%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-ofMillis-1]]static <> link:{java8-javadoc}/java/time/Duration.html#ofMillis%2Dlong%2D[ofMillis](long)++ (link:{java9-javadoc}/java/time/Duration.html#ofMillis%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-ofMinutes-1]]static <> link:{java8-javadoc}/java/time/Duration.html#ofMinutes%2Dlong%2D[ofMinutes](long)++ (link:{java9-javadoc}/java/time/Duration.html#ofMinutes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-ofNanos-1]]static <> link:{java8-javadoc}/java/time/Duration.html#ofNanos%2Dlong%2D[ofNanos](long)++ (link:{java9-javadoc}/java/time/Duration.html#ofNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-ofSeconds-1]]static <> link:{java8-javadoc}/java/time/Duration.html#ofSeconds%2Dlong%2D[ofSeconds](long)++ (link:{java9-javadoc}/java/time/Duration.html#ofSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-ofSeconds-2]]static <> link:{java8-javadoc}/java/time/Duration.html#ofSeconds%2Dlong%2Dlong%2D[ofSeconds](long, long)++ (link:{java9-javadoc}/java/time/Duration.html#ofSeconds%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-parse-1]]static <> link:{java8-javadoc}/java/time/Duration.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/Duration.html#parse%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-Duration-abs-0]]<> link:{java8-javadoc}/java/time/Duration.html#abs%2D%2D[abs]()++ (link:{java9-javadoc}/java/time/Duration.html#abs%2D%2D[java 9]) -* ++[[painless-api-reference-Duration-compareTo-1]]int link:{java8-javadoc}/java/time/Duration.html#compareTo%2Djava.time.Duration%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/Duration.html#compareTo%2Djava.time.Duration%2D[java 9]) -* ++[[painless-api-reference-Duration-dividedBy-1]]<> link:{java8-javadoc}/java/time/Duration.html#dividedBy%2Dlong%2D[dividedBy](long)++ (link:{java9-javadoc}/java/time/Duration.html#dividedBy%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-getNano-0]]int link:{java8-javadoc}/java/time/Duration.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/Duration.html#getNano%2D%2D[java 9]) -* ++[[painless-api-reference-Duration-getSeconds-0]]long link:{java8-javadoc}/java/time/Duration.html#getSeconds%2D%2D[getSeconds]()++ (link:{java9-javadoc}/java/time/Duration.html#getSeconds%2D%2D[java 9]) -* ++[[painless-api-reference-Duration-isNegative-0]]boolean link:{java8-javadoc}/java/time/Duration.html#isNegative%2D%2D[isNegative]()++ (link:{java9-javadoc}/java/time/Duration.html#isNegative%2D%2D[java 9]) -* ++[[painless-api-reference-Duration-isZero-0]]boolean link:{java8-javadoc}/java/time/Duration.html#isZero%2D%2D[isZero]()++ (link:{java9-javadoc}/java/time/Duration.html#isZero%2D%2D[java 9]) -* ++[[painless-api-reference-Duration-minus-1]]<> link:{java8-javadoc}/java/time/Duration.html#minus%2Djava.time.Duration%2D[minus](<>)++ (link:{java9-javadoc}/java/time/Duration.html#minus%2Djava.time.Duration%2D[java 9]) -* ++[[painless-api-reference-Duration-minus-2]]<> link:{java8-javadoc}/java/time/Duration.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/Duration.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-Duration-minusDays-1]]<> link:{java8-javadoc}/java/time/Duration.html#minusDays%2Dlong%2D[minusDays](long)++ (link:{java9-javadoc}/java/time/Duration.html#minusDays%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-minusHours-1]]<> link:{java8-javadoc}/java/time/Duration.html#minusHours%2Dlong%2D[minusHours](long)++ (link:{java9-javadoc}/java/time/Duration.html#minusHours%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-minusMillis-1]]<> link:{java8-javadoc}/java/time/Duration.html#minusMillis%2Dlong%2D[minusMillis](long)++ (link:{java9-javadoc}/java/time/Duration.html#minusMillis%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-minusMinutes-1]]<> link:{java8-javadoc}/java/time/Duration.html#minusMinutes%2Dlong%2D[minusMinutes](long)++ (link:{java9-javadoc}/java/time/Duration.html#minusMinutes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-minusNanos-1]]<> link:{java8-javadoc}/java/time/Duration.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/Duration.html#minusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-minusSeconds-1]]<> link:{java8-javadoc}/java/time/Duration.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/Duration.html#minusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-multipliedBy-1]]<> link:{java8-javadoc}/java/time/Duration.html#multipliedBy%2Dlong%2D[multipliedBy](long)++ (link:{java9-javadoc}/java/time/Duration.html#multipliedBy%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-negated-0]]<> link:{java8-javadoc}/java/time/Duration.html#negated%2D%2D[negated]()++ (link:{java9-javadoc}/java/time/Duration.html#negated%2D%2D[java 9]) -* ++[[painless-api-reference-Duration-plus-1]]<> link:{java8-javadoc}/java/time/Duration.html#plus%2Djava.time.Duration%2D[plus](<>)++ (link:{java9-javadoc}/java/time/Duration.html#plus%2Djava.time.Duration%2D[java 9]) -* ++[[painless-api-reference-Duration-plus-2]]<> link:{java8-javadoc}/java/time/Duration.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/Duration.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-Duration-plusDays-1]]<> link:{java8-javadoc}/java/time/Duration.html#plusDays%2Dlong%2D[plusDays](long)++ (link:{java9-javadoc}/java/time/Duration.html#plusDays%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-plusHours-1]]<> link:{java8-javadoc}/java/time/Duration.html#plusHours%2Dlong%2D[plusHours](long)++ (link:{java9-javadoc}/java/time/Duration.html#plusHours%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-plusMillis-1]]<> link:{java8-javadoc}/java/time/Duration.html#plusMillis%2Dlong%2D[plusMillis](long)++ (link:{java9-javadoc}/java/time/Duration.html#plusMillis%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-plusMinutes-1]]<> link:{java8-javadoc}/java/time/Duration.html#plusMinutes%2Dlong%2D[plusMinutes](long)++ (link:{java9-javadoc}/java/time/Duration.html#plusMinutes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-plusNanos-1]]<> link:{java8-javadoc}/java/time/Duration.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/Duration.html#plusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-plusSeconds-1]]<> link:{java8-javadoc}/java/time/Duration.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/Duration.html#plusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Duration-toDays-0]]long link:{java8-javadoc}/java/time/Duration.html#toDays%2D%2D[toDays]()++ (link:{java9-javadoc}/java/time/Duration.html#toDays%2D%2D[java 9]) -* ++[[painless-api-reference-Duration-toHours-0]]long link:{java8-javadoc}/java/time/Duration.html#toHours%2D%2D[toHours]()++ (link:{java9-javadoc}/java/time/Duration.html#toHours%2D%2D[java 9]) -* ++[[painless-api-reference-Duration-toMillis-0]]long link:{java8-javadoc}/java/time/Duration.html#toMillis%2D%2D[toMillis]()++ (link:{java9-javadoc}/java/time/Duration.html#toMillis%2D%2D[java 9]) -* ++[[painless-api-reference-Duration-toMinutes-0]]long link:{java8-javadoc}/java/time/Duration.html#toMinutes%2D%2D[toMinutes]()++ (link:{java9-javadoc}/java/time/Duration.html#toMinutes%2D%2D[java 9]) -* ++[[painless-api-reference-Duration-toNanos-0]]long link:{java8-javadoc}/java/time/Duration.html#toNanos%2D%2D[toNanos]()++ (link:{java9-javadoc}/java/time/Duration.html#toNanos%2D%2D[java 9]) -* ++[[painless-api-reference-Duration-withNanos-1]]<> link:{java8-javadoc}/java/time/Duration.html#withNanos%2Dint%2D[withNanos](int)++ (link:{java9-javadoc}/java/time/Duration.html#withNanos%2Dint%2D[java 9]) -* ++[[painless-api-reference-Duration-withSeconds-1]]<> link:{java8-javadoc}/java/time/Duration.html#withSeconds%2Dlong%2D[withSeconds](long)++ (link:{java9-javadoc}/java/time/Duration.html#withSeconds%2Dlong%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/EmptyStackException.asciidoc b/docs/reference/painless-api-reference/EmptyStackException.asciidoc deleted file mode 100644 index 09ca4ca96be20..0000000000000 --- a/docs/reference/painless-api-reference/EmptyStackException.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-EmptyStackException]]++EmptyStackException++:: -* ++[[painless-api-reference-EmptyStackException-EmptyStackException-0]]link:{java8-javadoc}/java/util/EmptyStackException.html#EmptyStackException%2D%2D[EmptyStackException]()++ (link:{java9-javadoc}/java/util/EmptyStackException.html#EmptyStackException%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Enum.asciidoc b/docs/reference/painless-api-reference/Enum.asciidoc deleted file mode 100644 index 077044dbfb9cd..0000000000000 --- a/docs/reference/painless-api-reference/Enum.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Enum]]++Enum++:: -* ++[[painless-api-reference-Enum-compareTo-1]]int link:{java8-javadoc}/java/lang/Enum.html#compareTo%2Djava.lang.Enum%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Enum.html#compareTo%2Djava.lang.Enum%2D[java 9]) -* ++[[painless-api-reference-Enum-name-0]]<> link:{java8-javadoc}/java/lang/Enum.html#name%2D%2D[name]()++ (link:{java9-javadoc}/java/lang/Enum.html#name%2D%2D[java 9]) -* ++[[painless-api-reference-Enum-ordinal-0]]int link:{java8-javadoc}/java/lang/Enum.html#ordinal%2D%2D[ordinal]()++ (link:{java9-javadoc}/java/lang/Enum.html#ordinal%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/EnumConstantNotPresentException.asciidoc b/docs/reference/painless-api-reference/EnumConstantNotPresentException.asciidoc deleted file mode 100644 index 66a709379d797..0000000000000 --- a/docs/reference/painless-api-reference/EnumConstantNotPresentException.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-EnumConstantNotPresentException]]++EnumConstantNotPresentException++:: -* ++[[painless-api-reference-EnumConstantNotPresentException-constantName-0]]<> link:{java8-javadoc}/java/lang/EnumConstantNotPresentException.html#constantName%2D%2D[constantName]()++ (link:{java9-javadoc}/java/lang/EnumConstantNotPresentException.html#constantName%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Enumeration.asciidoc b/docs/reference/painless-api-reference/Enumeration.asciidoc deleted file mode 100644 index 51849ec27de0f..0000000000000 --- a/docs/reference/painless-api-reference/Enumeration.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Enumeration]]++Enumeration++:: -* ++[[painless-api-reference-Enumeration-hasMoreElements-0]]boolean link:{java8-javadoc}/java/util/Enumeration.html#hasMoreElements%2D%2D[hasMoreElements]()++ (link:{java9-javadoc}/java/util/Enumeration.html#hasMoreElements%2D%2D[java 9]) -* ++[[painless-api-reference-Enumeration-nextElement-0]]def link:{java8-javadoc}/java/util/Enumeration.html#nextElement%2D%2D[nextElement]()++ (link:{java9-javadoc}/java/util/Enumeration.html#nextElement%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Era.asciidoc b/docs/reference/painless-api-reference/Era.asciidoc deleted file mode 100644 index db5ee6aa86b0d..0000000000000 --- a/docs/reference/painless-api-reference/Era.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Era]]++Era++:: -* ++[[painless-api-reference-Era-getDisplayName-2]]<> link:{java8-javadoc}/java/time/chrono/Era.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[getDisplayName](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/Era.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Era-getValue-0]]int link:{java8-javadoc}/java/time/chrono/Era.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/chrono/Era.html#getValue%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/EventListener.asciidoc b/docs/reference/painless-api-reference/EventListener.asciidoc deleted file mode 100644 index 499095ae020a0..0000000000000 --- a/docs/reference/painless-api-reference/EventListener.asciidoc +++ /dev/null @@ -1,7 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-EventListener]]++EventListener++:: -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/EventListenerProxy.asciidoc b/docs/reference/painless-api-reference/EventListenerProxy.asciidoc deleted file mode 100644 index a3a4958ec7115..0000000000000 --- a/docs/reference/painless-api-reference/EventListenerProxy.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-EventListenerProxy]]++EventListenerProxy++:: -* ++[[painless-api-reference-EventListenerProxy-getListener-0]]<> link:{java8-javadoc}/java/util/EventListenerProxy.html#getListener%2D%2D[getListener]()++ (link:{java9-javadoc}/java/util/EventListenerProxy.html#getListener%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/EventObject.asciidoc b/docs/reference/painless-api-reference/EventObject.asciidoc deleted file mode 100644 index 4ac6b14150cb1..0000000000000 --- a/docs/reference/painless-api-reference/EventObject.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-EventObject]]++EventObject++:: -* ++[[painless-api-reference-EventObject-EventObject-1]]link:{java8-javadoc}/java/util/EventObject.html#EventObject%2Djava.lang.Object%2D[EventObject](<>)++ (link:{java9-javadoc}/java/util/EventObject.html#EventObject%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-EventObject-getSource-0]]<> link:{java8-javadoc}/java/util/EventObject.html#getSource%2D%2D[getSource]()++ (link:{java9-javadoc}/java/util/EventObject.html#getSource%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Exception.asciidoc b/docs/reference/painless-api-reference/Exception.asciidoc deleted file mode 100644 index d6ff7b0a7e53e..0000000000000 --- a/docs/reference/painless-api-reference/Exception.asciidoc +++ /dev/null @@ -1,12 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Exception]]++Exception++:: -* ++[[painless-api-reference-Exception-Exception-0]]link:{java8-javadoc}/java/lang/Exception.html#Exception%2D%2D[Exception]()++ (link:{java9-javadoc}/java/lang/Exception.html#Exception%2D%2D[java 9]) -* ++[[painless-api-reference-Exception-Exception-1]]link:{java8-javadoc}/java/lang/Exception.html#Exception%2Djava.lang.String%2D[Exception](<>)++ (link:{java9-javadoc}/java/lang/Exception.html#Exception%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Exception-getLocalizedMessage-0]]<> link:{java8-javadoc}/java/lang/Exception.html#getLocalizedMessage%2D%2D[getLocalizedMessage]()++ (link:{java9-javadoc}/java/lang/Exception.html#getLocalizedMessage%2D%2D[java 9]) -* ++[[painless-api-reference-Exception-getMessage-0]]<> link:{java8-javadoc}/java/lang/Exception.html#getMessage%2D%2D[getMessage]()++ (link:{java9-javadoc}/java/lang/Exception.html#getMessage%2D%2D[java 9]) -* ++[[painless-api-reference-Exception-getStackTrace-0]]<>[] link:{java8-javadoc}/java/lang/Exception.html#getStackTrace%2D%2D[getStackTrace]()++ (link:{java9-javadoc}/java/lang/Exception.html#getStackTrace%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/FieldPosition.asciidoc b/docs/reference/painless-api-reference/FieldPosition.asciidoc deleted file mode 100644 index 7a4b5d78407ae..0000000000000 --- a/docs/reference/painless-api-reference/FieldPosition.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-FieldPosition]]++FieldPosition++:: -* ++[[painless-api-reference-FieldPosition-FieldPosition-1]]link:{java8-javadoc}/java/text/FieldPosition.html#FieldPosition%2Dint%2D[FieldPosition](int)++ (link:{java9-javadoc}/java/text/FieldPosition.html#FieldPosition%2Dint%2D[java 9]) -* ++[[painless-api-reference-FieldPosition-FieldPosition-2]]link:{java8-javadoc}/java/text/FieldPosition.html#FieldPosition%2Djava.text.Format$Field%2Dint%2D[FieldPosition](<>, int)++ (link:{java9-javadoc}/java/text/FieldPosition.html#FieldPosition%2Djava.text.Format$Field%2Dint%2D[java 9]) -* ++[[painless-api-reference-FieldPosition-getBeginIndex-0]]int link:{java8-javadoc}/java/text/FieldPosition.html#getBeginIndex%2D%2D[getBeginIndex]()++ (link:{java9-javadoc}/java/text/FieldPosition.html#getBeginIndex%2D%2D[java 9]) -* ++[[painless-api-reference-FieldPosition-getEndIndex-0]]int link:{java8-javadoc}/java/text/FieldPosition.html#getEndIndex%2D%2D[getEndIndex]()++ (link:{java9-javadoc}/java/text/FieldPosition.html#getEndIndex%2D%2D[java 9]) -* ++[[painless-api-reference-FieldPosition-getField-0]]int link:{java8-javadoc}/java/text/FieldPosition.html#getField%2D%2D[getField]()++ (link:{java9-javadoc}/java/text/FieldPosition.html#getField%2D%2D[java 9]) -* ++[[painless-api-reference-FieldPosition-getFieldAttribute-0]]<> link:{java8-javadoc}/java/text/FieldPosition.html#getFieldAttribute%2D%2D[getFieldAttribute]()++ (link:{java9-javadoc}/java/text/FieldPosition.html#getFieldAttribute%2D%2D[java 9]) -* ++[[painless-api-reference-FieldPosition-setBeginIndex-1]]void link:{java8-javadoc}/java/text/FieldPosition.html#setBeginIndex%2Dint%2D[setBeginIndex](int)++ (link:{java9-javadoc}/java/text/FieldPosition.html#setBeginIndex%2Dint%2D[java 9]) -* ++[[painless-api-reference-FieldPosition-setEndIndex-1]]void link:{java8-javadoc}/java/text/FieldPosition.html#setEndIndex%2Dint%2D[setEndIndex](int)++ (link:{java9-javadoc}/java/text/FieldPosition.html#setEndIndex%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Float.asciidoc b/docs/reference/painless-api-reference/Float.asciidoc deleted file mode 100644 index 1d20357842cfc..0000000000000 --- a/docs/reference/painless-api-reference/Float.asciidoc +++ /dev/null @@ -1,35 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Float]]++Float++:: -** [[painless-api-reference-Float-BYTES]]static int link:{java8-javadoc}/java/lang/Float.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Float.html#BYTES[java 9]) -** [[painless-api-reference-Float-MAX_EXPONENT]]static int link:{java8-javadoc}/java/lang/Float.html#MAX_EXPONENT[MAX_EXPONENT] (link:{java9-javadoc}/java/lang/Float.html#MAX_EXPONENT[java 9]) -** [[painless-api-reference-Float-MAX_VALUE]]static float link:{java8-javadoc}/java/lang/Float.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Float.html#MAX_VALUE[java 9]) -** [[painless-api-reference-Float-MIN_EXPONENT]]static int link:{java8-javadoc}/java/lang/Float.html#MIN_EXPONENT[MIN_EXPONENT] (link:{java9-javadoc}/java/lang/Float.html#MIN_EXPONENT[java 9]) -** [[painless-api-reference-Float-MIN_NORMAL]]static float link:{java8-javadoc}/java/lang/Float.html#MIN_NORMAL[MIN_NORMAL] (link:{java9-javadoc}/java/lang/Float.html#MIN_NORMAL[java 9]) -** [[painless-api-reference-Float-MIN_VALUE]]static float link:{java8-javadoc}/java/lang/Float.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Float.html#MIN_VALUE[java 9]) -** [[painless-api-reference-Float-NEGATIVE_INFINITY]]static float link:{java8-javadoc}/java/lang/Float.html#NEGATIVE_INFINITY[NEGATIVE_INFINITY] (link:{java9-javadoc}/java/lang/Float.html#NEGATIVE_INFINITY[java 9]) -** [[painless-api-reference-Float-NaN]]static float link:{java8-javadoc}/java/lang/Float.html#NaN[NaN] (link:{java9-javadoc}/java/lang/Float.html#NaN[java 9]) -** [[painless-api-reference-Float-POSITIVE_INFINITY]]static float link:{java8-javadoc}/java/lang/Float.html#POSITIVE_INFINITY[POSITIVE_INFINITY] (link:{java9-javadoc}/java/lang/Float.html#POSITIVE_INFINITY[java 9]) -** [[painless-api-reference-Float-SIZE]]static int link:{java8-javadoc}/java/lang/Float.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Float.html#SIZE[java 9]) -* ++[[painless-api-reference-Float-compare-2]]static int link:{java8-javadoc}/java/lang/Float.html#compare%2Dfloat%2Dfloat%2D[compare](float, float)++ (link:{java9-javadoc}/java/lang/Float.html#compare%2Dfloat%2Dfloat%2D[java 9]) -* ++[[painless-api-reference-Float-floatToIntBits-1]]static int link:{java8-javadoc}/java/lang/Float.html#floatToIntBits%2Dfloat%2D[floatToIntBits](float)++ (link:{java9-javadoc}/java/lang/Float.html#floatToIntBits%2Dfloat%2D[java 9]) -* ++[[painless-api-reference-Float-floatToRawIntBits-1]]static int link:{java8-javadoc}/java/lang/Float.html#floatToRawIntBits%2Dfloat%2D[floatToRawIntBits](float)++ (link:{java9-javadoc}/java/lang/Float.html#floatToRawIntBits%2Dfloat%2D[java 9]) -* ++[[painless-api-reference-Float-hashCode-1]]static int link:{java8-javadoc}/java/lang/Float.html#hashCode%2Dfloat%2D[hashCode](float)++ (link:{java9-javadoc}/java/lang/Float.html#hashCode%2Dfloat%2D[java 9]) -* ++[[painless-api-reference-Float-intBitsToFloat-1]]static float link:{java8-javadoc}/java/lang/Float.html#intBitsToFloat%2Dint%2D[intBitsToFloat](int)++ (link:{java9-javadoc}/java/lang/Float.html#intBitsToFloat%2Dint%2D[java 9]) -* ++[[painless-api-reference-Float-isFinite-1]]static boolean link:{java8-javadoc}/java/lang/Float.html#isFinite%2Dfloat%2D[isFinite](float)++ (link:{java9-javadoc}/java/lang/Float.html#isFinite%2Dfloat%2D[java 9]) -* ++[[painless-api-reference-Float-isInfinite-1]]static boolean link:{java8-javadoc}/java/lang/Float.html#isInfinite%2Dfloat%2D[isInfinite](float)++ (link:{java9-javadoc}/java/lang/Float.html#isInfinite%2Dfloat%2D[java 9]) -* ++[[painless-api-reference-Float-isNaN-1]]static boolean link:{java8-javadoc}/java/lang/Float.html#isNaN%2Dfloat%2D[isNaN](float)++ (link:{java9-javadoc}/java/lang/Float.html#isNaN%2Dfloat%2D[java 9]) -* ++[[painless-api-reference-Float-max-2]]static float link:{java8-javadoc}/java/lang/Float.html#max%2Dfloat%2Dfloat%2D[max](float, float)++ (link:{java9-javadoc}/java/lang/Float.html#max%2Dfloat%2Dfloat%2D[java 9]) -* ++[[painless-api-reference-Float-min-2]]static float link:{java8-javadoc}/java/lang/Float.html#min%2Dfloat%2Dfloat%2D[min](float, float)++ (link:{java9-javadoc}/java/lang/Float.html#min%2Dfloat%2Dfloat%2D[java 9]) -* ++[[painless-api-reference-Float-parseFloat-1]]static float link:{java8-javadoc}/java/lang/Float.html#parseFloat%2Djava.lang.String%2D[parseFloat](<>)++ (link:{java9-javadoc}/java/lang/Float.html#parseFloat%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Float-sum-2]]static float link:{java8-javadoc}/java/lang/Float.html#sum%2Dfloat%2Dfloat%2D[sum](float, float)++ (link:{java9-javadoc}/java/lang/Float.html#sum%2Dfloat%2Dfloat%2D[java 9]) -* ++[[painless-api-reference-Float-toHexString-1]]static <> link:{java8-javadoc}/java/lang/Float.html#toHexString%2Dfloat%2D[toHexString](float)++ (link:{java9-javadoc}/java/lang/Float.html#toHexString%2Dfloat%2D[java 9]) -* ++[[painless-api-reference-Float-toString-1]]static <> link:{java8-javadoc}/java/lang/Float.html#toString%2Dfloat%2D[toString](float)++ (link:{java9-javadoc}/java/lang/Float.html#toString%2Dfloat%2D[java 9]) -* ++[[painless-api-reference-Float-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Float.html#valueOf%2Dfloat%2D[valueOf](float)++ (link:{java9-javadoc}/java/lang/Float.html#valueOf%2Dfloat%2D[java 9]) -* ++[[painless-api-reference-Float-compareTo-1]]int link:{java8-javadoc}/java/lang/Float.html#compareTo%2Djava.lang.Float%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Float.html#compareTo%2Djava.lang.Float%2D[java 9]) -* ++[[painless-api-reference-Float-isInfinite-0]]boolean link:{java8-javadoc}/java/lang/Float.html#isInfinite%2D%2D[isInfinite]()++ (link:{java9-javadoc}/java/lang/Float.html#isInfinite%2D%2D[java 9]) -* ++[[painless-api-reference-Float-isNaN-0]]boolean link:{java8-javadoc}/java/lang/Float.html#isNaN%2D%2D[isNaN]()++ (link:{java9-javadoc}/java/lang/Float.html#isNaN%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Format.Field.asciidoc b/docs/reference/painless-api-reference/Format.Field.asciidoc deleted file mode 100644 index 3ddf6ec6d2f15..0000000000000 --- a/docs/reference/painless-api-reference/Format.Field.asciidoc +++ /dev/null @@ -1,7 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Format-Field]]++Format.Field++:: -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Format.asciidoc b/docs/reference/painless-api-reference/Format.asciidoc deleted file mode 100644 index afa42a0598dce..0000000000000 --- a/docs/reference/painless-api-reference/Format.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Format]]++Format++:: -* ++[[painless-api-reference-Format-clone-0]]def link:{java8-javadoc}/java/text/Format.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/text/Format.html#clone%2D%2D[java 9]) -* ++[[painless-api-reference-Format-format-1]]<> link:{java8-javadoc}/java/text/Format.html#format%2Djava.lang.Object%2D[format](<>)++ (link:{java9-javadoc}/java/text/Format.html#format%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Format-format-3]]<> link:{java8-javadoc}/java/text/Format.html#format%2Djava.lang.Object%2Djava.lang.StringBuffer%2Djava.text.FieldPosition%2D[format](<>, <>, <>)++ (link:{java9-javadoc}/java/text/Format.html#format%2Djava.lang.Object%2Djava.lang.StringBuffer%2Djava.text.FieldPosition%2D[java 9]) -* ++[[painless-api-reference-Format-formatToCharacterIterator-1]]<> link:{java8-javadoc}/java/text/Format.html#formatToCharacterIterator%2Djava.lang.Object%2D[formatToCharacterIterator](<>)++ (link:{java9-javadoc}/java/text/Format.html#formatToCharacterIterator%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Format-parseObject-1]]<> link:{java8-javadoc}/java/text/Format.html#parseObject%2Djava.lang.String%2D[parseObject](<>)++ (link:{java9-javadoc}/java/text/Format.html#parseObject%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Format-parseObject-2]]<> link:{java8-javadoc}/java/text/Format.html#parseObject%2Djava.lang.String%2Djava.text.ParsePosition%2D[parseObject](<>, <>)++ (link:{java9-javadoc}/java/text/Format.html#parseObject%2Djava.lang.String%2Djava.text.ParsePosition%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/FormatFlagsConversionMismatchException.asciidoc b/docs/reference/painless-api-reference/FormatFlagsConversionMismatchException.asciidoc deleted file mode 100644 index b72d827b196e8..0000000000000 --- a/docs/reference/painless-api-reference/FormatFlagsConversionMismatchException.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-FormatFlagsConversionMismatchException]]++FormatFlagsConversionMismatchException++:: -* ++[[painless-api-reference-FormatFlagsConversionMismatchException-FormatFlagsConversionMismatchException-2]]link:{java8-javadoc}/java/util/FormatFlagsConversionMismatchException.html#FormatFlagsConversionMismatchException%2Djava.lang.String%2Dchar%2D[FormatFlagsConversionMismatchException](<>, char)++ (link:{java9-javadoc}/java/util/FormatFlagsConversionMismatchException.html#FormatFlagsConversionMismatchException%2Djava.lang.String%2Dchar%2D[java 9]) -* ++[[painless-api-reference-FormatFlagsConversionMismatchException-getConversion-0]]char link:{java8-javadoc}/java/util/FormatFlagsConversionMismatchException.html#getConversion%2D%2D[getConversion]()++ (link:{java9-javadoc}/java/util/FormatFlagsConversionMismatchException.html#getConversion%2D%2D[java 9]) -* ++[[painless-api-reference-FormatFlagsConversionMismatchException-getFlags-0]]<> link:{java8-javadoc}/java/util/FormatFlagsConversionMismatchException.html#getFlags%2D%2D[getFlags]()++ (link:{java9-javadoc}/java/util/FormatFlagsConversionMismatchException.html#getFlags%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/FormatStyle.asciidoc b/docs/reference/painless-api-reference/FormatStyle.asciidoc deleted file mode 100644 index 4db787c051ae6..0000000000000 --- a/docs/reference/painless-api-reference/FormatStyle.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-FormatStyle]]++FormatStyle++:: -** [[painless-api-reference-FormatStyle-FULL]]static <> link:{java8-javadoc}/java/time/format/FormatStyle.html#FULL[FULL] (link:{java9-javadoc}/java/time/format/FormatStyle.html#FULL[java 9]) -** [[painless-api-reference-FormatStyle-LONG]]static <> link:{java8-javadoc}/java/time/format/FormatStyle.html#LONG[LONG] (link:{java9-javadoc}/java/time/format/FormatStyle.html#LONG[java 9]) -** [[painless-api-reference-FormatStyle-MEDIUM]]static <> link:{java8-javadoc}/java/time/format/FormatStyle.html#MEDIUM[MEDIUM] (link:{java9-javadoc}/java/time/format/FormatStyle.html#MEDIUM[java 9]) -** [[painless-api-reference-FormatStyle-SHORT]]static <> link:{java8-javadoc}/java/time/format/FormatStyle.html#SHORT[SHORT] (link:{java9-javadoc}/java/time/format/FormatStyle.html#SHORT[java 9]) -* ++[[painless-api-reference-FormatStyle-valueOf-1]]static <> link:{java8-javadoc}/java/time/format/FormatStyle.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/format/FormatStyle.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-FormatStyle-values-0]]static <>[] link:{java8-javadoc}/java/time/format/FormatStyle.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/format/FormatStyle.html#values%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Formattable.asciidoc b/docs/reference/painless-api-reference/Formattable.asciidoc deleted file mode 100644 index ab70d1319e939..0000000000000 --- a/docs/reference/painless-api-reference/Formattable.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Formattable]]++Formattable++:: -* ++[[painless-api-reference-Formattable-formatTo-4]]void link:{java8-javadoc}/java/util/Formattable.html#formatTo%2Djava.util.Formatter%2Dint%2Dint%2Dint%2D[formatTo](<>, int, int, int)++ (link:{java9-javadoc}/java/util/Formattable.html#formatTo%2Djava.util.Formatter%2Dint%2Dint%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/FormattableFlags.asciidoc b/docs/reference/painless-api-reference/FormattableFlags.asciidoc deleted file mode 100644 index dec639fe70b3e..0000000000000 --- a/docs/reference/painless-api-reference/FormattableFlags.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-FormattableFlags]]++FormattableFlags++:: -** [[painless-api-reference-FormattableFlags-ALTERNATE]]static int link:{java8-javadoc}/java/util/FormattableFlags.html#ALTERNATE[ALTERNATE] (link:{java9-javadoc}/java/util/FormattableFlags.html#ALTERNATE[java 9]) -** [[painless-api-reference-FormattableFlags-LEFT_JUSTIFY]]static int link:{java8-javadoc}/java/util/FormattableFlags.html#LEFT_JUSTIFY[LEFT_JUSTIFY] (link:{java9-javadoc}/java/util/FormattableFlags.html#LEFT_JUSTIFY[java 9]) -** [[painless-api-reference-FormattableFlags-UPPERCASE]]static int link:{java8-javadoc}/java/util/FormattableFlags.html#UPPERCASE[UPPERCASE] (link:{java9-javadoc}/java/util/FormattableFlags.html#UPPERCASE[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Formatter.BigDecimalLayoutForm.asciidoc b/docs/reference/painless-api-reference/Formatter.BigDecimalLayoutForm.asciidoc deleted file mode 100644 index 92d8852e13c09..0000000000000 --- a/docs/reference/painless-api-reference/Formatter.BigDecimalLayoutForm.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Formatter-BigDecimalLayoutForm]]++Formatter.BigDecimalLayoutForm++:: -** [[painless-api-reference-Formatter-BigDecimalLayoutForm-DECIMAL_FLOAT]]static <> link:{java8-javadoc}/java/util/Formatter$BigDecimalLayoutForm.html#DECIMAL_FLOAT[DECIMAL_FLOAT] (link:{java9-javadoc}/java/util/Formatter$BigDecimalLayoutForm.html#DECIMAL_FLOAT[java 9]) -** [[painless-api-reference-Formatter-BigDecimalLayoutForm-SCIENTIFIC]]static <> link:{java8-javadoc}/java/util/Formatter$BigDecimalLayoutForm.html#SCIENTIFIC[SCIENTIFIC] (link:{java9-javadoc}/java/util/Formatter$BigDecimalLayoutForm.html#SCIENTIFIC[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Formatter.asciidoc b/docs/reference/painless-api-reference/Formatter.asciidoc deleted file mode 100644 index 4cad434f1308f..0000000000000 --- a/docs/reference/painless-api-reference/Formatter.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Formatter]]++Formatter++:: -* ++[[painless-api-reference-Formatter-Formatter-0]]link:{java8-javadoc}/java/util/Formatter.html#Formatter%2D%2D[Formatter]()++ (link:{java9-javadoc}/java/util/Formatter.html#Formatter%2D%2D[java 9]) -* ++[[painless-api-reference-Formatter-Formatter-1]]link:{java8-javadoc}/java/util/Formatter.html#Formatter%2Djava.lang.Appendable%2D[Formatter](<>)++ (link:{java9-javadoc}/java/util/Formatter.html#Formatter%2Djava.lang.Appendable%2D[java 9]) -* ++[[painless-api-reference-Formatter-Formatter-2]]link:{java8-javadoc}/java/util/Formatter.html#Formatter%2Djava.lang.Appendable%2Djava.util.Locale%2D[Formatter](<>, <>)++ (link:{java9-javadoc}/java/util/Formatter.html#Formatter%2Djava.lang.Appendable%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Formatter-format-2]]<> link:{java8-javadoc}/java/util/Formatter.html#format%2Djava.lang.String%2Djava.lang.Object:A%2D[format](<>, def[])++ (link:{java9-javadoc}/java/util/Formatter.html#format%2Djava.lang.String%2Djava.lang.Object:A%2D[java 9]) -* ++[[painless-api-reference-Formatter-format-3]]<> link:{java8-javadoc}/java/util/Formatter.html#format%2Djava.util.Locale%2Djava.lang.String%2Djava.lang.Object:A%2D[format](<>, <>, def[])++ (link:{java9-javadoc}/java/util/Formatter.html#format%2Djava.util.Locale%2Djava.lang.String%2Djava.lang.Object:A%2D[java 9]) -* ++[[painless-api-reference-Formatter-locale-0]]<> link:{java8-javadoc}/java/util/Formatter.html#locale%2D%2D[locale]()++ (link:{java9-javadoc}/java/util/Formatter.html#locale%2D%2D[java 9]) -* ++[[painless-api-reference-Formatter-out-0]]<> link:{java8-javadoc}/java/util/Formatter.html#out%2D%2D[out]()++ (link:{java9-javadoc}/java/util/Formatter.html#out%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/FormatterClosedException.asciidoc b/docs/reference/painless-api-reference/FormatterClosedException.asciidoc deleted file mode 100644 index 00c4f5971d335..0000000000000 --- a/docs/reference/painless-api-reference/FormatterClosedException.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-FormatterClosedException]]++FormatterClosedException++:: -* ++[[painless-api-reference-FormatterClosedException-FormatterClosedException-0]]link:{java8-javadoc}/java/util/FormatterClosedException.html#FormatterClosedException%2D%2D[FormatterClosedException]()++ (link:{java9-javadoc}/java/util/FormatterClosedException.html#FormatterClosedException%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Function.asciidoc b/docs/reference/painless-api-reference/Function.asciidoc deleted file mode 100644 index 61fe3b6b5da70..0000000000000 --- a/docs/reference/painless-api-reference/Function.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Function]]++Function++:: -* ++[[painless-api-reference-Function-identity-0]]static <> link:{java8-javadoc}/java/util/function/Function.html#identity%2D%2D[identity]()++ (link:{java9-javadoc}/java/util/function/Function.html#identity%2D%2D[java 9]) -* ++[[painless-api-reference-Function-andThen-1]]<> link:{java8-javadoc}/java/util/function/Function.html#andThen%2Djava.util.function.Function%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/Function.html#andThen%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-Function-apply-1]]def link:{java8-javadoc}/java/util/function/Function.html#apply%2Djava.lang.Object%2D[apply](def)++ (link:{java9-javadoc}/java/util/function/Function.html#apply%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Function-compose-1]]<> link:{java8-javadoc}/java/util/function/Function.html#compose%2Djava.util.function.Function%2D[compose](<>)++ (link:{java9-javadoc}/java/util/function/Function.html#compose%2Djava.util.function.Function%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/GregorianCalendar.asciidoc b/docs/reference/painless-api-reference/GregorianCalendar.asciidoc deleted file mode 100644 index 8f7b9ccecc7e6..0000000000000 --- a/docs/reference/painless-api-reference/GregorianCalendar.asciidoc +++ /dev/null @@ -1,20 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-GregorianCalendar]]++GregorianCalendar++:: -** [[painless-api-reference-GregorianCalendar-AD]]static int link:{java8-javadoc}/java/util/GregorianCalendar.html#AD[AD] (link:{java9-javadoc}/java/util/GregorianCalendar.html#AD[java 9]) -** [[painless-api-reference-GregorianCalendar-BC]]static int link:{java8-javadoc}/java/util/GregorianCalendar.html#BC[BC] (link:{java9-javadoc}/java/util/GregorianCalendar.html#BC[java 9]) -* ++[[painless-api-reference-GregorianCalendar-from-1]]static <> link:{java8-javadoc}/java/util/GregorianCalendar.html#from%2Djava.time.ZonedDateTime%2D[from](<>)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#from%2Djava.time.ZonedDateTime%2D[java 9]) -* ++[[painless-api-reference-GregorianCalendar-GregorianCalendar-0]]link:{java8-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2D%2D[GregorianCalendar]()++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2D%2D[java 9]) -* ++[[painless-api-reference-GregorianCalendar-GregorianCalendar-1]]link:{java8-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Djava.util.TimeZone%2D[GregorianCalendar](<>)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Djava.util.TimeZone%2D[java 9]) -* ++[[painless-api-reference-GregorianCalendar-GregorianCalendar-2]]link:{java8-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Djava.util.TimeZone%2Djava.util.Locale%2D[GregorianCalendar](<>, <>)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Djava.util.TimeZone%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-GregorianCalendar-GregorianCalendar-3]]link:{java8-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Dint%2Dint%2Dint%2D[GregorianCalendar](int, int, int)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-GregorianCalendar-GregorianCalendar-5]]link:{java8-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Dint%2Dint%2Dint%2Dint%2Dint%2D[GregorianCalendar](int, int, int, int, int)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-GregorianCalendar-GregorianCalendar-6]]link:{java8-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[GregorianCalendar](int, int, int, int, int, int)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-GregorianCalendar-getGregorianChange-0]]<> link:{java8-javadoc}/java/util/GregorianCalendar.html#getGregorianChange%2D%2D[getGregorianChange]()++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#getGregorianChange%2D%2D[java 9]) -* ++[[painless-api-reference-GregorianCalendar-isLeapYear-1]]boolean link:{java8-javadoc}/java/util/GregorianCalendar.html#isLeapYear%2Dint%2D[isLeapYear](int)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#isLeapYear%2Dint%2D[java 9]) -* ++[[painless-api-reference-GregorianCalendar-setGregorianChange-1]]void link:{java8-javadoc}/java/util/GregorianCalendar.html#setGregorianChange%2Djava.util.Date%2D[setGregorianChange](<>)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#setGregorianChange%2Djava.util.Date%2D[java 9]) -* ++[[painless-api-reference-GregorianCalendar-toZonedDateTime-0]]<> link:{java8-javadoc}/java/util/GregorianCalendar.html#toZonedDateTime%2D%2D[toZonedDateTime]()++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#toZonedDateTime%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/HashMap.asciidoc b/docs/reference/painless-api-reference/HashMap.asciidoc deleted file mode 100644 index f67e75d702f0c..0000000000000 --- a/docs/reference/painless-api-reference/HashMap.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-HashMap]]++HashMap++:: -* ++[[painless-api-reference-HashMap-HashMap-0]]link:{java8-javadoc}/java/util/HashMap.html#HashMap%2D%2D[HashMap]()++ (link:{java9-javadoc}/java/util/HashMap.html#HashMap%2D%2D[java 9]) -* ++[[painless-api-reference-HashMap-HashMap-1]]link:{java8-javadoc}/java/util/HashMap.html#HashMap%2Djava.util.Map%2D[HashMap](<>)++ (link:{java9-javadoc}/java/util/HashMap.html#HashMap%2Djava.util.Map%2D[java 9]) -* ++[[painless-api-reference-HashMap-clone-0]]def link:{java8-javadoc}/java/util/HashMap.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/HashMap.html#clone%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/HashSet.asciidoc b/docs/reference/painless-api-reference/HashSet.asciidoc deleted file mode 100644 index af4171def2262..0000000000000 --- a/docs/reference/painless-api-reference/HashSet.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-HashSet]]++HashSet++:: -* ++[[painless-api-reference-HashSet-HashSet-0]]link:{java8-javadoc}/java/util/HashSet.html#HashSet%2D%2D[HashSet]()++ (link:{java9-javadoc}/java/util/HashSet.html#HashSet%2D%2D[java 9]) -* ++[[painless-api-reference-HashSet-HashSet-1]]link:{java8-javadoc}/java/util/HashSet.html#HashSet%2Djava.util.Collection%2D[HashSet](<>)++ (link:{java9-javadoc}/java/util/HashSet.html#HashSet%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-HashSet-clone-0]]def link:{java8-javadoc}/java/util/HashSet.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/HashSet.html#clone%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Hashtable.asciidoc b/docs/reference/painless-api-reference/Hashtable.asciidoc deleted file mode 100644 index 34702e8466065..0000000000000 --- a/docs/reference/painless-api-reference/Hashtable.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Hashtable]]++Hashtable++:: -* ++[[painless-api-reference-Hashtable-Hashtable-0]]link:{java8-javadoc}/java/util/Hashtable.html#Hashtable%2D%2D[Hashtable]()++ (link:{java9-javadoc}/java/util/Hashtable.html#Hashtable%2D%2D[java 9]) -* ++[[painless-api-reference-Hashtable-Hashtable-1]]link:{java8-javadoc}/java/util/Hashtable.html#Hashtable%2Djava.util.Map%2D[Hashtable](<>)++ (link:{java9-javadoc}/java/util/Hashtable.html#Hashtable%2Djava.util.Map%2D[java 9]) -* ++[[painless-api-reference-Hashtable-clone-0]]def link:{java8-javadoc}/java/util/Hashtable.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/Hashtable.html#clone%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/HijrahChronology.asciidoc b/docs/reference/painless-api-reference/HijrahChronology.asciidoc deleted file mode 100644 index b7577fbdfd720..0000000000000 --- a/docs/reference/painless-api-reference/HijrahChronology.asciidoc +++ /dev/null @@ -1,16 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-HijrahChronology]]++HijrahChronology++:: -** [[painless-api-reference-HijrahChronology-INSTANCE]]static <> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#INSTANCE[INSTANCE] (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#INSTANCE[java 9]) -* ++[[painless-api-reference-HijrahChronology-date-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[date](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-HijrahChronology-date-3]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#date%2Dint%2Dint%2Dint%2D[date](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#date%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-HijrahChronology-date-4]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[date](<>, int, int, int)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-HijrahChronology-dateEpochDay-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#dateEpochDay%2Dlong%2D[dateEpochDay](long)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#dateEpochDay%2Dlong%2D[java 9]) -* ++[[painless-api-reference-HijrahChronology-dateYearDay-2]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#dateYearDay%2Dint%2Dint%2D[dateYearDay](int, int)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#dateYearDay%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-HijrahChronology-dateYearDay-3]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[dateYearDay](<>, int, int)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-HijrahChronology-eraOf-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#eraOf%2Dint%2D[eraOf](int)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#eraOf%2Dint%2D[java 9]) -* ++[[painless-api-reference-HijrahChronology-resolveDate-2]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[resolveDate](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/HijrahDate.asciidoc b/docs/reference/painless-api-reference/HijrahDate.asciidoc deleted file mode 100644 index 09ebf31682a13..0000000000000 --- a/docs/reference/painless-api-reference/HijrahDate.asciidoc +++ /dev/null @@ -1,18 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-HijrahDate]]++HijrahDate++:: -* ++[[painless-api-reference-HijrahDate-from-1]]static <> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-HijrahDate-of-3]]static <> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#of%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-HijrahDate-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#getChronology%2D%2D[java 9]) -* ++[[painless-api-reference-HijrahDate-getEra-0]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#getEra%2D%2D[getEra]()++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#getEra%2D%2D[java 9]) -* ++[[painless-api-reference-HijrahDate-minus-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-HijrahDate-minus-2]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-HijrahDate-plus-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-HijrahDate-plus-2]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-HijrahDate-with-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-HijrahDate-with-2]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* ++[[painless-api-reference-HijrahDate-withVariant-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#withVariant%2Djava.time.chrono.HijrahChronology%2D[withVariant](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#withVariant%2Djava.time.chrono.HijrahChronology%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/HijrahEra.asciidoc b/docs/reference/painless-api-reference/HijrahEra.asciidoc deleted file mode 100644 index 9bfe561d2fa58..0000000000000 --- a/docs/reference/painless-api-reference/HijrahEra.asciidoc +++ /dev/null @@ -1,12 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-HijrahEra]]++HijrahEra++:: -** [[painless-api-reference-HijrahEra-AH]]static <> link:{java8-javadoc}/java/time/chrono/HijrahEra.html#AH[AH] (link:{java9-javadoc}/java/time/chrono/HijrahEra.html#AH[java 9]) -* ++[[painless-api-reference-HijrahEra-of-1]]static <> link:{java8-javadoc}/java/time/chrono/HijrahEra.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/chrono/HijrahEra.html#of%2Dint%2D[java 9]) -* ++[[painless-api-reference-HijrahEra-valueOf-1]]static <> link:{java8-javadoc}/java/time/chrono/HijrahEra.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahEra.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-HijrahEra-values-0]]static <>[] link:{java8-javadoc}/java/time/chrono/HijrahEra.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/chrono/HijrahEra.html#values%2D%2D[java 9]) -* ++[[painless-api-reference-HijrahEra-getValue-0]]int link:{java8-javadoc}/java/time/chrono/HijrahEra.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/chrono/HijrahEra.html#getValue%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IdentityHashMap.asciidoc b/docs/reference/painless-api-reference/IdentityHashMap.asciidoc deleted file mode 100644 index 050d0eaeb1c8e..0000000000000 --- a/docs/reference/painless-api-reference/IdentityHashMap.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IdentityHashMap]]++IdentityHashMap++:: -* ++[[painless-api-reference-IdentityHashMap-IdentityHashMap-0]]link:{java8-javadoc}/java/util/IdentityHashMap.html#IdentityHashMap%2D%2D[IdentityHashMap]()++ (link:{java9-javadoc}/java/util/IdentityHashMap.html#IdentityHashMap%2D%2D[java 9]) -* ++[[painless-api-reference-IdentityHashMap-IdentityHashMap-1]]link:{java8-javadoc}/java/util/IdentityHashMap.html#IdentityHashMap%2Djava.util.Map%2D[IdentityHashMap](<>)++ (link:{java9-javadoc}/java/util/IdentityHashMap.html#IdentityHashMap%2Djava.util.Map%2D[java 9]) -* ++[[painless-api-reference-IdentityHashMap-clone-0]]def link:{java8-javadoc}/java/util/IdentityHashMap.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/IdentityHashMap.html#clone%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IllegalAccessException.asciidoc b/docs/reference/painless-api-reference/IllegalAccessException.asciidoc deleted file mode 100644 index 30c8ba4536d02..0000000000000 --- a/docs/reference/painless-api-reference/IllegalAccessException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IllegalAccessException]]++IllegalAccessException++:: -* ++[[painless-api-reference-IllegalAccessException-IllegalAccessException-0]]link:{java8-javadoc}/java/lang/IllegalAccessException.html#IllegalAccessException%2D%2D[IllegalAccessException]()++ (link:{java9-javadoc}/java/lang/IllegalAccessException.html#IllegalAccessException%2D%2D[java 9]) -* ++[[painless-api-reference-IllegalAccessException-IllegalAccessException-1]]link:{java8-javadoc}/java/lang/IllegalAccessException.html#IllegalAccessException%2Djava.lang.String%2D[IllegalAccessException](<>)++ (link:{java9-javadoc}/java/lang/IllegalAccessException.html#IllegalAccessException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IllegalArgumentException.asciidoc b/docs/reference/painless-api-reference/IllegalArgumentException.asciidoc deleted file mode 100644 index 538fa46a656e2..0000000000000 --- a/docs/reference/painless-api-reference/IllegalArgumentException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IllegalArgumentException]]++IllegalArgumentException++:: -* ++[[painless-api-reference-IllegalArgumentException-IllegalArgumentException-0]]link:{java8-javadoc}/java/lang/IllegalArgumentException.html#IllegalArgumentException%2D%2D[IllegalArgumentException]()++ (link:{java9-javadoc}/java/lang/IllegalArgumentException.html#IllegalArgumentException%2D%2D[java 9]) -* ++[[painless-api-reference-IllegalArgumentException-IllegalArgumentException-1]]link:{java8-javadoc}/java/lang/IllegalArgumentException.html#IllegalArgumentException%2Djava.lang.String%2D[IllegalArgumentException](<>)++ (link:{java9-javadoc}/java/lang/IllegalArgumentException.html#IllegalArgumentException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IllegalFormatCodePointException.asciidoc b/docs/reference/painless-api-reference/IllegalFormatCodePointException.asciidoc deleted file mode 100644 index 2a533384198d1..0000000000000 --- a/docs/reference/painless-api-reference/IllegalFormatCodePointException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IllegalFormatCodePointException]]++IllegalFormatCodePointException++:: -* ++[[painless-api-reference-IllegalFormatCodePointException-IllegalFormatCodePointException-1]]link:{java8-javadoc}/java/util/IllegalFormatCodePointException.html#IllegalFormatCodePointException%2Dint%2D[IllegalFormatCodePointException](int)++ (link:{java9-javadoc}/java/util/IllegalFormatCodePointException.html#IllegalFormatCodePointException%2Dint%2D[java 9]) -* ++[[painless-api-reference-IllegalFormatCodePointException-getCodePoint-0]]int link:{java8-javadoc}/java/util/IllegalFormatCodePointException.html#getCodePoint%2D%2D[getCodePoint]()++ (link:{java9-javadoc}/java/util/IllegalFormatCodePointException.html#getCodePoint%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IllegalFormatConversionException.asciidoc b/docs/reference/painless-api-reference/IllegalFormatConversionException.asciidoc deleted file mode 100644 index 2a36b5367a339..0000000000000 --- a/docs/reference/painless-api-reference/IllegalFormatConversionException.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IllegalFormatConversionException]]++IllegalFormatConversionException++:: -* ++[[painless-api-reference-IllegalFormatConversionException-getConversion-0]]char link:{java8-javadoc}/java/util/IllegalFormatConversionException.html#getConversion%2D%2D[getConversion]()++ (link:{java9-javadoc}/java/util/IllegalFormatConversionException.html#getConversion%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IllegalFormatException.asciidoc b/docs/reference/painless-api-reference/IllegalFormatException.asciidoc deleted file mode 100644 index 98a58ceddd265..0000000000000 --- a/docs/reference/painless-api-reference/IllegalFormatException.asciidoc +++ /dev/null @@ -1,7 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IllegalFormatException]]++IllegalFormatException++:: -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IllegalFormatFlagsException.asciidoc b/docs/reference/painless-api-reference/IllegalFormatFlagsException.asciidoc deleted file mode 100644 index 9ed7810f0e60f..0000000000000 --- a/docs/reference/painless-api-reference/IllegalFormatFlagsException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IllegalFormatFlagsException]]++IllegalFormatFlagsException++:: -* ++[[painless-api-reference-IllegalFormatFlagsException-IllegalFormatFlagsException-1]]link:{java8-javadoc}/java/util/IllegalFormatFlagsException.html#IllegalFormatFlagsException%2Djava.lang.String%2D[IllegalFormatFlagsException](<>)++ (link:{java9-javadoc}/java/util/IllegalFormatFlagsException.html#IllegalFormatFlagsException%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-IllegalFormatFlagsException-getFlags-0]]<> link:{java8-javadoc}/java/util/IllegalFormatFlagsException.html#getFlags%2D%2D[getFlags]()++ (link:{java9-javadoc}/java/util/IllegalFormatFlagsException.html#getFlags%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IllegalFormatPrecisionException.asciidoc b/docs/reference/painless-api-reference/IllegalFormatPrecisionException.asciidoc deleted file mode 100644 index 318b80dcfaa6c..0000000000000 --- a/docs/reference/painless-api-reference/IllegalFormatPrecisionException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IllegalFormatPrecisionException]]++IllegalFormatPrecisionException++:: -* ++[[painless-api-reference-IllegalFormatPrecisionException-IllegalFormatPrecisionException-1]]link:{java8-javadoc}/java/util/IllegalFormatPrecisionException.html#IllegalFormatPrecisionException%2Dint%2D[IllegalFormatPrecisionException](int)++ (link:{java9-javadoc}/java/util/IllegalFormatPrecisionException.html#IllegalFormatPrecisionException%2Dint%2D[java 9]) -* ++[[painless-api-reference-IllegalFormatPrecisionException-getPrecision-0]]int link:{java8-javadoc}/java/util/IllegalFormatPrecisionException.html#getPrecision%2D%2D[getPrecision]()++ (link:{java9-javadoc}/java/util/IllegalFormatPrecisionException.html#getPrecision%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IllegalFormatWidthException.asciidoc b/docs/reference/painless-api-reference/IllegalFormatWidthException.asciidoc deleted file mode 100644 index 96f008b4a5334..0000000000000 --- a/docs/reference/painless-api-reference/IllegalFormatWidthException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IllegalFormatWidthException]]++IllegalFormatWidthException++:: -* ++[[painless-api-reference-IllegalFormatWidthException-IllegalFormatWidthException-1]]link:{java8-javadoc}/java/util/IllegalFormatWidthException.html#IllegalFormatWidthException%2Dint%2D[IllegalFormatWidthException](int)++ (link:{java9-javadoc}/java/util/IllegalFormatWidthException.html#IllegalFormatWidthException%2Dint%2D[java 9]) -* ++[[painless-api-reference-IllegalFormatWidthException-getWidth-0]]int link:{java8-javadoc}/java/util/IllegalFormatWidthException.html#getWidth%2D%2D[getWidth]()++ (link:{java9-javadoc}/java/util/IllegalFormatWidthException.html#getWidth%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IllegalMonitorStateException.asciidoc b/docs/reference/painless-api-reference/IllegalMonitorStateException.asciidoc deleted file mode 100644 index 057c58f948b73..0000000000000 --- a/docs/reference/painless-api-reference/IllegalMonitorStateException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IllegalMonitorStateException]]++IllegalMonitorStateException++:: -* ++[[painless-api-reference-IllegalMonitorStateException-IllegalMonitorStateException-0]]link:{java8-javadoc}/java/lang/IllegalMonitorStateException.html#IllegalMonitorStateException%2D%2D[IllegalMonitorStateException]()++ (link:{java9-javadoc}/java/lang/IllegalMonitorStateException.html#IllegalMonitorStateException%2D%2D[java 9]) -* ++[[painless-api-reference-IllegalMonitorStateException-IllegalMonitorStateException-1]]link:{java8-javadoc}/java/lang/IllegalMonitorStateException.html#IllegalMonitorStateException%2Djava.lang.String%2D[IllegalMonitorStateException](<>)++ (link:{java9-javadoc}/java/lang/IllegalMonitorStateException.html#IllegalMonitorStateException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IllegalStateException.asciidoc b/docs/reference/painless-api-reference/IllegalStateException.asciidoc deleted file mode 100644 index fa8ddf72fa5bd..0000000000000 --- a/docs/reference/painless-api-reference/IllegalStateException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IllegalStateException]]++IllegalStateException++:: -* ++[[painless-api-reference-IllegalStateException-IllegalStateException-0]]link:{java8-javadoc}/java/lang/IllegalStateException.html#IllegalStateException%2D%2D[IllegalStateException]()++ (link:{java9-javadoc}/java/lang/IllegalStateException.html#IllegalStateException%2D%2D[java 9]) -* ++[[painless-api-reference-IllegalStateException-IllegalStateException-1]]link:{java8-javadoc}/java/lang/IllegalStateException.html#IllegalStateException%2Djava.lang.String%2D[IllegalStateException](<>)++ (link:{java9-javadoc}/java/lang/IllegalStateException.html#IllegalStateException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IllegalThreadStateException.asciidoc b/docs/reference/painless-api-reference/IllegalThreadStateException.asciidoc deleted file mode 100644 index 5fc8293fe0933..0000000000000 --- a/docs/reference/painless-api-reference/IllegalThreadStateException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IllegalThreadStateException]]++IllegalThreadStateException++:: -* ++[[painless-api-reference-IllegalThreadStateException-IllegalThreadStateException-0]]link:{java8-javadoc}/java/lang/IllegalThreadStateException.html#IllegalThreadStateException%2D%2D[IllegalThreadStateException]()++ (link:{java9-javadoc}/java/lang/IllegalThreadStateException.html#IllegalThreadStateException%2D%2D[java 9]) -* ++[[painless-api-reference-IllegalThreadStateException-IllegalThreadStateException-1]]link:{java8-javadoc}/java/lang/IllegalThreadStateException.html#IllegalThreadStateException%2Djava.lang.String%2D[IllegalThreadStateException](<>)++ (link:{java9-javadoc}/java/lang/IllegalThreadStateException.html#IllegalThreadStateException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IllformedLocaleException.asciidoc b/docs/reference/painless-api-reference/IllformedLocaleException.asciidoc deleted file mode 100644 index 59a814d9520dd..0000000000000 --- a/docs/reference/painless-api-reference/IllformedLocaleException.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IllformedLocaleException]]++IllformedLocaleException++:: -* ++[[painless-api-reference-IllformedLocaleException-IllformedLocaleException-0]]link:{java8-javadoc}/java/util/IllformedLocaleException.html#IllformedLocaleException%2D%2D[IllformedLocaleException]()++ (link:{java9-javadoc}/java/util/IllformedLocaleException.html#IllformedLocaleException%2D%2D[java 9]) -* ++[[painless-api-reference-IllformedLocaleException-IllformedLocaleException-1]]link:{java8-javadoc}/java/util/IllformedLocaleException.html#IllformedLocaleException%2Djava.lang.String%2D[IllformedLocaleException](<>)++ (link:{java9-javadoc}/java/util/IllformedLocaleException.html#IllformedLocaleException%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-IllformedLocaleException-IllformedLocaleException-2]]link:{java8-javadoc}/java/util/IllformedLocaleException.html#IllformedLocaleException%2Djava.lang.String%2Dint%2D[IllformedLocaleException](<>, int)++ (link:{java9-javadoc}/java/util/IllformedLocaleException.html#IllformedLocaleException%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-IllformedLocaleException-getErrorIndex-0]]int link:{java8-javadoc}/java/util/IllformedLocaleException.html#getErrorIndex%2D%2D[getErrorIndex]()++ (link:{java9-javadoc}/java/util/IllformedLocaleException.html#getErrorIndex%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IndexOutOfBoundsException.asciidoc b/docs/reference/painless-api-reference/IndexOutOfBoundsException.asciidoc deleted file mode 100644 index a141ec6b04152..0000000000000 --- a/docs/reference/painless-api-reference/IndexOutOfBoundsException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IndexOutOfBoundsException]]++IndexOutOfBoundsException++:: -* ++[[painless-api-reference-IndexOutOfBoundsException-IndexOutOfBoundsException-0]]link:{java8-javadoc}/java/lang/IndexOutOfBoundsException.html#IndexOutOfBoundsException%2D%2D[IndexOutOfBoundsException]()++ (link:{java9-javadoc}/java/lang/IndexOutOfBoundsException.html#IndexOutOfBoundsException%2D%2D[java 9]) -* ++[[painless-api-reference-IndexOutOfBoundsException-IndexOutOfBoundsException-1]]link:{java8-javadoc}/java/lang/IndexOutOfBoundsException.html#IndexOutOfBoundsException%2Djava.lang.String%2D[IndexOutOfBoundsException](<>)++ (link:{java9-javadoc}/java/lang/IndexOutOfBoundsException.html#IndexOutOfBoundsException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/InputMismatchException.asciidoc b/docs/reference/painless-api-reference/InputMismatchException.asciidoc deleted file mode 100644 index 9497cf9486e03..0000000000000 --- a/docs/reference/painless-api-reference/InputMismatchException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-InputMismatchException]]++InputMismatchException++:: -* ++[[painless-api-reference-InputMismatchException-InputMismatchException-0]]link:{java8-javadoc}/java/util/InputMismatchException.html#InputMismatchException%2D%2D[InputMismatchException]()++ (link:{java9-javadoc}/java/util/InputMismatchException.html#InputMismatchException%2D%2D[java 9]) -* ++[[painless-api-reference-InputMismatchException-InputMismatchException-1]]link:{java8-javadoc}/java/util/InputMismatchException.html#InputMismatchException%2Djava.lang.String%2D[InputMismatchException](<>)++ (link:{java9-javadoc}/java/util/InputMismatchException.html#InputMismatchException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Instant.asciidoc b/docs/reference/painless-api-reference/Instant.asciidoc deleted file mode 100644 index 4a7ffe45d991c..0000000000000 --- a/docs/reference/painless-api-reference/Instant.asciidoc +++ /dev/null @@ -1,36 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Instant]]++Instant++:: -** [[painless-api-reference-Instant-EPOCH]]static <> link:{java8-javadoc}/java/time/Instant.html#EPOCH[EPOCH] (link:{java9-javadoc}/java/time/Instant.html#EPOCH[java 9]) -** [[painless-api-reference-Instant-MAX]]static <> link:{java8-javadoc}/java/time/Instant.html#MAX[MAX] (link:{java9-javadoc}/java/time/Instant.html#MAX[java 9]) -** [[painless-api-reference-Instant-MIN]]static <> link:{java8-javadoc}/java/time/Instant.html#MIN[MIN] (link:{java9-javadoc}/java/time/Instant.html#MIN[java 9]) -* ++[[painless-api-reference-Instant-from-1]]static <> link:{java8-javadoc}/java/time/Instant.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/Instant.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-Instant-ofEpochMilli-1]]static <> link:{java8-javadoc}/java/time/Instant.html#ofEpochMilli%2Dlong%2D[ofEpochMilli](long)++ (link:{java9-javadoc}/java/time/Instant.html#ofEpochMilli%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Instant-ofEpochSecond-1]]static <> link:{java8-javadoc}/java/time/Instant.html#ofEpochSecond%2Dlong%2D[ofEpochSecond](long)++ (link:{java9-javadoc}/java/time/Instant.html#ofEpochSecond%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Instant-ofEpochSecond-2]]static <> link:{java8-javadoc}/java/time/Instant.html#ofEpochSecond%2Dlong%2Dlong%2D[ofEpochSecond](long, long)++ (link:{java9-javadoc}/java/time/Instant.html#ofEpochSecond%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Instant-parse-1]]static <> link:{java8-javadoc}/java/time/Instant.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/Instant.html#parse%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-Instant-atOffset-1]]<> link:{java8-javadoc}/java/time/Instant.html#atOffset%2Djava.time.ZoneOffset%2D[atOffset](<>)++ (link:{java9-javadoc}/java/time/Instant.html#atOffset%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-Instant-atZone-1]]<> link:{java8-javadoc}/java/time/Instant.html#atZone%2Djava.time.ZoneId%2D[atZone](<>)++ (link:{java9-javadoc}/java/time/Instant.html#atZone%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-Instant-compareTo-1]]int link:{java8-javadoc}/java/time/Instant.html#compareTo%2Djava.time.Instant%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/Instant.html#compareTo%2Djava.time.Instant%2D[java 9]) -* ++[[painless-api-reference-Instant-getEpochSecond-0]]long link:{java8-javadoc}/java/time/Instant.html#getEpochSecond%2D%2D[getEpochSecond]()++ (link:{java9-javadoc}/java/time/Instant.html#getEpochSecond%2D%2D[java 9]) -* ++[[painless-api-reference-Instant-getNano-0]]int link:{java8-javadoc}/java/time/Instant.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/Instant.html#getNano%2D%2D[java 9]) -* ++[[painless-api-reference-Instant-isAfter-1]]boolean link:{java8-javadoc}/java/time/Instant.html#isAfter%2Djava.time.Instant%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/Instant.html#isAfter%2Djava.time.Instant%2D[java 9]) -* ++[[painless-api-reference-Instant-isBefore-1]]boolean link:{java8-javadoc}/java/time/Instant.html#isBefore%2Djava.time.Instant%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/Instant.html#isBefore%2Djava.time.Instant%2D[java 9]) -* ++[[painless-api-reference-Instant-minus-1]]<> link:{java8-javadoc}/java/time/Instant.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/Instant.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-Instant-minus-2]]<> link:{java8-javadoc}/java/time/Instant.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/Instant.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-Instant-minusMillis-1]]<> link:{java8-javadoc}/java/time/Instant.html#minusMillis%2Dlong%2D[minusMillis](long)++ (link:{java9-javadoc}/java/time/Instant.html#minusMillis%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Instant-minusNanos-1]]<> link:{java8-javadoc}/java/time/Instant.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/Instant.html#minusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Instant-minusSeconds-1]]<> link:{java8-javadoc}/java/time/Instant.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/Instant.html#minusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Instant-plus-1]]<> link:{java8-javadoc}/java/time/Instant.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/Instant.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-Instant-plus-2]]<> link:{java8-javadoc}/java/time/Instant.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/Instant.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-Instant-plusMillis-1]]<> link:{java8-javadoc}/java/time/Instant.html#plusMillis%2Dlong%2D[plusMillis](long)++ (link:{java9-javadoc}/java/time/Instant.html#plusMillis%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Instant-plusNanos-1]]<> link:{java8-javadoc}/java/time/Instant.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/Instant.html#plusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Instant-plusSeconds-1]]<> link:{java8-javadoc}/java/time/Instant.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/Instant.html#plusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Instant-toEpochMilli-0]]long link:{java8-javadoc}/java/time/Instant.html#toEpochMilli%2D%2D[toEpochMilli]()++ (link:{java9-javadoc}/java/time/Instant.html#toEpochMilli%2D%2D[java 9]) -* ++[[painless-api-reference-Instant-truncatedTo-1]]<> link:{java8-javadoc}/java/time/Instant.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[truncatedTo](<>)++ (link:{java9-javadoc}/java/time/Instant.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-Instant-with-1]]<> link:{java8-javadoc}/java/time/Instant.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/Instant.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-Instant-with-2]]<> link:{java8-javadoc}/java/time/Instant.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/Instant.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/InstantiationException.asciidoc b/docs/reference/painless-api-reference/InstantiationException.asciidoc deleted file mode 100644 index 06d88eb654f00..0000000000000 --- a/docs/reference/painless-api-reference/InstantiationException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-InstantiationException]]++InstantiationException++:: -* ++[[painless-api-reference-InstantiationException-InstantiationException-0]]link:{java8-javadoc}/java/lang/InstantiationException.html#InstantiationException%2D%2D[InstantiationException]()++ (link:{java9-javadoc}/java/lang/InstantiationException.html#InstantiationException%2D%2D[java 9]) -* ++[[painless-api-reference-InstantiationException-InstantiationException-1]]link:{java8-javadoc}/java/lang/InstantiationException.html#InstantiationException%2Djava.lang.String%2D[InstantiationException](<>)++ (link:{java9-javadoc}/java/lang/InstantiationException.html#InstantiationException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IntBinaryOperator.asciidoc b/docs/reference/painless-api-reference/IntBinaryOperator.asciidoc deleted file mode 100644 index ca3c64ab3d898..0000000000000 --- a/docs/reference/painless-api-reference/IntBinaryOperator.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IntBinaryOperator]]++IntBinaryOperator++:: -* ++[[painless-api-reference-IntBinaryOperator-applyAsInt-2]]int link:{java8-javadoc}/java/util/function/IntBinaryOperator.html#applyAsInt%2Dint%2Dint%2D[applyAsInt](int, int)++ (link:{java9-javadoc}/java/util/function/IntBinaryOperator.html#applyAsInt%2Dint%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/IntConsumer.asciidoc b/docs/reference/painless-api-reference/IntConsumer.asciidoc deleted file mode 100644 index e8325838dac51..0000000000000 --- a/docs/reference/painless-api-reference/IntConsumer.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IntConsumer]]++IntConsumer++:: -* ++[[painless-api-reference-IntConsumer-accept-1]]void link:{java8-javadoc}/java/util/function/IntConsumer.html#accept%2Dint%2D[accept](int)++ (link:{java9-javadoc}/java/util/function/IntConsumer.html#accept%2Dint%2D[java 9]) -* ++[[painless-api-reference-IntConsumer-andThen-1]]<> link:{java8-javadoc}/java/util/function/IntConsumer.html#andThen%2Djava.util.function.IntConsumer%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/IntConsumer.html#andThen%2Djava.util.function.IntConsumer%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/IntFunction.asciidoc b/docs/reference/painless-api-reference/IntFunction.asciidoc deleted file mode 100644 index 336c1f7c3cb70..0000000000000 --- a/docs/reference/painless-api-reference/IntFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IntFunction]]++IntFunction++:: -* ++[[painless-api-reference-IntFunction-apply-1]]def link:{java8-javadoc}/java/util/function/IntFunction.html#apply%2Dint%2D[apply](int)++ (link:{java9-javadoc}/java/util/function/IntFunction.html#apply%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/IntPredicate.asciidoc b/docs/reference/painless-api-reference/IntPredicate.asciidoc deleted file mode 100644 index a9c45c95af855..0000000000000 --- a/docs/reference/painless-api-reference/IntPredicate.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IntPredicate]]++IntPredicate++:: -* ++[[painless-api-reference-IntPredicate-and-1]]<> link:{java8-javadoc}/java/util/function/IntPredicate.html#and%2Djava.util.function.IntPredicate%2D[and](<>)++ (link:{java9-javadoc}/java/util/function/IntPredicate.html#and%2Djava.util.function.IntPredicate%2D[java 9]) -* ++[[painless-api-reference-IntPredicate-negate-0]]<> link:{java8-javadoc}/java/util/function/IntPredicate.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/util/function/IntPredicate.html#negate%2D%2D[java 9]) -* ++[[painless-api-reference-IntPredicate-or-1]]<> link:{java8-javadoc}/java/util/function/IntPredicate.html#or%2Djava.util.function.IntPredicate%2D[or](<>)++ (link:{java9-javadoc}/java/util/function/IntPredicate.html#or%2Djava.util.function.IntPredicate%2D[java 9]) -* ++[[painless-api-reference-IntPredicate-test-1]]boolean link:{java8-javadoc}/java/util/function/IntPredicate.html#test%2Dint%2D[test](int)++ (link:{java9-javadoc}/java/util/function/IntPredicate.html#test%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/IntStream.Builder.asciidoc b/docs/reference/painless-api-reference/IntStream.Builder.asciidoc deleted file mode 100644 index d99913881733a..0000000000000 --- a/docs/reference/painless-api-reference/IntStream.Builder.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IntStream-Builder]]++IntStream.Builder++:: -* ++[[painless-api-reference-IntStream-Builder-add-1]]<> link:{java8-javadoc}/java/util/stream/IntStream$Builder.html#add%2Dint%2D[add](int)++ (link:{java9-javadoc}/java/util/stream/IntStream$Builder.html#add%2Dint%2D[java 9]) -* ++[[painless-api-reference-IntStream-Builder-build-0]]<> link:{java8-javadoc}/java/util/stream/IntStream$Builder.html#build%2D%2D[build]()++ (link:{java9-javadoc}/java/util/stream/IntStream$Builder.html#build%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IntStream.asciidoc b/docs/reference/painless-api-reference/IntStream.asciidoc deleted file mode 100644 index 10e1149268986..0000000000000 --- a/docs/reference/painless-api-reference/IntStream.asciidoc +++ /dev/null @@ -1,47 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IntStream]]++IntStream++:: -* ++[[painless-api-reference-IntStream-builder-0]]static <> link:{java8-javadoc}/java/util/stream/IntStream.html#builder%2D%2D[builder]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#builder%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-concat-2]]static <> link:{java8-javadoc}/java/util/stream/IntStream.html#concat%2Djava.util.stream.IntStream%2Djava.util.stream.IntStream%2D[concat](<>, <>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#concat%2Djava.util.stream.IntStream%2Djava.util.stream.IntStream%2D[java 9]) -* ++[[painless-api-reference-IntStream-empty-0]]static <> link:{java8-javadoc}/java/util/stream/IntStream.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#empty%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-of-1]]static <> link:{java8-javadoc}/java/util/stream/IntStream.html#of%2Dint:A%2D[of](int[])++ (link:{java9-javadoc}/java/util/stream/IntStream.html#of%2Dint:A%2D[java 9]) -* ++[[painless-api-reference-IntStream-range-2]]static <> link:{java8-javadoc}/java/util/stream/IntStream.html#range%2Dint%2Dint%2D[range](int, int)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#range%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-IntStream-rangeClosed-2]]static <> link:{java8-javadoc}/java/util/stream/IntStream.html#rangeClosed%2Dint%2Dint%2D[rangeClosed](int, int)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#rangeClosed%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-IntStream-allMatch-1]]boolean link:{java8-javadoc}/java/util/stream/IntStream.html#allMatch%2Djava.util.function.IntPredicate%2D[allMatch](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#allMatch%2Djava.util.function.IntPredicate%2D[java 9]) -* ++[[painless-api-reference-IntStream-anyMatch-1]]boolean link:{java8-javadoc}/java/util/stream/IntStream.html#anyMatch%2Djava.util.function.IntPredicate%2D[anyMatch](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#anyMatch%2Djava.util.function.IntPredicate%2D[java 9]) -* ++[[painless-api-reference-IntStream-asDoubleStream-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#asDoubleStream%2D%2D[asDoubleStream]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#asDoubleStream%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-asLongStream-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#asLongStream%2D%2D[asLongStream]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#asLongStream%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-average-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#average%2D%2D[average]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#average%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-boxed-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#boxed%2D%2D[boxed]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#boxed%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-collect-3]]def link:{java8-javadoc}/java/util/stream/IntStream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.ObjIntConsumer%2Djava.util.function.BiConsumer%2D[collect](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.ObjIntConsumer%2Djava.util.function.BiConsumer%2D[java 9]) -* ++[[painless-api-reference-IntStream-count-0]]long link:{java8-javadoc}/java/util/stream/IntStream.html#count%2D%2D[count]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#count%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-distinct-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#distinct%2D%2D[distinct]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#distinct%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-filter-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#filter%2Djava.util.function.IntPredicate%2D[filter](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#filter%2Djava.util.function.IntPredicate%2D[java 9]) -* ++[[painless-api-reference-IntStream-findAny-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#findAny%2D%2D[findAny]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#findAny%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-findFirst-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#findFirst%2D%2D[findFirst]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#findFirst%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-flatMap-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#flatMap%2Djava.util.function.IntFunction%2D[flatMap](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#flatMap%2Djava.util.function.IntFunction%2D[java 9]) -* ++[[painless-api-reference-IntStream-forEach-1]]void link:{java8-javadoc}/java/util/stream/IntStream.html#forEach%2Djava.util.function.IntConsumer%2D[forEach](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#forEach%2Djava.util.function.IntConsumer%2D[java 9]) -* ++[[painless-api-reference-IntStream-forEachOrdered-1]]void link:{java8-javadoc}/java/util/stream/IntStream.html#forEachOrdered%2Djava.util.function.IntConsumer%2D[forEachOrdered](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#forEachOrdered%2Djava.util.function.IntConsumer%2D[java 9]) -* ++[[painless-api-reference-IntStream-iterator-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#iterator%2D%2D[iterator]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#iterator%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-limit-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#limit%2Dlong%2D[limit](long)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#limit%2Dlong%2D[java 9]) -* ++[[painless-api-reference-IntStream-map-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#map%2Djava.util.function.IntUnaryOperator%2D[map](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#map%2Djava.util.function.IntUnaryOperator%2D[java 9]) -* ++[[painless-api-reference-IntStream-mapToDouble-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#mapToDouble%2Djava.util.function.IntToDoubleFunction%2D[mapToDouble](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#mapToDouble%2Djava.util.function.IntToDoubleFunction%2D[java 9]) -* ++[[painless-api-reference-IntStream-mapToLong-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#mapToLong%2Djava.util.function.IntToLongFunction%2D[mapToLong](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#mapToLong%2Djava.util.function.IntToLongFunction%2D[java 9]) -* ++[[painless-api-reference-IntStream-mapToObj-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#mapToObj%2Djava.util.function.IntFunction%2D[mapToObj](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#mapToObj%2Djava.util.function.IntFunction%2D[java 9]) -* ++[[painless-api-reference-IntStream-max-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#max%2D%2D[max]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#max%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-min-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#min%2D%2D[min]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#min%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-noneMatch-1]]boolean link:{java8-javadoc}/java/util/stream/IntStream.html#noneMatch%2Djava.util.function.IntPredicate%2D[noneMatch](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#noneMatch%2Djava.util.function.IntPredicate%2D[java 9]) -* ++[[painless-api-reference-IntStream-peek-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#peek%2Djava.util.function.IntConsumer%2D[peek](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#peek%2Djava.util.function.IntConsumer%2D[java 9]) -* ++[[painless-api-reference-IntStream-reduce-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#reduce%2Djava.util.function.IntBinaryOperator%2D[reduce](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#reduce%2Djava.util.function.IntBinaryOperator%2D[java 9]) -* ++[[painless-api-reference-IntStream-reduce-2]]int link:{java8-javadoc}/java/util/stream/IntStream.html#reduce%2Dint%2Djava.util.function.IntBinaryOperator%2D[reduce](int, <>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#reduce%2Dint%2Djava.util.function.IntBinaryOperator%2D[java 9]) -* ++[[painless-api-reference-IntStream-sequential-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#sequential%2D%2D[sequential]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#sequential%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-skip-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#skip%2Dlong%2D[skip](long)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#skip%2Dlong%2D[java 9]) -* ++[[painless-api-reference-IntStream-sorted-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#sorted%2D%2D[sorted]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#sorted%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-spliterator-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#spliterator%2D%2D[spliterator]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#spliterator%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-sum-0]]int link:{java8-javadoc}/java/util/stream/IntStream.html#sum%2D%2D[sum]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#sum%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-summaryStatistics-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#summaryStatistics%2D%2D[summaryStatistics]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#summaryStatistics%2D%2D[java 9]) -* ++[[painless-api-reference-IntStream-toArray-0]]int[] link:{java8-javadoc}/java/util/stream/IntStream.html#toArray%2D%2D[toArray]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#toArray%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IntSummaryStatistics.asciidoc b/docs/reference/painless-api-reference/IntSummaryStatistics.asciidoc deleted file mode 100644 index e13d47ad7c4aa..0000000000000 --- a/docs/reference/painless-api-reference/IntSummaryStatistics.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IntSummaryStatistics]]++IntSummaryStatistics++:: -* ++[[painless-api-reference-IntSummaryStatistics-IntSummaryStatistics-0]]link:{java8-javadoc}/java/util/IntSummaryStatistics.html#IntSummaryStatistics%2D%2D[IntSummaryStatistics]()++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#IntSummaryStatistics%2D%2D[java 9]) -* ++[[painless-api-reference-IntSummaryStatistics-combine-1]]void link:{java8-javadoc}/java/util/IntSummaryStatistics.html#combine%2Djava.util.IntSummaryStatistics%2D[combine](<>)++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#combine%2Djava.util.IntSummaryStatistics%2D[java 9]) -* ++[[painless-api-reference-IntSummaryStatistics-getAverage-0]]double link:{java8-javadoc}/java/util/IntSummaryStatistics.html#getAverage%2D%2D[getAverage]()++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#getAverage%2D%2D[java 9]) -* ++[[painless-api-reference-IntSummaryStatistics-getCount-0]]long link:{java8-javadoc}/java/util/IntSummaryStatistics.html#getCount%2D%2D[getCount]()++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#getCount%2D%2D[java 9]) -* ++[[painless-api-reference-IntSummaryStatistics-getMax-0]]int link:{java8-javadoc}/java/util/IntSummaryStatistics.html#getMax%2D%2D[getMax]()++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#getMax%2D%2D[java 9]) -* ++[[painless-api-reference-IntSummaryStatistics-getMin-0]]int link:{java8-javadoc}/java/util/IntSummaryStatistics.html#getMin%2D%2D[getMin]()++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#getMin%2D%2D[java 9]) -* ++[[painless-api-reference-IntSummaryStatistics-getSum-0]]long link:{java8-javadoc}/java/util/IntSummaryStatistics.html#getSum%2D%2D[getSum]()++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#getSum%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IntSupplier.asciidoc b/docs/reference/painless-api-reference/IntSupplier.asciidoc deleted file mode 100644 index bca332f7e6cf4..0000000000000 --- a/docs/reference/painless-api-reference/IntSupplier.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IntSupplier]]++IntSupplier++:: -* ++[[painless-api-reference-IntSupplier-getAsInt-0]]int link:{java8-javadoc}/java/util/function/IntSupplier.html#getAsInt%2D%2D[getAsInt]()++ (link:{java9-javadoc}/java/util/function/IntSupplier.html#getAsInt%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/IntToDoubleFunction.asciidoc b/docs/reference/painless-api-reference/IntToDoubleFunction.asciidoc deleted file mode 100644 index 1cfadec16b2d8..0000000000000 --- a/docs/reference/painless-api-reference/IntToDoubleFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IntToDoubleFunction]]++IntToDoubleFunction++:: -* ++[[painless-api-reference-IntToDoubleFunction-applyAsDouble-1]]double link:{java8-javadoc}/java/util/function/IntToDoubleFunction.html#applyAsDouble%2Dint%2D[applyAsDouble](int)++ (link:{java9-javadoc}/java/util/function/IntToDoubleFunction.html#applyAsDouble%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/IntToLongFunction.asciidoc b/docs/reference/painless-api-reference/IntToLongFunction.asciidoc deleted file mode 100644 index 3cffe6cddea16..0000000000000 --- a/docs/reference/painless-api-reference/IntToLongFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IntToLongFunction]]++IntToLongFunction++:: -* ++[[painless-api-reference-IntToLongFunction-applyAsLong-1]]long link:{java8-javadoc}/java/util/function/IntToLongFunction.html#applyAsLong%2Dint%2D[applyAsLong](int)++ (link:{java9-javadoc}/java/util/function/IntToLongFunction.html#applyAsLong%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/IntUnaryOperator.asciidoc b/docs/reference/painless-api-reference/IntUnaryOperator.asciidoc deleted file mode 100644 index a1bb1651fee56..0000000000000 --- a/docs/reference/painless-api-reference/IntUnaryOperator.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IntUnaryOperator]]++IntUnaryOperator++:: -* ++[[painless-api-reference-IntUnaryOperator-identity-0]]static <> link:{java8-javadoc}/java/util/function/IntUnaryOperator.html#identity%2D%2D[identity]()++ (link:{java9-javadoc}/java/util/function/IntUnaryOperator.html#identity%2D%2D[java 9]) -* ++[[painless-api-reference-IntUnaryOperator-andThen-1]]<> link:{java8-javadoc}/java/util/function/IntUnaryOperator.html#andThen%2Djava.util.function.IntUnaryOperator%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/IntUnaryOperator.html#andThen%2Djava.util.function.IntUnaryOperator%2D[java 9]) -* ++[[painless-api-reference-IntUnaryOperator-applyAsInt-1]]int link:{java8-javadoc}/java/util/function/IntUnaryOperator.html#applyAsInt%2Dint%2D[applyAsInt](int)++ (link:{java9-javadoc}/java/util/function/IntUnaryOperator.html#applyAsInt%2Dint%2D[java 9]) -* ++[[painless-api-reference-IntUnaryOperator-compose-1]]<> link:{java8-javadoc}/java/util/function/IntUnaryOperator.html#compose%2Djava.util.function.IntUnaryOperator%2D[compose](<>)++ (link:{java9-javadoc}/java/util/function/IntUnaryOperator.html#compose%2Djava.util.function.IntUnaryOperator%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Integer.asciidoc b/docs/reference/painless-api-reference/Integer.asciidoc deleted file mode 100644 index 2f608bcc662f7..0000000000000 --- a/docs/reference/painless-api-reference/Integer.asciidoc +++ /dev/null @@ -1,44 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Integer]]++Integer++:: -** [[painless-api-reference-Integer-BYTES]]static int link:{java8-javadoc}/java/lang/Integer.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Integer.html#BYTES[java 9]) -** [[painless-api-reference-Integer-MAX_VALUE]]static int link:{java8-javadoc}/java/lang/Integer.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Integer.html#MAX_VALUE[java 9]) -** [[painless-api-reference-Integer-MIN_VALUE]]static int link:{java8-javadoc}/java/lang/Integer.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Integer.html#MIN_VALUE[java 9]) -** [[painless-api-reference-Integer-SIZE]]static int link:{java8-javadoc}/java/lang/Integer.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Integer.html#SIZE[java 9]) -* ++[[painless-api-reference-Integer-bitCount-1]]static int link:{java8-javadoc}/java/lang/Integer.html#bitCount%2Dint%2D[bitCount](int)++ (link:{java9-javadoc}/java/lang/Integer.html#bitCount%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-compare-2]]static int link:{java8-javadoc}/java/lang/Integer.html#compare%2Dint%2Dint%2D[compare](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#compare%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-compareUnsigned-2]]static int link:{java8-javadoc}/java/lang/Integer.html#compareUnsigned%2Dint%2Dint%2D[compareUnsigned](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#compareUnsigned%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-decode-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#decode%2Djava.lang.String%2D[decode](<>)++ (link:{java9-javadoc}/java/lang/Integer.html#decode%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Integer-divideUnsigned-2]]static int link:{java8-javadoc}/java/lang/Integer.html#divideUnsigned%2Dint%2Dint%2D[divideUnsigned](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#divideUnsigned%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-hashCode-1]]static int link:{java8-javadoc}/java/lang/Integer.html#hashCode%2Dint%2D[hashCode](int)++ (link:{java9-javadoc}/java/lang/Integer.html#hashCode%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-highestOneBit-1]]static int link:{java8-javadoc}/java/lang/Integer.html#highestOneBit%2Dint%2D[highestOneBit](int)++ (link:{java9-javadoc}/java/lang/Integer.html#highestOneBit%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-lowestOneBit-1]]static int link:{java8-javadoc}/java/lang/Integer.html#lowestOneBit%2Dint%2D[lowestOneBit](int)++ (link:{java9-javadoc}/java/lang/Integer.html#lowestOneBit%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-max-2]]static int link:{java8-javadoc}/java/lang/Integer.html#max%2Dint%2Dint%2D[max](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#max%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-min-2]]static int link:{java8-javadoc}/java/lang/Integer.html#min%2Dint%2Dint%2D[min](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#min%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-numberOfLeadingZeros-1]]static int link:{java8-javadoc}/java/lang/Integer.html#numberOfLeadingZeros%2Dint%2D[numberOfLeadingZeros](int)++ (link:{java9-javadoc}/java/lang/Integer.html#numberOfLeadingZeros%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-numberOfTrailingZeros-1]]static int link:{java8-javadoc}/java/lang/Integer.html#numberOfTrailingZeros%2Dint%2D[numberOfTrailingZeros](int)++ (link:{java9-javadoc}/java/lang/Integer.html#numberOfTrailingZeros%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-parseInt-1]]static int link:{java8-javadoc}/java/lang/Integer.html#parseInt%2Djava.lang.String%2D[parseInt](<>)++ (link:{java9-javadoc}/java/lang/Integer.html#parseInt%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Integer-parseInt-2]]static int link:{java8-javadoc}/java/lang/Integer.html#parseInt%2Djava.lang.String%2Dint%2D[parseInt](<>, int)++ (link:{java9-javadoc}/java/lang/Integer.html#parseInt%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-parseUnsignedInt-1]]static int link:{java8-javadoc}/java/lang/Integer.html#parseUnsignedInt%2Djava.lang.String%2D[parseUnsignedInt](<>)++ (link:{java9-javadoc}/java/lang/Integer.html#parseUnsignedInt%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Integer-parseUnsignedInt-2]]static int link:{java8-javadoc}/java/lang/Integer.html#parseUnsignedInt%2Djava.lang.String%2Dint%2D[parseUnsignedInt](<>, int)++ (link:{java9-javadoc}/java/lang/Integer.html#parseUnsignedInt%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-remainderUnsigned-2]]static int link:{java8-javadoc}/java/lang/Integer.html#remainderUnsigned%2Dint%2Dint%2D[remainderUnsigned](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#remainderUnsigned%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-reverse-1]]static int link:{java8-javadoc}/java/lang/Integer.html#reverse%2Dint%2D[reverse](int)++ (link:{java9-javadoc}/java/lang/Integer.html#reverse%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-reverseBytes-1]]static int link:{java8-javadoc}/java/lang/Integer.html#reverseBytes%2Dint%2D[reverseBytes](int)++ (link:{java9-javadoc}/java/lang/Integer.html#reverseBytes%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-rotateLeft-2]]static int link:{java8-javadoc}/java/lang/Integer.html#rotateLeft%2Dint%2Dint%2D[rotateLeft](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#rotateLeft%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-rotateRight-2]]static int link:{java8-javadoc}/java/lang/Integer.html#rotateRight%2Dint%2Dint%2D[rotateRight](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#rotateRight%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-signum-1]]static int link:{java8-javadoc}/java/lang/Integer.html#signum%2Dint%2D[signum](int)++ (link:{java9-javadoc}/java/lang/Integer.html#signum%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-toBinaryString-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#toBinaryString%2Dint%2D[toBinaryString](int)++ (link:{java9-javadoc}/java/lang/Integer.html#toBinaryString%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-toHexString-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#toHexString%2Dint%2D[toHexString](int)++ (link:{java9-javadoc}/java/lang/Integer.html#toHexString%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-toOctalString-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#toOctalString%2Dint%2D[toOctalString](int)++ (link:{java9-javadoc}/java/lang/Integer.html#toOctalString%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-toString-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#toString%2Dint%2D[toString](int)++ (link:{java9-javadoc}/java/lang/Integer.html#toString%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-toString-2]]static <> link:{java8-javadoc}/java/lang/Integer.html#toString%2Dint%2Dint%2D[toString](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#toString%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-toUnsignedLong-1]]static long link:{java8-javadoc}/java/lang/Integer.html#toUnsignedLong%2Dint%2D[toUnsignedLong](int)++ (link:{java9-javadoc}/java/lang/Integer.html#toUnsignedLong%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-toUnsignedString-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#toUnsignedString%2Dint%2D[toUnsignedString](int)++ (link:{java9-javadoc}/java/lang/Integer.html#toUnsignedString%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-toUnsignedString-2]]static <> link:{java8-javadoc}/java/lang/Integer.html#toUnsignedString%2Dint%2Dint%2D[toUnsignedString](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#toUnsignedString%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#valueOf%2Dint%2D[valueOf](int)++ (link:{java9-javadoc}/java/lang/Integer.html#valueOf%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-valueOf-2]]static <> link:{java8-javadoc}/java/lang/Integer.html#valueOf%2Djava.lang.String%2Dint%2D[valueOf](<>, int)++ (link:{java9-javadoc}/java/lang/Integer.html#valueOf%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-Integer-compareTo-1]]int link:{java8-javadoc}/java/lang/Integer.html#compareTo%2Djava.lang.Integer%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Integer.html#compareTo%2Djava.lang.Integer%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/InterruptedException.asciidoc b/docs/reference/painless-api-reference/InterruptedException.asciidoc deleted file mode 100644 index cd0ef457f76ab..0000000000000 --- a/docs/reference/painless-api-reference/InterruptedException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-InterruptedException]]++InterruptedException++:: -* ++[[painless-api-reference-InterruptedException-InterruptedException-0]]link:{java8-javadoc}/java/lang/InterruptedException.html#InterruptedException%2D%2D[InterruptedException]()++ (link:{java9-javadoc}/java/lang/InterruptedException.html#InterruptedException%2D%2D[java 9]) -* ++[[painless-api-reference-InterruptedException-InterruptedException-1]]link:{java8-javadoc}/java/lang/InterruptedException.html#InterruptedException%2Djava.lang.String%2D[InterruptedException](<>)++ (link:{java9-javadoc}/java/lang/InterruptedException.html#InterruptedException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IsoChronology.asciidoc b/docs/reference/painless-api-reference/IsoChronology.asciidoc deleted file mode 100644 index 3990839b041fc..0000000000000 --- a/docs/reference/painless-api-reference/IsoChronology.asciidoc +++ /dev/null @@ -1,20 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IsoChronology]]++IsoChronology++:: -** [[painless-api-reference-IsoChronology-INSTANCE]]static <> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#INSTANCE[INSTANCE] (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#INSTANCE[java 9]) -* ++[[painless-api-reference-IsoChronology-date-1]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[date](<>)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-IsoChronology-date-3]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#date%2Dint%2Dint%2Dint%2D[date](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#date%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-IsoChronology-date-4]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[date](<>, int, int, int)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-IsoChronology-dateEpochDay-1]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#dateEpochDay%2Dlong%2D[dateEpochDay](long)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#dateEpochDay%2Dlong%2D[java 9]) -* ++[[painless-api-reference-IsoChronology-dateYearDay-2]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#dateYearDay%2Dint%2Dint%2D[dateYearDay](int, int)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#dateYearDay%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-IsoChronology-dateYearDay-3]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[dateYearDay](<>, int, int)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-IsoChronology-eraOf-1]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#eraOf%2Dint%2D[eraOf](int)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#eraOf%2Dint%2D[java 9]) -* ++[[painless-api-reference-IsoChronology-localDateTime-1]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#localDateTime%2Djava.time.temporal.TemporalAccessor%2D[localDateTime](<>)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#localDateTime%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-IsoChronology-period-3]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#period%2Dint%2Dint%2Dint%2D[period](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#period%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-IsoChronology-resolveDate-2]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[resolveDate](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[java 9]) -* ++[[painless-api-reference-IsoChronology-zonedDateTime-1]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#zonedDateTime%2Djava.time.temporal.TemporalAccessor%2D[zonedDateTime](<>)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#zonedDateTime%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-IsoChronology-zonedDateTime-2]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#zonedDateTime%2Djava.time.Instant%2Djava.time.ZoneId%2D[zonedDateTime](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#zonedDateTime%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/IsoEra.asciidoc b/docs/reference/painless-api-reference/IsoEra.asciidoc deleted file mode 100644 index 975d7d1153aef..0000000000000 --- a/docs/reference/painless-api-reference/IsoEra.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IsoEra]]++IsoEra++:: -** [[painless-api-reference-IsoEra-BCE]]static <> link:{java8-javadoc}/java/time/chrono/IsoEra.html#BCE[BCE] (link:{java9-javadoc}/java/time/chrono/IsoEra.html#BCE[java 9]) -** [[painless-api-reference-IsoEra-CE]]static <> link:{java8-javadoc}/java/time/chrono/IsoEra.html#CE[CE] (link:{java9-javadoc}/java/time/chrono/IsoEra.html#CE[java 9]) -* ++[[painless-api-reference-IsoEra-of-1]]static <> link:{java8-javadoc}/java/time/chrono/IsoEra.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/chrono/IsoEra.html#of%2Dint%2D[java 9]) -* ++[[painless-api-reference-IsoEra-valueOf-1]]static <> link:{java8-javadoc}/java/time/chrono/IsoEra.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/chrono/IsoEra.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-IsoEra-values-0]]static <>[] link:{java8-javadoc}/java/time/chrono/IsoEra.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/chrono/IsoEra.html#values%2D%2D[java 9]) -* ++[[painless-api-reference-IsoEra-getValue-0]]int link:{java8-javadoc}/java/time/chrono/IsoEra.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/chrono/IsoEra.html#getValue%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/IsoFields.asciidoc b/docs/reference/painless-api-reference/IsoFields.asciidoc deleted file mode 100644 index db3f5bbefbb5e..0000000000000 --- a/docs/reference/painless-api-reference/IsoFields.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-IsoFields]]++IsoFields++:: -** [[painless-api-reference-IsoFields-DAY_OF_QUARTER]]static <> link:{java8-javadoc}/java/time/temporal/IsoFields.html#DAY_OF_QUARTER[DAY_OF_QUARTER] (link:{java9-javadoc}/java/time/temporal/IsoFields.html#DAY_OF_QUARTER[java 9]) -** [[painless-api-reference-IsoFields-QUARTER_OF_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/IsoFields.html#QUARTER_OF_YEAR[QUARTER_OF_YEAR] (link:{java9-javadoc}/java/time/temporal/IsoFields.html#QUARTER_OF_YEAR[java 9]) -** [[painless-api-reference-IsoFields-QUARTER_YEARS]]static <> link:{java8-javadoc}/java/time/temporal/IsoFields.html#QUARTER_YEARS[QUARTER_YEARS] (link:{java9-javadoc}/java/time/temporal/IsoFields.html#QUARTER_YEARS[java 9]) -** [[painless-api-reference-IsoFields-WEEK_BASED_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/IsoFields.html#WEEK_BASED_YEAR[WEEK_BASED_YEAR] (link:{java9-javadoc}/java/time/temporal/IsoFields.html#WEEK_BASED_YEAR[java 9]) -** [[painless-api-reference-IsoFields-WEEK_BASED_YEARS]]static <> link:{java8-javadoc}/java/time/temporal/IsoFields.html#WEEK_BASED_YEARS[WEEK_BASED_YEARS] (link:{java9-javadoc}/java/time/temporal/IsoFields.html#WEEK_BASED_YEARS[java 9]) -** [[painless-api-reference-IsoFields-WEEK_OF_WEEK_BASED_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/IsoFields.html#WEEK_OF_WEEK_BASED_YEAR[WEEK_OF_WEEK_BASED_YEAR] (link:{java9-javadoc}/java/time/temporal/IsoFields.html#WEEK_OF_WEEK_BASED_YEAR[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Iterable.asciidoc b/docs/reference/painless-api-reference/Iterable.asciidoc deleted file mode 100644 index b7d626767c710..0000000000000 --- a/docs/reference/painless-api-reference/Iterable.asciidoc +++ /dev/null @@ -1,21 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Iterable]]++Iterable++:: -* ++[[painless-api-reference-Iterable-any-1]]boolean link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#any%2Djava.lang.Iterable%2Djava.util.function.Predicate%2D[any](<>)++ -* ++[[painless-api-reference-Iterable-asCollection-0]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#asCollection%2Djava.lang.Iterable%2D[asCollection]()++ -* ++[[painless-api-reference-Iterable-asList-0]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#asList%2Djava.lang.Iterable%2D[asList]()++ -* ++[[painless-api-reference-Iterable-each-1]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#each%2Djava.lang.Iterable%2Djava.util.function.Consumer%2D[each](<>)++ -* ++[[painless-api-reference-Iterable-eachWithIndex-1]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#eachWithIndex%2Djava.lang.Iterable%2Djava.util.function.ObjIntConsumer%2D[eachWithIndex](<>)++ -* ++[[painless-api-reference-Iterable-every-1]]boolean link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#every%2Djava.lang.Iterable%2Djava.util.function.Predicate%2D[every](<>)++ -* ++[[painless-api-reference-Iterable-findResults-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findResults%2Djava.lang.Iterable%2Djava.util.function.Function%2D[findResults](<>)++ -* ++[[painless-api-reference-Iterable-forEach-1]]void link:{java8-javadoc}/java/lang/Iterable.html#forEach%2Djava.util.function.Consumer%2D[forEach](<>)++ (link:{java9-javadoc}/java/lang/Iterable.html#forEach%2Djava.util.function.Consumer%2D[java 9]) -* ++[[painless-api-reference-Iterable-groupBy-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#groupBy%2Djava.lang.Iterable%2Djava.util.function.Function%2D[groupBy](<>)++ -* ++[[painless-api-reference-Iterable-iterator-0]]<> link:{java8-javadoc}/java/lang/Iterable.html#iterator%2D%2D[iterator]()++ (link:{java9-javadoc}/java/lang/Iterable.html#iterator%2D%2D[java 9]) -* ++[[painless-api-reference-Iterable-join-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#join%2Djava.lang.Iterable%2Djava.lang.String%2D[join](<>)++ -* ++[[painless-api-reference-Iterable-spliterator-0]]<> link:{java8-javadoc}/java/lang/Iterable.html#spliterator%2D%2D[spliterator]()++ (link:{java9-javadoc}/java/lang/Iterable.html#spliterator%2D%2D[java 9]) -* ++[[painless-api-reference-Iterable-sum-0]]double link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#sum%2Djava.lang.Iterable%2D[sum]()++ -* ++[[painless-api-reference-Iterable-sum-1]]double link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#sum%2Djava.lang.Iterable%2Djava.util.function.ToDoubleFunction%2D[sum](<>)++ -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Iterator.asciidoc b/docs/reference/painless-api-reference/Iterator.asciidoc deleted file mode 100644 index 9dd3f5601d54b..0000000000000 --- a/docs/reference/painless-api-reference/Iterator.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Iterator]]++Iterator++:: -* ++[[painless-api-reference-Iterator-forEachRemaining-1]]void link:{java8-javadoc}/java/util/Iterator.html#forEachRemaining%2Djava.util.function.Consumer%2D[forEachRemaining](<>)++ (link:{java9-javadoc}/java/util/Iterator.html#forEachRemaining%2Djava.util.function.Consumer%2D[java 9]) -* ++[[painless-api-reference-Iterator-hasNext-0]]boolean link:{java8-javadoc}/java/util/Iterator.html#hasNext%2D%2D[hasNext]()++ (link:{java9-javadoc}/java/util/Iterator.html#hasNext%2D%2D[java 9]) -* ++[[painless-api-reference-Iterator-next-0]]def link:{java8-javadoc}/java/util/Iterator.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/util/Iterator.html#next%2D%2D[java 9]) -* ++[[painless-api-reference-Iterator-remove-0]]void link:{java8-javadoc}/java/util/Iterator.html#remove%2D%2D[remove]()++ (link:{java9-javadoc}/java/util/Iterator.html#remove%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/JapaneseChronology.asciidoc b/docs/reference/painless-api-reference/JapaneseChronology.asciidoc deleted file mode 100644 index 166f3ca176d09..0000000000000 --- a/docs/reference/painless-api-reference/JapaneseChronology.asciidoc +++ /dev/null @@ -1,16 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-JapaneseChronology]]++JapaneseChronology++:: -** [[painless-api-reference-JapaneseChronology-INSTANCE]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#INSTANCE[INSTANCE] (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#INSTANCE[java 9]) -* ++[[painless-api-reference-JapaneseChronology-date-1]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[date](<>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-JapaneseChronology-date-3]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#date%2Dint%2Dint%2Dint%2D[date](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#date%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-JapaneseChronology-date-4]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[date](<>, int, int, int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-JapaneseChronology-dateEpochDay-1]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#dateEpochDay%2Dlong%2D[dateEpochDay](long)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#dateEpochDay%2Dlong%2D[java 9]) -* ++[[painless-api-reference-JapaneseChronology-dateYearDay-2]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#dateYearDay%2Dint%2Dint%2D[dateYearDay](int, int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#dateYearDay%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-JapaneseChronology-dateYearDay-3]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[dateYearDay](<>, int, int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-JapaneseChronology-eraOf-1]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#eraOf%2Dint%2D[eraOf](int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#eraOf%2Dint%2D[java 9]) -* ++[[painless-api-reference-JapaneseChronology-resolveDate-2]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[resolveDate](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/JapaneseDate.asciidoc b/docs/reference/painless-api-reference/JapaneseDate.asciidoc deleted file mode 100644 index 560ebc7a664bc..0000000000000 --- a/docs/reference/painless-api-reference/JapaneseDate.asciidoc +++ /dev/null @@ -1,17 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-JapaneseDate]]++JapaneseDate++:: -* ++[[painless-api-reference-JapaneseDate-from-1]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-JapaneseDate-of-3]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#of%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-JapaneseDate-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#getChronology%2D%2D[java 9]) -* ++[[painless-api-reference-JapaneseDate-getEra-0]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#getEra%2D%2D[getEra]()++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#getEra%2D%2D[java 9]) -* ++[[painless-api-reference-JapaneseDate-minus-1]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-JapaneseDate-minus-2]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-JapaneseDate-plus-1]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-JapaneseDate-plus-2]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-JapaneseDate-with-1]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-JapaneseDate-with-2]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/JapaneseEra.asciidoc b/docs/reference/painless-api-reference/JapaneseEra.asciidoc deleted file mode 100644 index b5a9c7f63bc04..0000000000000 --- a/docs/reference/painless-api-reference/JapaneseEra.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-JapaneseEra]]++JapaneseEra++:: -** [[painless-api-reference-JapaneseEra-HEISEI]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#HEISEI[HEISEI] (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#HEISEI[java 9]) -** [[painless-api-reference-JapaneseEra-MEIJI]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#MEIJI[MEIJI] (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#MEIJI[java 9]) -** [[painless-api-reference-JapaneseEra-SHOWA]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#SHOWA[SHOWA] (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#SHOWA[java 9]) -** [[painless-api-reference-JapaneseEra-TAISHO]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#TAISHO[TAISHO] (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#TAISHO[java 9]) -* ++[[painless-api-reference-JapaneseEra-of-1]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#of%2Dint%2D[java 9]) -* ++[[painless-api-reference-JapaneseEra-valueOf-1]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-JapaneseEra-values-0]]static <>[] link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#values%2D%2D[java 9]) -* ++[[painless-api-reference-JapaneseEra-getValue-0]]int link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#getValue%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/JulianFields.asciidoc b/docs/reference/painless-api-reference/JulianFields.asciidoc deleted file mode 100644 index 19ee6c5e2ebd6..0000000000000 --- a/docs/reference/painless-api-reference/JulianFields.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-JulianFields]]++JulianFields++:: -** [[painless-api-reference-JulianFields-JULIAN_DAY]]static <> link:{java8-javadoc}/java/time/temporal/JulianFields.html#JULIAN_DAY[JULIAN_DAY] (link:{java9-javadoc}/java/time/temporal/JulianFields.html#JULIAN_DAY[java 9]) -** [[painless-api-reference-JulianFields-MODIFIED_JULIAN_DAY]]static <> link:{java8-javadoc}/java/time/temporal/JulianFields.html#MODIFIED_JULIAN_DAY[MODIFIED_JULIAN_DAY] (link:{java9-javadoc}/java/time/temporal/JulianFields.html#MODIFIED_JULIAN_DAY[java 9]) -** [[painless-api-reference-JulianFields-RATA_DIE]]static <> link:{java8-javadoc}/java/time/temporal/JulianFields.html#RATA_DIE[RATA_DIE] (link:{java9-javadoc}/java/time/temporal/JulianFields.html#RATA_DIE[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/LinkedHashMap.asciidoc b/docs/reference/painless-api-reference/LinkedHashMap.asciidoc deleted file mode 100644 index bda2d45f22cb6..0000000000000 --- a/docs/reference/painless-api-reference/LinkedHashMap.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LinkedHashMap]]++LinkedHashMap++:: -* ++[[painless-api-reference-LinkedHashMap-LinkedHashMap-0]]link:{java8-javadoc}/java/util/LinkedHashMap.html#LinkedHashMap%2D%2D[LinkedHashMap]()++ (link:{java9-javadoc}/java/util/LinkedHashMap.html#LinkedHashMap%2D%2D[java 9]) -* ++[[painless-api-reference-LinkedHashMap-LinkedHashMap-1]]link:{java8-javadoc}/java/util/LinkedHashMap.html#LinkedHashMap%2Djava.util.Map%2D[LinkedHashMap](<>)++ (link:{java9-javadoc}/java/util/LinkedHashMap.html#LinkedHashMap%2Djava.util.Map%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/LinkedHashSet.asciidoc b/docs/reference/painless-api-reference/LinkedHashSet.asciidoc deleted file mode 100644 index 3a7710771e8ba..0000000000000 --- a/docs/reference/painless-api-reference/LinkedHashSet.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LinkedHashSet]]++LinkedHashSet++:: -* ++[[painless-api-reference-LinkedHashSet-LinkedHashSet-0]]link:{java8-javadoc}/java/util/LinkedHashSet.html#LinkedHashSet%2D%2D[LinkedHashSet]()++ (link:{java9-javadoc}/java/util/LinkedHashSet.html#LinkedHashSet%2D%2D[java 9]) -* ++[[painless-api-reference-LinkedHashSet-LinkedHashSet-1]]link:{java8-javadoc}/java/util/LinkedHashSet.html#LinkedHashSet%2Djava.util.Collection%2D[LinkedHashSet](<>)++ (link:{java9-javadoc}/java/util/LinkedHashSet.html#LinkedHashSet%2Djava.util.Collection%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/LinkedList.asciidoc b/docs/reference/painless-api-reference/LinkedList.asciidoc deleted file mode 100644 index 5d18b373ba360..0000000000000 --- a/docs/reference/painless-api-reference/LinkedList.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LinkedList]]++LinkedList++:: -* ++[[painless-api-reference-LinkedList-LinkedList-0]]link:{java8-javadoc}/java/util/LinkedList.html#LinkedList%2D%2D[LinkedList]()++ (link:{java9-javadoc}/java/util/LinkedList.html#LinkedList%2D%2D[java 9]) -* ++[[painless-api-reference-LinkedList-LinkedList-1]]link:{java8-javadoc}/java/util/LinkedList.html#LinkedList%2Djava.util.Collection%2D[LinkedList](<>)++ (link:{java9-javadoc}/java/util/LinkedList.html#LinkedList%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-LinkedList-clone-0]]def link:{java8-javadoc}/java/util/LinkedList.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/LinkedList.html#clone%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/List.asciidoc b/docs/reference/painless-api-reference/List.asciidoc deleted file mode 100644 index abe438451b86b..0000000000000 --- a/docs/reference/painless-api-reference/List.asciidoc +++ /dev/null @@ -1,22 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-List]]++List++:: -* ++[[painless-api-reference-List-add-2]]void link:{java8-javadoc}/java/util/List.html#add%2Dint%2Djava.lang.Object%2D[add](int, def)++ (link:{java9-javadoc}/java/util/List.html#add%2Dint%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-List-addAll-2]]boolean link:{java8-javadoc}/java/util/List.html#addAll%2Dint%2Djava.util.Collection%2D[addAll](int, <>)++ (link:{java9-javadoc}/java/util/List.html#addAll%2Dint%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-List-equals-1]]boolean link:{java8-javadoc}/java/util/List.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/util/List.html#equals%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-List-get-1]]def link:{java8-javadoc}/java/util/List.html#get%2Dint%2D[get](int)++ (link:{java9-javadoc}/java/util/List.html#get%2Dint%2D[java 9]) -* ++[[painless-api-reference-List-getLength-0]]int link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#getLength%2Djava.util.List%2D[getLength]()++ -* ++[[painless-api-reference-List-hashCode-0]]int link:{java8-javadoc}/java/util/List.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/util/List.html#hashCode%2D%2D[java 9]) -* ++[[painless-api-reference-List-indexOf-1]]int link:{java8-javadoc}/java/util/List.html#indexOf%2Djava.lang.Object%2D[indexOf](def)++ (link:{java9-javadoc}/java/util/List.html#indexOf%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-List-lastIndexOf-1]]int link:{java8-javadoc}/java/util/List.html#lastIndexOf%2Djava.lang.Object%2D[lastIndexOf](def)++ (link:{java9-javadoc}/java/util/List.html#lastIndexOf%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-List-listIterator-0]]<> link:{java8-javadoc}/java/util/List.html#listIterator%2D%2D[listIterator]()++ (link:{java9-javadoc}/java/util/List.html#listIterator%2D%2D[java 9]) -* ++[[painless-api-reference-List-listIterator-1]]<> link:{java8-javadoc}/java/util/List.html#listIterator%2Dint%2D[listIterator](int)++ (link:{java9-javadoc}/java/util/List.html#listIterator%2Dint%2D[java 9]) -* ++[[painless-api-reference-List-remove-1]]def link:{java8-javadoc}/java/util/List.html#remove%2Dint%2D[remove](int)++ (link:{java9-javadoc}/java/util/List.html#remove%2Dint%2D[java 9]) -* ++[[painless-api-reference-List-replaceAll-1]]void link:{java8-javadoc}/java/util/List.html#replaceAll%2Djava.util.function.UnaryOperator%2D[replaceAll](<>)++ (link:{java9-javadoc}/java/util/List.html#replaceAll%2Djava.util.function.UnaryOperator%2D[java 9]) -* ++[[painless-api-reference-List-set-2]]def link:{java8-javadoc}/java/util/List.html#set%2Dint%2Djava.lang.Object%2D[set](int, def)++ (link:{java9-javadoc}/java/util/List.html#set%2Dint%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-List-sort-1]]void link:{java8-javadoc}/java/util/List.html#sort%2Djava.util.Comparator%2D[sort](<>)++ (link:{java9-javadoc}/java/util/List.html#sort%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-List-subList-2]]<> link:{java8-javadoc}/java/util/List.html#subList%2Dint%2Dint%2D[subList](int, int)++ (link:{java9-javadoc}/java/util/List.html#subList%2Dint%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ListIterator.asciidoc b/docs/reference/painless-api-reference/ListIterator.asciidoc deleted file mode 100644 index 37d04b2a755cf..0000000000000 --- a/docs/reference/painless-api-reference/ListIterator.asciidoc +++ /dev/null @@ -1,12 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ListIterator]]++ListIterator++:: -* ++[[painless-api-reference-ListIterator-add-1]]void link:{java8-javadoc}/java/util/ListIterator.html#add%2Djava.lang.Object%2D[add](def)++ (link:{java9-javadoc}/java/util/ListIterator.html#add%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-ListIterator-hasPrevious-0]]boolean link:{java8-javadoc}/java/util/ListIterator.html#hasPrevious%2D%2D[hasPrevious]()++ (link:{java9-javadoc}/java/util/ListIterator.html#hasPrevious%2D%2D[java 9]) -* ++[[painless-api-reference-ListIterator-nextIndex-0]]int link:{java8-javadoc}/java/util/ListIterator.html#nextIndex%2D%2D[nextIndex]()++ (link:{java9-javadoc}/java/util/ListIterator.html#nextIndex%2D%2D[java 9]) -* ++[[painless-api-reference-ListIterator-previousIndex-0]]int link:{java8-javadoc}/java/util/ListIterator.html#previousIndex%2D%2D[previousIndex]()++ (link:{java9-javadoc}/java/util/ListIterator.html#previousIndex%2D%2D[java 9]) -* ++[[painless-api-reference-ListIterator-set-1]]void link:{java8-javadoc}/java/util/ListIterator.html#set%2Djava.lang.Object%2D[set](def)++ (link:{java9-javadoc}/java/util/ListIterator.html#set%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/LocalDate.asciidoc b/docs/reference/painless-api-reference/LocalDate.asciidoc deleted file mode 100644 index 8498474d02a0f..0000000000000 --- a/docs/reference/painless-api-reference/LocalDate.asciidoc +++ /dev/null @@ -1,47 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LocalDate]]++LocalDate++:: -** [[painless-api-reference-LocalDate-MAX]]static <> link:{java8-javadoc}/java/time/LocalDate.html#MAX[MAX] (link:{java9-javadoc}/java/time/LocalDate.html#MAX[java 9]) -** [[painless-api-reference-LocalDate-MIN]]static <> link:{java8-javadoc}/java/time/LocalDate.html#MIN[MIN] (link:{java9-javadoc}/java/time/LocalDate.html#MIN[java 9]) -* ++[[painless-api-reference-LocalDate-from-1]]static <> link:{java8-javadoc}/java/time/LocalDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-LocalDate-of-3]]static <> link:{java8-javadoc}/java/time/LocalDate.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/LocalDate.html#of%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDate-ofEpochDay-1]]static <> link:{java8-javadoc}/java/time/LocalDate.html#ofEpochDay%2Dlong%2D[ofEpochDay](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#ofEpochDay%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDate-ofYearDay-2]]static <> link:{java8-javadoc}/java/time/LocalDate.html#ofYearDay%2Dint%2Dint%2D[ofYearDay](int, int)++ (link:{java9-javadoc}/java/time/LocalDate.html#ofYearDay%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDate-parse-1]]static <> link:{java8-javadoc}/java/time/LocalDate.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#parse%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-LocalDate-parse-2]]static <> link:{java8-javadoc}/java/time/LocalDate.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/LocalDate.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-LocalDate-atStartOfDay-0]]<> link:{java8-javadoc}/java/time/LocalDate.html#atStartOfDay%2D%2D[atStartOfDay]()++ (link:{java9-javadoc}/java/time/LocalDate.html#atStartOfDay%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDate-atStartOfDay-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#atStartOfDay%2Djava.time.ZoneId%2D[atStartOfDay](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#atStartOfDay%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-LocalDate-atTime-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#atTime%2Djava.time.LocalTime%2D[atTime](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#atTime%2Djava.time.LocalTime%2D[java 9]) -* ++[[painless-api-reference-LocalDate-atTime-2]]<> link:{java8-javadoc}/java/time/LocalDate.html#atTime%2Dint%2Dint%2D[atTime](int, int)++ (link:{java9-javadoc}/java/time/LocalDate.html#atTime%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDate-atTime-3]]<> link:{java8-javadoc}/java/time/LocalDate.html#atTime%2Dint%2Dint%2Dint%2D[atTime](int, int, int)++ (link:{java9-javadoc}/java/time/LocalDate.html#atTime%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDate-atTime-4]]<> link:{java8-javadoc}/java/time/LocalDate.html#atTime%2Dint%2Dint%2Dint%2Dint%2D[atTime](int, int, int, int)++ (link:{java9-javadoc}/java/time/LocalDate.html#atTime%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDate-getChronology-0]]<> link:{java8-javadoc}/java/time/LocalDate.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getChronology%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDate-getDayOfMonth-0]]int link:{java8-javadoc}/java/time/LocalDate.html#getDayOfMonth%2D%2D[getDayOfMonth]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getDayOfMonth%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDate-getDayOfWeek-0]]<> link:{java8-javadoc}/java/time/LocalDate.html#getDayOfWeek%2D%2D[getDayOfWeek]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getDayOfWeek%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDate-getDayOfYear-0]]int link:{java8-javadoc}/java/time/LocalDate.html#getDayOfYear%2D%2D[getDayOfYear]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getDayOfYear%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDate-getMonth-0]]<> link:{java8-javadoc}/java/time/LocalDate.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getMonth%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDate-getMonthValue-0]]int link:{java8-javadoc}/java/time/LocalDate.html#getMonthValue%2D%2D[getMonthValue]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getMonthValue%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDate-getYear-0]]int link:{java8-javadoc}/java/time/LocalDate.html#getYear%2D%2D[getYear]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getYear%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDate-minus-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-LocalDate-minus-2]]<> link:{java8-javadoc}/java/time/LocalDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/LocalDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-LocalDate-minusDays-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#minusDays%2Dlong%2D[minusDays](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#minusDays%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDate-minusMonths-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#minusMonths%2Dlong%2D[minusMonths](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#minusMonths%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDate-minusWeeks-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#minusWeeks%2Dlong%2D[minusWeeks](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#minusWeeks%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDate-minusYears-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#minusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDate-plus-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-LocalDate-plus-2]]<> link:{java8-javadoc}/java/time/LocalDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/LocalDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-LocalDate-plusDays-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#plusDays%2Dlong%2D[plusDays](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#plusDays%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDate-plusMonths-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#plusMonths%2Dlong%2D[plusMonths](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#plusMonths%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDate-plusWeeks-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#plusWeeks%2Dlong%2D[plusWeeks](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#plusWeeks%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDate-plusYears-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#plusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDate-until-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#until%2Djava.time.chrono.ChronoLocalDate%2D[until](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#until%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) -* ++[[painless-api-reference-LocalDate-with-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-LocalDate-with-2]]<> link:{java8-javadoc}/java/time/LocalDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/LocalDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDate-withDayOfMonth-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#withDayOfMonth%2Dint%2D[withDayOfMonth](int)++ (link:{java9-javadoc}/java/time/LocalDate.html#withDayOfMonth%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDate-withDayOfYear-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#withDayOfYear%2Dint%2D[withDayOfYear](int)++ (link:{java9-javadoc}/java/time/LocalDate.html#withDayOfYear%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDate-withMonth-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#withMonth%2Dint%2D[withMonth](int)++ (link:{java9-javadoc}/java/time/LocalDate.html#withMonth%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDate-withYear-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#withYear%2Dint%2D[withYear](int)++ (link:{java9-javadoc}/java/time/LocalDate.html#withYear%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/LocalDateTime.asciidoc b/docs/reference/painless-api-reference/LocalDateTime.asciidoc deleted file mode 100644 index 9507ff1dd5231..0000000000000 --- a/docs/reference/painless-api-reference/LocalDateTime.asciidoc +++ /dev/null @@ -1,61 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LocalDateTime]]++LocalDateTime++:: -** [[painless-api-reference-LocalDateTime-MAX]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#MAX[MAX] (link:{java9-javadoc}/java/time/LocalDateTime.html#MAX[java 9]) -** [[painless-api-reference-LocalDateTime-MIN]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#MIN[MIN] (link:{java9-javadoc}/java/time/LocalDateTime.html#MIN[java 9]) -* ++[[painless-api-reference-LocalDateTime-from-1]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-of-2]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#of%2Djava.time.LocalDate%2Djava.time.LocalTime%2D[of](<>, <>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#of%2Djava.time.LocalDate%2Djava.time.LocalTime%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-of-5]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2D[of](int, int, int, int, int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-of-6]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[of](int, int, int, int, int, int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-of-7]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[of](int, int, int, int, int, int, int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-ofEpochSecond-3]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#ofEpochSecond%2Dlong%2Dint%2Djava.time.ZoneOffset%2D[ofEpochSecond](long, int, <>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#ofEpochSecond%2Dlong%2Dint%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-ofInstant-2]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[ofInstant](<>, <>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-parse-1]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#parse%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-parse-2]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-atOffset-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#atOffset%2Djava.time.ZoneOffset%2D[atOffset](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#atOffset%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-atZone-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#atZone%2Djava.time.ZoneId%2D[atZone](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#atZone%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-getDayOfMonth-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getDayOfMonth%2D%2D[getDayOfMonth]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getDayOfMonth%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-getDayOfWeek-0]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#getDayOfWeek%2D%2D[getDayOfWeek]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getDayOfWeek%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-getDayOfYear-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getDayOfYear%2D%2D[getDayOfYear]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getDayOfYear%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-getHour-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getHour%2D%2D[getHour]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getHour%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-getMinute-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getMinute%2D%2D[getMinute]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getMinute%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-getMonth-0]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getMonth%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-getMonthValue-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getMonthValue%2D%2D[getMonthValue]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getMonthValue%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-getNano-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getNano%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-getSecond-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getSecond%2D%2D[getSecond]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getSecond%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-getYear-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getYear%2D%2D[getYear]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getYear%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-minus-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-minus-2]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-minusDays-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusDays%2Dlong%2D[minusDays](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusDays%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-minusHours-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusHours%2Dlong%2D[minusHours](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusHours%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-minusMinutes-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusMinutes%2Dlong%2D[minusMinutes](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusMinutes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-minusMonths-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusMonths%2Dlong%2D[minusMonths](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusMonths%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-minusNanos-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-minusSeconds-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-minusWeeks-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusWeeks%2Dlong%2D[minusWeeks](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusWeeks%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-minusYears-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-plus-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-plus-2]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-plusDays-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusDays%2Dlong%2D[plusDays](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusDays%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-plusHours-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusHours%2Dlong%2D[plusHours](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusHours%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-plusMinutes-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusMinutes%2Dlong%2D[plusMinutes](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusMinutes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-plusMonths-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusMonths%2Dlong%2D[plusMonths](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusMonths%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-plusNanos-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-plusSeconds-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-plusWeeks-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusWeeks%2Dlong%2D[plusWeeks](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusWeeks%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-plusYears-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-toLocalDate-0]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#toLocalDate%2D%2D[toLocalDate]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#toLocalDate%2D%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-truncatedTo-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[truncatedTo](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-with-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-with-2]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-withDayOfMonth-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withDayOfMonth%2Dint%2D[withDayOfMonth](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withDayOfMonth%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-withDayOfYear-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withDayOfYear%2Dint%2D[withDayOfYear](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withDayOfYear%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-withHour-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withHour%2Dint%2D[withHour](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withHour%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-withMinute-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withMinute%2Dint%2D[withMinute](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withMinute%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-withMonth-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withMonth%2Dint%2D[withMonth](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withMonth%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-withSecond-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withSecond%2Dint%2D[withSecond](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withSecond%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalDateTime-withYear-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withYear%2Dint%2D[withYear](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withYear%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/LocalTime.asciidoc b/docs/reference/painless-api-reference/LocalTime.asciidoc deleted file mode 100644 index f12e13f4e7854..0000000000000 --- a/docs/reference/painless-api-reference/LocalTime.asciidoc +++ /dev/null @@ -1,50 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LocalTime]]++LocalTime++:: -** [[painless-api-reference-LocalTime-MAX]]static <> link:{java8-javadoc}/java/time/LocalTime.html#MAX[MAX] (link:{java9-javadoc}/java/time/LocalTime.html#MAX[java 9]) -** [[painless-api-reference-LocalTime-MIDNIGHT]]static <> link:{java8-javadoc}/java/time/LocalTime.html#MIDNIGHT[MIDNIGHT] (link:{java9-javadoc}/java/time/LocalTime.html#MIDNIGHT[java 9]) -** [[painless-api-reference-LocalTime-MIN]]static <> link:{java8-javadoc}/java/time/LocalTime.html#MIN[MIN] (link:{java9-javadoc}/java/time/LocalTime.html#MIN[java 9]) -** [[painless-api-reference-LocalTime-NOON]]static <> link:{java8-javadoc}/java/time/LocalTime.html#NOON[NOON] (link:{java9-javadoc}/java/time/LocalTime.html#NOON[java 9]) -* ++[[painless-api-reference-LocalTime-from-1]]static <> link:{java8-javadoc}/java/time/LocalTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-LocalTime-of-2]]static <> link:{java8-javadoc}/java/time/LocalTime.html#of%2Dint%2Dint%2D[of](int, int)++ (link:{java9-javadoc}/java/time/LocalTime.html#of%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalTime-of-3]]static <> link:{java8-javadoc}/java/time/LocalTime.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/LocalTime.html#of%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalTime-of-4]]static <> link:{java8-javadoc}/java/time/LocalTime.html#of%2Dint%2Dint%2Dint%2Dint%2D[of](int, int, int, int)++ (link:{java9-javadoc}/java/time/LocalTime.html#of%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalTime-ofNanoOfDay-1]]static <> link:{java8-javadoc}/java/time/LocalTime.html#ofNanoOfDay%2Dlong%2D[ofNanoOfDay](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#ofNanoOfDay%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalTime-ofSecondOfDay-1]]static <> link:{java8-javadoc}/java/time/LocalTime.html#ofSecondOfDay%2Dlong%2D[ofSecondOfDay](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#ofSecondOfDay%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalTime-parse-1]]static <> link:{java8-javadoc}/java/time/LocalTime.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#parse%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-LocalTime-parse-2]]static <> link:{java8-javadoc}/java/time/LocalTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/LocalTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-LocalTime-atDate-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#atDate%2Djava.time.LocalDate%2D[atDate](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#atDate%2Djava.time.LocalDate%2D[java 9]) -* ++[[painless-api-reference-LocalTime-atOffset-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#atOffset%2Djava.time.ZoneOffset%2D[atOffset](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#atOffset%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-LocalTime-compareTo-1]]int link:{java8-javadoc}/java/time/LocalTime.html#compareTo%2Djava.time.LocalTime%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#compareTo%2Djava.time.LocalTime%2D[java 9]) -* ++[[painless-api-reference-LocalTime-format-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-LocalTime-getHour-0]]int link:{java8-javadoc}/java/time/LocalTime.html#getHour%2D%2D[getHour]()++ (link:{java9-javadoc}/java/time/LocalTime.html#getHour%2D%2D[java 9]) -* ++[[painless-api-reference-LocalTime-getMinute-0]]int link:{java8-javadoc}/java/time/LocalTime.html#getMinute%2D%2D[getMinute]()++ (link:{java9-javadoc}/java/time/LocalTime.html#getMinute%2D%2D[java 9]) -* ++[[painless-api-reference-LocalTime-getNano-0]]int link:{java8-javadoc}/java/time/LocalTime.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/LocalTime.html#getNano%2D%2D[java 9]) -* ++[[painless-api-reference-LocalTime-getSecond-0]]int link:{java8-javadoc}/java/time/LocalTime.html#getSecond%2D%2D[getSecond]()++ (link:{java9-javadoc}/java/time/LocalTime.html#getSecond%2D%2D[java 9]) -* ++[[painless-api-reference-LocalTime-isAfter-1]]boolean link:{java8-javadoc}/java/time/LocalTime.html#isAfter%2Djava.time.LocalTime%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#isAfter%2Djava.time.LocalTime%2D[java 9]) -* ++[[painless-api-reference-LocalTime-isBefore-1]]boolean link:{java8-javadoc}/java/time/LocalTime.html#isBefore%2Djava.time.LocalTime%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#isBefore%2Djava.time.LocalTime%2D[java 9]) -* ++[[painless-api-reference-LocalTime-minus-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-LocalTime-minus-2]]<> link:{java8-javadoc}/java/time/LocalTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/LocalTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-LocalTime-minusHours-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#minusHours%2Dlong%2D[minusHours](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#minusHours%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalTime-minusMinutes-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#minusMinutes%2Dlong%2D[minusMinutes](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#minusMinutes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalTime-minusNanos-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#minusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalTime-minusSeconds-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#minusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalTime-plus-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-LocalTime-plus-2]]<> link:{java8-javadoc}/java/time/LocalTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/LocalTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-LocalTime-plusHours-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#plusHours%2Dlong%2D[plusHours](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#plusHours%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalTime-plusMinutes-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#plusMinutes%2Dlong%2D[plusMinutes](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#plusMinutes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalTime-plusNanos-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#plusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalTime-plusSeconds-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#plusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalTime-toNanoOfDay-0]]long link:{java8-javadoc}/java/time/LocalTime.html#toNanoOfDay%2D%2D[toNanoOfDay]()++ (link:{java9-javadoc}/java/time/LocalTime.html#toNanoOfDay%2D%2D[java 9]) -* ++[[painless-api-reference-LocalTime-toSecondOfDay-0]]int link:{java8-javadoc}/java/time/LocalTime.html#toSecondOfDay%2D%2D[toSecondOfDay]()++ (link:{java9-javadoc}/java/time/LocalTime.html#toSecondOfDay%2D%2D[java 9]) -* ++[[painless-api-reference-LocalTime-truncatedTo-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[truncatedTo](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-LocalTime-with-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-LocalTime-with-2]]<> link:{java8-javadoc}/java/time/LocalTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/LocalTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LocalTime-withHour-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#withHour%2Dint%2D[withHour](int)++ (link:{java9-javadoc}/java/time/LocalTime.html#withHour%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalTime-withMinute-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#withMinute%2Dint%2D[withMinute](int)++ (link:{java9-javadoc}/java/time/LocalTime.html#withMinute%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalTime-withNano-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#withNano%2Dint%2D[withNano](int)++ (link:{java9-javadoc}/java/time/LocalTime.html#withNano%2Dint%2D[java 9]) -* ++[[painless-api-reference-LocalTime-withSecond-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#withSecond%2Dint%2D[withSecond](int)++ (link:{java9-javadoc}/java/time/LocalTime.html#withSecond%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Locale.Builder.asciidoc b/docs/reference/painless-api-reference/Locale.Builder.asciidoc deleted file mode 100644 index 8fad8099de9d9..0000000000000 --- a/docs/reference/painless-api-reference/Locale.Builder.asciidoc +++ /dev/null @@ -1,21 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Locale-Builder]]++Locale.Builder++:: -* ++[[painless-api-reference-Locale-Builder-Locale.Builder-0]]link:{java8-javadoc}/java/util/Locale$Builder.html#Locale.Builder%2D%2D[Locale.Builder]()++ (link:{java9-javadoc}/java/util/Locale$Builder.html#Locale.Builder%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-Builder-addUnicodeLocaleAttribute-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#addUnicodeLocaleAttribute%2Djava.lang.String%2D[addUnicodeLocaleAttribute](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#addUnicodeLocaleAttribute%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-Builder-build-0]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#build%2D%2D[build]()++ (link:{java9-javadoc}/java/util/Locale$Builder.html#build%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-Builder-clear-0]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#clear%2D%2D[clear]()++ (link:{java9-javadoc}/java/util/Locale$Builder.html#clear%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-Builder-clearExtensions-0]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#clearExtensions%2D%2D[clearExtensions]()++ (link:{java9-javadoc}/java/util/Locale$Builder.html#clearExtensions%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-Builder-removeUnicodeLocaleAttribute-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#removeUnicodeLocaleAttribute%2Djava.lang.String%2D[removeUnicodeLocaleAttribute](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#removeUnicodeLocaleAttribute%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-Builder-setExtension-2]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setExtension%2Dchar%2Djava.lang.String%2D[setExtension](char, <>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setExtension%2Dchar%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-Builder-setLanguage-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setLanguage%2Djava.lang.String%2D[setLanguage](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setLanguage%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-Builder-setLanguageTag-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setLanguageTag%2Djava.lang.String%2D[setLanguageTag](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setLanguageTag%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-Builder-setLocale-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setLocale%2Djava.util.Locale%2D[setLocale](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setLocale%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Locale-Builder-setRegion-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setRegion%2Djava.lang.String%2D[setRegion](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setRegion%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-Builder-setScript-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setScript%2Djava.lang.String%2D[setScript](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setScript%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-Builder-setUnicodeLocaleKeyword-2]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setUnicodeLocaleKeyword%2Djava.lang.String%2Djava.lang.String%2D[setUnicodeLocaleKeyword](<>, <>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setUnicodeLocaleKeyword%2Djava.lang.String%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-Builder-setVariant-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setVariant%2Djava.lang.String%2D[setVariant](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setVariant%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Locale.Category.asciidoc b/docs/reference/painless-api-reference/Locale.Category.asciidoc deleted file mode 100644 index 37a57018963dc..0000000000000 --- a/docs/reference/painless-api-reference/Locale.Category.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Locale-Category]]++Locale.Category++:: -** [[painless-api-reference-Locale-Category-DISPLAY]]static <> link:{java8-javadoc}/java/util/Locale$Category.html#DISPLAY[DISPLAY] (link:{java9-javadoc}/java/util/Locale$Category.html#DISPLAY[java 9]) -** [[painless-api-reference-Locale-Category-FORMAT]]static <> link:{java8-javadoc}/java/util/Locale$Category.html#FORMAT[FORMAT] (link:{java9-javadoc}/java/util/Locale$Category.html#FORMAT[java 9]) -* ++[[painless-api-reference-Locale-Category-valueOf-1]]static <> link:{java8-javadoc}/java/util/Locale$Category.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/util/Locale$Category.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-Category-values-0]]static <>[] link:{java8-javadoc}/java/util/Locale$Category.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/util/Locale$Category.html#values%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Locale.FilteringMode.asciidoc b/docs/reference/painless-api-reference/Locale.FilteringMode.asciidoc deleted file mode 100644 index e4399e146fcc0..0000000000000 --- a/docs/reference/painless-api-reference/Locale.FilteringMode.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Locale-FilteringMode]]++Locale.FilteringMode++:: -** [[painless-api-reference-Locale-FilteringMode-AUTOSELECT_FILTERING]]static <> link:{java8-javadoc}/java/util/Locale$FilteringMode.html#AUTOSELECT_FILTERING[AUTOSELECT_FILTERING] (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#AUTOSELECT_FILTERING[java 9]) -** [[painless-api-reference-Locale-FilteringMode-EXTENDED_FILTERING]]static <> link:{java8-javadoc}/java/util/Locale$FilteringMode.html#EXTENDED_FILTERING[EXTENDED_FILTERING] (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#EXTENDED_FILTERING[java 9]) -** [[painless-api-reference-Locale-FilteringMode-IGNORE_EXTENDED_RANGES]]static <> link:{java8-javadoc}/java/util/Locale$FilteringMode.html#IGNORE_EXTENDED_RANGES[IGNORE_EXTENDED_RANGES] (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#IGNORE_EXTENDED_RANGES[java 9]) -** [[painless-api-reference-Locale-FilteringMode-MAP_EXTENDED_RANGES]]static <> link:{java8-javadoc}/java/util/Locale$FilteringMode.html#MAP_EXTENDED_RANGES[MAP_EXTENDED_RANGES] (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#MAP_EXTENDED_RANGES[java 9]) -** [[painless-api-reference-Locale-FilteringMode-REJECT_EXTENDED_RANGES]]static <> link:{java8-javadoc}/java/util/Locale$FilteringMode.html#REJECT_EXTENDED_RANGES[REJECT_EXTENDED_RANGES] (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#REJECT_EXTENDED_RANGES[java 9]) -* ++[[painless-api-reference-Locale-FilteringMode-valueOf-1]]static <> link:{java8-javadoc}/java/util/Locale$FilteringMode.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-FilteringMode-values-0]]static <>[] link:{java8-javadoc}/java/util/Locale$FilteringMode.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#values%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Locale.LanguageRange.asciidoc b/docs/reference/painless-api-reference/Locale.LanguageRange.asciidoc deleted file mode 100644 index 0e0038d3dd68d..0000000000000 --- a/docs/reference/painless-api-reference/Locale.LanguageRange.asciidoc +++ /dev/null @@ -1,16 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Locale-LanguageRange]]++Locale.LanguageRange++:: -** [[painless-api-reference-Locale-LanguageRange-MAX_WEIGHT]]static double link:{java8-javadoc}/java/util/Locale$LanguageRange.html#MAX_WEIGHT[MAX_WEIGHT] (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#MAX_WEIGHT[java 9]) -** [[painless-api-reference-Locale-LanguageRange-MIN_WEIGHT]]static double link:{java8-javadoc}/java/util/Locale$LanguageRange.html#MIN_WEIGHT[MIN_WEIGHT] (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#MIN_WEIGHT[java 9]) -* ++[[painless-api-reference-Locale-LanguageRange-mapEquivalents-2]]static <> link:{java8-javadoc}/java/util/Locale$LanguageRange.html#mapEquivalents%2Djava.util.List%2Djava.util.Map%2D[mapEquivalents](<>, <>)++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#mapEquivalents%2Djava.util.List%2Djava.util.Map%2D[java 9]) -* ++[[painless-api-reference-Locale-LanguageRange-parse-1]]static <> link:{java8-javadoc}/java/util/Locale$LanguageRange.html#parse%2Djava.lang.String%2D[parse](<>)++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#parse%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-LanguageRange-parse-2]]static <> link:{java8-javadoc}/java/util/Locale$LanguageRange.html#parse%2Djava.lang.String%2Djava.util.Map%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#parse%2Djava.lang.String%2Djava.util.Map%2D[java 9]) -* ++[[painless-api-reference-Locale-LanguageRange-Locale.LanguageRange-1]]link:{java8-javadoc}/java/util/Locale$LanguageRange.html#Locale.LanguageRange%2Djava.lang.String%2D[Locale.LanguageRange](<>)++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#Locale.LanguageRange%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-LanguageRange-Locale.LanguageRange-2]]link:{java8-javadoc}/java/util/Locale$LanguageRange.html#Locale.LanguageRange%2Djava.lang.String%2Ddouble%2D[Locale.LanguageRange](<>, double)++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#Locale.LanguageRange%2Djava.lang.String%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Locale-LanguageRange-getRange-0]]<> link:{java8-javadoc}/java/util/Locale$LanguageRange.html#getRange%2D%2D[getRange]()++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#getRange%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-LanguageRange-getWeight-0]]double link:{java8-javadoc}/java/util/Locale$LanguageRange.html#getWeight%2D%2D[getWeight]()++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#getWeight%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Locale.asciidoc b/docs/reference/painless-api-reference/Locale.asciidoc deleted file mode 100644 index d041c6c1f7fb3..0000000000000 --- a/docs/reference/painless-api-reference/Locale.asciidoc +++ /dev/null @@ -1,69 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Locale]]++Locale++:: -** [[painless-api-reference-Locale-CANADA]]static <> link:{java8-javadoc}/java/util/Locale.html#CANADA[CANADA] (link:{java9-javadoc}/java/util/Locale.html#CANADA[java 9]) -** [[painless-api-reference-Locale-CANADA_FRENCH]]static <> link:{java8-javadoc}/java/util/Locale.html#CANADA_FRENCH[CANADA_FRENCH] (link:{java9-javadoc}/java/util/Locale.html#CANADA_FRENCH[java 9]) -** [[painless-api-reference-Locale-CHINA]]static <> link:{java8-javadoc}/java/util/Locale.html#CHINA[CHINA] (link:{java9-javadoc}/java/util/Locale.html#CHINA[java 9]) -** [[painless-api-reference-Locale-CHINESE]]static <> link:{java8-javadoc}/java/util/Locale.html#CHINESE[CHINESE] (link:{java9-javadoc}/java/util/Locale.html#CHINESE[java 9]) -** [[painless-api-reference-Locale-ENGLISH]]static <> link:{java8-javadoc}/java/util/Locale.html#ENGLISH[ENGLISH] (link:{java9-javadoc}/java/util/Locale.html#ENGLISH[java 9]) -** [[painless-api-reference-Locale-FRANCE]]static <> link:{java8-javadoc}/java/util/Locale.html#FRANCE[FRANCE] (link:{java9-javadoc}/java/util/Locale.html#FRANCE[java 9]) -** [[painless-api-reference-Locale-FRENCH]]static <> link:{java8-javadoc}/java/util/Locale.html#FRENCH[FRENCH] (link:{java9-javadoc}/java/util/Locale.html#FRENCH[java 9]) -** [[painless-api-reference-Locale-GERMAN]]static <> link:{java8-javadoc}/java/util/Locale.html#GERMAN[GERMAN] (link:{java9-javadoc}/java/util/Locale.html#GERMAN[java 9]) -** [[painless-api-reference-Locale-GERMANY]]static <> link:{java8-javadoc}/java/util/Locale.html#GERMANY[GERMANY] (link:{java9-javadoc}/java/util/Locale.html#GERMANY[java 9]) -** [[painless-api-reference-Locale-ITALIAN]]static <> link:{java8-javadoc}/java/util/Locale.html#ITALIAN[ITALIAN] (link:{java9-javadoc}/java/util/Locale.html#ITALIAN[java 9]) -** [[painless-api-reference-Locale-ITALY]]static <> link:{java8-javadoc}/java/util/Locale.html#ITALY[ITALY] (link:{java9-javadoc}/java/util/Locale.html#ITALY[java 9]) -** [[painless-api-reference-Locale-JAPAN]]static <> link:{java8-javadoc}/java/util/Locale.html#JAPAN[JAPAN] (link:{java9-javadoc}/java/util/Locale.html#JAPAN[java 9]) -** [[painless-api-reference-Locale-JAPANESE]]static <> link:{java8-javadoc}/java/util/Locale.html#JAPANESE[JAPANESE] (link:{java9-javadoc}/java/util/Locale.html#JAPANESE[java 9]) -** [[painless-api-reference-Locale-KOREA]]static <> link:{java8-javadoc}/java/util/Locale.html#KOREA[KOREA] (link:{java9-javadoc}/java/util/Locale.html#KOREA[java 9]) -** [[painless-api-reference-Locale-KOREAN]]static <> link:{java8-javadoc}/java/util/Locale.html#KOREAN[KOREAN] (link:{java9-javadoc}/java/util/Locale.html#KOREAN[java 9]) -** [[painless-api-reference-Locale-PRC]]static <> link:{java8-javadoc}/java/util/Locale.html#PRC[PRC] (link:{java9-javadoc}/java/util/Locale.html#PRC[java 9]) -** [[painless-api-reference-Locale-PRIVATE_USE_EXTENSION]]static char link:{java8-javadoc}/java/util/Locale.html#PRIVATE_USE_EXTENSION[PRIVATE_USE_EXTENSION] (link:{java9-javadoc}/java/util/Locale.html#PRIVATE_USE_EXTENSION[java 9]) -** [[painless-api-reference-Locale-ROOT]]static <> link:{java8-javadoc}/java/util/Locale.html#ROOT[ROOT] (link:{java9-javadoc}/java/util/Locale.html#ROOT[java 9]) -** [[painless-api-reference-Locale-SIMPLIFIED_CHINESE]]static <> link:{java8-javadoc}/java/util/Locale.html#SIMPLIFIED_CHINESE[SIMPLIFIED_CHINESE] (link:{java9-javadoc}/java/util/Locale.html#SIMPLIFIED_CHINESE[java 9]) -** [[painless-api-reference-Locale-TAIWAN]]static <> link:{java8-javadoc}/java/util/Locale.html#TAIWAN[TAIWAN] (link:{java9-javadoc}/java/util/Locale.html#TAIWAN[java 9]) -** [[painless-api-reference-Locale-TRADITIONAL_CHINESE]]static <> link:{java8-javadoc}/java/util/Locale.html#TRADITIONAL_CHINESE[TRADITIONAL_CHINESE] (link:{java9-javadoc}/java/util/Locale.html#TRADITIONAL_CHINESE[java 9]) -** [[painless-api-reference-Locale-UK]]static <> link:{java8-javadoc}/java/util/Locale.html#UK[UK] (link:{java9-javadoc}/java/util/Locale.html#UK[java 9]) -** [[painless-api-reference-Locale-UNICODE_LOCALE_EXTENSION]]static char link:{java8-javadoc}/java/util/Locale.html#UNICODE_LOCALE_EXTENSION[UNICODE_LOCALE_EXTENSION] (link:{java9-javadoc}/java/util/Locale.html#UNICODE_LOCALE_EXTENSION[java 9]) -** [[painless-api-reference-Locale-US]]static <> link:{java8-javadoc}/java/util/Locale.html#US[US] (link:{java9-javadoc}/java/util/Locale.html#US[java 9]) -* ++[[painless-api-reference-Locale-filter-2]]static <> link:{java8-javadoc}/java/util/Locale.html#filter%2Djava.util.List%2Djava.util.Collection%2D[filter](<>, <>)++ (link:{java9-javadoc}/java/util/Locale.html#filter%2Djava.util.List%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Locale-filterTags-2]]static <> link:{java8-javadoc}/java/util/Locale.html#filterTags%2Djava.util.List%2Djava.util.Collection%2D[filterTags](<>, <>)++ (link:{java9-javadoc}/java/util/Locale.html#filterTags%2Djava.util.List%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Locale-forLanguageTag-1]]static <> link:{java8-javadoc}/java/util/Locale.html#forLanguageTag%2Djava.lang.String%2D[forLanguageTag](<>)++ (link:{java9-javadoc}/java/util/Locale.html#forLanguageTag%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/util/Locale.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/util/Locale.html#getAvailableLocales%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getDefault-0]]static <> link:{java8-javadoc}/java/util/Locale.html#getDefault%2D%2D[getDefault]()++ (link:{java9-javadoc}/java/util/Locale.html#getDefault%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getDefault-1]]static <> link:{java8-javadoc}/java/util/Locale.html#getDefault%2Djava.util.Locale$Category%2D[getDefault](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getDefault%2Djava.util.Locale$Category%2D[java 9]) -* ++[[painless-api-reference-Locale-getISOCountries-0]]static <>[] link:{java8-javadoc}/java/util/Locale.html#getISOCountries%2D%2D[getISOCountries]()++ (link:{java9-javadoc}/java/util/Locale.html#getISOCountries%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getISOLanguages-0]]static <>[] link:{java8-javadoc}/java/util/Locale.html#getISOLanguages%2D%2D[getISOLanguages]()++ (link:{java9-javadoc}/java/util/Locale.html#getISOLanguages%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-lookup-2]]static <> link:{java8-javadoc}/java/util/Locale.html#lookup%2Djava.util.List%2Djava.util.Collection%2D[lookup](<>, <>)++ (link:{java9-javadoc}/java/util/Locale.html#lookup%2Djava.util.List%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Locale-lookupTag-2]]static <> link:{java8-javadoc}/java/util/Locale.html#lookupTag%2Djava.util.List%2Djava.util.Collection%2D[lookupTag](<>, <>)++ (link:{java9-javadoc}/java/util/Locale.html#lookupTag%2Djava.util.List%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Locale-Locale-1]]link:{java8-javadoc}/java/util/Locale.html#Locale%2Djava.lang.String%2D[Locale](<>)++ (link:{java9-javadoc}/java/util/Locale.html#Locale%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-Locale-2]]link:{java8-javadoc}/java/util/Locale.html#Locale%2Djava.lang.String%2Djava.lang.String%2D[Locale](<>, <>)++ (link:{java9-javadoc}/java/util/Locale.html#Locale%2Djava.lang.String%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-Locale-3]]link:{java8-javadoc}/java/util/Locale.html#Locale%2Djava.lang.String%2Djava.lang.String%2Djava.lang.String%2D[Locale](<>, <>, <>)++ (link:{java9-javadoc}/java/util/Locale.html#Locale%2Djava.lang.String%2Djava.lang.String%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-clone-0]]def link:{java8-javadoc}/java/util/Locale.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/Locale.html#clone%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getCountry-0]]<> link:{java8-javadoc}/java/util/Locale.html#getCountry%2D%2D[getCountry]()++ (link:{java9-javadoc}/java/util/Locale.html#getCountry%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getDisplayCountry-0]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayCountry%2D%2D[getDisplayCountry]()++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayCountry%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getDisplayCountry-1]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayCountry%2Djava.util.Locale%2D[getDisplayCountry](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayCountry%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Locale-getDisplayLanguage-0]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayLanguage%2D%2D[getDisplayLanguage]()++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayLanguage%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getDisplayLanguage-1]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayLanguage%2Djava.util.Locale%2D[getDisplayLanguage](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayLanguage%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Locale-getDisplayName-0]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayName%2D%2D[getDisplayName]()++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayName%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getDisplayName-1]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayName%2Djava.util.Locale%2D[getDisplayName](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayName%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Locale-getDisplayScript-0]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayScript%2D%2D[getDisplayScript]()++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayScript%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getDisplayScript-1]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayScript%2Djava.util.Locale%2D[getDisplayScript](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayScript%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Locale-getDisplayVariant-0]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayVariant%2D%2D[getDisplayVariant]()++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayVariant%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getDisplayVariant-1]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayVariant%2Djava.util.Locale%2D[getDisplayVariant](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayVariant%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Locale-getExtension-1]]<> link:{java8-javadoc}/java/util/Locale.html#getExtension%2Dchar%2D[getExtension](char)++ (link:{java9-javadoc}/java/util/Locale.html#getExtension%2Dchar%2D[java 9]) -* ++[[painless-api-reference-Locale-getExtensionKeys-0]]<> link:{java8-javadoc}/java/util/Locale.html#getExtensionKeys%2D%2D[getExtensionKeys]()++ (link:{java9-javadoc}/java/util/Locale.html#getExtensionKeys%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getISO3Country-0]]<> link:{java8-javadoc}/java/util/Locale.html#getISO3Country%2D%2D[getISO3Country]()++ (link:{java9-javadoc}/java/util/Locale.html#getISO3Country%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getISO3Language-0]]<> link:{java8-javadoc}/java/util/Locale.html#getISO3Language%2D%2D[getISO3Language]()++ (link:{java9-javadoc}/java/util/Locale.html#getISO3Language%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getLanguage-0]]<> link:{java8-javadoc}/java/util/Locale.html#getLanguage%2D%2D[getLanguage]()++ (link:{java9-javadoc}/java/util/Locale.html#getLanguage%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getScript-0]]<> link:{java8-javadoc}/java/util/Locale.html#getScript%2D%2D[getScript]()++ (link:{java9-javadoc}/java/util/Locale.html#getScript%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getUnicodeLocaleAttributes-0]]<> link:{java8-javadoc}/java/util/Locale.html#getUnicodeLocaleAttributes%2D%2D[getUnicodeLocaleAttributes]()++ (link:{java9-javadoc}/java/util/Locale.html#getUnicodeLocaleAttributes%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getUnicodeLocaleKeys-0]]<> link:{java8-javadoc}/java/util/Locale.html#getUnicodeLocaleKeys%2D%2D[getUnicodeLocaleKeys]()++ (link:{java9-javadoc}/java/util/Locale.html#getUnicodeLocaleKeys%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-getUnicodeLocaleType-1]]<> link:{java8-javadoc}/java/util/Locale.html#getUnicodeLocaleType%2Djava.lang.String%2D[getUnicodeLocaleType](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getUnicodeLocaleType%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Locale-getVariant-0]]<> link:{java8-javadoc}/java/util/Locale.html#getVariant%2D%2D[getVariant]()++ (link:{java9-javadoc}/java/util/Locale.html#getVariant%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-hasExtensions-0]]boolean link:{java8-javadoc}/java/util/Locale.html#hasExtensions%2D%2D[hasExtensions]()++ (link:{java9-javadoc}/java/util/Locale.html#hasExtensions%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-stripExtensions-0]]<> link:{java8-javadoc}/java/util/Locale.html#stripExtensions%2D%2D[stripExtensions]()++ (link:{java9-javadoc}/java/util/Locale.html#stripExtensions%2D%2D[java 9]) -* ++[[painless-api-reference-Locale-toLanguageTag-0]]<> link:{java8-javadoc}/java/util/Locale.html#toLanguageTag%2D%2D[toLanguageTag]()++ (link:{java9-javadoc}/java/util/Locale.html#toLanguageTag%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Long.asciidoc b/docs/reference/painless-api-reference/Long.asciidoc deleted file mode 100644 index e35c80f63b96d..0000000000000 --- a/docs/reference/painless-api-reference/Long.asciidoc +++ /dev/null @@ -1,44 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Long]]++Long++:: -** [[painless-api-reference-Long-BYTES]]static int link:{java8-javadoc}/java/lang/Long.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Long.html#BYTES[java 9]) -** [[painless-api-reference-Long-MAX_VALUE]]static long link:{java8-javadoc}/java/lang/Long.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Long.html#MAX_VALUE[java 9]) -** [[painless-api-reference-Long-MIN_VALUE]]static long link:{java8-javadoc}/java/lang/Long.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Long.html#MIN_VALUE[java 9]) -** [[painless-api-reference-Long-SIZE]]static int link:{java8-javadoc}/java/lang/Long.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Long.html#SIZE[java 9]) -* ++[[painless-api-reference-Long-bitCount-1]]static int link:{java8-javadoc}/java/lang/Long.html#bitCount%2Dlong%2D[bitCount](long)++ (link:{java9-javadoc}/java/lang/Long.html#bitCount%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-compare-2]]static int link:{java8-javadoc}/java/lang/Long.html#compare%2Dlong%2Dlong%2D[compare](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#compare%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-compareUnsigned-2]]static int link:{java8-javadoc}/java/lang/Long.html#compareUnsigned%2Dlong%2Dlong%2D[compareUnsigned](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#compareUnsigned%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-decode-1]]static <> link:{java8-javadoc}/java/lang/Long.html#decode%2Djava.lang.String%2D[decode](<>)++ (link:{java9-javadoc}/java/lang/Long.html#decode%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Long-divideUnsigned-2]]static long link:{java8-javadoc}/java/lang/Long.html#divideUnsigned%2Dlong%2Dlong%2D[divideUnsigned](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#divideUnsigned%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-hashCode-1]]static int link:{java8-javadoc}/java/lang/Long.html#hashCode%2Dlong%2D[hashCode](long)++ (link:{java9-javadoc}/java/lang/Long.html#hashCode%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-highestOneBit-1]]static long link:{java8-javadoc}/java/lang/Long.html#highestOneBit%2Dlong%2D[highestOneBit](long)++ (link:{java9-javadoc}/java/lang/Long.html#highestOneBit%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-lowestOneBit-1]]static long link:{java8-javadoc}/java/lang/Long.html#lowestOneBit%2Dlong%2D[lowestOneBit](long)++ (link:{java9-javadoc}/java/lang/Long.html#lowestOneBit%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-max-2]]static long link:{java8-javadoc}/java/lang/Long.html#max%2Dlong%2Dlong%2D[max](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#max%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-min-2]]static long link:{java8-javadoc}/java/lang/Long.html#min%2Dlong%2Dlong%2D[min](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#min%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-numberOfLeadingZeros-1]]static int link:{java8-javadoc}/java/lang/Long.html#numberOfLeadingZeros%2Dlong%2D[numberOfLeadingZeros](long)++ (link:{java9-javadoc}/java/lang/Long.html#numberOfLeadingZeros%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-numberOfTrailingZeros-1]]static int link:{java8-javadoc}/java/lang/Long.html#numberOfTrailingZeros%2Dlong%2D[numberOfTrailingZeros](long)++ (link:{java9-javadoc}/java/lang/Long.html#numberOfTrailingZeros%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-parseLong-1]]static long link:{java8-javadoc}/java/lang/Long.html#parseLong%2Djava.lang.String%2D[parseLong](<>)++ (link:{java9-javadoc}/java/lang/Long.html#parseLong%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Long-parseLong-2]]static long link:{java8-javadoc}/java/lang/Long.html#parseLong%2Djava.lang.String%2Dint%2D[parseLong](<>, int)++ (link:{java9-javadoc}/java/lang/Long.html#parseLong%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-Long-parseUnsignedLong-1]]static long link:{java8-javadoc}/java/lang/Long.html#parseUnsignedLong%2Djava.lang.String%2D[parseUnsignedLong](<>)++ (link:{java9-javadoc}/java/lang/Long.html#parseUnsignedLong%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Long-parseUnsignedLong-2]]static long link:{java8-javadoc}/java/lang/Long.html#parseUnsignedLong%2Djava.lang.String%2Dint%2D[parseUnsignedLong](<>, int)++ (link:{java9-javadoc}/java/lang/Long.html#parseUnsignedLong%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-Long-remainderUnsigned-2]]static long link:{java8-javadoc}/java/lang/Long.html#remainderUnsigned%2Dlong%2Dlong%2D[remainderUnsigned](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#remainderUnsigned%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-reverse-1]]static long link:{java8-javadoc}/java/lang/Long.html#reverse%2Dlong%2D[reverse](long)++ (link:{java9-javadoc}/java/lang/Long.html#reverse%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-reverseBytes-1]]static long link:{java8-javadoc}/java/lang/Long.html#reverseBytes%2Dlong%2D[reverseBytes](long)++ (link:{java9-javadoc}/java/lang/Long.html#reverseBytes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-rotateLeft-2]]static long link:{java8-javadoc}/java/lang/Long.html#rotateLeft%2Dlong%2Dint%2D[rotateLeft](long, int)++ (link:{java9-javadoc}/java/lang/Long.html#rotateLeft%2Dlong%2Dint%2D[java 9]) -* ++[[painless-api-reference-Long-rotateRight-2]]static long link:{java8-javadoc}/java/lang/Long.html#rotateRight%2Dlong%2Dint%2D[rotateRight](long, int)++ (link:{java9-javadoc}/java/lang/Long.html#rotateRight%2Dlong%2Dint%2D[java 9]) -* ++[[painless-api-reference-Long-signum-1]]static int link:{java8-javadoc}/java/lang/Long.html#signum%2Dlong%2D[signum](long)++ (link:{java9-javadoc}/java/lang/Long.html#signum%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-sum-2]]static long link:{java8-javadoc}/java/lang/Long.html#sum%2Dlong%2Dlong%2D[sum](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#sum%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-toBinaryString-1]]static <> link:{java8-javadoc}/java/lang/Long.html#toBinaryString%2Dlong%2D[toBinaryString](long)++ (link:{java9-javadoc}/java/lang/Long.html#toBinaryString%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-toHexString-1]]static <> link:{java8-javadoc}/java/lang/Long.html#toHexString%2Dlong%2D[toHexString](long)++ (link:{java9-javadoc}/java/lang/Long.html#toHexString%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-toOctalString-1]]static <> link:{java8-javadoc}/java/lang/Long.html#toOctalString%2Dlong%2D[toOctalString](long)++ (link:{java9-javadoc}/java/lang/Long.html#toOctalString%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-toString-1]]static <> link:{java8-javadoc}/java/lang/Long.html#toString%2Dlong%2D[toString](long)++ (link:{java9-javadoc}/java/lang/Long.html#toString%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-toString-2]]static <> link:{java8-javadoc}/java/lang/Long.html#toString%2Dlong%2Dint%2D[toString](long, int)++ (link:{java9-javadoc}/java/lang/Long.html#toString%2Dlong%2Dint%2D[java 9]) -* ++[[painless-api-reference-Long-toUnsignedString-1]]static <> link:{java8-javadoc}/java/lang/Long.html#toUnsignedString%2Dlong%2D[toUnsignedString](long)++ (link:{java9-javadoc}/java/lang/Long.html#toUnsignedString%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-toUnsignedString-2]]static <> link:{java8-javadoc}/java/lang/Long.html#toUnsignedString%2Dlong%2Dint%2D[toUnsignedString](long, int)++ (link:{java9-javadoc}/java/lang/Long.html#toUnsignedString%2Dlong%2Dint%2D[java 9]) -* ++[[painless-api-reference-Long-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Long.html#valueOf%2Dlong%2D[valueOf](long)++ (link:{java9-javadoc}/java/lang/Long.html#valueOf%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Long-valueOf-2]]static <> link:{java8-javadoc}/java/lang/Long.html#valueOf%2Djava.lang.String%2Dint%2D[valueOf](<>, int)++ (link:{java9-javadoc}/java/lang/Long.html#valueOf%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-Long-compareTo-1]]int link:{java8-javadoc}/java/lang/Long.html#compareTo%2Djava.lang.Long%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Long.html#compareTo%2Djava.lang.Long%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/LongBinaryOperator.asciidoc b/docs/reference/painless-api-reference/LongBinaryOperator.asciidoc deleted file mode 100644 index 45a777a69715b..0000000000000 --- a/docs/reference/painless-api-reference/LongBinaryOperator.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LongBinaryOperator]]++LongBinaryOperator++:: -* ++[[painless-api-reference-LongBinaryOperator-applyAsLong-2]]long link:{java8-javadoc}/java/util/function/LongBinaryOperator.html#applyAsLong%2Dlong%2Dlong%2D[applyAsLong](long, long)++ (link:{java9-javadoc}/java/util/function/LongBinaryOperator.html#applyAsLong%2Dlong%2Dlong%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/LongConsumer.asciidoc b/docs/reference/painless-api-reference/LongConsumer.asciidoc deleted file mode 100644 index 28bbc92c03121..0000000000000 --- a/docs/reference/painless-api-reference/LongConsumer.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LongConsumer]]++LongConsumer++:: -* ++[[painless-api-reference-LongConsumer-accept-1]]void link:{java8-javadoc}/java/util/function/LongConsumer.html#accept%2Dlong%2D[accept](long)++ (link:{java9-javadoc}/java/util/function/LongConsumer.html#accept%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LongConsumer-andThen-1]]<> link:{java8-javadoc}/java/util/function/LongConsumer.html#andThen%2Djava.util.function.LongConsumer%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/LongConsumer.html#andThen%2Djava.util.function.LongConsumer%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/LongFunction.asciidoc b/docs/reference/painless-api-reference/LongFunction.asciidoc deleted file mode 100644 index 033392914a5e3..0000000000000 --- a/docs/reference/painless-api-reference/LongFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LongFunction]]++LongFunction++:: -* ++[[painless-api-reference-LongFunction-apply-1]]def link:{java8-javadoc}/java/util/function/LongFunction.html#apply%2Dlong%2D[apply](long)++ (link:{java9-javadoc}/java/util/function/LongFunction.html#apply%2Dlong%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/LongPredicate.asciidoc b/docs/reference/painless-api-reference/LongPredicate.asciidoc deleted file mode 100644 index 59b719572e3d1..0000000000000 --- a/docs/reference/painless-api-reference/LongPredicate.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LongPredicate]]++LongPredicate++:: -* ++[[painless-api-reference-LongPredicate-and-1]]<> link:{java8-javadoc}/java/util/function/LongPredicate.html#and%2Djava.util.function.LongPredicate%2D[and](<>)++ (link:{java9-javadoc}/java/util/function/LongPredicate.html#and%2Djava.util.function.LongPredicate%2D[java 9]) -* ++[[painless-api-reference-LongPredicate-negate-0]]<> link:{java8-javadoc}/java/util/function/LongPredicate.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/util/function/LongPredicate.html#negate%2D%2D[java 9]) -* ++[[painless-api-reference-LongPredicate-or-1]]<> link:{java8-javadoc}/java/util/function/LongPredicate.html#or%2Djava.util.function.LongPredicate%2D[or](<>)++ (link:{java9-javadoc}/java/util/function/LongPredicate.html#or%2Djava.util.function.LongPredicate%2D[java 9]) -* ++[[painless-api-reference-LongPredicate-test-1]]boolean link:{java8-javadoc}/java/util/function/LongPredicate.html#test%2Dlong%2D[test](long)++ (link:{java9-javadoc}/java/util/function/LongPredicate.html#test%2Dlong%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/LongStream.Builder.asciidoc b/docs/reference/painless-api-reference/LongStream.Builder.asciidoc deleted file mode 100644 index 98c55b149ae68..0000000000000 --- a/docs/reference/painless-api-reference/LongStream.Builder.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LongStream-Builder]]++LongStream.Builder++:: -* ++[[painless-api-reference-LongStream-Builder-add-1]]<> link:{java8-javadoc}/java/util/stream/LongStream$Builder.html#add%2Dlong%2D[add](long)++ (link:{java9-javadoc}/java/util/stream/LongStream$Builder.html#add%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LongStream-Builder-build-0]]<> link:{java8-javadoc}/java/util/stream/LongStream$Builder.html#build%2D%2D[build]()++ (link:{java9-javadoc}/java/util/stream/LongStream$Builder.html#build%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/LongStream.asciidoc b/docs/reference/painless-api-reference/LongStream.asciidoc deleted file mode 100644 index 08ea178451a0a..0000000000000 --- a/docs/reference/painless-api-reference/LongStream.asciidoc +++ /dev/null @@ -1,46 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LongStream]]++LongStream++:: -* ++[[painless-api-reference-LongStream-builder-0]]static <> link:{java8-javadoc}/java/util/stream/LongStream.html#builder%2D%2D[builder]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#builder%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-concat-2]]static <> link:{java8-javadoc}/java/util/stream/LongStream.html#concat%2Djava.util.stream.LongStream%2Djava.util.stream.LongStream%2D[concat](<>, <>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#concat%2Djava.util.stream.LongStream%2Djava.util.stream.LongStream%2D[java 9]) -* ++[[painless-api-reference-LongStream-empty-0]]static <> link:{java8-javadoc}/java/util/stream/LongStream.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#empty%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-of-1]]static <> link:{java8-javadoc}/java/util/stream/LongStream.html#of%2Dlong:A%2D[of](long[])++ (link:{java9-javadoc}/java/util/stream/LongStream.html#of%2Dlong:A%2D[java 9]) -* ++[[painless-api-reference-LongStream-range-2]]static <> link:{java8-javadoc}/java/util/stream/LongStream.html#range%2Dlong%2Dlong%2D[range](long, long)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#range%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LongStream-rangeClosed-2]]static <> link:{java8-javadoc}/java/util/stream/LongStream.html#rangeClosed%2Dlong%2Dlong%2D[rangeClosed](long, long)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#rangeClosed%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LongStream-allMatch-1]]boolean link:{java8-javadoc}/java/util/stream/LongStream.html#allMatch%2Djava.util.function.LongPredicate%2D[allMatch](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#allMatch%2Djava.util.function.LongPredicate%2D[java 9]) -* ++[[painless-api-reference-LongStream-anyMatch-1]]boolean link:{java8-javadoc}/java/util/stream/LongStream.html#anyMatch%2Djava.util.function.LongPredicate%2D[anyMatch](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#anyMatch%2Djava.util.function.LongPredicate%2D[java 9]) -* ++[[painless-api-reference-LongStream-asDoubleStream-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#asDoubleStream%2D%2D[asDoubleStream]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#asDoubleStream%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-average-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#average%2D%2D[average]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#average%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-boxed-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#boxed%2D%2D[boxed]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#boxed%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-collect-3]]def link:{java8-javadoc}/java/util/stream/LongStream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.ObjLongConsumer%2Djava.util.function.BiConsumer%2D[collect](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.ObjLongConsumer%2Djava.util.function.BiConsumer%2D[java 9]) -* ++[[painless-api-reference-LongStream-count-0]]long link:{java8-javadoc}/java/util/stream/LongStream.html#count%2D%2D[count]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#count%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-distinct-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#distinct%2D%2D[distinct]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#distinct%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-filter-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#filter%2Djava.util.function.LongPredicate%2D[filter](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#filter%2Djava.util.function.LongPredicate%2D[java 9]) -* ++[[painless-api-reference-LongStream-findAny-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#findAny%2D%2D[findAny]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#findAny%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-findFirst-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#findFirst%2D%2D[findFirst]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#findFirst%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-flatMap-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#flatMap%2Djava.util.function.LongFunction%2D[flatMap](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#flatMap%2Djava.util.function.LongFunction%2D[java 9]) -* ++[[painless-api-reference-LongStream-forEach-1]]void link:{java8-javadoc}/java/util/stream/LongStream.html#forEach%2Djava.util.function.LongConsumer%2D[forEach](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#forEach%2Djava.util.function.LongConsumer%2D[java 9]) -* ++[[painless-api-reference-LongStream-forEachOrdered-1]]void link:{java8-javadoc}/java/util/stream/LongStream.html#forEachOrdered%2Djava.util.function.LongConsumer%2D[forEachOrdered](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#forEachOrdered%2Djava.util.function.LongConsumer%2D[java 9]) -* ++[[painless-api-reference-LongStream-iterator-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#iterator%2D%2D[iterator]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#iterator%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-limit-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#limit%2Dlong%2D[limit](long)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#limit%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LongStream-map-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#map%2Djava.util.function.LongUnaryOperator%2D[map](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#map%2Djava.util.function.LongUnaryOperator%2D[java 9]) -* ++[[painless-api-reference-LongStream-mapToDouble-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#mapToDouble%2Djava.util.function.LongToDoubleFunction%2D[mapToDouble](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#mapToDouble%2Djava.util.function.LongToDoubleFunction%2D[java 9]) -* ++[[painless-api-reference-LongStream-mapToInt-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#mapToInt%2Djava.util.function.LongToIntFunction%2D[mapToInt](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#mapToInt%2Djava.util.function.LongToIntFunction%2D[java 9]) -* ++[[painless-api-reference-LongStream-mapToObj-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#mapToObj%2Djava.util.function.LongFunction%2D[mapToObj](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#mapToObj%2Djava.util.function.LongFunction%2D[java 9]) -* ++[[painless-api-reference-LongStream-max-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#max%2D%2D[max]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#max%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-min-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#min%2D%2D[min]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#min%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-noneMatch-1]]boolean link:{java8-javadoc}/java/util/stream/LongStream.html#noneMatch%2Djava.util.function.LongPredicate%2D[noneMatch](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#noneMatch%2Djava.util.function.LongPredicate%2D[java 9]) -* ++[[painless-api-reference-LongStream-peek-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#peek%2Djava.util.function.LongConsumer%2D[peek](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#peek%2Djava.util.function.LongConsumer%2D[java 9]) -* ++[[painless-api-reference-LongStream-reduce-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#reduce%2Djava.util.function.LongBinaryOperator%2D[reduce](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#reduce%2Djava.util.function.LongBinaryOperator%2D[java 9]) -* ++[[painless-api-reference-LongStream-reduce-2]]long link:{java8-javadoc}/java/util/stream/LongStream.html#reduce%2Dlong%2Djava.util.function.LongBinaryOperator%2D[reduce](long, <>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#reduce%2Dlong%2Djava.util.function.LongBinaryOperator%2D[java 9]) -* ++[[painless-api-reference-LongStream-sequential-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#sequential%2D%2D[sequential]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#sequential%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-skip-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#skip%2Dlong%2D[skip](long)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#skip%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LongStream-sorted-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#sorted%2D%2D[sorted]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#sorted%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-spliterator-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#spliterator%2D%2D[spliterator]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#spliterator%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-sum-0]]long link:{java8-javadoc}/java/util/stream/LongStream.html#sum%2D%2D[sum]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#sum%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-summaryStatistics-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#summaryStatistics%2D%2D[summaryStatistics]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#summaryStatistics%2D%2D[java 9]) -* ++[[painless-api-reference-LongStream-toArray-0]]long[] link:{java8-javadoc}/java/util/stream/LongStream.html#toArray%2D%2D[toArray]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#toArray%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/LongSummaryStatistics.asciidoc b/docs/reference/painless-api-reference/LongSummaryStatistics.asciidoc deleted file mode 100644 index b678d51efa4ac..0000000000000 --- a/docs/reference/painless-api-reference/LongSummaryStatistics.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LongSummaryStatistics]]++LongSummaryStatistics++:: -* ++[[painless-api-reference-LongSummaryStatistics-LongSummaryStatistics-0]]link:{java8-javadoc}/java/util/LongSummaryStatistics.html#LongSummaryStatistics%2D%2D[LongSummaryStatistics]()++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#LongSummaryStatistics%2D%2D[java 9]) -* ++[[painless-api-reference-LongSummaryStatistics-combine-1]]void link:{java8-javadoc}/java/util/LongSummaryStatistics.html#combine%2Djava.util.LongSummaryStatistics%2D[combine](<>)++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#combine%2Djava.util.LongSummaryStatistics%2D[java 9]) -* ++[[painless-api-reference-LongSummaryStatistics-getAverage-0]]double link:{java8-javadoc}/java/util/LongSummaryStatistics.html#getAverage%2D%2D[getAverage]()++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#getAverage%2D%2D[java 9]) -* ++[[painless-api-reference-LongSummaryStatistics-getCount-0]]long link:{java8-javadoc}/java/util/LongSummaryStatistics.html#getCount%2D%2D[getCount]()++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#getCount%2D%2D[java 9]) -* ++[[painless-api-reference-LongSummaryStatistics-getMax-0]]long link:{java8-javadoc}/java/util/LongSummaryStatistics.html#getMax%2D%2D[getMax]()++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#getMax%2D%2D[java 9]) -* ++[[painless-api-reference-LongSummaryStatistics-getMin-0]]long link:{java8-javadoc}/java/util/LongSummaryStatistics.html#getMin%2D%2D[getMin]()++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#getMin%2D%2D[java 9]) -* ++[[painless-api-reference-LongSummaryStatistics-getSum-0]]long link:{java8-javadoc}/java/util/LongSummaryStatistics.html#getSum%2D%2D[getSum]()++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#getSum%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/LongSupplier.asciidoc b/docs/reference/painless-api-reference/LongSupplier.asciidoc deleted file mode 100644 index 6693e51f17a47..0000000000000 --- a/docs/reference/painless-api-reference/LongSupplier.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LongSupplier]]++LongSupplier++:: -* ++[[painless-api-reference-LongSupplier-getAsLong-0]]long link:{java8-javadoc}/java/util/function/LongSupplier.html#getAsLong%2D%2D[getAsLong]()++ (link:{java9-javadoc}/java/util/function/LongSupplier.html#getAsLong%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/LongToDoubleFunction.asciidoc b/docs/reference/painless-api-reference/LongToDoubleFunction.asciidoc deleted file mode 100644 index 0bed3833371c7..0000000000000 --- a/docs/reference/painless-api-reference/LongToDoubleFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LongToDoubleFunction]]++LongToDoubleFunction++:: -* ++[[painless-api-reference-LongToDoubleFunction-applyAsDouble-1]]double link:{java8-javadoc}/java/util/function/LongToDoubleFunction.html#applyAsDouble%2Dlong%2D[applyAsDouble](long)++ (link:{java9-javadoc}/java/util/function/LongToDoubleFunction.html#applyAsDouble%2Dlong%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/LongToIntFunction.asciidoc b/docs/reference/painless-api-reference/LongToIntFunction.asciidoc deleted file mode 100644 index 2586ba85511b0..0000000000000 --- a/docs/reference/painless-api-reference/LongToIntFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LongToIntFunction]]++LongToIntFunction++:: -* ++[[painless-api-reference-LongToIntFunction-applyAsInt-1]]int link:{java8-javadoc}/java/util/function/LongToIntFunction.html#applyAsInt%2Dlong%2D[applyAsInt](long)++ (link:{java9-javadoc}/java/util/function/LongToIntFunction.html#applyAsInt%2Dlong%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/LongUnaryOperator.asciidoc b/docs/reference/painless-api-reference/LongUnaryOperator.asciidoc deleted file mode 100644 index eb84a3688bdeb..0000000000000 --- a/docs/reference/painless-api-reference/LongUnaryOperator.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-LongUnaryOperator]]++LongUnaryOperator++:: -* ++[[painless-api-reference-LongUnaryOperator-identity-0]]static <> link:{java8-javadoc}/java/util/function/LongUnaryOperator.html#identity%2D%2D[identity]()++ (link:{java9-javadoc}/java/util/function/LongUnaryOperator.html#identity%2D%2D[java 9]) -* ++[[painless-api-reference-LongUnaryOperator-andThen-1]]<> link:{java8-javadoc}/java/util/function/LongUnaryOperator.html#andThen%2Djava.util.function.LongUnaryOperator%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/LongUnaryOperator.html#andThen%2Djava.util.function.LongUnaryOperator%2D[java 9]) -* ++[[painless-api-reference-LongUnaryOperator-applyAsLong-1]]long link:{java8-javadoc}/java/util/function/LongUnaryOperator.html#applyAsLong%2Dlong%2D[applyAsLong](long)++ (link:{java9-javadoc}/java/util/function/LongUnaryOperator.html#applyAsLong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-LongUnaryOperator-compose-1]]<> link:{java8-javadoc}/java/util/function/LongUnaryOperator.html#compose%2Djava.util.function.LongUnaryOperator%2D[compose](<>)++ (link:{java9-javadoc}/java/util/function/LongUnaryOperator.html#compose%2Djava.util.function.LongUnaryOperator%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Map.Entry.asciidoc b/docs/reference/painless-api-reference/Map.Entry.asciidoc deleted file mode 100644 index f40e61a70b060..0000000000000 --- a/docs/reference/painless-api-reference/Map.Entry.asciidoc +++ /dev/null @@ -1,16 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Map-Entry]]++Map.Entry++:: -* ++[[painless-api-reference-Map-Entry-comparingByKey-0]]static <> link:{java8-javadoc}/java/util/Map$Entry.html#comparingByKey%2D%2D[comparingByKey]()++ (link:{java9-javadoc}/java/util/Map$Entry.html#comparingByKey%2D%2D[java 9]) -* ++[[painless-api-reference-Map-Entry-comparingByKey-1]]static <> link:{java8-javadoc}/java/util/Map$Entry.html#comparingByKey%2Djava.util.Comparator%2D[comparingByKey](<>)++ (link:{java9-javadoc}/java/util/Map$Entry.html#comparingByKey%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Map-Entry-comparingByValue-0]]static <> link:{java8-javadoc}/java/util/Map$Entry.html#comparingByValue%2D%2D[comparingByValue]()++ (link:{java9-javadoc}/java/util/Map$Entry.html#comparingByValue%2D%2D[java 9]) -* ++[[painless-api-reference-Map-Entry-comparingByValue-1]]static <> link:{java8-javadoc}/java/util/Map$Entry.html#comparingByValue%2Djava.util.Comparator%2D[comparingByValue](<>)++ (link:{java9-javadoc}/java/util/Map$Entry.html#comparingByValue%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Map-Entry-equals-1]]boolean link:{java8-javadoc}/java/util/Map$Entry.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/util/Map$Entry.html#equals%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Map-Entry-getKey-0]]def link:{java8-javadoc}/java/util/Map$Entry.html#getKey%2D%2D[getKey]()++ (link:{java9-javadoc}/java/util/Map$Entry.html#getKey%2D%2D[java 9]) -* ++[[painless-api-reference-Map-Entry-getValue-0]]def link:{java8-javadoc}/java/util/Map$Entry.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/util/Map$Entry.html#getValue%2D%2D[java 9]) -* ++[[painless-api-reference-Map-Entry-hashCode-0]]int link:{java8-javadoc}/java/util/Map$Entry.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/util/Map$Entry.html#hashCode%2D%2D[java 9]) -* ++[[painless-api-reference-Map-Entry-setValue-1]]def link:{java8-javadoc}/java/util/Map$Entry.html#setValue%2Djava.lang.Object%2D[setValue](def)++ (link:{java9-javadoc}/java/util/Map$Entry.html#setValue%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Map.asciidoc b/docs/reference/painless-api-reference/Map.asciidoc deleted file mode 100644 index a32c9c137d8cd..0000000000000 --- a/docs/reference/painless-api-reference/Map.asciidoc +++ /dev/null @@ -1,42 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Map]]++Map++:: -* ++[[painless-api-reference-Map-clear-0]]void link:{java8-javadoc}/java/util/Map.html#clear%2D%2D[clear]()++ (link:{java9-javadoc}/java/util/Map.html#clear%2D%2D[java 9]) -* ++[[painless-api-reference-Map-collect-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#collect%2Djava.util.Map%2Djava.util.function.BiFunction%2D[collect](<>)++ -* ++[[painless-api-reference-Map-collect-2]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#collect%2Djava.util.Map%2Djava.util.Collection%2Djava.util.function.BiFunction%2D[collect](<>, <>)++ -* ++[[painless-api-reference-Map-compute-2]]def link:{java8-javadoc}/java/util/Map.html#compute%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[compute](def, <>)++ (link:{java9-javadoc}/java/util/Map.html#compute%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[java 9]) -* ++[[painless-api-reference-Map-computeIfAbsent-2]]def link:{java8-javadoc}/java/util/Map.html#computeIfAbsent%2Djava.lang.Object%2Djava.util.function.Function%2D[computeIfAbsent](def, <>)++ (link:{java9-javadoc}/java/util/Map.html#computeIfAbsent%2Djava.lang.Object%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-Map-computeIfPresent-2]]def link:{java8-javadoc}/java/util/Map.html#computeIfPresent%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[computeIfPresent](def, <>)++ (link:{java9-javadoc}/java/util/Map.html#computeIfPresent%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[java 9]) -* ++[[painless-api-reference-Map-containsKey-1]]boolean link:{java8-javadoc}/java/util/Map.html#containsKey%2Djava.lang.Object%2D[containsKey](def)++ (link:{java9-javadoc}/java/util/Map.html#containsKey%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Map-containsValue-1]]boolean link:{java8-javadoc}/java/util/Map.html#containsValue%2Djava.lang.Object%2D[containsValue](def)++ (link:{java9-javadoc}/java/util/Map.html#containsValue%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Map-count-1]]int link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#count%2Djava.util.Map%2Djava.util.function.BiPredicate%2D[count](<>)++ -* ++[[painless-api-reference-Map-each-1]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#each%2Djava.util.Map%2Djava.util.function.BiConsumer%2D[each](<>)++ -* ++[[painless-api-reference-Map-entrySet-0]]<> link:{java8-javadoc}/java/util/Map.html#entrySet%2D%2D[entrySet]()++ (link:{java9-javadoc}/java/util/Map.html#entrySet%2D%2D[java 9]) -* ++[[painless-api-reference-Map-equals-1]]boolean link:{java8-javadoc}/java/util/Map.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/util/Map.html#equals%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Map-every-1]]boolean link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#every%2Djava.util.Map%2Djava.util.function.BiPredicate%2D[every](<>)++ -* ++[[painless-api-reference-Map-find-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#find%2Djava.util.Map%2Djava.util.function.BiPredicate%2D[find](<>)++ -* ++[[painless-api-reference-Map-findAll-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findAll%2Djava.util.Map%2Djava.util.function.BiPredicate%2D[findAll](<>)++ -* ++[[painless-api-reference-Map-findResult-1]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findResult%2Djava.util.Map%2Djava.util.function.BiFunction%2D[findResult](<>)++ -* ++[[painless-api-reference-Map-findResult-2]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findResult%2Djava.util.Map%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[findResult](def, <>)++ -* ++[[painless-api-reference-Map-findResults-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findResults%2Djava.util.Map%2Djava.util.function.BiFunction%2D[findResults](<>)++ -* ++[[painless-api-reference-Map-forEach-1]]void link:{java8-javadoc}/java/util/Map.html#forEach%2Djava.util.function.BiConsumer%2D[forEach](<>)++ (link:{java9-javadoc}/java/util/Map.html#forEach%2Djava.util.function.BiConsumer%2D[java 9]) -* ++[[painless-api-reference-Map-get-1]]def link:{java8-javadoc}/java/util/Map.html#get%2Djava.lang.Object%2D[get](def)++ (link:{java9-javadoc}/java/util/Map.html#get%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Map-getOrDefault-2]]def link:{java8-javadoc}/java/util/Map.html#getOrDefault%2Djava.lang.Object%2Djava.lang.Object%2D[getOrDefault](def, def)++ (link:{java9-javadoc}/java/util/Map.html#getOrDefault%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Map-groupBy-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#groupBy%2Djava.util.Map%2Djava.util.function.BiFunction%2D[groupBy](<>)++ -* ++[[painless-api-reference-Map-isEmpty-0]]boolean link:{java8-javadoc}/java/util/Map.html#isEmpty%2D%2D[isEmpty]()++ (link:{java9-javadoc}/java/util/Map.html#isEmpty%2D%2D[java 9]) -* ++[[painless-api-reference-Map-keySet-0]]<> link:{java8-javadoc}/java/util/Map.html#keySet%2D%2D[keySet]()++ (link:{java9-javadoc}/java/util/Map.html#keySet%2D%2D[java 9]) -* ++[[painless-api-reference-Map-merge-3]]def link:{java8-javadoc}/java/util/Map.html#merge%2Djava.lang.Object%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[merge](def, def, <>)++ (link:{java9-javadoc}/java/util/Map.html#merge%2Djava.lang.Object%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[java 9]) -* ++[[painless-api-reference-Map-put-2]]def link:{java8-javadoc}/java/util/Map.html#put%2Djava.lang.Object%2Djava.lang.Object%2D[put](def, def)++ (link:{java9-javadoc}/java/util/Map.html#put%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Map-putAll-1]]void link:{java8-javadoc}/java/util/Map.html#putAll%2Djava.util.Map%2D[putAll](<>)++ (link:{java9-javadoc}/java/util/Map.html#putAll%2Djava.util.Map%2D[java 9]) -* ++[[painless-api-reference-Map-putIfAbsent-2]]def link:{java8-javadoc}/java/util/Map.html#putIfAbsent%2Djava.lang.Object%2Djava.lang.Object%2D[putIfAbsent](def, def)++ (link:{java9-javadoc}/java/util/Map.html#putIfAbsent%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Map-remove-1]]def link:{java8-javadoc}/java/util/Map.html#remove%2Djava.lang.Object%2D[remove](def)++ (link:{java9-javadoc}/java/util/Map.html#remove%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Map-remove-2]]boolean link:{java8-javadoc}/java/util/Map.html#remove%2Djava.lang.Object%2Djava.lang.Object%2D[remove](def, def)++ (link:{java9-javadoc}/java/util/Map.html#remove%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Map-replace-2]]def link:{java8-javadoc}/java/util/Map.html#replace%2Djava.lang.Object%2Djava.lang.Object%2D[replace](def, def)++ (link:{java9-javadoc}/java/util/Map.html#replace%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Map-replace-3]]boolean link:{java8-javadoc}/java/util/Map.html#replace%2Djava.lang.Object%2Djava.lang.Object%2Djava.lang.Object%2D[replace](def, def, def)++ (link:{java9-javadoc}/java/util/Map.html#replace%2Djava.lang.Object%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Map-replaceAll-1]]void link:{java8-javadoc}/java/util/Map.html#replaceAll%2Djava.util.function.BiFunction%2D[replaceAll](<>)++ (link:{java9-javadoc}/java/util/Map.html#replaceAll%2Djava.util.function.BiFunction%2D[java 9]) -* ++[[painless-api-reference-Map-size-0]]int link:{java8-javadoc}/java/util/Map.html#size%2D%2D[size]()++ (link:{java9-javadoc}/java/util/Map.html#size%2D%2D[java 9]) -* ++[[painless-api-reference-Map-values-0]]<> link:{java8-javadoc}/java/util/Map.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/util/Map.html#values%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Matcher.asciidoc b/docs/reference/painless-api-reference/Matcher.asciidoc deleted file mode 100644 index 85198e0bc8db1..0000000000000 --- a/docs/reference/painless-api-reference/Matcher.asciidoc +++ /dev/null @@ -1,34 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Matcher]]++Matcher++:: -* ++[[painless-api-reference-Matcher-quoteReplacement-1]]static <> link:{java8-javadoc}/java/util/regex/Matcher.html#quoteReplacement%2Djava.lang.String%2D[quoteReplacement](<>)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#quoteReplacement%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Matcher-end-0]]int link:{java8-javadoc}/java/util/regex/Matcher.html#end%2D%2D[end]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#end%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-end-1]]int link:{java8-javadoc}/java/util/regex/Matcher.html#end%2Dint%2D[end](int)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#end%2Dint%2D[java 9]) -* ++[[painless-api-reference-Matcher-find-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#find%2D%2D[find]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#find%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-find-1]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#find%2Dint%2D[find](int)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#find%2Dint%2D[java 9]) -* ++[[painless-api-reference-Matcher-group-0]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#group%2D%2D[group]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#group%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-group-1]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#group%2Dint%2D[group](int)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#group%2Dint%2D[java 9]) -* ++[[painless-api-reference-Matcher-groupCount-0]]int link:{java8-javadoc}/java/util/regex/Matcher.html#groupCount%2D%2D[groupCount]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#groupCount%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-hasAnchoringBounds-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#hasAnchoringBounds%2D%2D[hasAnchoringBounds]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#hasAnchoringBounds%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-hasTransparentBounds-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#hasTransparentBounds%2D%2D[hasTransparentBounds]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#hasTransparentBounds%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-hitEnd-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#hitEnd%2D%2D[hitEnd]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#hitEnd%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-lookingAt-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#lookingAt%2D%2D[lookingAt]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#lookingAt%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-matches-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#matches%2D%2D[matches]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#matches%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-namedGroup-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#namedGroup%2Djava.util.regex.Matcher%2Djava.lang.String%2D[namedGroup](<>)++ -* ++[[painless-api-reference-Matcher-pattern-0]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#pattern%2D%2D[pattern]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#pattern%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-region-2]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#region%2Dint%2Dint%2D[region](int, int)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#region%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Matcher-regionEnd-0]]int link:{java8-javadoc}/java/util/regex/Matcher.html#regionEnd%2D%2D[regionEnd]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#regionEnd%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-regionStart-0]]int link:{java8-javadoc}/java/util/regex/Matcher.html#regionStart%2D%2D[regionStart]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#regionStart%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-replaceAll-1]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#replaceAll%2Djava.lang.String%2D[replaceAll](<>)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#replaceAll%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Matcher-replaceFirst-1]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#replaceFirst%2Djava.lang.String%2D[replaceFirst](<>)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#replaceFirst%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Matcher-requireEnd-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#requireEnd%2D%2D[requireEnd]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#requireEnd%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-reset-0]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#reset%2D%2D[reset]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#reset%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-start-0]]int link:{java8-javadoc}/java/util/regex/Matcher.html#start%2D%2D[start]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#start%2D%2D[java 9]) -* ++[[painless-api-reference-Matcher-start-1]]int link:{java8-javadoc}/java/util/regex/Matcher.html#start%2Dint%2D[start](int)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#start%2Dint%2D[java 9]) -* ++[[painless-api-reference-Matcher-useAnchoringBounds-1]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#useAnchoringBounds%2Dboolean%2D[useAnchoringBounds](boolean)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#useAnchoringBounds%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-Matcher-usePattern-1]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#usePattern%2Djava.util.regex.Pattern%2D[usePattern](<>)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#usePattern%2Djava.util.regex.Pattern%2D[java 9]) -* ++[[painless-api-reference-Matcher-useTransparentBounds-1]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#useTransparentBounds%2Dboolean%2D[useTransparentBounds](boolean)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#useTransparentBounds%2Dboolean%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Math.asciidoc b/docs/reference/painless-api-reference/Math.asciidoc deleted file mode 100644 index 4b8322c15fc50..0000000000000 --- a/docs/reference/painless-api-reference/Math.asciidoc +++ /dev/null @@ -1,46 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Math]]++Math++:: -** [[painless-api-reference-Math-E]]static double link:{java8-javadoc}/java/lang/Math.html#E[E] (link:{java9-javadoc}/java/lang/Math.html#E[java 9]) -** [[painless-api-reference-Math-PI]]static double link:{java8-javadoc}/java/lang/Math.html#PI[PI] (link:{java9-javadoc}/java/lang/Math.html#PI[java 9]) -* ++[[painless-api-reference-Math-IEEEremainder-2]]static double link:{java8-javadoc}/java/lang/Math.html#IEEEremainder%2Ddouble%2Ddouble%2D[IEEEremainder](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#IEEEremainder%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-abs-1]]static double link:{java8-javadoc}/java/lang/Math.html#abs%2Ddouble%2D[abs](double)++ (link:{java9-javadoc}/java/lang/Math.html#abs%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-acos-1]]static double link:{java8-javadoc}/java/lang/Math.html#acos%2Ddouble%2D[acos](double)++ (link:{java9-javadoc}/java/lang/Math.html#acos%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-asin-1]]static double link:{java8-javadoc}/java/lang/Math.html#asin%2Ddouble%2D[asin](double)++ (link:{java9-javadoc}/java/lang/Math.html#asin%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-atan-1]]static double link:{java8-javadoc}/java/lang/Math.html#atan%2Ddouble%2D[atan](double)++ (link:{java9-javadoc}/java/lang/Math.html#atan%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-atan2-2]]static double link:{java8-javadoc}/java/lang/Math.html#atan2%2Ddouble%2Ddouble%2D[atan2](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#atan2%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-cbrt-1]]static double link:{java8-javadoc}/java/lang/Math.html#cbrt%2Ddouble%2D[cbrt](double)++ (link:{java9-javadoc}/java/lang/Math.html#cbrt%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-ceil-1]]static double link:{java8-javadoc}/java/lang/Math.html#ceil%2Ddouble%2D[ceil](double)++ (link:{java9-javadoc}/java/lang/Math.html#ceil%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-copySign-2]]static double link:{java8-javadoc}/java/lang/Math.html#copySign%2Ddouble%2Ddouble%2D[copySign](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#copySign%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-cos-1]]static double link:{java8-javadoc}/java/lang/Math.html#cos%2Ddouble%2D[cos](double)++ (link:{java9-javadoc}/java/lang/Math.html#cos%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-cosh-1]]static double link:{java8-javadoc}/java/lang/Math.html#cosh%2Ddouble%2D[cosh](double)++ (link:{java9-javadoc}/java/lang/Math.html#cosh%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-exp-1]]static double link:{java8-javadoc}/java/lang/Math.html#exp%2Ddouble%2D[exp](double)++ (link:{java9-javadoc}/java/lang/Math.html#exp%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-expm1-1]]static double link:{java8-javadoc}/java/lang/Math.html#expm1%2Ddouble%2D[expm1](double)++ (link:{java9-javadoc}/java/lang/Math.html#expm1%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-floor-1]]static double link:{java8-javadoc}/java/lang/Math.html#floor%2Ddouble%2D[floor](double)++ (link:{java9-javadoc}/java/lang/Math.html#floor%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-hypot-2]]static double link:{java8-javadoc}/java/lang/Math.html#hypot%2Ddouble%2Ddouble%2D[hypot](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#hypot%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-log-1]]static double link:{java8-javadoc}/java/lang/Math.html#log%2Ddouble%2D[log](double)++ (link:{java9-javadoc}/java/lang/Math.html#log%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-log10-1]]static double link:{java8-javadoc}/java/lang/Math.html#log10%2Ddouble%2D[log10](double)++ (link:{java9-javadoc}/java/lang/Math.html#log10%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-log1p-1]]static double link:{java8-javadoc}/java/lang/Math.html#log1p%2Ddouble%2D[log1p](double)++ (link:{java9-javadoc}/java/lang/Math.html#log1p%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-max-2]]static double link:{java8-javadoc}/java/lang/Math.html#max%2Ddouble%2Ddouble%2D[max](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#max%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-min-2]]static double link:{java8-javadoc}/java/lang/Math.html#min%2Ddouble%2Ddouble%2D[min](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#min%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-nextAfter-2]]static double link:{java8-javadoc}/java/lang/Math.html#nextAfter%2Ddouble%2Ddouble%2D[nextAfter](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#nextAfter%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-nextDown-1]]static double link:{java8-javadoc}/java/lang/Math.html#nextDown%2Ddouble%2D[nextDown](double)++ (link:{java9-javadoc}/java/lang/Math.html#nextDown%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-nextUp-1]]static double link:{java8-javadoc}/java/lang/Math.html#nextUp%2Ddouble%2D[nextUp](double)++ (link:{java9-javadoc}/java/lang/Math.html#nextUp%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-pow-2]]static double link:{java8-javadoc}/java/lang/Math.html#pow%2Ddouble%2Ddouble%2D[pow](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#pow%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-random-0]]static double link:{java8-javadoc}/java/lang/Math.html#random%2D%2D[random]()++ (link:{java9-javadoc}/java/lang/Math.html#random%2D%2D[java 9]) -* ++[[painless-api-reference-Math-rint-1]]static double link:{java8-javadoc}/java/lang/Math.html#rint%2Ddouble%2D[rint](double)++ (link:{java9-javadoc}/java/lang/Math.html#rint%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-round-1]]static long link:{java8-javadoc}/java/lang/Math.html#round%2Ddouble%2D[round](double)++ (link:{java9-javadoc}/java/lang/Math.html#round%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-scalb-2]]static double link:{java8-javadoc}/java/lang/Math.html#scalb%2Ddouble%2Dint%2D[scalb](double, int)++ (link:{java9-javadoc}/java/lang/Math.html#scalb%2Ddouble%2Dint%2D[java 9]) -* ++[[painless-api-reference-Math-signum-1]]static double link:{java8-javadoc}/java/lang/Math.html#signum%2Ddouble%2D[signum](double)++ (link:{java9-javadoc}/java/lang/Math.html#signum%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-sin-1]]static double link:{java8-javadoc}/java/lang/Math.html#sin%2Ddouble%2D[sin](double)++ (link:{java9-javadoc}/java/lang/Math.html#sin%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-sinh-1]]static double link:{java8-javadoc}/java/lang/Math.html#sinh%2Ddouble%2D[sinh](double)++ (link:{java9-javadoc}/java/lang/Math.html#sinh%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-sqrt-1]]static double link:{java8-javadoc}/java/lang/Math.html#sqrt%2Ddouble%2D[sqrt](double)++ (link:{java9-javadoc}/java/lang/Math.html#sqrt%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-tan-1]]static double link:{java8-javadoc}/java/lang/Math.html#tan%2Ddouble%2D[tan](double)++ (link:{java9-javadoc}/java/lang/Math.html#tan%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-tanh-1]]static double link:{java8-javadoc}/java/lang/Math.html#tanh%2Ddouble%2D[tanh](double)++ (link:{java9-javadoc}/java/lang/Math.html#tanh%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-toDegrees-1]]static double link:{java8-javadoc}/java/lang/Math.html#toDegrees%2Ddouble%2D[toDegrees](double)++ (link:{java9-javadoc}/java/lang/Math.html#toDegrees%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-toRadians-1]]static double link:{java8-javadoc}/java/lang/Math.html#toRadians%2Ddouble%2D[toRadians](double)++ (link:{java9-javadoc}/java/lang/Math.html#toRadians%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Math-ulp-1]]static double link:{java8-javadoc}/java/lang/Math.html#ulp%2Ddouble%2D[ulp](double)++ (link:{java9-javadoc}/java/lang/Math.html#ulp%2Ddouble%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/MathContext.asciidoc b/docs/reference/painless-api-reference/MathContext.asciidoc deleted file mode 100644 index c8167367fd59b..0000000000000 --- a/docs/reference/painless-api-reference/MathContext.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-MathContext]]++MathContext++:: -** [[painless-api-reference-MathContext-DECIMAL128]]static <> link:{java8-javadoc}/java/math/MathContext.html#DECIMAL128[DECIMAL128] (link:{java9-javadoc}/java/math/MathContext.html#DECIMAL128[java 9]) -** [[painless-api-reference-MathContext-DECIMAL32]]static <> link:{java8-javadoc}/java/math/MathContext.html#DECIMAL32[DECIMAL32] (link:{java9-javadoc}/java/math/MathContext.html#DECIMAL32[java 9]) -** [[painless-api-reference-MathContext-DECIMAL64]]static <> link:{java8-javadoc}/java/math/MathContext.html#DECIMAL64[DECIMAL64] (link:{java9-javadoc}/java/math/MathContext.html#DECIMAL64[java 9]) -** [[painless-api-reference-MathContext-UNLIMITED]]static <> link:{java8-javadoc}/java/math/MathContext.html#UNLIMITED[UNLIMITED] (link:{java9-javadoc}/java/math/MathContext.html#UNLIMITED[java 9]) -* ++[[painless-api-reference-MathContext-MathContext-1]]link:{java8-javadoc}/java/math/MathContext.html#MathContext%2Dint%2D[MathContext](int)++ (link:{java9-javadoc}/java/math/MathContext.html#MathContext%2Dint%2D[java 9]) -* ++[[painless-api-reference-MathContext-MathContext-2]]link:{java8-javadoc}/java/math/MathContext.html#MathContext%2Dint%2Djava.math.RoundingMode%2D[MathContext](int, <>)++ (link:{java9-javadoc}/java/math/MathContext.html#MathContext%2Dint%2Djava.math.RoundingMode%2D[java 9]) -* ++[[painless-api-reference-MathContext-getPrecision-0]]int link:{java8-javadoc}/java/math/MathContext.html#getPrecision%2D%2D[getPrecision]()++ (link:{java9-javadoc}/java/math/MathContext.html#getPrecision%2D%2D[java 9]) -* ++[[painless-api-reference-MathContext-getRoundingMode-0]]<> link:{java8-javadoc}/java/math/MathContext.html#getRoundingMode%2D%2D[getRoundingMode]()++ (link:{java9-javadoc}/java/math/MathContext.html#getRoundingMode%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/MessageFormat.Field.asciidoc b/docs/reference/painless-api-reference/MessageFormat.Field.asciidoc deleted file mode 100644 index 6b0e71e0a5f43..0000000000000 --- a/docs/reference/painless-api-reference/MessageFormat.Field.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-MessageFormat-Field]]++MessageFormat.Field++:: -** [[painless-api-reference-MessageFormat-Field-ARGUMENT]]static <> link:{java8-javadoc}/java/text/MessageFormat$Field.html#ARGUMENT[ARGUMENT] (link:{java9-javadoc}/java/text/MessageFormat$Field.html#ARGUMENT[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/MessageFormat.asciidoc b/docs/reference/painless-api-reference/MessageFormat.asciidoc deleted file mode 100644 index eaed777d9f0f7..0000000000000 --- a/docs/reference/painless-api-reference/MessageFormat.asciidoc +++ /dev/null @@ -1,20 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-MessageFormat]]++MessageFormat++:: -* ++[[painless-api-reference-MessageFormat-format-2]]static <> link:{java8-javadoc}/java/text/MessageFormat.html#format%2Djava.lang.String%2Djava.lang.Object:A%2D[format](<>, <>[])++ (link:{java9-javadoc}/java/text/MessageFormat.html#format%2Djava.lang.String%2Djava.lang.Object:A%2D[java 9]) -* ++[[painless-api-reference-MessageFormat-applyPattern-1]]void link:{java8-javadoc}/java/text/MessageFormat.html#applyPattern%2Djava.lang.String%2D[applyPattern](<>)++ (link:{java9-javadoc}/java/text/MessageFormat.html#applyPattern%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-MessageFormat-getFormats-0]]<>[] link:{java8-javadoc}/java/text/MessageFormat.html#getFormats%2D%2D[getFormats]()++ (link:{java9-javadoc}/java/text/MessageFormat.html#getFormats%2D%2D[java 9]) -* ++[[painless-api-reference-MessageFormat-getFormatsByArgumentIndex-0]]<>[] link:{java8-javadoc}/java/text/MessageFormat.html#getFormatsByArgumentIndex%2D%2D[getFormatsByArgumentIndex]()++ (link:{java9-javadoc}/java/text/MessageFormat.html#getFormatsByArgumentIndex%2D%2D[java 9]) -* ++[[painless-api-reference-MessageFormat-getLocale-0]]<> link:{java8-javadoc}/java/text/MessageFormat.html#getLocale%2D%2D[getLocale]()++ (link:{java9-javadoc}/java/text/MessageFormat.html#getLocale%2D%2D[java 9]) -* ++[[painless-api-reference-MessageFormat-parse-1]]<>[] link:{java8-javadoc}/java/text/MessageFormat.html#parse%2Djava.lang.String%2D[parse](<>)++ (link:{java9-javadoc}/java/text/MessageFormat.html#parse%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-MessageFormat-parse-2]]<>[] link:{java8-javadoc}/java/text/MessageFormat.html#parse%2Djava.lang.String%2Djava.text.ParsePosition%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/text/MessageFormat.html#parse%2Djava.lang.String%2Djava.text.ParsePosition%2D[java 9]) -* ++[[painless-api-reference-MessageFormat-setFormat-2]]void link:{java8-javadoc}/java/text/MessageFormat.html#setFormat%2Dint%2Djava.text.Format%2D[setFormat](int, <>)++ (link:{java9-javadoc}/java/text/MessageFormat.html#setFormat%2Dint%2Djava.text.Format%2D[java 9]) -* ++[[painless-api-reference-MessageFormat-setFormatByArgumentIndex-2]]void link:{java8-javadoc}/java/text/MessageFormat.html#setFormatByArgumentIndex%2Dint%2Djava.text.Format%2D[setFormatByArgumentIndex](int, <>)++ (link:{java9-javadoc}/java/text/MessageFormat.html#setFormatByArgumentIndex%2Dint%2Djava.text.Format%2D[java 9]) -* ++[[painless-api-reference-MessageFormat-setFormats-1]]void link:{java8-javadoc}/java/text/MessageFormat.html#setFormats%2Djava.text.Format:A%2D[setFormats](<>[])++ (link:{java9-javadoc}/java/text/MessageFormat.html#setFormats%2Djava.text.Format:A%2D[java 9]) -* ++[[painless-api-reference-MessageFormat-setFormatsByArgumentIndex-1]]void link:{java8-javadoc}/java/text/MessageFormat.html#setFormatsByArgumentIndex%2Djava.text.Format:A%2D[setFormatsByArgumentIndex](<>[])++ (link:{java9-javadoc}/java/text/MessageFormat.html#setFormatsByArgumentIndex%2Djava.text.Format:A%2D[java 9]) -* ++[[painless-api-reference-MessageFormat-setLocale-1]]void link:{java8-javadoc}/java/text/MessageFormat.html#setLocale%2Djava.util.Locale%2D[setLocale](<>)++ (link:{java9-javadoc}/java/text/MessageFormat.html#setLocale%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-MessageFormat-toPattern-0]]<> link:{java8-javadoc}/java/text/MessageFormat.html#toPattern%2D%2D[toPattern]()++ (link:{java9-javadoc}/java/text/MessageFormat.html#toPattern%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/MinguoChronology.asciidoc b/docs/reference/painless-api-reference/MinguoChronology.asciidoc deleted file mode 100644 index 9e9afb2cedfa2..0000000000000 --- a/docs/reference/painless-api-reference/MinguoChronology.asciidoc +++ /dev/null @@ -1,16 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-MinguoChronology]]++MinguoChronology++:: -** [[painless-api-reference-MinguoChronology-INSTANCE]]static <> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#INSTANCE[INSTANCE] (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#INSTANCE[java 9]) -* ++[[painless-api-reference-MinguoChronology-date-1]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[date](<>)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-MinguoChronology-date-3]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#date%2Dint%2Dint%2Dint%2D[date](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#date%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-MinguoChronology-date-4]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[date](<>, int, int, int)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-MinguoChronology-dateEpochDay-1]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#dateEpochDay%2Dlong%2D[dateEpochDay](long)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#dateEpochDay%2Dlong%2D[java 9]) -* ++[[painless-api-reference-MinguoChronology-dateYearDay-2]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#dateYearDay%2Dint%2Dint%2D[dateYearDay](int, int)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#dateYearDay%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-MinguoChronology-dateYearDay-3]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[dateYearDay](<>, int, int)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-MinguoChronology-eraOf-1]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#eraOf%2Dint%2D[eraOf](int)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#eraOf%2Dint%2D[java 9]) -* ++[[painless-api-reference-MinguoChronology-resolveDate-2]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[resolveDate](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/MinguoDate.asciidoc b/docs/reference/painless-api-reference/MinguoDate.asciidoc deleted file mode 100644 index 38d0b87da762d..0000000000000 --- a/docs/reference/painless-api-reference/MinguoDate.asciidoc +++ /dev/null @@ -1,17 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-MinguoDate]]++MinguoDate++:: -* ++[[painless-api-reference-MinguoDate-from-1]]static <> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-MinguoDate-of-3]]static <> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#of%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-MinguoDate-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#getChronology%2D%2D[java 9]) -* ++[[painless-api-reference-MinguoDate-getEra-0]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#getEra%2D%2D[getEra]()++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#getEra%2D%2D[java 9]) -* ++[[painless-api-reference-MinguoDate-minus-1]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-MinguoDate-minus-2]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-MinguoDate-plus-1]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-MinguoDate-plus-2]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-MinguoDate-with-1]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-MinguoDate-with-2]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/MinguoEra.asciidoc b/docs/reference/painless-api-reference/MinguoEra.asciidoc deleted file mode 100644 index 0b32509913d91..0000000000000 --- a/docs/reference/painless-api-reference/MinguoEra.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-MinguoEra]]++MinguoEra++:: -** [[painless-api-reference-MinguoEra-BEFORE_ROC]]static <> link:{java8-javadoc}/java/time/chrono/MinguoEra.html#BEFORE_ROC[BEFORE_ROC] (link:{java9-javadoc}/java/time/chrono/MinguoEra.html#BEFORE_ROC[java 9]) -** [[painless-api-reference-MinguoEra-ROC]]static <> link:{java8-javadoc}/java/time/chrono/MinguoEra.html#ROC[ROC] (link:{java9-javadoc}/java/time/chrono/MinguoEra.html#ROC[java 9]) -* ++[[painless-api-reference-MinguoEra-of-1]]static <> link:{java8-javadoc}/java/time/chrono/MinguoEra.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/chrono/MinguoEra.html#of%2Dint%2D[java 9]) -* ++[[painless-api-reference-MinguoEra-valueOf-1]]static <> link:{java8-javadoc}/java/time/chrono/MinguoEra.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/chrono/MinguoEra.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-MinguoEra-values-0]]static <>[] link:{java8-javadoc}/java/time/chrono/MinguoEra.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/chrono/MinguoEra.html#values%2D%2D[java 9]) -* ++[[painless-api-reference-MinguoEra-getValue-0]]int link:{java8-javadoc}/java/time/chrono/MinguoEra.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/chrono/MinguoEra.html#getValue%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/MissingFormatArgumentException.asciidoc b/docs/reference/painless-api-reference/MissingFormatArgumentException.asciidoc deleted file mode 100644 index 7c09ad6ba5cb7..0000000000000 --- a/docs/reference/painless-api-reference/MissingFormatArgumentException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-MissingFormatArgumentException]]++MissingFormatArgumentException++:: -* ++[[painless-api-reference-MissingFormatArgumentException-MissingFormatArgumentException-1]]link:{java8-javadoc}/java/util/MissingFormatArgumentException.html#MissingFormatArgumentException%2Djava.lang.String%2D[MissingFormatArgumentException](<>)++ (link:{java9-javadoc}/java/util/MissingFormatArgumentException.html#MissingFormatArgumentException%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-MissingFormatArgumentException-getFormatSpecifier-0]]<> link:{java8-javadoc}/java/util/MissingFormatArgumentException.html#getFormatSpecifier%2D%2D[getFormatSpecifier]()++ (link:{java9-javadoc}/java/util/MissingFormatArgumentException.html#getFormatSpecifier%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/MissingFormatWidthException.asciidoc b/docs/reference/painless-api-reference/MissingFormatWidthException.asciidoc deleted file mode 100644 index 155637b48dd2e..0000000000000 --- a/docs/reference/painless-api-reference/MissingFormatWidthException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-MissingFormatWidthException]]++MissingFormatWidthException++:: -* ++[[painless-api-reference-MissingFormatWidthException-MissingFormatWidthException-1]]link:{java8-javadoc}/java/util/MissingFormatWidthException.html#MissingFormatWidthException%2Djava.lang.String%2D[MissingFormatWidthException](<>)++ (link:{java9-javadoc}/java/util/MissingFormatWidthException.html#MissingFormatWidthException%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-MissingFormatWidthException-getFormatSpecifier-0]]<> link:{java8-javadoc}/java/util/MissingFormatWidthException.html#getFormatSpecifier%2D%2D[getFormatSpecifier]()++ (link:{java9-javadoc}/java/util/MissingFormatWidthException.html#getFormatSpecifier%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/MissingResourceException.asciidoc b/docs/reference/painless-api-reference/MissingResourceException.asciidoc deleted file mode 100644 index a415708a728a6..0000000000000 --- a/docs/reference/painless-api-reference/MissingResourceException.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-MissingResourceException]]++MissingResourceException++:: -* ++[[painless-api-reference-MissingResourceException-MissingResourceException-3]]link:{java8-javadoc}/java/util/MissingResourceException.html#MissingResourceException%2Djava.lang.String%2Djava.lang.String%2Djava.lang.String%2D[MissingResourceException](<>, <>, <>)++ (link:{java9-javadoc}/java/util/MissingResourceException.html#MissingResourceException%2Djava.lang.String%2Djava.lang.String%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-MissingResourceException-getClassName-0]]<> link:{java8-javadoc}/java/util/MissingResourceException.html#getClassName%2D%2D[getClassName]()++ (link:{java9-javadoc}/java/util/MissingResourceException.html#getClassName%2D%2D[java 9]) -* ++[[painless-api-reference-MissingResourceException-getKey-0]]<> link:{java8-javadoc}/java/util/MissingResourceException.html#getKey%2D%2D[getKey]()++ (link:{java9-javadoc}/java/util/MissingResourceException.html#getKey%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Month.asciidoc b/docs/reference/painless-api-reference/Month.asciidoc deleted file mode 100644 index f4eaa9cc12944..0000000000000 --- a/docs/reference/painless-api-reference/Month.asciidoc +++ /dev/null @@ -1,32 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Month]]++Month++:: -** [[painless-api-reference-Month-APRIL]]static <> link:{java8-javadoc}/java/time/Month.html#APRIL[APRIL] (link:{java9-javadoc}/java/time/Month.html#APRIL[java 9]) -** [[painless-api-reference-Month-AUGUST]]static <> link:{java8-javadoc}/java/time/Month.html#AUGUST[AUGUST] (link:{java9-javadoc}/java/time/Month.html#AUGUST[java 9]) -** [[painless-api-reference-Month-DECEMBER]]static <> link:{java8-javadoc}/java/time/Month.html#DECEMBER[DECEMBER] (link:{java9-javadoc}/java/time/Month.html#DECEMBER[java 9]) -** [[painless-api-reference-Month-FEBRUARY]]static <> link:{java8-javadoc}/java/time/Month.html#FEBRUARY[FEBRUARY] (link:{java9-javadoc}/java/time/Month.html#FEBRUARY[java 9]) -** [[painless-api-reference-Month-JANUARY]]static <> link:{java8-javadoc}/java/time/Month.html#JANUARY[JANUARY] (link:{java9-javadoc}/java/time/Month.html#JANUARY[java 9]) -** [[painless-api-reference-Month-JULY]]static <> link:{java8-javadoc}/java/time/Month.html#JULY[JULY] (link:{java9-javadoc}/java/time/Month.html#JULY[java 9]) -** [[painless-api-reference-Month-JUNE]]static <> link:{java8-javadoc}/java/time/Month.html#JUNE[JUNE] (link:{java9-javadoc}/java/time/Month.html#JUNE[java 9]) -** [[painless-api-reference-Month-MARCH]]static <> link:{java8-javadoc}/java/time/Month.html#MARCH[MARCH] (link:{java9-javadoc}/java/time/Month.html#MARCH[java 9]) -** [[painless-api-reference-Month-MAY]]static <> link:{java8-javadoc}/java/time/Month.html#MAY[MAY] (link:{java9-javadoc}/java/time/Month.html#MAY[java 9]) -** [[painless-api-reference-Month-NOVEMBER]]static <> link:{java8-javadoc}/java/time/Month.html#NOVEMBER[NOVEMBER] (link:{java9-javadoc}/java/time/Month.html#NOVEMBER[java 9]) -** [[painless-api-reference-Month-OCTOBER]]static <> link:{java8-javadoc}/java/time/Month.html#OCTOBER[OCTOBER] (link:{java9-javadoc}/java/time/Month.html#OCTOBER[java 9]) -** [[painless-api-reference-Month-SEPTEMBER]]static <> link:{java8-javadoc}/java/time/Month.html#SEPTEMBER[SEPTEMBER] (link:{java9-javadoc}/java/time/Month.html#SEPTEMBER[java 9]) -* ++[[painless-api-reference-Month-from-1]]static <> link:{java8-javadoc}/java/time/Month.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/Month.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-Month-of-1]]static <> link:{java8-javadoc}/java/time/Month.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/Month.html#of%2Dint%2D[java 9]) -* ++[[painless-api-reference-Month-valueOf-1]]static <> link:{java8-javadoc}/java/time/Month.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/Month.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Month-values-0]]static <>[] link:{java8-javadoc}/java/time/Month.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/Month.html#values%2D%2D[java 9]) -* ++[[painless-api-reference-Month-firstDayOfYear-1]]int link:{java8-javadoc}/java/time/Month.html#firstDayOfYear%2Dboolean%2D[firstDayOfYear](boolean)++ (link:{java9-javadoc}/java/time/Month.html#firstDayOfYear%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-Month-firstMonthOfQuarter-0]]<> link:{java8-javadoc}/java/time/Month.html#firstMonthOfQuarter%2D%2D[firstMonthOfQuarter]()++ (link:{java9-javadoc}/java/time/Month.html#firstMonthOfQuarter%2D%2D[java 9]) -* ++[[painless-api-reference-Month-getDisplayName-2]]<> link:{java8-javadoc}/java/time/Month.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[getDisplayName](<>, <>)++ (link:{java9-javadoc}/java/time/Month.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-Month-getValue-0]]int link:{java8-javadoc}/java/time/Month.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/Month.html#getValue%2D%2D[java 9]) -* ++[[painless-api-reference-Month-length-1]]int link:{java8-javadoc}/java/time/Month.html#length%2Dboolean%2D[length](boolean)++ (link:{java9-javadoc}/java/time/Month.html#length%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-Month-maxLength-0]]int link:{java8-javadoc}/java/time/Month.html#maxLength%2D%2D[maxLength]()++ (link:{java9-javadoc}/java/time/Month.html#maxLength%2D%2D[java 9]) -* ++[[painless-api-reference-Month-minLength-0]]int link:{java8-javadoc}/java/time/Month.html#minLength%2D%2D[minLength]()++ (link:{java9-javadoc}/java/time/Month.html#minLength%2D%2D[java 9]) -* ++[[painless-api-reference-Month-minus-1]]<> link:{java8-javadoc}/java/time/Month.html#minus%2Dlong%2D[minus](long)++ (link:{java9-javadoc}/java/time/Month.html#minus%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Month-plus-1]]<> link:{java8-javadoc}/java/time/Month.html#plus%2Dlong%2D[plus](long)++ (link:{java9-javadoc}/java/time/Month.html#plus%2Dlong%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/MonthDay.asciidoc b/docs/reference/painless-api-reference/MonthDay.asciidoc deleted file mode 100644 index 4bb7c675d70da..0000000000000 --- a/docs/reference/painless-api-reference/MonthDay.asciidoc +++ /dev/null @@ -1,23 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-MonthDay]]++MonthDay++:: -* ++[[painless-api-reference-MonthDay-from-1]]static <> link:{java8-javadoc}/java/time/MonthDay.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-MonthDay-of-2]]static <> link:{java8-javadoc}/java/time/MonthDay.html#of%2Dint%2Dint%2D[of](int, int)++ (link:{java9-javadoc}/java/time/MonthDay.html#of%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-MonthDay-parse-1]]static <> link:{java8-javadoc}/java/time/MonthDay.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#parse%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-MonthDay-parse-2]]static <> link:{java8-javadoc}/java/time/MonthDay.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/MonthDay.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-MonthDay-atYear-1]]<> link:{java8-javadoc}/java/time/MonthDay.html#atYear%2Dint%2D[atYear](int)++ (link:{java9-javadoc}/java/time/MonthDay.html#atYear%2Dint%2D[java 9]) -* ++[[painless-api-reference-MonthDay-compareTo-1]]int link:{java8-javadoc}/java/time/MonthDay.html#compareTo%2Djava.time.MonthDay%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#compareTo%2Djava.time.MonthDay%2D[java 9]) -* ++[[painless-api-reference-MonthDay-format-1]]<> link:{java8-javadoc}/java/time/MonthDay.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-MonthDay-getDayOfMonth-0]]int link:{java8-javadoc}/java/time/MonthDay.html#getDayOfMonth%2D%2D[getDayOfMonth]()++ (link:{java9-javadoc}/java/time/MonthDay.html#getDayOfMonth%2D%2D[java 9]) -* ++[[painless-api-reference-MonthDay-getMonth-0]]<> link:{java8-javadoc}/java/time/MonthDay.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/MonthDay.html#getMonth%2D%2D[java 9]) -* ++[[painless-api-reference-MonthDay-getMonthValue-0]]int link:{java8-javadoc}/java/time/MonthDay.html#getMonthValue%2D%2D[getMonthValue]()++ (link:{java9-javadoc}/java/time/MonthDay.html#getMonthValue%2D%2D[java 9]) -* ++[[painless-api-reference-MonthDay-isAfter-1]]boolean link:{java8-javadoc}/java/time/MonthDay.html#isAfter%2Djava.time.MonthDay%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#isAfter%2Djava.time.MonthDay%2D[java 9]) -* ++[[painless-api-reference-MonthDay-isBefore-1]]boolean link:{java8-javadoc}/java/time/MonthDay.html#isBefore%2Djava.time.MonthDay%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#isBefore%2Djava.time.MonthDay%2D[java 9]) -* ++[[painless-api-reference-MonthDay-isValidYear-1]]boolean link:{java8-javadoc}/java/time/MonthDay.html#isValidYear%2Dint%2D[isValidYear](int)++ (link:{java9-javadoc}/java/time/MonthDay.html#isValidYear%2Dint%2D[java 9]) -* ++[[painless-api-reference-MonthDay-with-1]]<> link:{java8-javadoc}/java/time/MonthDay.html#with%2Djava.time.Month%2D[with](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#with%2Djava.time.Month%2D[java 9]) -* ++[[painless-api-reference-MonthDay-withDayOfMonth-1]]<> link:{java8-javadoc}/java/time/MonthDay.html#withDayOfMonth%2Dint%2D[withDayOfMonth](int)++ (link:{java9-javadoc}/java/time/MonthDay.html#withDayOfMonth%2Dint%2D[java 9]) -* ++[[painless-api-reference-MonthDay-withMonth-1]]<> link:{java8-javadoc}/java/time/MonthDay.html#withMonth%2Dint%2D[withMonth](int)++ (link:{java9-javadoc}/java/time/MonthDay.html#withMonth%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/NavigableMap.asciidoc b/docs/reference/painless-api-reference/NavigableMap.asciidoc deleted file mode 100644 index 507ab8acd61f3..0000000000000 --- a/docs/reference/painless-api-reference/NavigableMap.asciidoc +++ /dev/null @@ -1,24 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-NavigableMap]]++NavigableMap++:: -* ++[[painless-api-reference-NavigableMap-ceilingEntry-1]]<> link:{java8-javadoc}/java/util/NavigableMap.html#ceilingEntry%2Djava.lang.Object%2D[ceilingEntry](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#ceilingEntry%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-ceilingKey-1]]def link:{java8-javadoc}/java/util/NavigableMap.html#ceilingKey%2Djava.lang.Object%2D[ceilingKey](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#ceilingKey%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-descendingKeySet-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#descendingKeySet%2D%2D[descendingKeySet]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#descendingKeySet%2D%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-descendingMap-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#descendingMap%2D%2D[descendingMap]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#descendingMap%2D%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-firstEntry-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#firstEntry%2D%2D[firstEntry]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#firstEntry%2D%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-floorEntry-1]]<> link:{java8-javadoc}/java/util/NavigableMap.html#floorEntry%2Djava.lang.Object%2D[floorEntry](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#floorEntry%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-floorKey-1]]def link:{java8-javadoc}/java/util/NavigableMap.html#floorKey%2Djava.lang.Object%2D[floorKey](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#floorKey%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-headMap-2]]<> link:{java8-javadoc}/java/util/NavigableMap.html#headMap%2Djava.lang.Object%2Dboolean%2D[headMap](def, boolean)++ (link:{java9-javadoc}/java/util/NavigableMap.html#headMap%2Djava.lang.Object%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-higherEntry-1]]<> link:{java8-javadoc}/java/util/NavigableMap.html#higherEntry%2Djava.lang.Object%2D[higherEntry](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#higherEntry%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-higherKey-1]]def link:{java8-javadoc}/java/util/NavigableMap.html#higherKey%2Djava.lang.Object%2D[higherKey](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#higherKey%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-lastEntry-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#lastEntry%2D%2D[lastEntry]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#lastEntry%2D%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-lowerEntry-1]]<> link:{java8-javadoc}/java/util/NavigableMap.html#lowerEntry%2Djava.lang.Object%2D[lowerEntry](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#lowerEntry%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-navigableKeySet-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#navigableKeySet%2D%2D[navigableKeySet]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#navigableKeySet%2D%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-pollFirstEntry-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#pollFirstEntry%2D%2D[pollFirstEntry]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#pollFirstEntry%2D%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-pollLastEntry-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#pollLastEntry%2D%2D[pollLastEntry]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#pollLastEntry%2D%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-subMap-4]]<> link:{java8-javadoc}/java/util/NavigableMap.html#subMap%2Djava.lang.Object%2Dboolean%2Djava.lang.Object%2Dboolean%2D[subMap](def, boolean, def, boolean)++ (link:{java9-javadoc}/java/util/NavigableMap.html#subMap%2Djava.lang.Object%2Dboolean%2Djava.lang.Object%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-NavigableMap-tailMap-2]]<> link:{java8-javadoc}/java/util/NavigableMap.html#tailMap%2Djava.lang.Object%2Dboolean%2D[tailMap](def, boolean)++ (link:{java9-javadoc}/java/util/NavigableMap.html#tailMap%2Djava.lang.Object%2Dboolean%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/NavigableSet.asciidoc b/docs/reference/painless-api-reference/NavigableSet.asciidoc deleted file mode 100644 index 15fff25f44d5b..0000000000000 --- a/docs/reference/painless-api-reference/NavigableSet.asciidoc +++ /dev/null @@ -1,18 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-NavigableSet]]++NavigableSet++:: -* ++[[painless-api-reference-NavigableSet-ceiling-1]]def link:{java8-javadoc}/java/util/NavigableSet.html#ceiling%2Djava.lang.Object%2D[ceiling](def)++ (link:{java9-javadoc}/java/util/NavigableSet.html#ceiling%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-NavigableSet-descendingIterator-0]]<> link:{java8-javadoc}/java/util/NavigableSet.html#descendingIterator%2D%2D[descendingIterator]()++ (link:{java9-javadoc}/java/util/NavigableSet.html#descendingIterator%2D%2D[java 9]) -* ++[[painless-api-reference-NavigableSet-descendingSet-0]]<> link:{java8-javadoc}/java/util/NavigableSet.html#descendingSet%2D%2D[descendingSet]()++ (link:{java9-javadoc}/java/util/NavigableSet.html#descendingSet%2D%2D[java 9]) -* ++[[painless-api-reference-NavigableSet-floor-1]]def link:{java8-javadoc}/java/util/NavigableSet.html#floor%2Djava.lang.Object%2D[floor](def)++ (link:{java9-javadoc}/java/util/NavigableSet.html#floor%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-NavigableSet-headSet-2]]<> link:{java8-javadoc}/java/util/NavigableSet.html#headSet%2Djava.lang.Object%2Dboolean%2D[headSet](def, boolean)++ (link:{java9-javadoc}/java/util/NavigableSet.html#headSet%2Djava.lang.Object%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-NavigableSet-higher-1]]def link:{java8-javadoc}/java/util/NavigableSet.html#higher%2Djava.lang.Object%2D[higher](def)++ (link:{java9-javadoc}/java/util/NavigableSet.html#higher%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-NavigableSet-lower-1]]def link:{java8-javadoc}/java/util/NavigableSet.html#lower%2Djava.lang.Object%2D[lower](def)++ (link:{java9-javadoc}/java/util/NavigableSet.html#lower%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-NavigableSet-pollFirst-0]]def link:{java8-javadoc}/java/util/NavigableSet.html#pollFirst%2D%2D[pollFirst]()++ (link:{java9-javadoc}/java/util/NavigableSet.html#pollFirst%2D%2D[java 9]) -* ++[[painless-api-reference-NavigableSet-pollLast-0]]def link:{java8-javadoc}/java/util/NavigableSet.html#pollLast%2D%2D[pollLast]()++ (link:{java9-javadoc}/java/util/NavigableSet.html#pollLast%2D%2D[java 9]) -* ++[[painless-api-reference-NavigableSet-subSet-4]]<> link:{java8-javadoc}/java/util/NavigableSet.html#subSet%2Djava.lang.Object%2Dboolean%2Djava.lang.Object%2Dboolean%2D[subSet](def, boolean, def, boolean)++ (link:{java9-javadoc}/java/util/NavigableSet.html#subSet%2Djava.lang.Object%2Dboolean%2Djava.lang.Object%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-NavigableSet-tailSet-2]]<> link:{java8-javadoc}/java/util/NavigableSet.html#tailSet%2Djava.lang.Object%2Dboolean%2D[tailSet](def, boolean)++ (link:{java9-javadoc}/java/util/NavigableSet.html#tailSet%2Djava.lang.Object%2Dboolean%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/NegativeArraySizeException.asciidoc b/docs/reference/painless-api-reference/NegativeArraySizeException.asciidoc deleted file mode 100644 index 78ee0006eca71..0000000000000 --- a/docs/reference/painless-api-reference/NegativeArraySizeException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-NegativeArraySizeException]]++NegativeArraySizeException++:: -* ++[[painless-api-reference-NegativeArraySizeException-NegativeArraySizeException-0]]link:{java8-javadoc}/java/lang/NegativeArraySizeException.html#NegativeArraySizeException%2D%2D[NegativeArraySizeException]()++ (link:{java9-javadoc}/java/lang/NegativeArraySizeException.html#NegativeArraySizeException%2D%2D[java 9]) -* ++[[painless-api-reference-NegativeArraySizeException-NegativeArraySizeException-1]]link:{java8-javadoc}/java/lang/NegativeArraySizeException.html#NegativeArraySizeException%2Djava.lang.String%2D[NegativeArraySizeException](<>)++ (link:{java9-javadoc}/java/lang/NegativeArraySizeException.html#NegativeArraySizeException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/NoSuchElementException.asciidoc b/docs/reference/painless-api-reference/NoSuchElementException.asciidoc deleted file mode 100644 index 7a28bdfb5dd24..0000000000000 --- a/docs/reference/painless-api-reference/NoSuchElementException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-NoSuchElementException]]++NoSuchElementException++:: -* ++[[painless-api-reference-NoSuchElementException-NoSuchElementException-0]]link:{java8-javadoc}/java/util/NoSuchElementException.html#NoSuchElementException%2D%2D[NoSuchElementException]()++ (link:{java9-javadoc}/java/util/NoSuchElementException.html#NoSuchElementException%2D%2D[java 9]) -* ++[[painless-api-reference-NoSuchElementException-NoSuchElementException-1]]link:{java8-javadoc}/java/util/NoSuchElementException.html#NoSuchElementException%2Djava.lang.String%2D[NoSuchElementException](<>)++ (link:{java9-javadoc}/java/util/NoSuchElementException.html#NoSuchElementException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/NoSuchFieldException.asciidoc b/docs/reference/painless-api-reference/NoSuchFieldException.asciidoc deleted file mode 100644 index 6f3e12d16d7d0..0000000000000 --- a/docs/reference/painless-api-reference/NoSuchFieldException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-NoSuchFieldException]]++NoSuchFieldException++:: -* ++[[painless-api-reference-NoSuchFieldException-NoSuchFieldException-0]]link:{java8-javadoc}/java/lang/NoSuchFieldException.html#NoSuchFieldException%2D%2D[NoSuchFieldException]()++ (link:{java9-javadoc}/java/lang/NoSuchFieldException.html#NoSuchFieldException%2D%2D[java 9]) -* ++[[painless-api-reference-NoSuchFieldException-NoSuchFieldException-1]]link:{java8-javadoc}/java/lang/NoSuchFieldException.html#NoSuchFieldException%2Djava.lang.String%2D[NoSuchFieldException](<>)++ (link:{java9-javadoc}/java/lang/NoSuchFieldException.html#NoSuchFieldException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/NoSuchMethodException.asciidoc b/docs/reference/painless-api-reference/NoSuchMethodException.asciidoc deleted file mode 100644 index 0fec067e9d281..0000000000000 --- a/docs/reference/painless-api-reference/NoSuchMethodException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-NoSuchMethodException]]++NoSuchMethodException++:: -* ++[[painless-api-reference-NoSuchMethodException-NoSuchMethodException-0]]link:{java8-javadoc}/java/lang/NoSuchMethodException.html#NoSuchMethodException%2D%2D[NoSuchMethodException]()++ (link:{java9-javadoc}/java/lang/NoSuchMethodException.html#NoSuchMethodException%2D%2D[java 9]) -* ++[[painless-api-reference-NoSuchMethodException-NoSuchMethodException-1]]link:{java8-javadoc}/java/lang/NoSuchMethodException.html#NoSuchMethodException%2Djava.lang.String%2D[NoSuchMethodException](<>)++ (link:{java9-javadoc}/java/lang/NoSuchMethodException.html#NoSuchMethodException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Normalizer.Form.asciidoc b/docs/reference/painless-api-reference/Normalizer.Form.asciidoc deleted file mode 100644 index f74d4d243db95..0000000000000 --- a/docs/reference/painless-api-reference/Normalizer.Form.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Normalizer-Form]]++Normalizer.Form++:: -** [[painless-api-reference-Normalizer-Form-NFC]]static <> link:{java8-javadoc}/java/text/Normalizer$Form.html#NFC[NFC] (link:{java9-javadoc}/java/text/Normalizer$Form.html#NFC[java 9]) -** [[painless-api-reference-Normalizer-Form-NFD]]static <> link:{java8-javadoc}/java/text/Normalizer$Form.html#NFD[NFD] (link:{java9-javadoc}/java/text/Normalizer$Form.html#NFD[java 9]) -** [[painless-api-reference-Normalizer-Form-NFKC]]static <> link:{java8-javadoc}/java/text/Normalizer$Form.html#NFKC[NFKC] (link:{java9-javadoc}/java/text/Normalizer$Form.html#NFKC[java 9]) -** [[painless-api-reference-Normalizer-Form-NFKD]]static <> link:{java8-javadoc}/java/text/Normalizer$Form.html#NFKD[NFKD] (link:{java9-javadoc}/java/text/Normalizer$Form.html#NFKD[java 9]) -* ++[[painless-api-reference-Normalizer-Form-valueOf-1]]static <> link:{java8-javadoc}/java/text/Normalizer$Form.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/text/Normalizer$Form.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Normalizer-Form-values-0]]static <>[] link:{java8-javadoc}/java/text/Normalizer$Form.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/text/Normalizer$Form.html#values%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Normalizer.asciidoc b/docs/reference/painless-api-reference/Normalizer.asciidoc deleted file mode 100644 index ecfe03c7fb4bd..0000000000000 --- a/docs/reference/painless-api-reference/Normalizer.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Normalizer]]++Normalizer++:: -* ++[[painless-api-reference-Normalizer-isNormalized-2]]static boolean link:{java8-javadoc}/java/text/Normalizer.html#isNormalized%2Djava.lang.CharSequence%2Djava.text.Normalizer$Form%2D[isNormalized](<>, <>)++ (link:{java9-javadoc}/java/text/Normalizer.html#isNormalized%2Djava.lang.CharSequence%2Djava.text.Normalizer$Form%2D[java 9]) -* ++[[painless-api-reference-Normalizer-normalize-2]]static <> link:{java8-javadoc}/java/text/Normalizer.html#normalize%2Djava.lang.CharSequence%2Djava.text.Normalizer$Form%2D[normalize](<>, <>)++ (link:{java9-javadoc}/java/text/Normalizer.html#normalize%2Djava.lang.CharSequence%2Djava.text.Normalizer$Form%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/NullPointerException.asciidoc b/docs/reference/painless-api-reference/NullPointerException.asciidoc deleted file mode 100644 index 72795c7399060..0000000000000 --- a/docs/reference/painless-api-reference/NullPointerException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-NullPointerException]]++NullPointerException++:: -* ++[[painless-api-reference-NullPointerException-NullPointerException-0]]link:{java8-javadoc}/java/lang/NullPointerException.html#NullPointerException%2D%2D[NullPointerException]()++ (link:{java9-javadoc}/java/lang/NullPointerException.html#NullPointerException%2D%2D[java 9]) -* ++[[painless-api-reference-NullPointerException-NullPointerException-1]]link:{java8-javadoc}/java/lang/NullPointerException.html#NullPointerException%2Djava.lang.String%2D[NullPointerException](<>)++ (link:{java9-javadoc}/java/lang/NullPointerException.html#NullPointerException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Number.asciidoc b/docs/reference/painless-api-reference/Number.asciidoc deleted file mode 100644 index a8c588f33704e..0000000000000 --- a/docs/reference/painless-api-reference/Number.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Number]]++Number++:: -* ++[[painless-api-reference-Number-byteValue-0]]byte link:{java8-javadoc}/java/lang/Number.html#byteValue%2D%2D[byteValue]()++ (link:{java9-javadoc}/java/lang/Number.html#byteValue%2D%2D[java 9]) -* ++[[painless-api-reference-Number-doubleValue-0]]double link:{java8-javadoc}/java/lang/Number.html#doubleValue%2D%2D[doubleValue]()++ (link:{java9-javadoc}/java/lang/Number.html#doubleValue%2D%2D[java 9]) -* ++[[painless-api-reference-Number-floatValue-0]]float link:{java8-javadoc}/java/lang/Number.html#floatValue%2D%2D[floatValue]()++ (link:{java9-javadoc}/java/lang/Number.html#floatValue%2D%2D[java 9]) -* ++[[painless-api-reference-Number-intValue-0]]int link:{java8-javadoc}/java/lang/Number.html#intValue%2D%2D[intValue]()++ (link:{java9-javadoc}/java/lang/Number.html#intValue%2D%2D[java 9]) -* ++[[painless-api-reference-Number-longValue-0]]long link:{java8-javadoc}/java/lang/Number.html#longValue%2D%2D[longValue]()++ (link:{java9-javadoc}/java/lang/Number.html#longValue%2D%2D[java 9]) -* ++[[painless-api-reference-Number-shortValue-0]]short link:{java8-javadoc}/java/lang/Number.html#shortValue%2D%2D[shortValue]()++ (link:{java9-javadoc}/java/lang/Number.html#shortValue%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/NumberFormat.Field.asciidoc b/docs/reference/painless-api-reference/NumberFormat.Field.asciidoc deleted file mode 100644 index aec4fc0635790..0000000000000 --- a/docs/reference/painless-api-reference/NumberFormat.Field.asciidoc +++ /dev/null @@ -1,18 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-NumberFormat-Field]]++NumberFormat.Field++:: -** [[painless-api-reference-NumberFormat-Field-CURRENCY]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#CURRENCY[CURRENCY] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#CURRENCY[java 9]) -** [[painless-api-reference-NumberFormat-Field-DECIMAL_SEPARATOR]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#DECIMAL_SEPARATOR[DECIMAL_SEPARATOR] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#DECIMAL_SEPARATOR[java 9]) -** [[painless-api-reference-NumberFormat-Field-EXPONENT]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#EXPONENT[EXPONENT] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#EXPONENT[java 9]) -** [[painless-api-reference-NumberFormat-Field-EXPONENT_SIGN]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#EXPONENT_SIGN[EXPONENT_SIGN] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#EXPONENT_SIGN[java 9]) -** [[painless-api-reference-NumberFormat-Field-EXPONENT_SYMBOL]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#EXPONENT_SYMBOL[EXPONENT_SYMBOL] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#EXPONENT_SYMBOL[java 9]) -** [[painless-api-reference-NumberFormat-Field-FRACTION]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#FRACTION[FRACTION] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#FRACTION[java 9]) -** [[painless-api-reference-NumberFormat-Field-GROUPING_SEPARATOR]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#GROUPING_SEPARATOR[GROUPING_SEPARATOR] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#GROUPING_SEPARATOR[java 9]) -** [[painless-api-reference-NumberFormat-Field-INTEGER]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#INTEGER[INTEGER] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#INTEGER[java 9]) -** [[painless-api-reference-NumberFormat-Field-PERCENT]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#PERCENT[PERCENT] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#PERCENT[java 9]) -** [[painless-api-reference-NumberFormat-Field-PERMILLE]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#PERMILLE[PERMILLE] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#PERMILLE[java 9]) -** [[painless-api-reference-NumberFormat-Field-SIGN]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#SIGN[SIGN] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#SIGN[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/NumberFormat.asciidoc b/docs/reference/painless-api-reference/NumberFormat.asciidoc deleted file mode 100644 index ddca8ccd6826f..0000000000000 --- a/docs/reference/painless-api-reference/NumberFormat.asciidoc +++ /dev/null @@ -1,38 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-NumberFormat]]++NumberFormat++:: -** [[painless-api-reference-NumberFormat-FRACTION_FIELD]]static int link:{java8-javadoc}/java/text/NumberFormat.html#FRACTION_FIELD[FRACTION_FIELD] (link:{java9-javadoc}/java/text/NumberFormat.html#FRACTION_FIELD[java 9]) -** [[painless-api-reference-NumberFormat-INTEGER_FIELD]]static int link:{java8-javadoc}/java/text/NumberFormat.html#INTEGER_FIELD[INTEGER_FIELD] (link:{java9-javadoc}/java/text/NumberFormat.html#INTEGER_FIELD[java 9]) -* ++[[painless-api-reference-NumberFormat-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/text/NumberFormat.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getAvailableLocales%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getCurrencyInstance-0]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getCurrencyInstance%2D%2D[getCurrencyInstance]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getCurrencyInstance%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getCurrencyInstance-1]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getCurrencyInstance%2Djava.util.Locale%2D[getCurrencyInstance](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#getCurrencyInstance%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getInstance-0]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getInstance%2D%2D[getInstance]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getInstance%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getInstance-1]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getInstance%2Djava.util.Locale%2D[getInstance](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#getInstance%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getIntegerInstance-0]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getIntegerInstance%2D%2D[getIntegerInstance]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getIntegerInstance%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getIntegerInstance-1]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getIntegerInstance%2Djava.util.Locale%2D[getIntegerInstance](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#getIntegerInstance%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getNumberInstance-0]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getNumberInstance%2D%2D[getNumberInstance]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getNumberInstance%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getNumberInstance-1]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getNumberInstance%2Djava.util.Locale%2D[getNumberInstance](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#getNumberInstance%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getPercentInstance-0]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getPercentInstance%2D%2D[getPercentInstance]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getPercentInstance%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getPercentInstance-1]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getPercentInstance%2Djava.util.Locale%2D[getPercentInstance](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#getPercentInstance%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getCurrency-0]]<> link:{java8-javadoc}/java/text/NumberFormat.html#getCurrency%2D%2D[getCurrency]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getCurrency%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getMaximumFractionDigits-0]]int link:{java8-javadoc}/java/text/NumberFormat.html#getMaximumFractionDigits%2D%2D[getMaximumFractionDigits]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getMaximumFractionDigits%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getMaximumIntegerDigits-0]]int link:{java8-javadoc}/java/text/NumberFormat.html#getMaximumIntegerDigits%2D%2D[getMaximumIntegerDigits]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getMaximumIntegerDigits%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getMinimumFractionDigits-0]]int link:{java8-javadoc}/java/text/NumberFormat.html#getMinimumFractionDigits%2D%2D[getMinimumFractionDigits]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getMinimumFractionDigits%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getMinimumIntegerDigits-0]]int link:{java8-javadoc}/java/text/NumberFormat.html#getMinimumIntegerDigits%2D%2D[getMinimumIntegerDigits]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getMinimumIntegerDigits%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-getRoundingMode-0]]<> link:{java8-javadoc}/java/text/NumberFormat.html#getRoundingMode%2D%2D[getRoundingMode]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getRoundingMode%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-isGroupingUsed-0]]boolean link:{java8-javadoc}/java/text/NumberFormat.html#isGroupingUsed%2D%2D[isGroupingUsed]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#isGroupingUsed%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-isParseIntegerOnly-0]]boolean link:{java8-javadoc}/java/text/NumberFormat.html#isParseIntegerOnly%2D%2D[isParseIntegerOnly]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#isParseIntegerOnly%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-parse-1]]<> link:{java8-javadoc}/java/text/NumberFormat.html#parse%2Djava.lang.String%2D[parse](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#parse%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-parse-2]]<> link:{java8-javadoc}/java/text/NumberFormat.html#parse%2Djava.lang.String%2Djava.text.ParsePosition%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#parse%2Djava.lang.String%2Djava.text.ParsePosition%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-setCurrency-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setCurrency%2Djava.util.Currency%2D[setCurrency](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setCurrency%2Djava.util.Currency%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-setGroupingUsed-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setGroupingUsed%2Dboolean%2D[setGroupingUsed](boolean)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setGroupingUsed%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-setMaximumFractionDigits-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setMaximumFractionDigits%2Dint%2D[setMaximumFractionDigits](int)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setMaximumFractionDigits%2Dint%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-setMaximumIntegerDigits-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setMaximumIntegerDigits%2Dint%2D[setMaximumIntegerDigits](int)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setMaximumIntegerDigits%2Dint%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-setMinimumFractionDigits-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setMinimumFractionDigits%2Dint%2D[setMinimumFractionDigits](int)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setMinimumFractionDigits%2Dint%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-setMinimumIntegerDigits-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setMinimumIntegerDigits%2Dint%2D[setMinimumIntegerDigits](int)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setMinimumIntegerDigits%2Dint%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-setParseIntegerOnly-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setParseIntegerOnly%2Dboolean%2D[setParseIntegerOnly](boolean)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setParseIntegerOnly%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-NumberFormat-setRoundingMode-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setRoundingMode%2Djava.math.RoundingMode%2D[setRoundingMode](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setRoundingMode%2Djava.math.RoundingMode%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/NumberFormatException.asciidoc b/docs/reference/painless-api-reference/NumberFormatException.asciidoc deleted file mode 100644 index 4066147650c33..0000000000000 --- a/docs/reference/painless-api-reference/NumberFormatException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-NumberFormatException]]++NumberFormatException++:: -* ++[[painless-api-reference-NumberFormatException-NumberFormatException-0]]link:{java8-javadoc}/java/lang/NumberFormatException.html#NumberFormatException%2D%2D[NumberFormatException]()++ (link:{java9-javadoc}/java/lang/NumberFormatException.html#NumberFormatException%2D%2D[java 9]) -* ++[[painless-api-reference-NumberFormatException-NumberFormatException-1]]link:{java8-javadoc}/java/lang/NumberFormatException.html#NumberFormatException%2Djava.lang.String%2D[NumberFormatException](<>)++ (link:{java9-javadoc}/java/lang/NumberFormatException.html#NumberFormatException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ObjDoubleConsumer.asciidoc b/docs/reference/painless-api-reference/ObjDoubleConsumer.asciidoc deleted file mode 100644 index 2fc41f9ce0fb5..0000000000000 --- a/docs/reference/painless-api-reference/ObjDoubleConsumer.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ObjDoubleConsumer]]++ObjDoubleConsumer++:: -* ++[[painless-api-reference-ObjDoubleConsumer-accept-2]]void link:{java8-javadoc}/java/util/function/ObjDoubleConsumer.html#accept%2Djava.lang.Object%2Ddouble%2D[accept](def, double)++ (link:{java9-javadoc}/java/util/function/ObjDoubleConsumer.html#accept%2Djava.lang.Object%2Ddouble%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ObjIntConsumer.asciidoc b/docs/reference/painless-api-reference/ObjIntConsumer.asciidoc deleted file mode 100644 index 8779b333df92c..0000000000000 --- a/docs/reference/painless-api-reference/ObjIntConsumer.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ObjIntConsumer]]++ObjIntConsumer++:: -* ++[[painless-api-reference-ObjIntConsumer-accept-2]]void link:{java8-javadoc}/java/util/function/ObjIntConsumer.html#accept%2Djava.lang.Object%2Dint%2D[accept](def, int)++ (link:{java9-javadoc}/java/util/function/ObjIntConsumer.html#accept%2Djava.lang.Object%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ObjLongConsumer.asciidoc b/docs/reference/painless-api-reference/ObjLongConsumer.asciidoc deleted file mode 100644 index cdb0ed6546c4f..0000000000000 --- a/docs/reference/painless-api-reference/ObjLongConsumer.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ObjLongConsumer]]++ObjLongConsumer++:: -* ++[[painless-api-reference-ObjLongConsumer-accept-2]]void link:{java8-javadoc}/java/util/function/ObjLongConsumer.html#accept%2Djava.lang.Object%2Dlong%2D[accept](def, long)++ (link:{java9-javadoc}/java/util/function/ObjLongConsumer.html#accept%2Djava.lang.Object%2Dlong%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Object.asciidoc b/docs/reference/painless-api-reference/Object.asciidoc deleted file mode 100644 index 99c4ae5cffdb6..0000000000000 --- a/docs/reference/painless-api-reference/Object.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Object]]++Object++:: -* ++[[painless-api-reference-Object-equals-1]]boolean link:{java8-javadoc}/java/lang/Object.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/lang/Object.html#equals%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Object-hashCode-0]]int link:{java8-javadoc}/java/lang/Object.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/lang/Object.html#hashCode%2D%2D[java 9]) -* ++[[painless-api-reference-Object-toString-0]]<> link:{java8-javadoc}/java/lang/Object.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/lang/Object.html#toString%2D%2D[java 9]) diff --git a/docs/reference/painless-api-reference/Objects.asciidoc b/docs/reference/painless-api-reference/Objects.asciidoc deleted file mode 100644 index 9fdbad2e5b71d..0000000000000 --- a/docs/reference/painless-api-reference/Objects.asciidoc +++ /dev/null @@ -1,18 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Objects]]++Objects++:: -* ++[[painless-api-reference-Objects-compare-3]]static int link:{java8-javadoc}/java/util/Objects.html#compare%2Djava.lang.Object%2Djava.lang.Object%2Djava.util.Comparator%2D[compare](def, def, <>)++ (link:{java9-javadoc}/java/util/Objects.html#compare%2Djava.lang.Object%2Djava.lang.Object%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Objects-deepEquals-2]]static boolean link:{java8-javadoc}/java/util/Objects.html#deepEquals%2Djava.lang.Object%2Djava.lang.Object%2D[deepEquals](<>, <>)++ (link:{java9-javadoc}/java/util/Objects.html#deepEquals%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Objects-equals-2]]static boolean link:{java8-javadoc}/java/util/Objects.html#equals%2Djava.lang.Object%2Djava.lang.Object%2D[equals](<>, <>)++ (link:{java9-javadoc}/java/util/Objects.html#equals%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Objects-hash-1]]static int link:{java8-javadoc}/java/util/Objects.html#hash%2Djava.lang.Object:A%2D[hash](<>[])++ (link:{java9-javadoc}/java/util/Objects.html#hash%2Djava.lang.Object:A%2D[java 9]) -* ++[[painless-api-reference-Objects-hashCode-1]]static int link:{java8-javadoc}/java/util/Objects.html#hashCode%2Djava.lang.Object%2D[hashCode](<>)++ (link:{java9-javadoc}/java/util/Objects.html#hashCode%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Objects-isNull-1]]static boolean link:{java8-javadoc}/java/util/Objects.html#isNull%2Djava.lang.Object%2D[isNull](<>)++ (link:{java9-javadoc}/java/util/Objects.html#isNull%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Objects-nonNull-1]]static boolean link:{java8-javadoc}/java/util/Objects.html#nonNull%2Djava.lang.Object%2D[nonNull](<>)++ (link:{java9-javadoc}/java/util/Objects.html#nonNull%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Objects-requireNonNull-1]]static def link:{java8-javadoc}/java/util/Objects.html#requireNonNull%2Djava.lang.Object%2D[requireNonNull](def)++ (link:{java9-javadoc}/java/util/Objects.html#requireNonNull%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Objects-requireNonNull-2]]static def link:{java8-javadoc}/java/util/Objects.html#requireNonNull%2Djava.lang.Object%2Djava.lang.String%2D[requireNonNull](def, <>)++ (link:{java9-javadoc}/java/util/Objects.html#requireNonNull%2Djava.lang.Object%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Objects-toString-1]]static <> link:{java8-javadoc}/java/util/Objects.html#toString%2Djava.lang.Object%2D[toString](<>)++ (link:{java9-javadoc}/java/util/Objects.html#toString%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Objects-toString-2]]static <> link:{java8-javadoc}/java/util/Objects.html#toString%2Djava.lang.Object%2Djava.lang.String%2D[toString](<>, <>)++ (link:{java9-javadoc}/java/util/Objects.html#toString%2Djava.lang.Object%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Observable.asciidoc b/docs/reference/painless-api-reference/Observable.asciidoc deleted file mode 100644 index 65d8fae45ae36..0000000000000 --- a/docs/reference/painless-api-reference/Observable.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Observable]]++Observable++:: -* ++[[painless-api-reference-Observable-Observable-0]]link:{java8-javadoc}/java/util/Observable.html#Observable%2D%2D[Observable]()++ (link:{java9-javadoc}/java/util/Observable.html#Observable%2D%2D[java 9]) -* ++[[painless-api-reference-Observable-addObserver-1]]void link:{java8-javadoc}/java/util/Observable.html#addObserver%2Djava.util.Observer%2D[addObserver](<>)++ (link:{java9-javadoc}/java/util/Observable.html#addObserver%2Djava.util.Observer%2D[java 9]) -* ++[[painless-api-reference-Observable-countObservers-0]]int link:{java8-javadoc}/java/util/Observable.html#countObservers%2D%2D[countObservers]()++ (link:{java9-javadoc}/java/util/Observable.html#countObservers%2D%2D[java 9]) -* ++[[painless-api-reference-Observable-deleteObserver-1]]void link:{java8-javadoc}/java/util/Observable.html#deleteObserver%2Djava.util.Observer%2D[deleteObserver](<>)++ (link:{java9-javadoc}/java/util/Observable.html#deleteObserver%2Djava.util.Observer%2D[java 9]) -* ++[[painless-api-reference-Observable-deleteObservers-0]]void link:{java8-javadoc}/java/util/Observable.html#deleteObservers%2D%2D[deleteObservers]()++ (link:{java9-javadoc}/java/util/Observable.html#deleteObservers%2D%2D[java 9]) -* ++[[painless-api-reference-Observable-hasChanged-0]]boolean link:{java8-javadoc}/java/util/Observable.html#hasChanged%2D%2D[hasChanged]()++ (link:{java9-javadoc}/java/util/Observable.html#hasChanged%2D%2D[java 9]) -* ++[[painless-api-reference-Observable-notifyObservers-0]]void link:{java8-javadoc}/java/util/Observable.html#notifyObservers%2D%2D[notifyObservers]()++ (link:{java9-javadoc}/java/util/Observable.html#notifyObservers%2D%2D[java 9]) -* ++[[painless-api-reference-Observable-notifyObservers-1]]void link:{java8-javadoc}/java/util/Observable.html#notifyObservers%2Djava.lang.Object%2D[notifyObservers](<>)++ (link:{java9-javadoc}/java/util/Observable.html#notifyObservers%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Observer.asciidoc b/docs/reference/painless-api-reference/Observer.asciidoc deleted file mode 100644 index 9277663dd6232..0000000000000 --- a/docs/reference/painless-api-reference/Observer.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Observer]]++Observer++:: -* ++[[painless-api-reference-Observer-update-2]]void link:{java8-javadoc}/java/util/Observer.html#update%2Djava.util.Observable%2Djava.lang.Object%2D[update](<>, <>)++ (link:{java9-javadoc}/java/util/Observer.html#update%2Djava.util.Observable%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/OffsetDateTime.asciidoc b/docs/reference/painless-api-reference/OffsetDateTime.asciidoc deleted file mode 100644 index d476c181c55f0..0000000000000 --- a/docs/reference/painless-api-reference/OffsetDateTime.asciidoc +++ /dev/null @@ -1,75 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-OffsetDateTime]]++OffsetDateTime++:: -** [[painless-api-reference-OffsetDateTime-MAX]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#MAX[MAX] (link:{java9-javadoc}/java/time/OffsetDateTime.html#MAX[java 9]) -** [[painless-api-reference-OffsetDateTime-MIN]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#MIN[MIN] (link:{java9-javadoc}/java/time/OffsetDateTime.html#MIN[java 9]) -* ++[[painless-api-reference-OffsetDateTime-from-1]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-of-2]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#of%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2D[of](<>, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#of%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-of-3]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#of%2Djava.time.LocalDate%2Djava.time.LocalTime%2Djava.time.ZoneOffset%2D[of](<>, <>, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#of%2Djava.time.LocalDate%2Djava.time.LocalTime%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-of-8]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Djava.time.ZoneOffset%2D[of](int, int, int, int, int, int, int, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-ofInstant-2]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[ofInstant](<>, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-parse-1]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#parse%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-parse-2]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-timeLineOrder-0]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#timeLineOrder%2D%2D[timeLineOrder]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#timeLineOrder%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-atZoneSameInstant-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#atZoneSameInstant%2Djava.time.ZoneId%2D[atZoneSameInstant](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#atZoneSameInstant%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-atZoneSimilarLocal-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#atZoneSimilarLocal%2Djava.time.ZoneId%2D[atZoneSimilarLocal](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#atZoneSimilarLocal%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-compareTo-1]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#compareTo%2Djava.time.OffsetDateTime%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#compareTo%2Djava.time.OffsetDateTime%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-format-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-getDayOfMonth-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getDayOfMonth%2D%2D[getDayOfMonth]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getDayOfMonth%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-getDayOfWeek-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#getDayOfWeek%2D%2D[getDayOfWeek]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getDayOfWeek%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-getDayOfYear-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getDayOfYear%2D%2D[getDayOfYear]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getDayOfYear%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-getHour-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getHour%2D%2D[getHour]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getHour%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-getMinute-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getMinute%2D%2D[getMinute]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getMinute%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-getMonth-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getMonth%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-getMonthValue-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getMonthValue%2D%2D[getMonthValue]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getMonthValue%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-getNano-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getNano%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-getOffset-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#getOffset%2D%2D[getOffset]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getOffset%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-getSecond-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getSecond%2D%2D[getSecond]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getSecond%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-getYear-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getYear%2D%2D[getYear]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getYear%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-isAfter-1]]boolean link:{java8-javadoc}/java/time/OffsetDateTime.html#isAfter%2Djava.time.OffsetDateTime%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#isAfter%2Djava.time.OffsetDateTime%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-isBefore-1]]boolean link:{java8-javadoc}/java/time/OffsetDateTime.html#isBefore%2Djava.time.OffsetDateTime%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#isBefore%2Djava.time.OffsetDateTime%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-isEqual-1]]boolean link:{java8-javadoc}/java/time/OffsetDateTime.html#isEqual%2Djava.time.OffsetDateTime%2D[isEqual](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#isEqual%2Djava.time.OffsetDateTime%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-minus-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-minus-2]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-minusDays-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusDays%2Dlong%2D[minusDays](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusDays%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-minusHours-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusHours%2Dlong%2D[minusHours](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusHours%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-minusMinutes-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusMinutes%2Dlong%2D[minusMinutes](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusMinutes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-minusMonths-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusMonths%2Dlong%2D[minusMonths](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusMonths%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-minusNanos-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-minusSeconds-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-minusWeeks-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusWeeks%2Dlong%2D[minusWeeks](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusWeeks%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-minusYears-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-plus-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-plus-2]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-plusDays-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusDays%2Dlong%2D[plusDays](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusDays%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-plusHours-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusHours%2Dlong%2D[plusHours](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusHours%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-plusMinutes-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusMinutes%2Dlong%2D[plusMinutes](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusMinutes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-plusMonths-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusMonths%2Dlong%2D[plusMonths](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusMonths%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-plusNanos-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-plusSeconds-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-plusWeeks-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusWeeks%2Dlong%2D[plusWeeks](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusWeeks%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-plusYears-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-toEpochSecond-0]]long link:{java8-javadoc}/java/time/OffsetDateTime.html#toEpochSecond%2D%2D[toEpochSecond]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toEpochSecond%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-toInstant-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#toInstant%2D%2D[toInstant]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toInstant%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-toLocalDate-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#toLocalDate%2D%2D[toLocalDate]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toLocalDate%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-toLocalDateTime-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#toLocalDateTime%2D%2D[toLocalDateTime]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toLocalDateTime%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-toLocalTime-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#toLocalTime%2D%2D[toLocalTime]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toLocalTime%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-toOffsetTime-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#toOffsetTime%2D%2D[toOffsetTime]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toOffsetTime%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-toZonedDateTime-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#toZonedDateTime%2D%2D[toZonedDateTime]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toZonedDateTime%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-truncatedTo-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[truncatedTo](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-with-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-with-2]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-withDayOfMonth-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withDayOfMonth%2Dint%2D[withDayOfMonth](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withDayOfMonth%2Dint%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-withDayOfYear-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withDayOfYear%2Dint%2D[withDayOfYear](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withDayOfYear%2Dint%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-withHour-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withHour%2Dint%2D[withHour](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withHour%2Dint%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-withMinute-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withMinute%2Dint%2D[withMinute](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withMinute%2Dint%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-withMonth-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withMonth%2Dint%2D[withMonth](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withMonth%2Dint%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-withNano-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withNano%2Dint%2D[withNano](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withNano%2Dint%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-withOffsetSameInstant-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withOffsetSameInstant%2Djava.time.ZoneOffset%2D[withOffsetSameInstant](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withOffsetSameInstant%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-withOffsetSameLocal-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withOffsetSameLocal%2Djava.time.ZoneOffset%2D[withOffsetSameLocal](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withOffsetSameLocal%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-withSecond-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withSecond%2Dint%2D[withSecond](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withSecond%2Dint%2D[java 9]) -* ++[[painless-api-reference-OffsetDateTime-withYear-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withYear%2Dint%2D[withYear](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withYear%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/OffsetTime.asciidoc b/docs/reference/painless-api-reference/OffsetTime.asciidoc deleted file mode 100644 index b23e9bb9577d0..0000000000000 --- a/docs/reference/painless-api-reference/OffsetTime.asciidoc +++ /dev/null @@ -1,47 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-OffsetTime]]++OffsetTime++:: -** [[painless-api-reference-OffsetTime-MAX]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#MAX[MAX] (link:{java9-javadoc}/java/time/OffsetTime.html#MAX[java 9]) -** [[painless-api-reference-OffsetTime-MIN]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#MIN[MIN] (link:{java9-javadoc}/java/time/OffsetTime.html#MIN[java 9]) -* ++[[painless-api-reference-OffsetTime-from-1]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-of-2]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#of%2Djava.time.LocalTime%2Djava.time.ZoneOffset%2D[of](<>, <>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#of%2Djava.time.LocalTime%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-of-5]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#of%2Dint%2Dint%2Dint%2Dint%2Djava.time.ZoneOffset%2D[of](int, int, int, int, <>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#of%2Dint%2Dint%2Dint%2Dint%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-ofInstant-2]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[ofInstant](<>, <>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-parse-1]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#parse%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-parse-2]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-compareTo-1]]int link:{java8-javadoc}/java/time/OffsetTime.html#compareTo%2Djava.time.OffsetTime%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#compareTo%2Djava.time.OffsetTime%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-format-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-getHour-0]]int link:{java8-javadoc}/java/time/OffsetTime.html#getHour%2D%2D[getHour]()++ (link:{java9-javadoc}/java/time/OffsetTime.html#getHour%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-getMinute-0]]int link:{java8-javadoc}/java/time/OffsetTime.html#getMinute%2D%2D[getMinute]()++ (link:{java9-javadoc}/java/time/OffsetTime.html#getMinute%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-getNano-0]]int link:{java8-javadoc}/java/time/OffsetTime.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/OffsetTime.html#getNano%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-getOffset-0]]<> link:{java8-javadoc}/java/time/OffsetTime.html#getOffset%2D%2D[getOffset]()++ (link:{java9-javadoc}/java/time/OffsetTime.html#getOffset%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-getSecond-0]]int link:{java8-javadoc}/java/time/OffsetTime.html#getSecond%2D%2D[getSecond]()++ (link:{java9-javadoc}/java/time/OffsetTime.html#getSecond%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-isAfter-1]]boolean link:{java8-javadoc}/java/time/OffsetTime.html#isAfter%2Djava.time.OffsetTime%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#isAfter%2Djava.time.OffsetTime%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-isBefore-1]]boolean link:{java8-javadoc}/java/time/OffsetTime.html#isBefore%2Djava.time.OffsetTime%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#isBefore%2Djava.time.OffsetTime%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-isEqual-1]]boolean link:{java8-javadoc}/java/time/OffsetTime.html#isEqual%2Djava.time.OffsetTime%2D[isEqual](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#isEqual%2Djava.time.OffsetTime%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-minus-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-minus-2]]<> link:{java8-javadoc}/java/time/OffsetTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-minusHours-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#minusHours%2Dlong%2D[minusHours](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#minusHours%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-minusMinutes-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#minusMinutes%2Dlong%2D[minusMinutes](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#minusMinutes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-minusNanos-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#minusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-minusSeconds-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#minusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-plus-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-plus-2]]<> link:{java8-javadoc}/java/time/OffsetTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-plusHours-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#plusHours%2Dlong%2D[plusHours](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#plusHours%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-plusMinutes-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#plusMinutes%2Dlong%2D[plusMinutes](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#plusMinutes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-plusNanos-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#plusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-plusSeconds-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#plusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-toLocalTime-0]]<> link:{java8-javadoc}/java/time/OffsetTime.html#toLocalTime%2D%2D[toLocalTime]()++ (link:{java9-javadoc}/java/time/OffsetTime.html#toLocalTime%2D%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-truncatedTo-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[truncatedTo](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-with-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-with-2]]<> link:{java8-javadoc}/java/time/OffsetTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-withHour-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#withHour%2Dint%2D[withHour](int)++ (link:{java9-javadoc}/java/time/OffsetTime.html#withHour%2Dint%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-withMinute-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#withMinute%2Dint%2D[withMinute](int)++ (link:{java9-javadoc}/java/time/OffsetTime.html#withMinute%2Dint%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-withNano-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#withNano%2Dint%2D[withNano](int)++ (link:{java9-javadoc}/java/time/OffsetTime.html#withNano%2Dint%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-withOffsetSameInstant-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#withOffsetSameInstant%2Djava.time.ZoneOffset%2D[withOffsetSameInstant](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#withOffsetSameInstant%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-withOffsetSameLocal-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#withOffsetSameLocal%2Djava.time.ZoneOffset%2D[withOffsetSameLocal](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#withOffsetSameLocal%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-OffsetTime-withSecond-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#withSecond%2Dint%2D[withSecond](int)++ (link:{java9-javadoc}/java/time/OffsetTime.html#withSecond%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Optional.asciidoc b/docs/reference/painless-api-reference/Optional.asciidoc deleted file mode 100644 index a67616f006fea..0000000000000 --- a/docs/reference/painless-api-reference/Optional.asciidoc +++ /dev/null @@ -1,19 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Optional]]++Optional++:: -* ++[[painless-api-reference-Optional-empty-0]]static <> link:{java8-javadoc}/java/util/Optional.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/Optional.html#empty%2D%2D[java 9]) -* ++[[painless-api-reference-Optional-of-1]]static <> link:{java8-javadoc}/java/util/Optional.html#of%2Djava.lang.Object%2D[of](def)++ (link:{java9-javadoc}/java/util/Optional.html#of%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Optional-ofNullable-1]]static <> link:{java8-javadoc}/java/util/Optional.html#ofNullable%2Djava.lang.Object%2D[ofNullable](def)++ (link:{java9-javadoc}/java/util/Optional.html#ofNullable%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Optional-filter-1]]<> link:{java8-javadoc}/java/util/Optional.html#filter%2Djava.util.function.Predicate%2D[filter](<>)++ (link:{java9-javadoc}/java/util/Optional.html#filter%2Djava.util.function.Predicate%2D[java 9]) -* ++[[painless-api-reference-Optional-flatMap-1]]<> link:{java8-javadoc}/java/util/Optional.html#flatMap%2Djava.util.function.Function%2D[flatMap](<>)++ (link:{java9-javadoc}/java/util/Optional.html#flatMap%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-Optional-get-0]]def link:{java8-javadoc}/java/util/Optional.html#get%2D%2D[get]()++ (link:{java9-javadoc}/java/util/Optional.html#get%2D%2D[java 9]) -* ++[[painless-api-reference-Optional-ifPresent-1]]void link:{java8-javadoc}/java/util/Optional.html#ifPresent%2Djava.util.function.Consumer%2D[ifPresent](<>)++ (link:{java9-javadoc}/java/util/Optional.html#ifPresent%2Djava.util.function.Consumer%2D[java 9]) -* ++[[painless-api-reference-Optional-isPresent-0]]boolean link:{java8-javadoc}/java/util/Optional.html#isPresent%2D%2D[isPresent]()++ (link:{java9-javadoc}/java/util/Optional.html#isPresent%2D%2D[java 9]) -* ++[[painless-api-reference-Optional-map-1]]<> link:{java8-javadoc}/java/util/Optional.html#map%2Djava.util.function.Function%2D[map](<>)++ (link:{java9-javadoc}/java/util/Optional.html#map%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-Optional-orElse-1]]def link:{java8-javadoc}/java/util/Optional.html#orElse%2Djava.lang.Object%2D[orElse](def)++ (link:{java9-javadoc}/java/util/Optional.html#orElse%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Optional-orElseGet-1]]def link:{java8-javadoc}/java/util/Optional.html#orElseGet%2Djava.util.function.Supplier%2D[orElseGet](<>)++ (link:{java9-javadoc}/java/util/Optional.html#orElseGet%2Djava.util.function.Supplier%2D[java 9]) -* ++[[painless-api-reference-Optional-orElseThrow-1]]def link:{java8-javadoc}/java/util/Optional.html#orElseThrow%2Djava.util.function.Supplier%2D[orElseThrow](<>)++ (link:{java9-javadoc}/java/util/Optional.html#orElseThrow%2Djava.util.function.Supplier%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/OptionalDouble.asciidoc b/docs/reference/painless-api-reference/OptionalDouble.asciidoc deleted file mode 100644 index 56ac37deaa336..0000000000000 --- a/docs/reference/painless-api-reference/OptionalDouble.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-OptionalDouble]]++OptionalDouble++:: -* ++[[painless-api-reference-OptionalDouble-empty-0]]static <> link:{java8-javadoc}/java/util/OptionalDouble.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/OptionalDouble.html#empty%2D%2D[java 9]) -* ++[[painless-api-reference-OptionalDouble-of-1]]static <> link:{java8-javadoc}/java/util/OptionalDouble.html#of%2Ddouble%2D[of](double)++ (link:{java9-javadoc}/java/util/OptionalDouble.html#of%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-OptionalDouble-getAsDouble-0]]double link:{java8-javadoc}/java/util/OptionalDouble.html#getAsDouble%2D%2D[getAsDouble]()++ (link:{java9-javadoc}/java/util/OptionalDouble.html#getAsDouble%2D%2D[java 9]) -* ++[[painless-api-reference-OptionalDouble-ifPresent-1]]void link:{java8-javadoc}/java/util/OptionalDouble.html#ifPresent%2Djava.util.function.DoubleConsumer%2D[ifPresent](<>)++ (link:{java9-javadoc}/java/util/OptionalDouble.html#ifPresent%2Djava.util.function.DoubleConsumer%2D[java 9]) -* ++[[painless-api-reference-OptionalDouble-isPresent-0]]boolean link:{java8-javadoc}/java/util/OptionalDouble.html#isPresent%2D%2D[isPresent]()++ (link:{java9-javadoc}/java/util/OptionalDouble.html#isPresent%2D%2D[java 9]) -* ++[[painless-api-reference-OptionalDouble-orElse-1]]double link:{java8-javadoc}/java/util/OptionalDouble.html#orElse%2Ddouble%2D[orElse](double)++ (link:{java9-javadoc}/java/util/OptionalDouble.html#orElse%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-OptionalDouble-orElseGet-1]]double link:{java8-javadoc}/java/util/OptionalDouble.html#orElseGet%2Djava.util.function.DoubleSupplier%2D[orElseGet](<>)++ (link:{java9-javadoc}/java/util/OptionalDouble.html#orElseGet%2Djava.util.function.DoubleSupplier%2D[java 9]) -* ++[[painless-api-reference-OptionalDouble-orElseThrow-1]]double link:{java8-javadoc}/java/util/OptionalDouble.html#orElseThrow%2Djava.util.function.Supplier%2D[orElseThrow](<>)++ (link:{java9-javadoc}/java/util/OptionalDouble.html#orElseThrow%2Djava.util.function.Supplier%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/OptionalInt.asciidoc b/docs/reference/painless-api-reference/OptionalInt.asciidoc deleted file mode 100644 index 69f0059b85f9c..0000000000000 --- a/docs/reference/painless-api-reference/OptionalInt.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-OptionalInt]]++OptionalInt++:: -* ++[[painless-api-reference-OptionalInt-empty-0]]static <> link:{java8-javadoc}/java/util/OptionalInt.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/OptionalInt.html#empty%2D%2D[java 9]) -* ++[[painless-api-reference-OptionalInt-of-1]]static <> link:{java8-javadoc}/java/util/OptionalInt.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/util/OptionalInt.html#of%2Dint%2D[java 9]) -* ++[[painless-api-reference-OptionalInt-getAsInt-0]]int link:{java8-javadoc}/java/util/OptionalInt.html#getAsInt%2D%2D[getAsInt]()++ (link:{java9-javadoc}/java/util/OptionalInt.html#getAsInt%2D%2D[java 9]) -* ++[[painless-api-reference-OptionalInt-ifPresent-1]]void link:{java8-javadoc}/java/util/OptionalInt.html#ifPresent%2Djava.util.function.IntConsumer%2D[ifPresent](<>)++ (link:{java9-javadoc}/java/util/OptionalInt.html#ifPresent%2Djava.util.function.IntConsumer%2D[java 9]) -* ++[[painless-api-reference-OptionalInt-isPresent-0]]boolean link:{java8-javadoc}/java/util/OptionalInt.html#isPresent%2D%2D[isPresent]()++ (link:{java9-javadoc}/java/util/OptionalInt.html#isPresent%2D%2D[java 9]) -* ++[[painless-api-reference-OptionalInt-orElse-1]]int link:{java8-javadoc}/java/util/OptionalInt.html#orElse%2Dint%2D[orElse](int)++ (link:{java9-javadoc}/java/util/OptionalInt.html#orElse%2Dint%2D[java 9]) -* ++[[painless-api-reference-OptionalInt-orElseGet-1]]int link:{java8-javadoc}/java/util/OptionalInt.html#orElseGet%2Djava.util.function.IntSupplier%2D[orElseGet](<>)++ (link:{java9-javadoc}/java/util/OptionalInt.html#orElseGet%2Djava.util.function.IntSupplier%2D[java 9]) -* ++[[painless-api-reference-OptionalInt-orElseThrow-1]]int link:{java8-javadoc}/java/util/OptionalInt.html#orElseThrow%2Djava.util.function.Supplier%2D[orElseThrow](<>)++ (link:{java9-javadoc}/java/util/OptionalInt.html#orElseThrow%2Djava.util.function.Supplier%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/OptionalLong.asciidoc b/docs/reference/painless-api-reference/OptionalLong.asciidoc deleted file mode 100644 index a4b135e55115a..0000000000000 --- a/docs/reference/painless-api-reference/OptionalLong.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-OptionalLong]]++OptionalLong++:: -* ++[[painless-api-reference-OptionalLong-empty-0]]static <> link:{java8-javadoc}/java/util/OptionalLong.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/OptionalLong.html#empty%2D%2D[java 9]) -* ++[[painless-api-reference-OptionalLong-of-1]]static <> link:{java8-javadoc}/java/util/OptionalLong.html#of%2Dlong%2D[of](long)++ (link:{java9-javadoc}/java/util/OptionalLong.html#of%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OptionalLong-getAsLong-0]]long link:{java8-javadoc}/java/util/OptionalLong.html#getAsLong%2D%2D[getAsLong]()++ (link:{java9-javadoc}/java/util/OptionalLong.html#getAsLong%2D%2D[java 9]) -* ++[[painless-api-reference-OptionalLong-ifPresent-1]]void link:{java8-javadoc}/java/util/OptionalLong.html#ifPresent%2Djava.util.function.LongConsumer%2D[ifPresent](<>)++ (link:{java9-javadoc}/java/util/OptionalLong.html#ifPresent%2Djava.util.function.LongConsumer%2D[java 9]) -* ++[[painless-api-reference-OptionalLong-isPresent-0]]boolean link:{java8-javadoc}/java/util/OptionalLong.html#isPresent%2D%2D[isPresent]()++ (link:{java9-javadoc}/java/util/OptionalLong.html#isPresent%2D%2D[java 9]) -* ++[[painless-api-reference-OptionalLong-orElse-1]]long link:{java8-javadoc}/java/util/OptionalLong.html#orElse%2Dlong%2D[orElse](long)++ (link:{java9-javadoc}/java/util/OptionalLong.html#orElse%2Dlong%2D[java 9]) -* ++[[painless-api-reference-OptionalLong-orElseGet-1]]long link:{java8-javadoc}/java/util/OptionalLong.html#orElseGet%2Djava.util.function.LongSupplier%2D[orElseGet](<>)++ (link:{java9-javadoc}/java/util/OptionalLong.html#orElseGet%2Djava.util.function.LongSupplier%2D[java 9]) -* ++[[painless-api-reference-OptionalLong-orElseThrow-1]]long link:{java8-javadoc}/java/util/OptionalLong.html#orElseThrow%2Djava.util.function.Supplier%2D[orElseThrow](<>)++ (link:{java9-javadoc}/java/util/OptionalLong.html#orElseThrow%2Djava.util.function.Supplier%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ParseException.asciidoc b/docs/reference/painless-api-reference/ParseException.asciidoc deleted file mode 100644 index 086bcd0b7aaed..0000000000000 --- a/docs/reference/painless-api-reference/ParseException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ParseException]]++ParseException++:: -* ++[[painless-api-reference-ParseException-ParseException-2]]link:{java8-javadoc}/java/text/ParseException.html#ParseException%2Djava.lang.String%2Dint%2D[ParseException](<>, int)++ (link:{java9-javadoc}/java/text/ParseException.html#ParseException%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-ParseException-getErrorOffset-0]]int link:{java8-javadoc}/java/text/ParseException.html#getErrorOffset%2D%2D[getErrorOffset]()++ (link:{java9-javadoc}/java/text/ParseException.html#getErrorOffset%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ParsePosition.asciidoc b/docs/reference/painless-api-reference/ParsePosition.asciidoc deleted file mode 100644 index 88fe6369657bd..0000000000000 --- a/docs/reference/painless-api-reference/ParsePosition.asciidoc +++ /dev/null @@ -1,12 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ParsePosition]]++ParsePosition++:: -* ++[[painless-api-reference-ParsePosition-ParsePosition-1]]link:{java8-javadoc}/java/text/ParsePosition.html#ParsePosition%2Dint%2D[ParsePosition](int)++ (link:{java9-javadoc}/java/text/ParsePosition.html#ParsePosition%2Dint%2D[java 9]) -* ++[[painless-api-reference-ParsePosition-getErrorIndex-0]]int link:{java8-javadoc}/java/text/ParsePosition.html#getErrorIndex%2D%2D[getErrorIndex]()++ (link:{java9-javadoc}/java/text/ParsePosition.html#getErrorIndex%2D%2D[java 9]) -* ++[[painless-api-reference-ParsePosition-getIndex-0]]int link:{java8-javadoc}/java/text/ParsePosition.html#getIndex%2D%2D[getIndex]()++ (link:{java9-javadoc}/java/text/ParsePosition.html#getIndex%2D%2D[java 9]) -* ++[[painless-api-reference-ParsePosition-setErrorIndex-1]]void link:{java8-javadoc}/java/text/ParsePosition.html#setErrorIndex%2Dint%2D[setErrorIndex](int)++ (link:{java9-javadoc}/java/text/ParsePosition.html#setErrorIndex%2Dint%2D[java 9]) -* ++[[painless-api-reference-ParsePosition-setIndex-1]]void link:{java8-javadoc}/java/text/ParsePosition.html#setIndex%2Dint%2D[setIndex](int)++ (link:{java9-javadoc}/java/text/ParsePosition.html#setIndex%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Pattern.asciidoc b/docs/reference/painless-api-reference/Pattern.asciidoc deleted file mode 100644 index ad7b3603036b1..0000000000000 --- a/docs/reference/painless-api-reference/Pattern.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Pattern]]++Pattern++:: -* ++[[painless-api-reference-Pattern-quote-1]]static <> link:{java8-javadoc}/java/util/regex/Pattern.html#quote%2Djava.lang.String%2D[quote](<>)++ (link:{java9-javadoc}/java/util/regex/Pattern.html#quote%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Pattern-asPredicate-0]]<> link:{java8-javadoc}/java/util/regex/Pattern.html#asPredicate%2D%2D[asPredicate]()++ (link:{java9-javadoc}/java/util/regex/Pattern.html#asPredicate%2D%2D[java 9]) -* ++[[painless-api-reference-Pattern-flags-0]]int link:{java8-javadoc}/java/util/regex/Pattern.html#flags%2D%2D[flags]()++ (link:{java9-javadoc}/java/util/regex/Pattern.html#flags%2D%2D[java 9]) -* ++[[painless-api-reference-Pattern-matcher-1]]<> link:{java8-javadoc}/java/util/regex/Pattern.html#matcher%2Djava.lang.CharSequence%2D[matcher](<>)++ (link:{java9-javadoc}/java/util/regex/Pattern.html#matcher%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-Pattern-pattern-0]]<> link:{java8-javadoc}/java/util/regex/Pattern.html#pattern%2D%2D[pattern]()++ (link:{java9-javadoc}/java/util/regex/Pattern.html#pattern%2D%2D[java 9]) -* ++[[painless-api-reference-Pattern-split-1]]<>[] link:{java8-javadoc}/java/util/regex/Pattern.html#split%2Djava.lang.CharSequence%2D[split](<>)++ (link:{java9-javadoc}/java/util/regex/Pattern.html#split%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-Pattern-split-2]]<>[] link:{java8-javadoc}/java/util/regex/Pattern.html#split%2Djava.lang.CharSequence%2Dint%2D[split](<>, int)++ (link:{java9-javadoc}/java/util/regex/Pattern.html#split%2Djava.lang.CharSequence%2Dint%2D[java 9]) -* ++[[painless-api-reference-Pattern-splitAsStream-1]]<> link:{java8-javadoc}/java/util/regex/Pattern.html#splitAsStream%2Djava.lang.CharSequence%2D[splitAsStream](<>)++ (link:{java9-javadoc}/java/util/regex/Pattern.html#splitAsStream%2Djava.lang.CharSequence%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Period.asciidoc b/docs/reference/painless-api-reference/Period.asciidoc deleted file mode 100644 index bb20dddae60a8..0000000000000 --- a/docs/reference/painless-api-reference/Period.asciidoc +++ /dev/null @@ -1,35 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Period]]++Period++:: -** [[painless-api-reference-Period-ZERO]]static <> link:{java8-javadoc}/java/time/Period.html#ZERO[ZERO] (link:{java9-javadoc}/java/time/Period.html#ZERO[java 9]) -* ++[[painless-api-reference-Period-between-2]]static <> link:{java8-javadoc}/java/time/Period.html#between%2Djava.time.LocalDate%2Djava.time.LocalDate%2D[between](<>, <>)++ (link:{java9-javadoc}/java/time/Period.html#between%2Djava.time.LocalDate%2Djava.time.LocalDate%2D[java 9]) -* ++[[painless-api-reference-Period-from-1]]static <> link:{java8-javadoc}/java/time/Period.html#from%2Djava.time.temporal.TemporalAmount%2D[from](<>)++ (link:{java9-javadoc}/java/time/Period.html#from%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-Period-of-3]]static <> link:{java8-javadoc}/java/time/Period.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/Period.html#of%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Period-ofDays-1]]static <> link:{java8-javadoc}/java/time/Period.html#ofDays%2Dint%2D[ofDays](int)++ (link:{java9-javadoc}/java/time/Period.html#ofDays%2Dint%2D[java 9]) -* ++[[painless-api-reference-Period-ofMonths-1]]static <> link:{java8-javadoc}/java/time/Period.html#ofMonths%2Dint%2D[ofMonths](int)++ (link:{java9-javadoc}/java/time/Period.html#ofMonths%2Dint%2D[java 9]) -* ++[[painless-api-reference-Period-ofWeeks-1]]static <> link:{java8-javadoc}/java/time/Period.html#ofWeeks%2Dint%2D[ofWeeks](int)++ (link:{java9-javadoc}/java/time/Period.html#ofWeeks%2Dint%2D[java 9]) -* ++[[painless-api-reference-Period-ofYears-1]]static <> link:{java8-javadoc}/java/time/Period.html#ofYears%2Dint%2D[ofYears](int)++ (link:{java9-javadoc}/java/time/Period.html#ofYears%2Dint%2D[java 9]) -* ++[[painless-api-reference-Period-parse-1]]static <> link:{java8-javadoc}/java/time/Period.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/Period.html#parse%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-Period-getChronology-0]]<> link:{java8-javadoc}/java/time/Period.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/Period.html#getChronology%2D%2D[java 9]) -* ++[[painless-api-reference-Period-getDays-0]]int link:{java8-javadoc}/java/time/Period.html#getDays%2D%2D[getDays]()++ (link:{java9-javadoc}/java/time/Period.html#getDays%2D%2D[java 9]) -* ++[[painless-api-reference-Period-getMonths-0]]int link:{java8-javadoc}/java/time/Period.html#getMonths%2D%2D[getMonths]()++ (link:{java9-javadoc}/java/time/Period.html#getMonths%2D%2D[java 9]) -* ++[[painless-api-reference-Period-getYears-0]]int link:{java8-javadoc}/java/time/Period.html#getYears%2D%2D[getYears]()++ (link:{java9-javadoc}/java/time/Period.html#getYears%2D%2D[java 9]) -* ++[[painless-api-reference-Period-minus-1]]<> link:{java8-javadoc}/java/time/Period.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/Period.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-Period-minusDays-1]]<> link:{java8-javadoc}/java/time/Period.html#minusDays%2Dlong%2D[minusDays](long)++ (link:{java9-javadoc}/java/time/Period.html#minusDays%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Period-minusMonths-1]]<> link:{java8-javadoc}/java/time/Period.html#minusMonths%2Dlong%2D[minusMonths](long)++ (link:{java9-javadoc}/java/time/Period.html#minusMonths%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Period-minusYears-1]]<> link:{java8-javadoc}/java/time/Period.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/Period.html#minusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Period-multipliedBy-1]]<> link:{java8-javadoc}/java/time/Period.html#multipliedBy%2Dint%2D[multipliedBy](int)++ (link:{java9-javadoc}/java/time/Period.html#multipliedBy%2Dint%2D[java 9]) -* ++[[painless-api-reference-Period-negated-0]]<> link:{java8-javadoc}/java/time/Period.html#negated%2D%2D[negated]()++ (link:{java9-javadoc}/java/time/Period.html#negated%2D%2D[java 9]) -* ++[[painless-api-reference-Period-normalized-0]]<> link:{java8-javadoc}/java/time/Period.html#normalized%2D%2D[normalized]()++ (link:{java9-javadoc}/java/time/Period.html#normalized%2D%2D[java 9]) -* ++[[painless-api-reference-Period-plus-1]]<> link:{java8-javadoc}/java/time/Period.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/Period.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-Period-plusDays-1]]<> link:{java8-javadoc}/java/time/Period.html#plusDays%2Dlong%2D[plusDays](long)++ (link:{java9-javadoc}/java/time/Period.html#plusDays%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Period-plusMonths-1]]<> link:{java8-javadoc}/java/time/Period.html#plusMonths%2Dlong%2D[plusMonths](long)++ (link:{java9-javadoc}/java/time/Period.html#plusMonths%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Period-plusYears-1]]<> link:{java8-javadoc}/java/time/Period.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/Period.html#plusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Period-toTotalMonths-0]]long link:{java8-javadoc}/java/time/Period.html#toTotalMonths%2D%2D[toTotalMonths]()++ (link:{java9-javadoc}/java/time/Period.html#toTotalMonths%2D%2D[java 9]) -* ++[[painless-api-reference-Period-withDays-1]]<> link:{java8-javadoc}/java/time/Period.html#withDays%2Dint%2D[withDays](int)++ (link:{java9-javadoc}/java/time/Period.html#withDays%2Dint%2D[java 9]) -* ++[[painless-api-reference-Period-withMonths-1]]<> link:{java8-javadoc}/java/time/Period.html#withMonths%2Dint%2D[withMonths](int)++ (link:{java9-javadoc}/java/time/Period.html#withMonths%2Dint%2D[java 9]) -* ++[[painless-api-reference-Period-withYears-1]]<> link:{java8-javadoc}/java/time/Period.html#withYears%2Dint%2D[withYears](int)++ (link:{java9-javadoc}/java/time/Period.html#withYears%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Predicate.asciidoc b/docs/reference/painless-api-reference/Predicate.asciidoc deleted file mode 100644 index 6eef93ea27c02..0000000000000 --- a/docs/reference/painless-api-reference/Predicate.asciidoc +++ /dev/null @@ -1,12 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Predicate]]++Predicate++:: -* ++[[painless-api-reference-Predicate-isEqual-1]]static <> link:{java8-javadoc}/java/util/function/Predicate.html#isEqual%2Djava.lang.Object%2D[isEqual](def)++ (link:{java9-javadoc}/java/util/function/Predicate.html#isEqual%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Predicate-and-1]]<> link:{java8-javadoc}/java/util/function/Predicate.html#and%2Djava.util.function.Predicate%2D[and](<>)++ (link:{java9-javadoc}/java/util/function/Predicate.html#and%2Djava.util.function.Predicate%2D[java 9]) -* ++[[painless-api-reference-Predicate-negate-0]]<> link:{java8-javadoc}/java/util/function/Predicate.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/util/function/Predicate.html#negate%2D%2D[java 9]) -* ++[[painless-api-reference-Predicate-or-1]]<> link:{java8-javadoc}/java/util/function/Predicate.html#or%2Djava.util.function.Predicate%2D[or](<>)++ (link:{java9-javadoc}/java/util/function/Predicate.html#or%2Djava.util.function.Predicate%2D[java 9]) -* ++[[painless-api-reference-Predicate-test-1]]boolean link:{java8-javadoc}/java/util/function/Predicate.html#test%2Djava.lang.Object%2D[test](def)++ (link:{java9-javadoc}/java/util/function/Predicate.html#test%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/PrimitiveIterator.OfDouble.asciidoc b/docs/reference/painless-api-reference/PrimitiveIterator.OfDouble.asciidoc deleted file mode 100644 index bb26cf3b51477..0000000000000 --- a/docs/reference/painless-api-reference/PrimitiveIterator.OfDouble.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-PrimitiveIterator-OfDouble]]++PrimitiveIterator.OfDouble++:: -* ++[[painless-api-reference-PrimitiveIterator-OfDouble-next-0]]<> link:{java8-javadoc}/java/util/PrimitiveIterator$OfDouble.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/util/PrimitiveIterator$OfDouble.html#next%2D%2D[java 9]) -* ++[[painless-api-reference-PrimitiveIterator-OfDouble-nextDouble-0]]double link:{java8-javadoc}/java/util/PrimitiveIterator$OfDouble.html#nextDouble%2D%2D[nextDouble]()++ (link:{java9-javadoc}/java/util/PrimitiveIterator$OfDouble.html#nextDouble%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/PrimitiveIterator.OfInt.asciidoc b/docs/reference/painless-api-reference/PrimitiveIterator.OfInt.asciidoc deleted file mode 100644 index 1cd90b8e13f02..0000000000000 --- a/docs/reference/painless-api-reference/PrimitiveIterator.OfInt.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-PrimitiveIterator-OfInt]]++PrimitiveIterator.OfInt++:: -* ++[[painless-api-reference-PrimitiveIterator-OfInt-next-0]]<> link:{java8-javadoc}/java/util/PrimitiveIterator$OfInt.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/util/PrimitiveIterator$OfInt.html#next%2D%2D[java 9]) -* ++[[painless-api-reference-PrimitiveIterator-OfInt-nextInt-0]]int link:{java8-javadoc}/java/util/PrimitiveIterator$OfInt.html#nextInt%2D%2D[nextInt]()++ (link:{java9-javadoc}/java/util/PrimitiveIterator$OfInt.html#nextInt%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/PrimitiveIterator.OfLong.asciidoc b/docs/reference/painless-api-reference/PrimitiveIterator.OfLong.asciidoc deleted file mode 100644 index bcfff0e7edc14..0000000000000 --- a/docs/reference/painless-api-reference/PrimitiveIterator.OfLong.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-PrimitiveIterator-OfLong]]++PrimitiveIterator.OfLong++:: -* ++[[painless-api-reference-PrimitiveIterator-OfLong-next-0]]<> link:{java8-javadoc}/java/util/PrimitiveIterator$OfLong.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/util/PrimitiveIterator$OfLong.html#next%2D%2D[java 9]) -* ++[[painless-api-reference-PrimitiveIterator-OfLong-nextLong-0]]long link:{java8-javadoc}/java/util/PrimitiveIterator$OfLong.html#nextLong%2D%2D[nextLong]()++ (link:{java9-javadoc}/java/util/PrimitiveIterator$OfLong.html#nextLong%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/PrimitiveIterator.asciidoc b/docs/reference/painless-api-reference/PrimitiveIterator.asciidoc deleted file mode 100644 index 42742f79d3d40..0000000000000 --- a/docs/reference/painless-api-reference/PrimitiveIterator.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-PrimitiveIterator]]++PrimitiveIterator++:: -* ++[[painless-api-reference-PrimitiveIterator-forEachRemaining-1]]void link:{java8-javadoc}/java/util/PrimitiveIterator.html#forEachRemaining%2Djava.lang.Object%2D[forEachRemaining](def)++ (link:{java9-javadoc}/java/util/PrimitiveIterator.html#forEachRemaining%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/PriorityQueue.asciidoc b/docs/reference/painless-api-reference/PriorityQueue.asciidoc deleted file mode 100644 index 88334cd8b9c63..0000000000000 --- a/docs/reference/painless-api-reference/PriorityQueue.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-PriorityQueue]]++PriorityQueue++:: -* ++[[painless-api-reference-PriorityQueue-PriorityQueue-0]]link:{java8-javadoc}/java/util/PriorityQueue.html#PriorityQueue%2D%2D[PriorityQueue]()++ (link:{java9-javadoc}/java/util/PriorityQueue.html#PriorityQueue%2D%2D[java 9]) -* ++[[painless-api-reference-PriorityQueue-PriorityQueue-1]]link:{java8-javadoc}/java/util/PriorityQueue.html#PriorityQueue%2Djava.util.Comparator%2D[PriorityQueue](<>)++ (link:{java9-javadoc}/java/util/PriorityQueue.html#PriorityQueue%2Djava.util.Comparator%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Queue.asciidoc b/docs/reference/painless-api-reference/Queue.asciidoc deleted file mode 100644 index 7d323457591ff..0000000000000 --- a/docs/reference/painless-api-reference/Queue.asciidoc +++ /dev/null @@ -1,12 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Queue]]++Queue++:: -* ++[[painless-api-reference-Queue-element-0]]def link:{java8-javadoc}/java/util/Queue.html#element%2D%2D[element]()++ (link:{java9-javadoc}/java/util/Queue.html#element%2D%2D[java 9]) -* ++[[painless-api-reference-Queue-offer-1]]boolean link:{java8-javadoc}/java/util/Queue.html#offer%2Djava.lang.Object%2D[offer](def)++ (link:{java9-javadoc}/java/util/Queue.html#offer%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Queue-peek-0]]def link:{java8-javadoc}/java/util/Queue.html#peek%2D%2D[peek]()++ (link:{java9-javadoc}/java/util/Queue.html#peek%2D%2D[java 9]) -* ++[[painless-api-reference-Queue-poll-0]]def link:{java8-javadoc}/java/util/Queue.html#poll%2D%2D[poll]()++ (link:{java9-javadoc}/java/util/Queue.html#poll%2D%2D[java 9]) -* ++[[painless-api-reference-Queue-remove-0]]def link:{java8-javadoc}/java/util/Queue.html#remove%2D%2D[remove]()++ (link:{java9-javadoc}/java/util/Queue.html#remove%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Random.asciidoc b/docs/reference/painless-api-reference/Random.asciidoc deleted file mode 100644 index 4288c330850da..0000000000000 --- a/docs/reference/painless-api-reference/Random.asciidoc +++ /dev/null @@ -1,24 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Random]]++Random++:: -* ++[[painless-api-reference-Random-Random-0]]link:{java8-javadoc}/java/util/Random.html#Random%2D%2D[Random]()++ (link:{java9-javadoc}/java/util/Random.html#Random%2D%2D[java 9]) -* ++[[painless-api-reference-Random-Random-1]]link:{java8-javadoc}/java/util/Random.html#Random%2Dlong%2D[Random](long)++ (link:{java9-javadoc}/java/util/Random.html#Random%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Random-doubles-1]]<> link:{java8-javadoc}/java/util/Random.html#doubles%2Dlong%2D[doubles](long)++ (link:{java9-javadoc}/java/util/Random.html#doubles%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Random-doubles-3]]<> link:{java8-javadoc}/java/util/Random.html#doubles%2Dlong%2Ddouble%2Ddouble%2D[doubles](long, double, double)++ (link:{java9-javadoc}/java/util/Random.html#doubles%2Dlong%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-Random-ints-1]]<> link:{java8-javadoc}/java/util/Random.html#ints%2Dlong%2D[ints](long)++ (link:{java9-javadoc}/java/util/Random.html#ints%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Random-ints-3]]<> link:{java8-javadoc}/java/util/Random.html#ints%2Dlong%2Dint%2Dint%2D[ints](long, int, int)++ (link:{java9-javadoc}/java/util/Random.html#ints%2Dlong%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-Random-longs-1]]<> link:{java8-javadoc}/java/util/Random.html#longs%2Dlong%2D[longs](long)++ (link:{java9-javadoc}/java/util/Random.html#longs%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Random-longs-3]]<> link:{java8-javadoc}/java/util/Random.html#longs%2Dlong%2Dlong%2Dlong%2D[longs](long, long, long)++ (link:{java9-javadoc}/java/util/Random.html#longs%2Dlong%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Random-nextBoolean-0]]boolean link:{java8-javadoc}/java/util/Random.html#nextBoolean%2D%2D[nextBoolean]()++ (link:{java9-javadoc}/java/util/Random.html#nextBoolean%2D%2D[java 9]) -* ++[[painless-api-reference-Random-nextBytes-1]]void link:{java8-javadoc}/java/util/Random.html#nextBytes%2Dbyte:A%2D[nextBytes](byte[])++ (link:{java9-javadoc}/java/util/Random.html#nextBytes%2Dbyte:A%2D[java 9]) -* ++[[painless-api-reference-Random-nextDouble-0]]double link:{java8-javadoc}/java/util/Random.html#nextDouble%2D%2D[nextDouble]()++ (link:{java9-javadoc}/java/util/Random.html#nextDouble%2D%2D[java 9]) -* ++[[painless-api-reference-Random-nextFloat-0]]float link:{java8-javadoc}/java/util/Random.html#nextFloat%2D%2D[nextFloat]()++ (link:{java9-javadoc}/java/util/Random.html#nextFloat%2D%2D[java 9]) -* ++[[painless-api-reference-Random-nextGaussian-0]]double link:{java8-javadoc}/java/util/Random.html#nextGaussian%2D%2D[nextGaussian]()++ (link:{java9-javadoc}/java/util/Random.html#nextGaussian%2D%2D[java 9]) -* ++[[painless-api-reference-Random-nextInt-0]]int link:{java8-javadoc}/java/util/Random.html#nextInt%2D%2D[nextInt]()++ (link:{java9-javadoc}/java/util/Random.html#nextInt%2D%2D[java 9]) -* ++[[painless-api-reference-Random-nextInt-1]]int link:{java8-javadoc}/java/util/Random.html#nextInt%2Dint%2D[nextInt](int)++ (link:{java9-javadoc}/java/util/Random.html#nextInt%2Dint%2D[java 9]) -* ++[[painless-api-reference-Random-nextLong-0]]long link:{java8-javadoc}/java/util/Random.html#nextLong%2D%2D[nextLong]()++ (link:{java9-javadoc}/java/util/Random.html#nextLong%2D%2D[java 9]) -* ++[[painless-api-reference-Random-setSeed-1]]void link:{java8-javadoc}/java/util/Random.html#setSeed%2Dlong%2D[setSeed](long)++ (link:{java9-javadoc}/java/util/Random.html#setSeed%2Dlong%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/RandomAccess.asciidoc b/docs/reference/painless-api-reference/RandomAccess.asciidoc deleted file mode 100644 index b7afb205f0c6c..0000000000000 --- a/docs/reference/painless-api-reference/RandomAccess.asciidoc +++ /dev/null @@ -1,7 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-RandomAccess]]++RandomAccess++:: -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ReflectiveOperationException.asciidoc b/docs/reference/painless-api-reference/ReflectiveOperationException.asciidoc deleted file mode 100644 index eee134d373a93..0000000000000 --- a/docs/reference/painless-api-reference/ReflectiveOperationException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ReflectiveOperationException]]++ReflectiveOperationException++:: -* ++[[painless-api-reference-ReflectiveOperationException-ReflectiveOperationException-0]]link:{java8-javadoc}/java/lang/ReflectiveOperationException.html#ReflectiveOperationException%2D%2D[ReflectiveOperationException]()++ (link:{java9-javadoc}/java/lang/ReflectiveOperationException.html#ReflectiveOperationException%2D%2D[java 9]) -* ++[[painless-api-reference-ReflectiveOperationException-ReflectiveOperationException-1]]link:{java8-javadoc}/java/lang/ReflectiveOperationException.html#ReflectiveOperationException%2Djava.lang.String%2D[ReflectiveOperationException](<>)++ (link:{java9-javadoc}/java/lang/ReflectiveOperationException.html#ReflectiveOperationException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ResolverStyle.asciidoc b/docs/reference/painless-api-reference/ResolverStyle.asciidoc deleted file mode 100644 index 00576a2d93a0e..0000000000000 --- a/docs/reference/painless-api-reference/ResolverStyle.asciidoc +++ /dev/null @@ -1,12 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ResolverStyle]]++ResolverStyle++:: -** [[painless-api-reference-ResolverStyle-LENIENT]]static <> link:{java8-javadoc}/java/time/format/ResolverStyle.html#LENIENT[LENIENT] (link:{java9-javadoc}/java/time/format/ResolverStyle.html#LENIENT[java 9]) -** [[painless-api-reference-ResolverStyle-SMART]]static <> link:{java8-javadoc}/java/time/format/ResolverStyle.html#SMART[SMART] (link:{java9-javadoc}/java/time/format/ResolverStyle.html#SMART[java 9]) -** [[painless-api-reference-ResolverStyle-STRICT]]static <> link:{java8-javadoc}/java/time/format/ResolverStyle.html#STRICT[STRICT] (link:{java9-javadoc}/java/time/format/ResolverStyle.html#STRICT[java 9]) -* ++[[painless-api-reference-ResolverStyle-valueOf-1]]static <> link:{java8-javadoc}/java/time/format/ResolverStyle.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/format/ResolverStyle.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-ResolverStyle-values-0]]static <>[] link:{java8-javadoc}/java/time/format/ResolverStyle.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/format/ResolverStyle.html#values%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/RoundingMode.asciidoc b/docs/reference/painless-api-reference/RoundingMode.asciidoc deleted file mode 100644 index e0b4d579835e9..0000000000000 --- a/docs/reference/painless-api-reference/RoundingMode.asciidoc +++ /dev/null @@ -1,17 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-RoundingMode]]++RoundingMode++:: -** [[painless-api-reference-RoundingMode-CEILING]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#CEILING[CEILING] (link:{java9-javadoc}/java/math/RoundingMode.html#CEILING[java 9]) -** [[painless-api-reference-RoundingMode-DOWN]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#DOWN[DOWN] (link:{java9-javadoc}/java/math/RoundingMode.html#DOWN[java 9]) -** [[painless-api-reference-RoundingMode-FLOOR]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#FLOOR[FLOOR] (link:{java9-javadoc}/java/math/RoundingMode.html#FLOOR[java 9]) -** [[painless-api-reference-RoundingMode-HALF_DOWN]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#HALF_DOWN[HALF_DOWN] (link:{java9-javadoc}/java/math/RoundingMode.html#HALF_DOWN[java 9]) -** [[painless-api-reference-RoundingMode-HALF_EVEN]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#HALF_EVEN[HALF_EVEN] (link:{java9-javadoc}/java/math/RoundingMode.html#HALF_EVEN[java 9]) -** [[painless-api-reference-RoundingMode-HALF_UP]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#HALF_UP[HALF_UP] (link:{java9-javadoc}/java/math/RoundingMode.html#HALF_UP[java 9]) -** [[painless-api-reference-RoundingMode-UNNECESSARY]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#UNNECESSARY[UNNECESSARY] (link:{java9-javadoc}/java/math/RoundingMode.html#UNNECESSARY[java 9]) -** [[painless-api-reference-RoundingMode-UP]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#UP[UP] (link:{java9-javadoc}/java/math/RoundingMode.html#UP[java 9]) -* ++[[painless-api-reference-RoundingMode-valueOf-1]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/math/RoundingMode.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-RoundingMode-values-0]]static <>[] link:{java8-javadoc}/java/math/RoundingMode.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/math/RoundingMode.html#values%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/RuleBasedCollator.asciidoc b/docs/reference/painless-api-reference/RuleBasedCollator.asciidoc deleted file mode 100644 index 5581768e2f300..0000000000000 --- a/docs/reference/painless-api-reference/RuleBasedCollator.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-RuleBasedCollator]]++RuleBasedCollator++:: -* ++[[painless-api-reference-RuleBasedCollator-RuleBasedCollator-1]]link:{java8-javadoc}/java/text/RuleBasedCollator.html#RuleBasedCollator%2Djava.lang.String%2D[RuleBasedCollator](<>)++ (link:{java9-javadoc}/java/text/RuleBasedCollator.html#RuleBasedCollator%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-RuleBasedCollator-getCollationElementIterator-1]]<> link:{java8-javadoc}/java/text/RuleBasedCollator.html#getCollationElementIterator%2Djava.lang.String%2D[getCollationElementIterator](<>)++ (link:{java9-javadoc}/java/text/RuleBasedCollator.html#getCollationElementIterator%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-RuleBasedCollator-getRules-0]]<> link:{java8-javadoc}/java/text/RuleBasedCollator.html#getRules%2D%2D[getRules]()++ (link:{java9-javadoc}/java/text/RuleBasedCollator.html#getRules%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/RuntimeException.asciidoc b/docs/reference/painless-api-reference/RuntimeException.asciidoc deleted file mode 100644 index fc8523169e33d..0000000000000 --- a/docs/reference/painless-api-reference/RuntimeException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-RuntimeException]]++RuntimeException++:: -* ++[[painless-api-reference-RuntimeException-RuntimeException-0]]link:{java8-javadoc}/java/lang/RuntimeException.html#RuntimeException%2D%2D[RuntimeException]()++ (link:{java9-javadoc}/java/lang/RuntimeException.html#RuntimeException%2D%2D[java 9]) -* ++[[painless-api-reference-RuntimeException-RuntimeException-1]]link:{java8-javadoc}/java/lang/RuntimeException.html#RuntimeException%2Djava.lang.String%2D[RuntimeException](<>)++ (link:{java9-javadoc}/java/lang/RuntimeException.html#RuntimeException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/SecurityException.asciidoc b/docs/reference/painless-api-reference/SecurityException.asciidoc deleted file mode 100644 index ec6ea157e5587..0000000000000 --- a/docs/reference/painless-api-reference/SecurityException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-SecurityException]]++SecurityException++:: -* ++[[painless-api-reference-SecurityException-SecurityException-0]]link:{java8-javadoc}/java/lang/SecurityException.html#SecurityException%2D%2D[SecurityException]()++ (link:{java9-javadoc}/java/lang/SecurityException.html#SecurityException%2D%2D[java 9]) -* ++[[painless-api-reference-SecurityException-SecurityException-1]]link:{java8-javadoc}/java/lang/SecurityException.html#SecurityException%2Djava.lang.String%2D[SecurityException](<>)++ (link:{java9-javadoc}/java/lang/SecurityException.html#SecurityException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Set.asciidoc b/docs/reference/painless-api-reference/Set.asciidoc deleted file mode 100644 index 23d8a352f231f..0000000000000 --- a/docs/reference/painless-api-reference/Set.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Set]]++Set++:: -* ++[[painless-api-reference-Set-equals-1]]boolean link:{java8-javadoc}/java/util/Set.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/util/Set.html#equals%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Set-hashCode-0]]int link:{java8-javadoc}/java/util/Set.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/util/Set.html#hashCode%2D%2D[java 9]) -* ++[[painless-api-reference-Set-remove-1]]boolean link:{java8-javadoc}/java/util/Set.html#remove%2Djava.lang.Object%2D[remove](def)++ (link:{java9-javadoc}/java/util/Set.html#remove%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Short.asciidoc b/docs/reference/painless-api-reference/Short.asciidoc deleted file mode 100644 index 12f0336e8fd40..0000000000000 --- a/docs/reference/painless-api-reference/Short.asciidoc +++ /dev/null @@ -1,23 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Short]]++Short++:: -** [[painless-api-reference-Short-BYTES]]static int link:{java8-javadoc}/java/lang/Short.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Short.html#BYTES[java 9]) -** [[painless-api-reference-Short-MAX_VALUE]]static short link:{java8-javadoc}/java/lang/Short.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Short.html#MAX_VALUE[java 9]) -** [[painless-api-reference-Short-MIN_VALUE]]static short link:{java8-javadoc}/java/lang/Short.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Short.html#MIN_VALUE[java 9]) -** [[painless-api-reference-Short-SIZE]]static int link:{java8-javadoc}/java/lang/Short.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Short.html#SIZE[java 9]) -* ++[[painless-api-reference-Short-compare-2]]static int link:{java8-javadoc}/java/lang/Short.html#compare%2Dshort%2Dshort%2D[compare](short, short)++ (link:{java9-javadoc}/java/lang/Short.html#compare%2Dshort%2Dshort%2D[java 9]) -* ++[[painless-api-reference-Short-decode-1]]static <> link:{java8-javadoc}/java/lang/Short.html#decode%2Djava.lang.String%2D[decode](<>)++ (link:{java9-javadoc}/java/lang/Short.html#decode%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Short-hashCode-1]]static int link:{java8-javadoc}/java/lang/Short.html#hashCode%2Dshort%2D[hashCode](short)++ (link:{java9-javadoc}/java/lang/Short.html#hashCode%2Dshort%2D[java 9]) -* ++[[painless-api-reference-Short-parseShort-1]]static short link:{java8-javadoc}/java/lang/Short.html#parseShort%2Djava.lang.String%2D[parseShort](<>)++ (link:{java9-javadoc}/java/lang/Short.html#parseShort%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-Short-parseShort-2]]static short link:{java8-javadoc}/java/lang/Short.html#parseShort%2Djava.lang.String%2Dint%2D[parseShort](<>, int)++ (link:{java9-javadoc}/java/lang/Short.html#parseShort%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-Short-reverseBytes-1]]static short link:{java8-javadoc}/java/lang/Short.html#reverseBytes%2Dshort%2D[reverseBytes](short)++ (link:{java9-javadoc}/java/lang/Short.html#reverseBytes%2Dshort%2D[java 9]) -* ++[[painless-api-reference-Short-toString-1]]static <> link:{java8-javadoc}/java/lang/Short.html#toString%2Dshort%2D[toString](short)++ (link:{java9-javadoc}/java/lang/Short.html#toString%2Dshort%2D[java 9]) -* ++[[painless-api-reference-Short-toUnsignedInt-1]]static int link:{java8-javadoc}/java/lang/Short.html#toUnsignedInt%2Dshort%2D[toUnsignedInt](short)++ (link:{java9-javadoc}/java/lang/Short.html#toUnsignedInt%2Dshort%2D[java 9]) -* ++[[painless-api-reference-Short-toUnsignedLong-1]]static long link:{java8-javadoc}/java/lang/Short.html#toUnsignedLong%2Dshort%2D[toUnsignedLong](short)++ (link:{java9-javadoc}/java/lang/Short.html#toUnsignedLong%2Dshort%2D[java 9]) -* ++[[painless-api-reference-Short-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Short.html#valueOf%2Dshort%2D[valueOf](short)++ (link:{java9-javadoc}/java/lang/Short.html#valueOf%2Dshort%2D[java 9]) -* ++[[painless-api-reference-Short-valueOf-2]]static <> link:{java8-javadoc}/java/lang/Short.html#valueOf%2Djava.lang.String%2Dint%2D[valueOf](<>, int)++ (link:{java9-javadoc}/java/lang/Short.html#valueOf%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-Short-compareTo-1]]int link:{java8-javadoc}/java/lang/Short.html#compareTo%2Djava.lang.Short%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Short.html#compareTo%2Djava.lang.Short%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/SignStyle.asciidoc b/docs/reference/painless-api-reference/SignStyle.asciidoc deleted file mode 100644 index a42ce94ebfa33..0000000000000 --- a/docs/reference/painless-api-reference/SignStyle.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-SignStyle]]++SignStyle++:: -** [[painless-api-reference-SignStyle-ALWAYS]]static <> link:{java8-javadoc}/java/time/format/SignStyle.html#ALWAYS[ALWAYS] (link:{java9-javadoc}/java/time/format/SignStyle.html#ALWAYS[java 9]) -** [[painless-api-reference-SignStyle-EXCEEDS_PAD]]static <> link:{java8-javadoc}/java/time/format/SignStyle.html#EXCEEDS_PAD[EXCEEDS_PAD] (link:{java9-javadoc}/java/time/format/SignStyle.html#EXCEEDS_PAD[java 9]) -** [[painless-api-reference-SignStyle-NEVER]]static <> link:{java8-javadoc}/java/time/format/SignStyle.html#NEVER[NEVER] (link:{java9-javadoc}/java/time/format/SignStyle.html#NEVER[java 9]) -** [[painless-api-reference-SignStyle-NORMAL]]static <> link:{java8-javadoc}/java/time/format/SignStyle.html#NORMAL[NORMAL] (link:{java9-javadoc}/java/time/format/SignStyle.html#NORMAL[java 9]) -** [[painless-api-reference-SignStyle-NOT_NEGATIVE]]static <> link:{java8-javadoc}/java/time/format/SignStyle.html#NOT_NEGATIVE[NOT_NEGATIVE] (link:{java9-javadoc}/java/time/format/SignStyle.html#NOT_NEGATIVE[java 9]) -* ++[[painless-api-reference-SignStyle-valueOf-1]]static <> link:{java8-javadoc}/java/time/format/SignStyle.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/format/SignStyle.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-SignStyle-values-0]]static <>[] link:{java8-javadoc}/java/time/format/SignStyle.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/format/SignStyle.html#values%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/SimpleDateFormat.asciidoc b/docs/reference/painless-api-reference/SimpleDateFormat.asciidoc deleted file mode 100644 index b7a8ffc191c45..0000000000000 --- a/docs/reference/painless-api-reference/SimpleDateFormat.asciidoc +++ /dev/null @@ -1,18 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-SimpleDateFormat]]++SimpleDateFormat++:: -* ++[[painless-api-reference-SimpleDateFormat-SimpleDateFormat-0]]link:{java8-javadoc}/java/text/SimpleDateFormat.html#SimpleDateFormat%2D%2D[SimpleDateFormat]()++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#SimpleDateFormat%2D%2D[java 9]) -* ++[[painless-api-reference-SimpleDateFormat-SimpleDateFormat-1]]link:{java8-javadoc}/java/text/SimpleDateFormat.html#SimpleDateFormat%2Djava.lang.String%2D[SimpleDateFormat](<>)++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#SimpleDateFormat%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-SimpleDateFormat-SimpleDateFormat-2]]link:{java8-javadoc}/java/text/SimpleDateFormat.html#SimpleDateFormat%2Djava.lang.String%2Djava.util.Locale%2D[SimpleDateFormat](<>, <>)++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#SimpleDateFormat%2Djava.lang.String%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-SimpleDateFormat-applyLocalizedPattern-1]]void link:{java8-javadoc}/java/text/SimpleDateFormat.html#applyLocalizedPattern%2Djava.lang.String%2D[applyLocalizedPattern](<>)++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#applyLocalizedPattern%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-SimpleDateFormat-applyPattern-1]]void link:{java8-javadoc}/java/text/SimpleDateFormat.html#applyPattern%2Djava.lang.String%2D[applyPattern](<>)++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#applyPattern%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-SimpleDateFormat-get2DigitYearStart-0]]<> link:{java8-javadoc}/java/text/SimpleDateFormat.html#get2DigitYearStart%2D%2D[get2DigitYearStart]()++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#get2DigitYearStart%2D%2D[java 9]) -* ++[[painless-api-reference-SimpleDateFormat-getDateFormatSymbols-0]]<> link:{java8-javadoc}/java/text/SimpleDateFormat.html#getDateFormatSymbols%2D%2D[getDateFormatSymbols]()++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#getDateFormatSymbols%2D%2D[java 9]) -* ++[[painless-api-reference-SimpleDateFormat-set2DigitYearStart-1]]void link:{java8-javadoc}/java/text/SimpleDateFormat.html#set2DigitYearStart%2Djava.util.Date%2D[set2DigitYearStart](<>)++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#set2DigitYearStart%2Djava.util.Date%2D[java 9]) -* ++[[painless-api-reference-SimpleDateFormat-setDateFormatSymbols-1]]void link:{java8-javadoc}/java/text/SimpleDateFormat.html#setDateFormatSymbols%2Djava.text.DateFormatSymbols%2D[setDateFormatSymbols](<>)++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#setDateFormatSymbols%2Djava.text.DateFormatSymbols%2D[java 9]) -* ++[[painless-api-reference-SimpleDateFormat-toLocalizedPattern-0]]<> link:{java8-javadoc}/java/text/SimpleDateFormat.html#toLocalizedPattern%2D%2D[toLocalizedPattern]()++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#toLocalizedPattern%2D%2D[java 9]) -* ++[[painless-api-reference-SimpleDateFormat-toPattern-0]]<> link:{java8-javadoc}/java/text/SimpleDateFormat.html#toPattern%2D%2D[toPattern]()++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#toPattern%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/SimpleTimeZone.asciidoc b/docs/reference/painless-api-reference/SimpleTimeZone.asciidoc deleted file mode 100644 index eff23a40723a3..0000000000000 --- a/docs/reference/painless-api-reference/SimpleTimeZone.asciidoc +++ /dev/null @@ -1,23 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-SimpleTimeZone]]++SimpleTimeZone++:: -** [[painless-api-reference-SimpleTimeZone-STANDARD_TIME]]static int link:{java8-javadoc}/java/util/SimpleTimeZone.html#STANDARD_TIME[STANDARD_TIME] (link:{java9-javadoc}/java/util/SimpleTimeZone.html#STANDARD_TIME[java 9]) -** [[painless-api-reference-SimpleTimeZone-UTC_TIME]]static int link:{java8-javadoc}/java/util/SimpleTimeZone.html#UTC_TIME[UTC_TIME] (link:{java9-javadoc}/java/util/SimpleTimeZone.html#UTC_TIME[java 9]) -** [[painless-api-reference-SimpleTimeZone-WALL_TIME]]static int link:{java8-javadoc}/java/util/SimpleTimeZone.html#WALL_TIME[WALL_TIME] (link:{java9-javadoc}/java/util/SimpleTimeZone.html#WALL_TIME[java 9]) -* ++[[painless-api-reference-SimpleTimeZone-SimpleTimeZone-2]]link:{java8-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2D[SimpleTimeZone](int, <>)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-SimpleTimeZone-SimpleTimeZone-10]]link:{java8-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[SimpleTimeZone](int, <>, int, int, int, int, int, int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-SimpleTimeZone-SimpleTimeZone-11]]link:{java8-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[SimpleTimeZone](int, <>, int, int, int, int, int, int, int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-SimpleTimeZone-SimpleTimeZone-13]]link:{java8-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[SimpleTimeZone](int, <>, int, int, int, int, int, int, int, int, int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-SimpleTimeZone-getDSTSavings-0]]int link:{java8-javadoc}/java/util/SimpleTimeZone.html#getDSTSavings%2D%2D[getDSTSavings]()++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#getDSTSavings%2D%2D[java 9]) -* ++[[painless-api-reference-SimpleTimeZone-setDSTSavings-1]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setDSTSavings%2Dint%2D[setDSTSavings](int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setDSTSavings%2Dint%2D[java 9]) -* ++[[painless-api-reference-SimpleTimeZone-setEndRule-3]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setEndRule%2Dint%2Dint%2Dint%2D[setEndRule](int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setEndRule%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-SimpleTimeZone-setEndRule-4]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setEndRule%2Dint%2Dint%2Dint%2Dint%2D[setEndRule](int, int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setEndRule%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-SimpleTimeZone-setEndRule-5]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setEndRule%2Dint%2Dint%2Dint%2Dint%2Dboolean%2D[setEndRule](int, int, int, int, boolean)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setEndRule%2Dint%2Dint%2Dint%2Dint%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-SimpleTimeZone-setStartRule-3]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setStartRule%2Dint%2Dint%2Dint%2D[setStartRule](int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setStartRule%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-SimpleTimeZone-setStartRule-4]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setStartRule%2Dint%2Dint%2Dint%2Dint%2D[setStartRule](int, int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setStartRule%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-SimpleTimeZone-setStartRule-5]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setStartRule%2Dint%2Dint%2Dint%2Dint%2Dboolean%2D[setStartRule](int, int, int, int, boolean)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setStartRule%2Dint%2Dint%2Dint%2Dint%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-SimpleTimeZone-setStartYear-1]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setStartYear%2Dint%2D[setStartYear](int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setStartYear%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/SortedMap.asciidoc b/docs/reference/painless-api-reference/SortedMap.asciidoc deleted file mode 100644 index d4e31e6c8fa37..0000000000000 --- a/docs/reference/painless-api-reference/SortedMap.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-SortedMap]]++SortedMap++:: -* ++[[painless-api-reference-SortedMap-comparator-0]]<> link:{java8-javadoc}/java/util/SortedMap.html#comparator%2D%2D[comparator]()++ (link:{java9-javadoc}/java/util/SortedMap.html#comparator%2D%2D[java 9]) -* ++[[painless-api-reference-SortedMap-firstKey-0]]def link:{java8-javadoc}/java/util/SortedMap.html#firstKey%2D%2D[firstKey]()++ (link:{java9-javadoc}/java/util/SortedMap.html#firstKey%2D%2D[java 9]) -* ++[[painless-api-reference-SortedMap-headMap-1]]<> link:{java8-javadoc}/java/util/SortedMap.html#headMap%2Djava.lang.Object%2D[headMap](def)++ (link:{java9-javadoc}/java/util/SortedMap.html#headMap%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-SortedMap-lastKey-0]]def link:{java8-javadoc}/java/util/SortedMap.html#lastKey%2D%2D[lastKey]()++ (link:{java9-javadoc}/java/util/SortedMap.html#lastKey%2D%2D[java 9]) -* ++[[painless-api-reference-SortedMap-subMap-2]]<> link:{java8-javadoc}/java/util/SortedMap.html#subMap%2Djava.lang.Object%2Djava.lang.Object%2D[subMap](def, def)++ (link:{java9-javadoc}/java/util/SortedMap.html#subMap%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-SortedMap-tailMap-1]]<> link:{java8-javadoc}/java/util/SortedMap.html#tailMap%2Djava.lang.Object%2D[tailMap](def)++ (link:{java9-javadoc}/java/util/SortedMap.html#tailMap%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/SortedSet.asciidoc b/docs/reference/painless-api-reference/SortedSet.asciidoc deleted file mode 100644 index 2a7160aa2ba44..0000000000000 --- a/docs/reference/painless-api-reference/SortedSet.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-SortedSet]]++SortedSet++:: -* ++[[painless-api-reference-SortedSet-comparator-0]]<> link:{java8-javadoc}/java/util/SortedSet.html#comparator%2D%2D[comparator]()++ (link:{java9-javadoc}/java/util/SortedSet.html#comparator%2D%2D[java 9]) -* ++[[painless-api-reference-SortedSet-first-0]]def link:{java8-javadoc}/java/util/SortedSet.html#first%2D%2D[first]()++ (link:{java9-javadoc}/java/util/SortedSet.html#first%2D%2D[java 9]) -* ++[[painless-api-reference-SortedSet-headSet-1]]<> link:{java8-javadoc}/java/util/SortedSet.html#headSet%2Djava.lang.Object%2D[headSet](def)++ (link:{java9-javadoc}/java/util/SortedSet.html#headSet%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-SortedSet-last-0]]def link:{java8-javadoc}/java/util/SortedSet.html#last%2D%2D[last]()++ (link:{java9-javadoc}/java/util/SortedSet.html#last%2D%2D[java 9]) -* ++[[painless-api-reference-SortedSet-subSet-2]]<> link:{java8-javadoc}/java/util/SortedSet.html#subSet%2Djava.lang.Object%2Djava.lang.Object%2D[subSet](def, def)++ (link:{java9-javadoc}/java/util/SortedSet.html#subSet%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-SortedSet-tailSet-1]]<> link:{java8-javadoc}/java/util/SortedSet.html#tailSet%2Djava.lang.Object%2D[tailSet](def)++ (link:{java9-javadoc}/java/util/SortedSet.html#tailSet%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Spliterator.OfDouble.asciidoc b/docs/reference/painless-api-reference/Spliterator.OfDouble.asciidoc deleted file mode 100644 index 7292d2bffca8a..0000000000000 --- a/docs/reference/painless-api-reference/Spliterator.OfDouble.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Spliterator-OfDouble]]++Spliterator.OfDouble++:: -* ++[[painless-api-reference-Spliterator-OfDouble-trySplit-0]]<> link:{java8-javadoc}/java/util/Spliterator$OfDouble.html#trySplit%2D%2D[trySplit]()++ (link:{java9-javadoc}/java/util/Spliterator$OfDouble.html#trySplit%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Spliterator.OfInt.asciidoc b/docs/reference/painless-api-reference/Spliterator.OfInt.asciidoc deleted file mode 100644 index 1f19e7013aa6c..0000000000000 --- a/docs/reference/painless-api-reference/Spliterator.OfInt.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Spliterator-OfInt]]++Spliterator.OfInt++:: -* ++[[painless-api-reference-Spliterator-OfInt-trySplit-0]]<> link:{java8-javadoc}/java/util/Spliterator$OfInt.html#trySplit%2D%2D[trySplit]()++ (link:{java9-javadoc}/java/util/Spliterator$OfInt.html#trySplit%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Spliterator.OfLong.asciidoc b/docs/reference/painless-api-reference/Spliterator.OfLong.asciidoc deleted file mode 100644 index a1d56d5e1ee3c..0000000000000 --- a/docs/reference/painless-api-reference/Spliterator.OfLong.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Spliterator-OfLong]]++Spliterator.OfLong++:: -* ++[[painless-api-reference-Spliterator-OfLong-trySplit-0]]<> link:{java8-javadoc}/java/util/Spliterator$OfLong.html#trySplit%2D%2D[trySplit]()++ (link:{java9-javadoc}/java/util/Spliterator$OfLong.html#trySplit%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Spliterator.OfPrimitive.asciidoc b/docs/reference/painless-api-reference/Spliterator.OfPrimitive.asciidoc deleted file mode 100644 index 00cd8b472618b..0000000000000 --- a/docs/reference/painless-api-reference/Spliterator.OfPrimitive.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Spliterator-OfPrimitive]]++Spliterator.OfPrimitive++:: -* ++[[painless-api-reference-Spliterator-OfPrimitive-forEachRemaining-1]]void link:{java8-javadoc}/java/util/Spliterator$OfPrimitive.html#forEachRemaining%2Djava.lang.Object%2D[forEachRemaining](def)++ (link:{java9-javadoc}/java/util/Spliterator$OfPrimitive.html#forEachRemaining%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Spliterator-OfPrimitive-tryAdvance-1]]boolean link:{java8-javadoc}/java/util/Spliterator$OfPrimitive.html#tryAdvance%2Djava.lang.Object%2D[tryAdvance](def)++ (link:{java9-javadoc}/java/util/Spliterator$OfPrimitive.html#tryAdvance%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Spliterator-OfPrimitive-trySplit-0]]<> link:{java8-javadoc}/java/util/Spliterator$OfPrimitive.html#trySplit%2D%2D[trySplit]()++ (link:{java9-javadoc}/java/util/Spliterator$OfPrimitive.html#trySplit%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Spliterator.asciidoc b/docs/reference/painless-api-reference/Spliterator.asciidoc deleted file mode 100644 index fcf919409858d..0000000000000 --- a/docs/reference/painless-api-reference/Spliterator.asciidoc +++ /dev/null @@ -1,23 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Spliterator]]++Spliterator++:: -** [[painless-api-reference-Spliterator-CONCURRENT]]static int link:{java8-javadoc}/java/util/Spliterator.html#CONCURRENT[CONCURRENT] (link:{java9-javadoc}/java/util/Spliterator.html#CONCURRENT[java 9]) -** [[painless-api-reference-Spliterator-DISTINCT]]static int link:{java8-javadoc}/java/util/Spliterator.html#DISTINCT[DISTINCT] (link:{java9-javadoc}/java/util/Spliterator.html#DISTINCT[java 9]) -** [[painless-api-reference-Spliterator-IMMUTABLE]]static int link:{java8-javadoc}/java/util/Spliterator.html#IMMUTABLE[IMMUTABLE] (link:{java9-javadoc}/java/util/Spliterator.html#IMMUTABLE[java 9]) -** [[painless-api-reference-Spliterator-NONNULL]]static int link:{java8-javadoc}/java/util/Spliterator.html#NONNULL[NONNULL] (link:{java9-javadoc}/java/util/Spliterator.html#NONNULL[java 9]) -** [[painless-api-reference-Spliterator-ORDERED]]static int link:{java8-javadoc}/java/util/Spliterator.html#ORDERED[ORDERED] (link:{java9-javadoc}/java/util/Spliterator.html#ORDERED[java 9]) -** [[painless-api-reference-Spliterator-SIZED]]static int link:{java8-javadoc}/java/util/Spliterator.html#SIZED[SIZED] (link:{java9-javadoc}/java/util/Spliterator.html#SIZED[java 9]) -** [[painless-api-reference-Spliterator-SORTED]]static int link:{java8-javadoc}/java/util/Spliterator.html#SORTED[SORTED] (link:{java9-javadoc}/java/util/Spliterator.html#SORTED[java 9]) -** [[painless-api-reference-Spliterator-SUBSIZED]]static int link:{java8-javadoc}/java/util/Spliterator.html#SUBSIZED[SUBSIZED] (link:{java9-javadoc}/java/util/Spliterator.html#SUBSIZED[java 9]) -* ++[[painless-api-reference-Spliterator-characteristics-0]]int link:{java8-javadoc}/java/util/Spliterator.html#characteristics%2D%2D[characteristics]()++ (link:{java9-javadoc}/java/util/Spliterator.html#characteristics%2D%2D[java 9]) -* ++[[painless-api-reference-Spliterator-estimateSize-0]]long link:{java8-javadoc}/java/util/Spliterator.html#estimateSize%2D%2D[estimateSize]()++ (link:{java9-javadoc}/java/util/Spliterator.html#estimateSize%2D%2D[java 9]) -* ++[[painless-api-reference-Spliterator-forEachRemaining-1]]void link:{java8-javadoc}/java/util/Spliterator.html#forEachRemaining%2Djava.util.function.Consumer%2D[forEachRemaining](<>)++ (link:{java9-javadoc}/java/util/Spliterator.html#forEachRemaining%2Djava.util.function.Consumer%2D[java 9]) -* ++[[painless-api-reference-Spliterator-getComparator-0]]<> link:{java8-javadoc}/java/util/Spliterator.html#getComparator%2D%2D[getComparator]()++ (link:{java9-javadoc}/java/util/Spliterator.html#getComparator%2D%2D[java 9]) -* ++[[painless-api-reference-Spliterator-getExactSizeIfKnown-0]]long link:{java8-javadoc}/java/util/Spliterator.html#getExactSizeIfKnown%2D%2D[getExactSizeIfKnown]()++ (link:{java9-javadoc}/java/util/Spliterator.html#getExactSizeIfKnown%2D%2D[java 9]) -* ++[[painless-api-reference-Spliterator-hasCharacteristics-1]]boolean link:{java8-javadoc}/java/util/Spliterator.html#hasCharacteristics%2Dint%2D[hasCharacteristics](int)++ (link:{java9-javadoc}/java/util/Spliterator.html#hasCharacteristics%2Dint%2D[java 9]) -* ++[[painless-api-reference-Spliterator-tryAdvance-1]]boolean link:{java8-javadoc}/java/util/Spliterator.html#tryAdvance%2Djava.util.function.Consumer%2D[tryAdvance](<>)++ (link:{java9-javadoc}/java/util/Spliterator.html#tryAdvance%2Djava.util.function.Consumer%2D[java 9]) -* ++[[painless-api-reference-Spliterator-trySplit-0]]<> link:{java8-javadoc}/java/util/Spliterator.html#trySplit%2D%2D[trySplit]()++ (link:{java9-javadoc}/java/util/Spliterator.html#trySplit%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Spliterators.asciidoc b/docs/reference/painless-api-reference/Spliterators.asciidoc deleted file mode 100644 index 6d8202a567411..0000000000000 --- a/docs/reference/painless-api-reference/Spliterators.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Spliterators]]++Spliterators++:: -* ++[[painless-api-reference-Spliterators-emptyDoubleSpliterator-0]]static <> link:{java8-javadoc}/java/util/Spliterators.html#emptyDoubleSpliterator%2D%2D[emptyDoubleSpliterator]()++ (link:{java9-javadoc}/java/util/Spliterators.html#emptyDoubleSpliterator%2D%2D[java 9]) -* ++[[painless-api-reference-Spliterators-emptyIntSpliterator-0]]static <> link:{java8-javadoc}/java/util/Spliterators.html#emptyIntSpliterator%2D%2D[emptyIntSpliterator]()++ (link:{java9-javadoc}/java/util/Spliterators.html#emptyIntSpliterator%2D%2D[java 9]) -* ++[[painless-api-reference-Spliterators-emptyLongSpliterator-0]]static <> link:{java8-javadoc}/java/util/Spliterators.html#emptyLongSpliterator%2D%2D[emptyLongSpliterator]()++ (link:{java9-javadoc}/java/util/Spliterators.html#emptyLongSpliterator%2D%2D[java 9]) -* ++[[painless-api-reference-Spliterators-emptySpliterator-0]]static <> link:{java8-javadoc}/java/util/Spliterators.html#emptySpliterator%2D%2D[emptySpliterator]()++ (link:{java9-javadoc}/java/util/Spliterators.html#emptySpliterator%2D%2D[java 9]) -* ++[[painless-api-reference-Spliterators-iterator-1]]static <> link:{java8-javadoc}/java/util/Spliterators.html#iterator%2Djava.util.Spliterator%2D[iterator](<>)++ (link:{java9-javadoc}/java/util/Spliterators.html#iterator%2Djava.util.Spliterator%2D[java 9]) -* ++[[painless-api-reference-Spliterators-spliterator-2]]static <> link:{java8-javadoc}/java/util/Spliterators.html#spliterator%2Djava.util.Collection%2Dint%2D[spliterator](<>, int)++ (link:{java9-javadoc}/java/util/Spliterators.html#spliterator%2Djava.util.Collection%2Dint%2D[java 9]) -* ++[[painless-api-reference-Spliterators-spliterator-3]]static <> link:{java8-javadoc}/java/util/Spliterators.html#spliterator%2Djava.util.Iterator%2Dlong%2Dint%2D[spliterator](<>, long, int)++ (link:{java9-javadoc}/java/util/Spliterators.html#spliterator%2Djava.util.Iterator%2Dlong%2Dint%2D[java 9]) -* ++[[painless-api-reference-Spliterators-spliteratorUnknownSize-2]]static <> link:{java8-javadoc}/java/util/Spliterators.html#spliteratorUnknownSize%2Djava.util.Iterator%2Dint%2D[spliteratorUnknownSize](<>, int)++ (link:{java9-javadoc}/java/util/Spliterators.html#spliteratorUnknownSize%2Djava.util.Iterator%2Dint%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Stack.asciidoc b/docs/reference/painless-api-reference/Stack.asciidoc deleted file mode 100644 index ad1520d9d885d..0000000000000 --- a/docs/reference/painless-api-reference/Stack.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Stack]]++Stack++:: -* ++[[painless-api-reference-Stack-Stack-0]]link:{java8-javadoc}/java/util/Stack.html#Stack%2D%2D[Stack]()++ (link:{java9-javadoc}/java/util/Stack.html#Stack%2D%2D[java 9]) -* ++[[painless-api-reference-Stack-empty-0]]boolean link:{java8-javadoc}/java/util/Stack.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/Stack.html#empty%2D%2D[java 9]) -* ++[[painless-api-reference-Stack-peek-0]]def link:{java8-javadoc}/java/util/Stack.html#peek%2D%2D[peek]()++ (link:{java9-javadoc}/java/util/Stack.html#peek%2D%2D[java 9]) -* ++[[painless-api-reference-Stack-pop-0]]def link:{java8-javadoc}/java/util/Stack.html#pop%2D%2D[pop]()++ (link:{java9-javadoc}/java/util/Stack.html#pop%2D%2D[java 9]) -* ++[[painless-api-reference-Stack-push-1]]def link:{java8-javadoc}/java/util/Stack.html#push%2Djava.lang.Object%2D[push](def)++ (link:{java9-javadoc}/java/util/Stack.html#push%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Stack-search-1]]int link:{java8-javadoc}/java/util/Stack.html#search%2Djava.lang.Object%2D[search](def)++ (link:{java9-javadoc}/java/util/Stack.html#search%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/StackTraceElement.asciidoc b/docs/reference/painless-api-reference/StackTraceElement.asciidoc deleted file mode 100644 index 046e56ef1f016..0000000000000 --- a/docs/reference/painless-api-reference/StackTraceElement.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-StackTraceElement]]++StackTraceElement++:: -* ++[[painless-api-reference-StackTraceElement-StackTraceElement-4]]link:{java8-javadoc}/java/lang/StackTraceElement.html#StackTraceElement%2Djava.lang.String%2Djava.lang.String%2Djava.lang.String%2Dint%2D[StackTraceElement](<>, <>, <>, int)++ (link:{java9-javadoc}/java/lang/StackTraceElement.html#StackTraceElement%2Djava.lang.String%2Djava.lang.String%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-StackTraceElement-getClassName-0]]<> link:{java8-javadoc}/java/lang/StackTraceElement.html#getClassName%2D%2D[getClassName]()++ (link:{java9-javadoc}/java/lang/StackTraceElement.html#getClassName%2D%2D[java 9]) -* ++[[painless-api-reference-StackTraceElement-getFileName-0]]<> link:{java8-javadoc}/java/lang/StackTraceElement.html#getFileName%2D%2D[getFileName]()++ (link:{java9-javadoc}/java/lang/StackTraceElement.html#getFileName%2D%2D[java 9]) -* ++[[painless-api-reference-StackTraceElement-getLineNumber-0]]int link:{java8-javadoc}/java/lang/StackTraceElement.html#getLineNumber%2D%2D[getLineNumber]()++ (link:{java9-javadoc}/java/lang/StackTraceElement.html#getLineNumber%2D%2D[java 9]) -* ++[[painless-api-reference-StackTraceElement-getMethodName-0]]<> link:{java8-javadoc}/java/lang/StackTraceElement.html#getMethodName%2D%2D[getMethodName]()++ (link:{java9-javadoc}/java/lang/StackTraceElement.html#getMethodName%2D%2D[java 9]) -* ++[[painless-api-reference-StackTraceElement-isNativeMethod-0]]boolean link:{java8-javadoc}/java/lang/StackTraceElement.html#isNativeMethod%2D%2D[isNativeMethod]()++ (link:{java9-javadoc}/java/lang/StackTraceElement.html#isNativeMethod%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Stream.Builder.asciidoc b/docs/reference/painless-api-reference/Stream.Builder.asciidoc deleted file mode 100644 index aa9146ce8ed61..0000000000000 --- a/docs/reference/painless-api-reference/Stream.Builder.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Stream-Builder]]++Stream.Builder++:: -* ++[[painless-api-reference-Stream-Builder-add-1]]<> link:{java8-javadoc}/java/util/stream/Stream$Builder.html#add%2Djava.lang.Object%2D[add](def)++ (link:{java9-javadoc}/java/util/stream/Stream$Builder.html#add%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Stream-Builder-build-0]]<> link:{java8-javadoc}/java/util/stream/Stream$Builder.html#build%2D%2D[build]()++ (link:{java9-javadoc}/java/util/stream/Stream$Builder.html#build%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Stream.asciidoc b/docs/reference/painless-api-reference/Stream.asciidoc deleted file mode 100644 index 0713ad1ce9d90..0000000000000 --- a/docs/reference/painless-api-reference/Stream.asciidoc +++ /dev/null @@ -1,43 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Stream]]++Stream++:: -* ++[[painless-api-reference-Stream-builder-0]]static <> link:{java8-javadoc}/java/util/stream/Stream.html#builder%2D%2D[builder]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#builder%2D%2D[java 9]) -* ++[[painless-api-reference-Stream-concat-2]]static <> link:{java8-javadoc}/java/util/stream/Stream.html#concat%2Djava.util.stream.Stream%2Djava.util.stream.Stream%2D[concat](<>, <>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#concat%2Djava.util.stream.Stream%2Djava.util.stream.Stream%2D[java 9]) -* ++[[painless-api-reference-Stream-empty-0]]static <> link:{java8-javadoc}/java/util/stream/Stream.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#empty%2D%2D[java 9]) -* ++[[painless-api-reference-Stream-of-1]]static <> link:{java8-javadoc}/java/util/stream/Stream.html#of%2Djava.lang.Object:A%2D[of](def[])++ (link:{java9-javadoc}/java/util/stream/Stream.html#of%2Djava.lang.Object:A%2D[java 9]) -* ++[[painless-api-reference-Stream-allMatch-1]]boolean link:{java8-javadoc}/java/util/stream/Stream.html#allMatch%2Djava.util.function.Predicate%2D[allMatch](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#allMatch%2Djava.util.function.Predicate%2D[java 9]) -* ++[[painless-api-reference-Stream-anyMatch-1]]boolean link:{java8-javadoc}/java/util/stream/Stream.html#anyMatch%2Djava.util.function.Predicate%2D[anyMatch](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#anyMatch%2Djava.util.function.Predicate%2D[java 9]) -* ++[[painless-api-reference-Stream-collect-1]]def link:{java8-javadoc}/java/util/stream/Stream.html#collect%2Djava.util.stream.Collector%2D[collect](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#collect%2Djava.util.stream.Collector%2D[java 9]) -* ++[[painless-api-reference-Stream-collect-3]]def link:{java8-javadoc}/java/util/stream/Stream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.BiConsumer%2Djava.util.function.BiConsumer%2D[collect](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.BiConsumer%2Djava.util.function.BiConsumer%2D[java 9]) -* ++[[painless-api-reference-Stream-count-0]]long link:{java8-javadoc}/java/util/stream/Stream.html#count%2D%2D[count]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#count%2D%2D[java 9]) -* ++[[painless-api-reference-Stream-distinct-0]]<> link:{java8-javadoc}/java/util/stream/Stream.html#distinct%2D%2D[distinct]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#distinct%2D%2D[java 9]) -* ++[[painless-api-reference-Stream-filter-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#filter%2Djava.util.function.Predicate%2D[filter](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#filter%2Djava.util.function.Predicate%2D[java 9]) -* ++[[painless-api-reference-Stream-findAny-0]]<> link:{java8-javadoc}/java/util/stream/Stream.html#findAny%2D%2D[findAny]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#findAny%2D%2D[java 9]) -* ++[[painless-api-reference-Stream-findFirst-0]]<> link:{java8-javadoc}/java/util/stream/Stream.html#findFirst%2D%2D[findFirst]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#findFirst%2D%2D[java 9]) -* ++[[painless-api-reference-Stream-flatMap-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#flatMap%2Djava.util.function.Function%2D[flatMap](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#flatMap%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-Stream-flatMapToDouble-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#flatMapToDouble%2Djava.util.function.Function%2D[flatMapToDouble](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#flatMapToDouble%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-Stream-flatMapToInt-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#flatMapToInt%2Djava.util.function.Function%2D[flatMapToInt](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#flatMapToInt%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-Stream-flatMapToLong-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#flatMapToLong%2Djava.util.function.Function%2D[flatMapToLong](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#flatMapToLong%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-Stream-forEach-1]]void link:{java8-javadoc}/java/util/stream/Stream.html#forEach%2Djava.util.function.Consumer%2D[forEach](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#forEach%2Djava.util.function.Consumer%2D[java 9]) -* ++[[painless-api-reference-Stream-forEachOrdered-1]]void link:{java8-javadoc}/java/util/stream/Stream.html#forEachOrdered%2Djava.util.function.Consumer%2D[forEachOrdered](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#forEachOrdered%2Djava.util.function.Consumer%2D[java 9]) -* ++[[painless-api-reference-Stream-limit-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#limit%2Dlong%2D[limit](long)++ (link:{java9-javadoc}/java/util/stream/Stream.html#limit%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Stream-map-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#map%2Djava.util.function.Function%2D[map](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#map%2Djava.util.function.Function%2D[java 9]) -* ++[[painless-api-reference-Stream-mapToDouble-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#mapToDouble%2Djava.util.function.ToDoubleFunction%2D[mapToDouble](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#mapToDouble%2Djava.util.function.ToDoubleFunction%2D[java 9]) -* ++[[painless-api-reference-Stream-mapToInt-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#mapToInt%2Djava.util.function.ToIntFunction%2D[mapToInt](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#mapToInt%2Djava.util.function.ToIntFunction%2D[java 9]) -* ++[[painless-api-reference-Stream-mapToLong-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#mapToLong%2Djava.util.function.ToLongFunction%2D[mapToLong](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#mapToLong%2Djava.util.function.ToLongFunction%2D[java 9]) -* ++[[painless-api-reference-Stream-max-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#max%2Djava.util.Comparator%2D[max](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#max%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Stream-min-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#min%2Djava.util.Comparator%2D[min](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#min%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Stream-noneMatch-1]]boolean link:{java8-javadoc}/java/util/stream/Stream.html#noneMatch%2Djava.util.function.Predicate%2D[noneMatch](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#noneMatch%2Djava.util.function.Predicate%2D[java 9]) -* ++[[painless-api-reference-Stream-peek-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#peek%2Djava.util.function.Consumer%2D[peek](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#peek%2Djava.util.function.Consumer%2D[java 9]) -* ++[[painless-api-reference-Stream-reduce-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#reduce%2Djava.util.function.BinaryOperator%2D[reduce](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#reduce%2Djava.util.function.BinaryOperator%2D[java 9]) -* ++[[painless-api-reference-Stream-reduce-2]]def link:{java8-javadoc}/java/util/stream/Stream.html#reduce%2Djava.lang.Object%2Djava.util.function.BinaryOperator%2D[reduce](def, <>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#reduce%2Djava.lang.Object%2Djava.util.function.BinaryOperator%2D[java 9]) -* ++[[painless-api-reference-Stream-reduce-3]]def link:{java8-javadoc}/java/util/stream/Stream.html#reduce%2Djava.lang.Object%2Djava.util.function.BiFunction%2Djava.util.function.BinaryOperator%2D[reduce](def, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#reduce%2Djava.lang.Object%2Djava.util.function.BiFunction%2Djava.util.function.BinaryOperator%2D[java 9]) -* ++[[painless-api-reference-Stream-skip-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#skip%2Dlong%2D[skip](long)++ (link:{java9-javadoc}/java/util/stream/Stream.html#skip%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Stream-sorted-0]]<> link:{java8-javadoc}/java/util/stream/Stream.html#sorted%2D%2D[sorted]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#sorted%2D%2D[java 9]) -* ++[[painless-api-reference-Stream-sorted-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#sorted%2Djava.util.Comparator%2D[sorted](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#sorted%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-Stream-toArray-0]]def[] link:{java8-javadoc}/java/util/stream/Stream.html#toArray%2D%2D[toArray]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#toArray%2D%2D[java 9]) -* ++[[painless-api-reference-Stream-toArray-1]]def[] link:{java8-javadoc}/java/util/stream/Stream.html#toArray%2Djava.util.function.IntFunction%2D[toArray](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#toArray%2Djava.util.function.IntFunction%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/StrictMath.asciidoc b/docs/reference/painless-api-reference/StrictMath.asciidoc deleted file mode 100644 index 17346c33ba3f9..0000000000000 --- a/docs/reference/painless-api-reference/StrictMath.asciidoc +++ /dev/null @@ -1,46 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-StrictMath]]++StrictMath++:: -** [[painless-api-reference-StrictMath-E]]static double link:{java8-javadoc}/java/lang/StrictMath.html#E[E] (link:{java9-javadoc}/java/lang/StrictMath.html#E[java 9]) -** [[painless-api-reference-StrictMath-PI]]static double link:{java8-javadoc}/java/lang/StrictMath.html#PI[PI] (link:{java9-javadoc}/java/lang/StrictMath.html#PI[java 9]) -* ++[[painless-api-reference-StrictMath-IEEEremainder-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#IEEEremainder%2Ddouble%2Ddouble%2D[IEEEremainder](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#IEEEremainder%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-abs-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#abs%2Ddouble%2D[abs](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#abs%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-acos-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#acos%2Ddouble%2D[acos](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#acos%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-asin-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#asin%2Ddouble%2D[asin](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#asin%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-atan-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#atan%2Ddouble%2D[atan](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#atan%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-atan2-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#atan2%2Ddouble%2Ddouble%2D[atan2](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#atan2%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-cbrt-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#cbrt%2Ddouble%2D[cbrt](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#cbrt%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-ceil-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#ceil%2Ddouble%2D[ceil](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#ceil%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-copySign-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#copySign%2Ddouble%2Ddouble%2D[copySign](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#copySign%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-cos-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#cos%2Ddouble%2D[cos](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#cos%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-cosh-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#cosh%2Ddouble%2D[cosh](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#cosh%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-exp-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#exp%2Ddouble%2D[exp](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#exp%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-expm1-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#expm1%2Ddouble%2D[expm1](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#expm1%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-floor-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#floor%2Ddouble%2D[floor](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#floor%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-hypot-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#hypot%2Ddouble%2Ddouble%2D[hypot](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#hypot%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-log-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#log%2Ddouble%2D[log](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#log%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-log10-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#log10%2Ddouble%2D[log10](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#log10%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-log1p-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#log1p%2Ddouble%2D[log1p](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#log1p%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-max-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#max%2Ddouble%2Ddouble%2D[max](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#max%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-min-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#min%2Ddouble%2Ddouble%2D[min](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#min%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-nextAfter-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#nextAfter%2Ddouble%2Ddouble%2D[nextAfter](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#nextAfter%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-nextDown-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#nextDown%2Ddouble%2D[nextDown](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#nextDown%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-nextUp-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#nextUp%2Ddouble%2D[nextUp](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#nextUp%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-pow-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#pow%2Ddouble%2Ddouble%2D[pow](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#pow%2Ddouble%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-random-0]]static double link:{java8-javadoc}/java/lang/StrictMath.html#random%2D%2D[random]()++ (link:{java9-javadoc}/java/lang/StrictMath.html#random%2D%2D[java 9]) -* ++[[painless-api-reference-StrictMath-rint-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#rint%2Ddouble%2D[rint](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#rint%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-round-1]]static long link:{java8-javadoc}/java/lang/StrictMath.html#round%2Ddouble%2D[round](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#round%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-scalb-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#scalb%2Ddouble%2Dint%2D[scalb](double, int)++ (link:{java9-javadoc}/java/lang/StrictMath.html#scalb%2Ddouble%2Dint%2D[java 9]) -* ++[[painless-api-reference-StrictMath-signum-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#signum%2Ddouble%2D[signum](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#signum%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-sin-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#sin%2Ddouble%2D[sin](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#sin%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-sinh-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#sinh%2Ddouble%2D[sinh](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#sinh%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-sqrt-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#sqrt%2Ddouble%2D[sqrt](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#sqrt%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-tan-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#tan%2Ddouble%2D[tan](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#tan%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-tanh-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#tanh%2Ddouble%2D[tanh](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#tanh%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-toDegrees-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#toDegrees%2Ddouble%2D[toDegrees](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#toDegrees%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-toRadians-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#toRadians%2Ddouble%2D[toRadians](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#toRadians%2Ddouble%2D[java 9]) -* ++[[painless-api-reference-StrictMath-ulp-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#ulp%2Ddouble%2D[ulp](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#ulp%2Ddouble%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/String.asciidoc b/docs/reference/painless-api-reference/String.asciidoc deleted file mode 100644 index a40e1811183d5..0000000000000 --- a/docs/reference/painless-api-reference/String.asciidoc +++ /dev/null @@ -1,46 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-String]]++String++:: -* ++[[painless-api-reference-String-copyValueOf-1]]static <> link:{java8-javadoc}/java/lang/String.html#copyValueOf%2Dchar:A%2D[copyValueOf](char[])++ (link:{java9-javadoc}/java/lang/String.html#copyValueOf%2Dchar:A%2D[java 9]) -* ++[[painless-api-reference-String-copyValueOf-3]]static <> link:{java8-javadoc}/java/lang/String.html#copyValueOf%2Dchar:A%2Dint%2Dint%2D[copyValueOf](char[], int, int)++ (link:{java9-javadoc}/java/lang/String.html#copyValueOf%2Dchar:A%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-String-format-2]]static <> link:{java8-javadoc}/java/lang/String.html#format%2Djava.lang.String%2Djava.lang.Object:A%2D[format](<>, def[])++ (link:{java9-javadoc}/java/lang/String.html#format%2Djava.lang.String%2Djava.lang.Object:A%2D[java 9]) -* ++[[painless-api-reference-String-format-3]]static <> link:{java8-javadoc}/java/lang/String.html#format%2Djava.util.Locale%2Djava.lang.String%2Djava.lang.Object:A%2D[format](<>, <>, def[])++ (link:{java9-javadoc}/java/lang/String.html#format%2Djava.util.Locale%2Djava.lang.String%2Djava.lang.Object:A%2D[java 9]) -* ++[[painless-api-reference-String-join-2]]static <> link:{java8-javadoc}/java/lang/String.html#join%2Djava.lang.CharSequence%2Djava.lang.Iterable%2D[join](<>, <>)++ (link:{java9-javadoc}/java/lang/String.html#join%2Djava.lang.CharSequence%2Djava.lang.Iterable%2D[java 9]) -* ++[[painless-api-reference-String-valueOf-1]]static <> link:{java8-javadoc}/java/lang/String.html#valueOf%2Djava.lang.Object%2D[valueOf](def)++ (link:{java9-javadoc}/java/lang/String.html#valueOf%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-String-String-0]]link:{java8-javadoc}/java/lang/String.html#String%2D%2D[String]()++ (link:{java9-javadoc}/java/lang/String.html#String%2D%2D[java 9]) -* ++[[painless-api-reference-String-codePointAt-1]]int link:{java8-javadoc}/java/lang/String.html#codePointAt%2Dint%2D[codePointAt](int)++ (link:{java9-javadoc}/java/lang/String.html#codePointAt%2Dint%2D[java 9]) -* ++[[painless-api-reference-String-codePointBefore-1]]int link:{java8-javadoc}/java/lang/String.html#codePointBefore%2Dint%2D[codePointBefore](int)++ (link:{java9-javadoc}/java/lang/String.html#codePointBefore%2Dint%2D[java 9]) -* ++[[painless-api-reference-String-codePointCount-2]]int link:{java8-javadoc}/java/lang/String.html#codePointCount%2Dint%2Dint%2D[codePointCount](int, int)++ (link:{java9-javadoc}/java/lang/String.html#codePointCount%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-String-compareTo-1]]int link:{java8-javadoc}/java/lang/String.html#compareTo%2Djava.lang.String%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/String.html#compareTo%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-String-compareToIgnoreCase-1]]int link:{java8-javadoc}/java/lang/String.html#compareToIgnoreCase%2Djava.lang.String%2D[compareToIgnoreCase](<>)++ (link:{java9-javadoc}/java/lang/String.html#compareToIgnoreCase%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-String-concat-1]]<> link:{java8-javadoc}/java/lang/String.html#concat%2Djava.lang.String%2D[concat](<>)++ (link:{java9-javadoc}/java/lang/String.html#concat%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-String-contains-1]]boolean link:{java8-javadoc}/java/lang/String.html#contains%2Djava.lang.CharSequence%2D[contains](<>)++ (link:{java9-javadoc}/java/lang/String.html#contains%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-String-contentEquals-1]]boolean link:{java8-javadoc}/java/lang/String.html#contentEquals%2Djava.lang.CharSequence%2D[contentEquals](<>)++ (link:{java9-javadoc}/java/lang/String.html#contentEquals%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-String-decodeBase64-0]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#decodeBase64%2Djava.lang.String%2D[decodeBase64]()++ -* ++[[painless-api-reference-String-encodeBase64-0]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#encodeBase64%2Djava.lang.String%2D[encodeBase64]()++ -* ++[[painless-api-reference-String-endsWith-1]]boolean link:{java8-javadoc}/java/lang/String.html#endsWith%2Djava.lang.String%2D[endsWith](<>)++ (link:{java9-javadoc}/java/lang/String.html#endsWith%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-String-equalsIgnoreCase-1]]boolean link:{java8-javadoc}/java/lang/String.html#equalsIgnoreCase%2Djava.lang.String%2D[equalsIgnoreCase](<>)++ (link:{java9-javadoc}/java/lang/String.html#equalsIgnoreCase%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-String-getChars-4]]void link:{java8-javadoc}/java/lang/String.html#getChars%2Dint%2Dint%2Dchar:A%2Dint%2D[getChars](int, int, char[], int)++ (link:{java9-javadoc}/java/lang/String.html#getChars%2Dint%2Dint%2Dchar:A%2Dint%2D[java 9]) -* ++[[painless-api-reference-String-indexOf-1]]int link:{java8-javadoc}/java/lang/String.html#indexOf%2Djava.lang.String%2D[indexOf](<>)++ (link:{java9-javadoc}/java/lang/String.html#indexOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-String-indexOf-2]]int link:{java8-javadoc}/java/lang/String.html#indexOf%2Djava.lang.String%2Dint%2D[indexOf](<>, int)++ (link:{java9-javadoc}/java/lang/String.html#indexOf%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-String-isEmpty-0]]boolean link:{java8-javadoc}/java/lang/String.html#isEmpty%2D%2D[isEmpty]()++ (link:{java9-javadoc}/java/lang/String.html#isEmpty%2D%2D[java 9]) -* ++[[painless-api-reference-String-lastIndexOf-1]]int link:{java8-javadoc}/java/lang/String.html#lastIndexOf%2Djava.lang.String%2D[lastIndexOf](<>)++ (link:{java9-javadoc}/java/lang/String.html#lastIndexOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-String-lastIndexOf-2]]int link:{java8-javadoc}/java/lang/String.html#lastIndexOf%2Djava.lang.String%2Dint%2D[lastIndexOf](<>, int)++ (link:{java9-javadoc}/java/lang/String.html#lastIndexOf%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-String-offsetByCodePoints-2]]int link:{java8-javadoc}/java/lang/String.html#offsetByCodePoints%2Dint%2Dint%2D[offsetByCodePoints](int, int)++ (link:{java9-javadoc}/java/lang/String.html#offsetByCodePoints%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-String-regionMatches-4]]boolean link:{java8-javadoc}/java/lang/String.html#regionMatches%2Dint%2Djava.lang.String%2Dint%2Dint%2D[regionMatches](int, <>, int, int)++ (link:{java9-javadoc}/java/lang/String.html#regionMatches%2Dint%2Djava.lang.String%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-String-regionMatches-5]]boolean link:{java8-javadoc}/java/lang/String.html#regionMatches%2Dboolean%2Dint%2Djava.lang.String%2Dint%2Dint%2D[regionMatches](boolean, int, <>, int, int)++ (link:{java9-javadoc}/java/lang/String.html#regionMatches%2Dboolean%2Dint%2Djava.lang.String%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-String-replace-2]]<> link:{java8-javadoc}/java/lang/String.html#replace%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2D[replace](<>, <>)++ (link:{java9-javadoc}/java/lang/String.html#replace%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-String-startsWith-1]]boolean link:{java8-javadoc}/java/lang/String.html#startsWith%2Djava.lang.String%2D[startsWith](<>)++ (link:{java9-javadoc}/java/lang/String.html#startsWith%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-String-startsWith-2]]boolean link:{java8-javadoc}/java/lang/String.html#startsWith%2Djava.lang.String%2Dint%2D[startsWith](<>, int)++ (link:{java9-javadoc}/java/lang/String.html#startsWith%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-String-substring-1]]<> link:{java8-javadoc}/java/lang/String.html#substring%2Dint%2D[substring](int)++ (link:{java9-javadoc}/java/lang/String.html#substring%2Dint%2D[java 9]) -* ++[[painless-api-reference-String-substring-2]]<> link:{java8-javadoc}/java/lang/String.html#substring%2Dint%2Dint%2D[substring](int, int)++ (link:{java9-javadoc}/java/lang/String.html#substring%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-String-toCharArray-0]]char[] link:{java8-javadoc}/java/lang/String.html#toCharArray%2D%2D[toCharArray]()++ (link:{java9-javadoc}/java/lang/String.html#toCharArray%2D%2D[java 9]) -* ++[[painless-api-reference-String-toLowerCase-0]]<> link:{java8-javadoc}/java/lang/String.html#toLowerCase%2D%2D[toLowerCase]()++ (link:{java9-javadoc}/java/lang/String.html#toLowerCase%2D%2D[java 9]) -* ++[[painless-api-reference-String-toLowerCase-1]]<> link:{java8-javadoc}/java/lang/String.html#toLowerCase%2Djava.util.Locale%2D[toLowerCase](<>)++ (link:{java9-javadoc}/java/lang/String.html#toLowerCase%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-String-toUpperCase-0]]<> link:{java8-javadoc}/java/lang/String.html#toUpperCase%2D%2D[toUpperCase]()++ (link:{java9-javadoc}/java/lang/String.html#toUpperCase%2D%2D[java 9]) -* ++[[painless-api-reference-String-toUpperCase-1]]<> link:{java8-javadoc}/java/lang/String.html#toUpperCase%2Djava.util.Locale%2D[toUpperCase](<>)++ (link:{java9-javadoc}/java/lang/String.html#toUpperCase%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-String-trim-0]]<> link:{java8-javadoc}/java/lang/String.html#trim%2D%2D[trim]()++ (link:{java9-javadoc}/java/lang/String.html#trim%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/StringBuffer.asciidoc b/docs/reference/painless-api-reference/StringBuffer.asciidoc deleted file mode 100644 index 5993832a983da..0000000000000 --- a/docs/reference/painless-api-reference/StringBuffer.asciidoc +++ /dev/null @@ -1,31 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-StringBuffer]]++StringBuffer++:: -* ++[[painless-api-reference-StringBuffer-StringBuffer-0]]link:{java8-javadoc}/java/lang/StringBuffer.html#StringBuffer%2D%2D[StringBuffer]()++ (link:{java9-javadoc}/java/lang/StringBuffer.html#StringBuffer%2D%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-StringBuffer-1]]link:{java8-javadoc}/java/lang/StringBuffer.html#StringBuffer%2Djava.lang.CharSequence%2D[StringBuffer](<>)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#StringBuffer%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-append-1]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#append%2Djava.lang.Object%2D[append](def)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#append%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-append-3]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#append%2Djava.lang.CharSequence%2Dint%2Dint%2D[append](<>, int, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#append%2Djava.lang.CharSequence%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-appendCodePoint-1]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#appendCodePoint%2Dint%2D[appendCodePoint](int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#appendCodePoint%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-capacity-0]]int link:{java8-javadoc}/java/lang/StringBuffer.html#capacity%2D%2D[capacity]()++ (link:{java9-javadoc}/java/lang/StringBuffer.html#capacity%2D%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-codePointAt-1]]int link:{java8-javadoc}/java/lang/StringBuffer.html#codePointAt%2Dint%2D[codePointAt](int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#codePointAt%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-codePointBefore-1]]int link:{java8-javadoc}/java/lang/StringBuffer.html#codePointBefore%2Dint%2D[codePointBefore](int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#codePointBefore%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-codePointCount-2]]int link:{java8-javadoc}/java/lang/StringBuffer.html#codePointCount%2Dint%2Dint%2D[codePointCount](int, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#codePointCount%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-delete-2]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#delete%2Dint%2Dint%2D[delete](int, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#delete%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-deleteCharAt-1]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#deleteCharAt%2Dint%2D[deleteCharAt](int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#deleteCharAt%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-getChars-4]]void link:{java8-javadoc}/java/lang/StringBuffer.html#getChars%2Dint%2Dint%2Dchar:A%2Dint%2D[getChars](int, int, char[], int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#getChars%2Dint%2Dint%2Dchar:A%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-indexOf-1]]int link:{java8-javadoc}/java/lang/StringBuffer.html#indexOf%2Djava.lang.String%2D[indexOf](<>)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#indexOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-indexOf-2]]int link:{java8-javadoc}/java/lang/StringBuffer.html#indexOf%2Djava.lang.String%2Dint%2D[indexOf](<>, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#indexOf%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-insert-2]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#insert%2Dint%2Djava.lang.Object%2D[insert](int, def)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#insert%2Dint%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-lastIndexOf-1]]int link:{java8-javadoc}/java/lang/StringBuffer.html#lastIndexOf%2Djava.lang.String%2D[lastIndexOf](<>)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#lastIndexOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-lastIndexOf-2]]int link:{java8-javadoc}/java/lang/StringBuffer.html#lastIndexOf%2Djava.lang.String%2Dint%2D[lastIndexOf](<>, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#lastIndexOf%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-offsetByCodePoints-2]]int link:{java8-javadoc}/java/lang/StringBuffer.html#offsetByCodePoints%2Dint%2Dint%2D[offsetByCodePoints](int, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#offsetByCodePoints%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-replace-3]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#replace%2Dint%2Dint%2Djava.lang.String%2D[replace](int, int, <>)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#replace%2Dint%2Dint%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-reverse-0]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#reverse%2D%2D[reverse]()++ (link:{java9-javadoc}/java/lang/StringBuffer.html#reverse%2D%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-setCharAt-2]]void link:{java8-javadoc}/java/lang/StringBuffer.html#setCharAt%2Dint%2Dchar%2D[setCharAt](int, char)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#setCharAt%2Dint%2Dchar%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-setLength-1]]void link:{java8-javadoc}/java/lang/StringBuffer.html#setLength%2Dint%2D[setLength](int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#setLength%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-substring-1]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#substring%2Dint%2D[substring](int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#substring%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuffer-substring-2]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#substring%2Dint%2Dint%2D[substring](int, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#substring%2Dint%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/StringBuilder.asciidoc b/docs/reference/painless-api-reference/StringBuilder.asciidoc deleted file mode 100644 index fd9c0c097cc03..0000000000000 --- a/docs/reference/painless-api-reference/StringBuilder.asciidoc +++ /dev/null @@ -1,31 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-StringBuilder]]++StringBuilder++:: -* ++[[painless-api-reference-StringBuilder-StringBuilder-0]]link:{java8-javadoc}/java/lang/StringBuilder.html#StringBuilder%2D%2D[StringBuilder]()++ (link:{java9-javadoc}/java/lang/StringBuilder.html#StringBuilder%2D%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-StringBuilder-1]]link:{java8-javadoc}/java/lang/StringBuilder.html#StringBuilder%2Djava.lang.CharSequence%2D[StringBuilder](<>)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#StringBuilder%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-append-1]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#append%2Djava.lang.Object%2D[append](def)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#append%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-append-3]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#append%2Djava.lang.CharSequence%2Dint%2Dint%2D[append](<>, int, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#append%2Djava.lang.CharSequence%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-appendCodePoint-1]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#appendCodePoint%2Dint%2D[appendCodePoint](int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#appendCodePoint%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-capacity-0]]int link:{java8-javadoc}/java/lang/StringBuilder.html#capacity%2D%2D[capacity]()++ (link:{java9-javadoc}/java/lang/StringBuilder.html#capacity%2D%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-codePointAt-1]]int link:{java8-javadoc}/java/lang/StringBuilder.html#codePointAt%2Dint%2D[codePointAt](int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#codePointAt%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-codePointBefore-1]]int link:{java8-javadoc}/java/lang/StringBuilder.html#codePointBefore%2Dint%2D[codePointBefore](int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#codePointBefore%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-codePointCount-2]]int link:{java8-javadoc}/java/lang/StringBuilder.html#codePointCount%2Dint%2Dint%2D[codePointCount](int, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#codePointCount%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-delete-2]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#delete%2Dint%2Dint%2D[delete](int, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#delete%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-deleteCharAt-1]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#deleteCharAt%2Dint%2D[deleteCharAt](int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#deleteCharAt%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-getChars-4]]void link:{java8-javadoc}/java/lang/StringBuilder.html#getChars%2Dint%2Dint%2Dchar:A%2Dint%2D[getChars](int, int, char[], int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#getChars%2Dint%2Dint%2Dchar:A%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-indexOf-1]]int link:{java8-javadoc}/java/lang/StringBuilder.html#indexOf%2Djava.lang.String%2D[indexOf](<>)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#indexOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-indexOf-2]]int link:{java8-javadoc}/java/lang/StringBuilder.html#indexOf%2Djava.lang.String%2Dint%2D[indexOf](<>, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#indexOf%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-insert-2]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#insert%2Dint%2Djava.lang.Object%2D[insert](int, def)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#insert%2Dint%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-lastIndexOf-1]]int link:{java8-javadoc}/java/lang/StringBuilder.html#lastIndexOf%2Djava.lang.String%2D[lastIndexOf](<>)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#lastIndexOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-lastIndexOf-2]]int link:{java8-javadoc}/java/lang/StringBuilder.html#lastIndexOf%2Djava.lang.String%2Dint%2D[lastIndexOf](<>, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#lastIndexOf%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-offsetByCodePoints-2]]int link:{java8-javadoc}/java/lang/StringBuilder.html#offsetByCodePoints%2Dint%2Dint%2D[offsetByCodePoints](int, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#offsetByCodePoints%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-replace-3]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#replace%2Dint%2Dint%2Djava.lang.String%2D[replace](int, int, <>)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#replace%2Dint%2Dint%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-reverse-0]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#reverse%2D%2D[reverse]()++ (link:{java9-javadoc}/java/lang/StringBuilder.html#reverse%2D%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-setCharAt-2]]void link:{java8-javadoc}/java/lang/StringBuilder.html#setCharAt%2Dint%2Dchar%2D[setCharAt](int, char)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#setCharAt%2Dint%2Dchar%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-setLength-1]]void link:{java8-javadoc}/java/lang/StringBuilder.html#setLength%2Dint%2D[setLength](int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#setLength%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-substring-1]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#substring%2Dint%2D[substring](int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#substring%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringBuilder-substring-2]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#substring%2Dint%2Dint%2D[substring](int, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#substring%2Dint%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/StringCharacterIterator.asciidoc b/docs/reference/painless-api-reference/StringCharacterIterator.asciidoc deleted file mode 100644 index 8f65f2b6c5e53..0000000000000 --- a/docs/reference/painless-api-reference/StringCharacterIterator.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-StringCharacterIterator]]++StringCharacterIterator++:: -* ++[[painless-api-reference-StringCharacterIterator-StringCharacterIterator-1]]link:{java8-javadoc}/java/text/StringCharacterIterator.html#StringCharacterIterator%2Djava.lang.String%2D[StringCharacterIterator](<>)++ (link:{java9-javadoc}/java/text/StringCharacterIterator.html#StringCharacterIterator%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-StringCharacterIterator-StringCharacterIterator-2]]link:{java8-javadoc}/java/text/StringCharacterIterator.html#StringCharacterIterator%2Djava.lang.String%2Dint%2D[StringCharacterIterator](<>, int)++ (link:{java9-javadoc}/java/text/StringCharacterIterator.html#StringCharacterIterator%2Djava.lang.String%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringCharacterIterator-StringCharacterIterator-4]]link:{java8-javadoc}/java/text/StringCharacterIterator.html#StringCharacterIterator%2Djava.lang.String%2Dint%2Dint%2Dint%2D[StringCharacterIterator](<>, int, int, int)++ (link:{java9-javadoc}/java/text/StringCharacterIterator.html#StringCharacterIterator%2Djava.lang.String%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-StringCharacterIterator-setText-1]]void link:{java8-javadoc}/java/text/StringCharacterIterator.html#setText%2Djava.lang.String%2D[setText](<>)++ (link:{java9-javadoc}/java/text/StringCharacterIterator.html#setText%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/StringIndexOutOfBoundsException.asciidoc b/docs/reference/painless-api-reference/StringIndexOutOfBoundsException.asciidoc deleted file mode 100644 index a29cae3f857f0..0000000000000 --- a/docs/reference/painless-api-reference/StringIndexOutOfBoundsException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-StringIndexOutOfBoundsException]]++StringIndexOutOfBoundsException++:: -* ++[[painless-api-reference-StringIndexOutOfBoundsException-StringIndexOutOfBoundsException-0]]link:{java8-javadoc}/java/lang/StringIndexOutOfBoundsException.html#StringIndexOutOfBoundsException%2D%2D[StringIndexOutOfBoundsException]()++ (link:{java9-javadoc}/java/lang/StringIndexOutOfBoundsException.html#StringIndexOutOfBoundsException%2D%2D[java 9]) -* ++[[painless-api-reference-StringIndexOutOfBoundsException-StringIndexOutOfBoundsException-1]]link:{java8-javadoc}/java/lang/StringIndexOutOfBoundsException.html#StringIndexOutOfBoundsException%2Djava.lang.String%2D[StringIndexOutOfBoundsException](<>)++ (link:{java9-javadoc}/java/lang/StringIndexOutOfBoundsException.html#StringIndexOutOfBoundsException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/StringJoiner.asciidoc b/docs/reference/painless-api-reference/StringJoiner.asciidoc deleted file mode 100644 index 7bd9baa4460ac..0000000000000 --- a/docs/reference/painless-api-reference/StringJoiner.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-StringJoiner]]++StringJoiner++:: -* ++[[painless-api-reference-StringJoiner-StringJoiner-1]]link:{java8-javadoc}/java/util/StringJoiner.html#StringJoiner%2Djava.lang.CharSequence%2D[StringJoiner](<>)++ (link:{java9-javadoc}/java/util/StringJoiner.html#StringJoiner%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-StringJoiner-StringJoiner-3]]link:{java8-javadoc}/java/util/StringJoiner.html#StringJoiner%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2D[StringJoiner](<>, <>, <>)++ (link:{java9-javadoc}/java/util/StringJoiner.html#StringJoiner%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-StringJoiner-add-1]]<> link:{java8-javadoc}/java/util/StringJoiner.html#add%2Djava.lang.CharSequence%2D[add](<>)++ (link:{java9-javadoc}/java/util/StringJoiner.html#add%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-StringJoiner-length-0]]int link:{java8-javadoc}/java/util/StringJoiner.html#length%2D%2D[length]()++ (link:{java9-javadoc}/java/util/StringJoiner.html#length%2D%2D[java 9]) -* ++[[painless-api-reference-StringJoiner-merge-1]]<> link:{java8-javadoc}/java/util/StringJoiner.html#merge%2Djava.util.StringJoiner%2D[merge](<>)++ (link:{java9-javadoc}/java/util/StringJoiner.html#merge%2Djava.util.StringJoiner%2D[java 9]) -* ++[[painless-api-reference-StringJoiner-setEmptyValue-1]]<> link:{java8-javadoc}/java/util/StringJoiner.html#setEmptyValue%2Djava.lang.CharSequence%2D[setEmptyValue](<>)++ (link:{java9-javadoc}/java/util/StringJoiner.html#setEmptyValue%2Djava.lang.CharSequence%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/StringTokenizer.asciidoc b/docs/reference/painless-api-reference/StringTokenizer.asciidoc deleted file mode 100644 index 9462cecc33b4e..0000000000000 --- a/docs/reference/painless-api-reference/StringTokenizer.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-StringTokenizer]]++StringTokenizer++:: -* ++[[painless-api-reference-StringTokenizer-StringTokenizer-1]]link:{java8-javadoc}/java/util/StringTokenizer.html#StringTokenizer%2Djava.lang.String%2D[StringTokenizer](<>)++ (link:{java9-javadoc}/java/util/StringTokenizer.html#StringTokenizer%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-StringTokenizer-StringTokenizer-2]]link:{java8-javadoc}/java/util/StringTokenizer.html#StringTokenizer%2Djava.lang.String%2Djava.lang.String%2D[StringTokenizer](<>, <>)++ (link:{java9-javadoc}/java/util/StringTokenizer.html#StringTokenizer%2Djava.lang.String%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-StringTokenizer-StringTokenizer-3]]link:{java8-javadoc}/java/util/StringTokenizer.html#StringTokenizer%2Djava.lang.String%2Djava.lang.String%2Dboolean%2D[StringTokenizer](<>, <>, boolean)++ (link:{java9-javadoc}/java/util/StringTokenizer.html#StringTokenizer%2Djava.lang.String%2Djava.lang.String%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-StringTokenizer-countTokens-0]]int link:{java8-javadoc}/java/util/StringTokenizer.html#countTokens%2D%2D[countTokens]()++ (link:{java9-javadoc}/java/util/StringTokenizer.html#countTokens%2D%2D[java 9]) -* ++[[painless-api-reference-StringTokenizer-hasMoreTokens-0]]boolean link:{java8-javadoc}/java/util/StringTokenizer.html#hasMoreTokens%2D%2D[hasMoreTokens]()++ (link:{java9-javadoc}/java/util/StringTokenizer.html#hasMoreTokens%2D%2D[java 9]) -* ++[[painless-api-reference-StringTokenizer-nextToken-0]]<> link:{java8-javadoc}/java/util/StringTokenizer.html#nextToken%2D%2D[nextToken]()++ (link:{java9-javadoc}/java/util/StringTokenizer.html#nextToken%2D%2D[java 9]) -* ++[[painless-api-reference-StringTokenizer-nextToken-1]]<> link:{java8-javadoc}/java/util/StringTokenizer.html#nextToken%2Djava.lang.String%2D[nextToken](<>)++ (link:{java9-javadoc}/java/util/StringTokenizer.html#nextToken%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/Supplier.asciidoc b/docs/reference/painless-api-reference/Supplier.asciidoc deleted file mode 100644 index ba3cd9da951ec..0000000000000 --- a/docs/reference/painless-api-reference/Supplier.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Supplier]]++Supplier++:: -* ++[[painless-api-reference-Supplier-get-0]]def link:{java8-javadoc}/java/util/function/Supplier.html#get%2D%2D[get]()++ (link:{java9-javadoc}/java/util/function/Supplier.html#get%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/System.asciidoc b/docs/reference/painless-api-reference/System.asciidoc deleted file mode 100644 index a791d5dc15966..0000000000000 --- a/docs/reference/painless-api-reference/System.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-System]]++System++:: -* ++[[painless-api-reference-System-arraycopy-5]]static void link:{java8-javadoc}/java/lang/System.html#arraycopy%2Djava.lang.Object%2Dint%2Djava.lang.Object%2Dint%2Dint%2D[arraycopy](<>, int, <>, int, int)++ (link:{java9-javadoc}/java/lang/System.html#arraycopy%2Djava.lang.Object%2Dint%2Djava.lang.Object%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-System-currentTimeMillis-0]]static long link:{java8-javadoc}/java/lang/System.html#currentTimeMillis%2D%2D[currentTimeMillis]()++ (link:{java9-javadoc}/java/lang/System.html#currentTimeMillis%2D%2D[java 9]) -* ++[[painless-api-reference-System-nanoTime-0]]static long link:{java8-javadoc}/java/lang/System.html#nanoTime%2D%2D[nanoTime]()++ (link:{java9-javadoc}/java/lang/System.html#nanoTime%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Temporal.asciidoc b/docs/reference/painless-api-reference/Temporal.asciidoc deleted file mode 100644 index ce693cfde51fa..0000000000000 --- a/docs/reference/painless-api-reference/Temporal.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Temporal]]++Temporal++:: -* ++[[painless-api-reference-Temporal-minus-1]]<> link:{java8-javadoc}/java/time/temporal/Temporal.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-Temporal-minus-2]]<> link:{java8-javadoc}/java/time/temporal/Temporal.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-Temporal-plus-1]]<> link:{java8-javadoc}/java/time/temporal/Temporal.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-Temporal-plus-2]]<> link:{java8-javadoc}/java/time/temporal/Temporal.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-Temporal-until-2]]long link:{java8-javadoc}/java/time/temporal/Temporal.html#until%2Djava.time.temporal.Temporal%2Djava.time.temporal.TemporalUnit%2D[until](<>, <>)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#until%2Djava.time.temporal.Temporal%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-Temporal-with-1]]<> link:{java8-javadoc}/java/time/temporal/Temporal.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-Temporal-with-2]]<> link:{java8-javadoc}/java/time/temporal/Temporal.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/TemporalAccessor.asciidoc b/docs/reference/painless-api-reference/TemporalAccessor.asciidoc deleted file mode 100644 index 7583b2180d940..0000000000000 --- a/docs/reference/painless-api-reference/TemporalAccessor.asciidoc +++ /dev/null @@ -1,12 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TemporalAccessor]]++TemporalAccessor++:: -* ++[[painless-api-reference-TemporalAccessor-get-1]]int link:{java8-javadoc}/java/time/temporal/TemporalAccessor.html#get%2Djava.time.temporal.TemporalField%2D[get](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAccessor.html#get%2Djava.time.temporal.TemporalField%2D[java 9]) -* ++[[painless-api-reference-TemporalAccessor-getLong-1]]long link:{java8-javadoc}/java/time/temporal/TemporalAccessor.html#getLong%2Djava.time.temporal.TemporalField%2D[getLong](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAccessor.html#getLong%2Djava.time.temporal.TemporalField%2D[java 9]) -* ++[[painless-api-reference-TemporalAccessor-isSupported-1]]boolean link:{java8-javadoc}/java/time/temporal/TemporalAccessor.html#isSupported%2Djava.time.temporal.TemporalField%2D[isSupported](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAccessor.html#isSupported%2Djava.time.temporal.TemporalField%2D[java 9]) -* ++[[painless-api-reference-TemporalAccessor-query-1]]def link:{java8-javadoc}/java/time/temporal/TemporalAccessor.html#query%2Djava.time.temporal.TemporalQuery%2D[query](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAccessor.html#query%2Djava.time.temporal.TemporalQuery%2D[java 9]) -* ++[[painless-api-reference-TemporalAccessor-range-1]]<> link:{java8-javadoc}/java/time/temporal/TemporalAccessor.html#range%2Djava.time.temporal.TemporalField%2D[range](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAccessor.html#range%2Djava.time.temporal.TemporalField%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/TemporalAdjuster.asciidoc b/docs/reference/painless-api-reference/TemporalAdjuster.asciidoc deleted file mode 100644 index b80f0d7afbfae..0000000000000 --- a/docs/reference/painless-api-reference/TemporalAdjuster.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TemporalAdjuster]]++TemporalAdjuster++:: -* ++[[painless-api-reference-TemporalAdjuster-adjustInto-1]]<> link:{java8-javadoc}/java/time/temporal/TemporalAdjuster.html#adjustInto%2Djava.time.temporal.Temporal%2D[adjustInto](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjuster.html#adjustInto%2Djava.time.temporal.Temporal%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/TemporalAdjusters.asciidoc b/docs/reference/painless-api-reference/TemporalAdjusters.asciidoc deleted file mode 100644 index 7299ae9256abc..0000000000000 --- a/docs/reference/painless-api-reference/TemporalAdjusters.asciidoc +++ /dev/null @@ -1,21 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TemporalAdjusters]]++TemporalAdjusters++:: -* ++[[painless-api-reference-TemporalAdjusters-dayOfWeekInMonth-2]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#dayOfWeekInMonth%2Dint%2Djava.time.DayOfWeek%2D[dayOfWeekInMonth](int, <>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#dayOfWeekInMonth%2Dint%2Djava.time.DayOfWeek%2D[java 9]) -* ++[[painless-api-reference-TemporalAdjusters-firstDayOfMonth-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfMonth%2D%2D[firstDayOfMonth]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfMonth%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalAdjusters-firstDayOfNextMonth-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfNextMonth%2D%2D[firstDayOfNextMonth]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfNextMonth%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalAdjusters-firstDayOfNextYear-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfNextYear%2D%2D[firstDayOfNextYear]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfNextYear%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalAdjusters-firstDayOfYear-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfYear%2D%2D[firstDayOfYear]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfYear%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalAdjusters-firstInMonth-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#firstInMonth%2Djava.time.DayOfWeek%2D[firstInMonth](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#firstInMonth%2Djava.time.DayOfWeek%2D[java 9]) -* ++[[painless-api-reference-TemporalAdjusters-lastDayOfMonth-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#lastDayOfMonth%2D%2D[lastDayOfMonth]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#lastDayOfMonth%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalAdjusters-lastDayOfYear-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#lastDayOfYear%2D%2D[lastDayOfYear]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#lastDayOfYear%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalAdjusters-lastInMonth-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#lastInMonth%2Djava.time.DayOfWeek%2D[lastInMonth](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#lastInMonth%2Djava.time.DayOfWeek%2D[java 9]) -* ++[[painless-api-reference-TemporalAdjusters-next-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#next%2Djava.time.DayOfWeek%2D[next](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#next%2Djava.time.DayOfWeek%2D[java 9]) -* ++[[painless-api-reference-TemporalAdjusters-nextOrSame-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#nextOrSame%2Djava.time.DayOfWeek%2D[nextOrSame](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#nextOrSame%2Djava.time.DayOfWeek%2D[java 9]) -* ++[[painless-api-reference-TemporalAdjusters-ofDateAdjuster-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#ofDateAdjuster%2Djava.util.function.UnaryOperator%2D[ofDateAdjuster](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#ofDateAdjuster%2Djava.util.function.UnaryOperator%2D[java 9]) -* ++[[painless-api-reference-TemporalAdjusters-previous-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#previous%2Djava.time.DayOfWeek%2D[previous](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#previous%2Djava.time.DayOfWeek%2D[java 9]) -* ++[[painless-api-reference-TemporalAdjusters-previousOrSame-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#previousOrSame%2Djava.time.DayOfWeek%2D[previousOrSame](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#previousOrSame%2Djava.time.DayOfWeek%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/TemporalAmount.asciidoc b/docs/reference/painless-api-reference/TemporalAmount.asciidoc deleted file mode 100644 index e27954b4db3e4..0000000000000 --- a/docs/reference/painless-api-reference/TemporalAmount.asciidoc +++ /dev/null @@ -1,11 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TemporalAmount]]++TemporalAmount++:: -* ++[[painless-api-reference-TemporalAmount-addTo-1]]<> link:{java8-javadoc}/java/time/temporal/TemporalAmount.html#addTo%2Djava.time.temporal.Temporal%2D[addTo](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAmount.html#addTo%2Djava.time.temporal.Temporal%2D[java 9]) -* ++[[painless-api-reference-TemporalAmount-get-1]]long link:{java8-javadoc}/java/time/temporal/TemporalAmount.html#get%2Djava.time.temporal.TemporalUnit%2D[get](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAmount.html#get%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-TemporalAmount-getUnits-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalAmount.html#getUnits%2D%2D[getUnits]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAmount.html#getUnits%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalAmount-subtractFrom-1]]<> link:{java8-javadoc}/java/time/temporal/TemporalAmount.html#subtractFrom%2Djava.time.temporal.Temporal%2D[subtractFrom](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAmount.html#subtractFrom%2Djava.time.temporal.Temporal%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/TemporalField.asciidoc b/docs/reference/painless-api-reference/TemporalField.asciidoc deleted file mode 100644 index dd38544d46e4c..0000000000000 --- a/docs/reference/painless-api-reference/TemporalField.asciidoc +++ /dev/null @@ -1,19 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TemporalField]]++TemporalField++:: -* ++[[painless-api-reference-TemporalField-adjustInto-2]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#adjustInto%2Djava.time.temporal.Temporal%2Dlong%2D[adjustInto](<>, long)++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#adjustInto%2Djava.time.temporal.Temporal%2Dlong%2D[java 9]) -* ++[[painless-api-reference-TemporalField-getBaseUnit-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#getBaseUnit%2D%2D[getBaseUnit]()++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#getBaseUnit%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalField-getDisplayName-1]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#getDisplayName%2Djava.util.Locale%2D[getDisplayName](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#getDisplayName%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-TemporalField-getFrom-1]]long link:{java8-javadoc}/java/time/temporal/TemporalField.html#getFrom%2Djava.time.temporal.TemporalAccessor%2D[getFrom](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#getFrom%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-TemporalField-getRangeUnit-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#getRangeUnit%2D%2D[getRangeUnit]()++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#getRangeUnit%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalField-isDateBased-0]]boolean link:{java8-javadoc}/java/time/temporal/TemporalField.html#isDateBased%2D%2D[isDateBased]()++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#isDateBased%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalField-isSupportedBy-1]]boolean link:{java8-javadoc}/java/time/temporal/TemporalField.html#isSupportedBy%2Djava.time.temporal.TemporalAccessor%2D[isSupportedBy](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#isSupportedBy%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-TemporalField-isTimeBased-0]]boolean link:{java8-javadoc}/java/time/temporal/TemporalField.html#isTimeBased%2D%2D[isTimeBased]()++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#isTimeBased%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalField-range-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#range%2D%2D[range]()++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#range%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalField-rangeRefinedBy-1]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#rangeRefinedBy%2Djava.time.temporal.TemporalAccessor%2D[rangeRefinedBy](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#rangeRefinedBy%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-TemporalField-resolve-3]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#resolve%2Djava.util.Map%2Djava.time.temporal.TemporalAccessor%2Djava.time.format.ResolverStyle%2D[resolve](<>, <>, <>)++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#resolve%2Djava.util.Map%2Djava.time.temporal.TemporalAccessor%2Djava.time.format.ResolverStyle%2D[java 9]) -* ++[[painless-api-reference-TemporalField-toString-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#toString%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/TemporalQueries.asciidoc b/docs/reference/painless-api-reference/TemporalQueries.asciidoc deleted file mode 100644 index bd01ba6c2e0ed..0000000000000 --- a/docs/reference/painless-api-reference/TemporalQueries.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TemporalQueries]]++TemporalQueries++:: -* ++[[painless-api-reference-TemporalQueries-chronology-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#chronology%2D%2D[chronology]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#chronology%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalQueries-localDate-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#localDate%2D%2D[localDate]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#localDate%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalQueries-localTime-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#localTime%2D%2D[localTime]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#localTime%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalQueries-offset-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#offset%2D%2D[offset]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#offset%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalQueries-precision-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#precision%2D%2D[precision]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#precision%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalQueries-zone-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#zone%2D%2D[zone]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#zone%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalQueries-zoneId-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#zoneId%2D%2D[zoneId]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#zoneId%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/TemporalQuery.asciidoc b/docs/reference/painless-api-reference/TemporalQuery.asciidoc deleted file mode 100644 index ca09eff368de3..0000000000000 --- a/docs/reference/painless-api-reference/TemporalQuery.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TemporalQuery]]++TemporalQuery++:: -* ++[[painless-api-reference-TemporalQuery-queryFrom-1]]def link:{java8-javadoc}/java/time/temporal/TemporalQuery.html#queryFrom%2Djava.time.temporal.TemporalAccessor%2D[queryFrom](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalQuery.html#queryFrom%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/TemporalUnit.asciidoc b/docs/reference/painless-api-reference/TemporalUnit.asciidoc deleted file mode 100644 index cc940d838efc2..0000000000000 --- a/docs/reference/painless-api-reference/TemporalUnit.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TemporalUnit]]++TemporalUnit++:: -* ++[[painless-api-reference-TemporalUnit-addTo-2]]<> link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#addTo%2Djava.time.temporal.Temporal%2Dlong%2D[addTo](<>, long)++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#addTo%2Djava.time.temporal.Temporal%2Dlong%2D[java 9]) -* ++[[painless-api-reference-TemporalUnit-between-2]]long link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#between%2Djava.time.temporal.Temporal%2Djava.time.temporal.Temporal%2D[between](<>, <>)++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#between%2Djava.time.temporal.Temporal%2Djava.time.temporal.Temporal%2D[java 9]) -* ++[[painless-api-reference-TemporalUnit-getDuration-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#getDuration%2D%2D[getDuration]()++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#getDuration%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalUnit-isDateBased-0]]boolean link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#isDateBased%2D%2D[isDateBased]()++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#isDateBased%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalUnit-isDurationEstimated-0]]boolean link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#isDurationEstimated%2D%2D[isDurationEstimated]()++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#isDurationEstimated%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalUnit-isSupportedBy-1]]boolean link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#isSupportedBy%2Djava.time.temporal.Temporal%2D[isSupportedBy](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#isSupportedBy%2Djava.time.temporal.Temporal%2D[java 9]) -* ++[[painless-api-reference-TemporalUnit-isTimeBased-0]]boolean link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#isTimeBased%2D%2D[isTimeBased]()++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#isTimeBased%2D%2D[java 9]) -* ++[[painless-api-reference-TemporalUnit-toString-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#toString%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/TextStyle.asciidoc b/docs/reference/painless-api-reference/TextStyle.asciidoc deleted file mode 100644 index c7b122f934592..0000000000000 --- a/docs/reference/painless-api-reference/TextStyle.asciidoc +++ /dev/null @@ -1,18 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TextStyle]]++TextStyle++:: -** [[painless-api-reference-TextStyle-FULL]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#FULL[FULL] (link:{java9-javadoc}/java/time/format/TextStyle.html#FULL[java 9]) -** [[painless-api-reference-TextStyle-FULL_STANDALONE]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#FULL_STANDALONE[FULL_STANDALONE] (link:{java9-javadoc}/java/time/format/TextStyle.html#FULL_STANDALONE[java 9]) -** [[painless-api-reference-TextStyle-NARROW]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#NARROW[NARROW] (link:{java9-javadoc}/java/time/format/TextStyle.html#NARROW[java 9]) -** [[painless-api-reference-TextStyle-NARROW_STANDALONE]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#NARROW_STANDALONE[NARROW_STANDALONE] (link:{java9-javadoc}/java/time/format/TextStyle.html#NARROW_STANDALONE[java 9]) -** [[painless-api-reference-TextStyle-SHORT]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#SHORT[SHORT] (link:{java9-javadoc}/java/time/format/TextStyle.html#SHORT[java 9]) -** [[painless-api-reference-TextStyle-SHORT_STANDALONE]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#SHORT_STANDALONE[SHORT_STANDALONE] (link:{java9-javadoc}/java/time/format/TextStyle.html#SHORT_STANDALONE[java 9]) -* ++[[painless-api-reference-TextStyle-valueOf-1]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/format/TextStyle.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-TextStyle-values-0]]static <>[] link:{java8-javadoc}/java/time/format/TextStyle.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/format/TextStyle.html#values%2D%2D[java 9]) -* ++[[painless-api-reference-TextStyle-asNormal-0]]<> link:{java8-javadoc}/java/time/format/TextStyle.html#asNormal%2D%2D[asNormal]()++ (link:{java9-javadoc}/java/time/format/TextStyle.html#asNormal%2D%2D[java 9]) -* ++[[painless-api-reference-TextStyle-asStandalone-0]]<> link:{java8-javadoc}/java/time/format/TextStyle.html#asStandalone%2D%2D[asStandalone]()++ (link:{java9-javadoc}/java/time/format/TextStyle.html#asStandalone%2D%2D[java 9]) -* ++[[painless-api-reference-TextStyle-isStandalone-0]]boolean link:{java8-javadoc}/java/time/format/TextStyle.html#isStandalone%2D%2D[isStandalone]()++ (link:{java9-javadoc}/java/time/format/TextStyle.html#isStandalone%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ThaiBuddhistChronology.asciidoc b/docs/reference/painless-api-reference/ThaiBuddhistChronology.asciidoc deleted file mode 100644 index 86c721fda7ada..0000000000000 --- a/docs/reference/painless-api-reference/ThaiBuddhistChronology.asciidoc +++ /dev/null @@ -1,16 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ThaiBuddhistChronology]]++ThaiBuddhistChronology++:: -** [[painless-api-reference-ThaiBuddhistChronology-INSTANCE]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#INSTANCE[INSTANCE] (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#INSTANCE[java 9]) -* ++[[painless-api-reference-ThaiBuddhistChronology-date-1]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[date](<>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistChronology-date-3]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#date%2Dint%2Dint%2Dint%2D[date](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#date%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistChronology-date-4]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[date](<>, int, int, int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistChronology-dateEpochDay-1]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#dateEpochDay%2Dlong%2D[dateEpochDay](long)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#dateEpochDay%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistChronology-dateYearDay-2]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#dateYearDay%2Dint%2Dint%2D[dateYearDay](int, int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#dateYearDay%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistChronology-dateYearDay-3]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[dateYearDay](<>, int, int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistChronology-eraOf-1]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#eraOf%2Dint%2D[eraOf](int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#eraOf%2Dint%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistChronology-resolveDate-2]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[resolveDate](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ThaiBuddhistDate.asciidoc b/docs/reference/painless-api-reference/ThaiBuddhistDate.asciidoc deleted file mode 100644 index 94d620182dff4..0000000000000 --- a/docs/reference/painless-api-reference/ThaiBuddhistDate.asciidoc +++ /dev/null @@ -1,17 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ThaiBuddhistDate]]++ThaiBuddhistDate++:: -* ++[[painless-api-reference-ThaiBuddhistDate-from-1]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistDate-of-3]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#of%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistDate-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#getChronology%2D%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistDate-getEra-0]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#getEra%2D%2D[getEra]()++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#getEra%2D%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistDate-minus-1]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistDate-minus-2]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistDate-plus-1]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistDate-plus-2]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistDate-with-1]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistDate-with-2]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ThaiBuddhistEra.asciidoc b/docs/reference/painless-api-reference/ThaiBuddhistEra.asciidoc deleted file mode 100644 index b6c070503a9ee..0000000000000 --- a/docs/reference/painless-api-reference/ThaiBuddhistEra.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ThaiBuddhistEra]]++ThaiBuddhistEra++:: -** [[painless-api-reference-ThaiBuddhistEra-BE]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistEra.html#BE[BE] (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistEra.html#BE[java 9]) -** [[painless-api-reference-ThaiBuddhistEra-BEFORE_BE]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistEra.html#BEFORE_BE[BEFORE_BE] (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistEra.html#BEFORE_BE[java 9]) -* ++[[painless-api-reference-ThaiBuddhistEra-of-1]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistEra.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistEra.html#of%2Dint%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistEra-valueOf-1]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistEra.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistEra.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistEra-values-0]]static <>[] link:{java8-javadoc}/java/time/chrono/ThaiBuddhistEra.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistEra.html#values%2D%2D[java 9]) -* ++[[painless-api-reference-ThaiBuddhistEra-getValue-0]]int link:{java8-javadoc}/java/time/chrono/ThaiBuddhistEra.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistEra.html#getValue%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/TimeZone.asciidoc b/docs/reference/painless-api-reference/TimeZone.asciidoc deleted file mode 100644 index 5618e388a4429..0000000000000 --- a/docs/reference/painless-api-reference/TimeZone.asciidoc +++ /dev/null @@ -1,29 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TimeZone]]++TimeZone++:: -** [[painless-api-reference-TimeZone-LONG]]static int link:{java8-javadoc}/java/util/TimeZone.html#LONG[LONG] (link:{java9-javadoc}/java/util/TimeZone.html#LONG[java 9]) -** [[painless-api-reference-TimeZone-SHORT]]static int link:{java8-javadoc}/java/util/TimeZone.html#SHORT[SHORT] (link:{java9-javadoc}/java/util/TimeZone.html#SHORT[java 9]) -* ++[[painless-api-reference-TimeZone-getAvailableIDs-0]]static <>[] link:{java8-javadoc}/java/util/TimeZone.html#getAvailableIDs%2D%2D[getAvailableIDs]()++ (link:{java9-javadoc}/java/util/TimeZone.html#getAvailableIDs%2D%2D[java 9]) -* ++[[painless-api-reference-TimeZone-getAvailableIDs-1]]static <>[] link:{java8-javadoc}/java/util/TimeZone.html#getAvailableIDs%2Dint%2D[getAvailableIDs](int)++ (link:{java9-javadoc}/java/util/TimeZone.html#getAvailableIDs%2Dint%2D[java 9]) -* ++[[painless-api-reference-TimeZone-getDefault-0]]static <> link:{java8-javadoc}/java/util/TimeZone.html#getDefault%2D%2D[getDefault]()++ (link:{java9-javadoc}/java/util/TimeZone.html#getDefault%2D%2D[java 9]) -* ++[[painless-api-reference-TimeZone-getTimeZone-1]]static <> link:{java8-javadoc}/java/util/TimeZone.html#getTimeZone%2Djava.lang.String%2D[getTimeZone](<>)++ (link:{java9-javadoc}/java/util/TimeZone.html#getTimeZone%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-TimeZone-clone-0]]def link:{java8-javadoc}/java/util/TimeZone.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/TimeZone.html#clone%2D%2D[java 9]) -* ++[[painless-api-reference-TimeZone-getDSTSavings-0]]int link:{java8-javadoc}/java/util/TimeZone.html#getDSTSavings%2D%2D[getDSTSavings]()++ (link:{java9-javadoc}/java/util/TimeZone.html#getDSTSavings%2D%2D[java 9]) -* ++[[painless-api-reference-TimeZone-getDisplayName-0]]<> link:{java8-javadoc}/java/util/TimeZone.html#getDisplayName%2D%2D[getDisplayName]()++ (link:{java9-javadoc}/java/util/TimeZone.html#getDisplayName%2D%2D[java 9]) -* ++[[painless-api-reference-TimeZone-getDisplayName-1]]<> link:{java8-javadoc}/java/util/TimeZone.html#getDisplayName%2Djava.util.Locale%2D[getDisplayName](<>)++ (link:{java9-javadoc}/java/util/TimeZone.html#getDisplayName%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-TimeZone-getDisplayName-2]]<> link:{java8-javadoc}/java/util/TimeZone.html#getDisplayName%2Dboolean%2Dint%2D[getDisplayName](boolean, int)++ (link:{java9-javadoc}/java/util/TimeZone.html#getDisplayName%2Dboolean%2Dint%2D[java 9]) -* ++[[painless-api-reference-TimeZone-getDisplayName-3]]<> link:{java8-javadoc}/java/util/TimeZone.html#getDisplayName%2Dboolean%2Dint%2Djava.util.Locale%2D[getDisplayName](boolean, int, <>)++ (link:{java9-javadoc}/java/util/TimeZone.html#getDisplayName%2Dboolean%2Dint%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-TimeZone-getID-0]]<> link:{java8-javadoc}/java/util/TimeZone.html#getID%2D%2D[getID]()++ (link:{java9-javadoc}/java/util/TimeZone.html#getID%2D%2D[java 9]) -* ++[[painless-api-reference-TimeZone-getOffset-1]]int link:{java8-javadoc}/java/util/TimeZone.html#getOffset%2Dlong%2D[getOffset](long)++ (link:{java9-javadoc}/java/util/TimeZone.html#getOffset%2Dlong%2D[java 9]) -* ++[[painless-api-reference-TimeZone-getOffset-6]]int link:{java8-javadoc}/java/util/TimeZone.html#getOffset%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[getOffset](int, int, int, int, int, int)++ (link:{java9-javadoc}/java/util/TimeZone.html#getOffset%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-TimeZone-getRawOffset-0]]int link:{java8-javadoc}/java/util/TimeZone.html#getRawOffset%2D%2D[getRawOffset]()++ (link:{java9-javadoc}/java/util/TimeZone.html#getRawOffset%2D%2D[java 9]) -* ++[[painless-api-reference-TimeZone-hasSameRules-1]]boolean link:{java8-javadoc}/java/util/TimeZone.html#hasSameRules%2Djava.util.TimeZone%2D[hasSameRules](<>)++ (link:{java9-javadoc}/java/util/TimeZone.html#hasSameRules%2Djava.util.TimeZone%2D[java 9]) -* ++[[painless-api-reference-TimeZone-inDaylightTime-1]]boolean link:{java8-javadoc}/java/util/TimeZone.html#inDaylightTime%2Djava.util.Date%2D[inDaylightTime](<>)++ (link:{java9-javadoc}/java/util/TimeZone.html#inDaylightTime%2Djava.util.Date%2D[java 9]) -* ++[[painless-api-reference-TimeZone-observesDaylightTime-0]]boolean link:{java8-javadoc}/java/util/TimeZone.html#observesDaylightTime%2D%2D[observesDaylightTime]()++ (link:{java9-javadoc}/java/util/TimeZone.html#observesDaylightTime%2D%2D[java 9]) -* ++[[painless-api-reference-TimeZone-setRawOffset-1]]void link:{java8-javadoc}/java/util/TimeZone.html#setRawOffset%2Dint%2D[setRawOffset](int)++ (link:{java9-javadoc}/java/util/TimeZone.html#setRawOffset%2Dint%2D[java 9]) -* ++[[painless-api-reference-TimeZone-toZoneId-0]]<> link:{java8-javadoc}/java/util/TimeZone.html#toZoneId%2D%2D[toZoneId]()++ (link:{java9-javadoc}/java/util/TimeZone.html#toZoneId%2D%2D[java 9]) -* ++[[painless-api-reference-TimeZone-useDaylightTime-0]]boolean link:{java8-javadoc}/java/util/TimeZone.html#useDaylightTime%2D%2D[useDaylightTime]()++ (link:{java9-javadoc}/java/util/TimeZone.html#useDaylightTime%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ToDoubleBiFunction.asciidoc b/docs/reference/painless-api-reference/ToDoubleBiFunction.asciidoc deleted file mode 100644 index 8ef0873fb5e15..0000000000000 --- a/docs/reference/painless-api-reference/ToDoubleBiFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ToDoubleBiFunction]]++ToDoubleBiFunction++:: -* ++[[painless-api-reference-ToDoubleBiFunction-applyAsDouble-2]]double link:{java8-javadoc}/java/util/function/ToDoubleBiFunction.html#applyAsDouble%2Djava.lang.Object%2Djava.lang.Object%2D[applyAsDouble](def, def)++ (link:{java9-javadoc}/java/util/function/ToDoubleBiFunction.html#applyAsDouble%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ToDoubleFunction.asciidoc b/docs/reference/painless-api-reference/ToDoubleFunction.asciidoc deleted file mode 100644 index b0d66be9dc09e..0000000000000 --- a/docs/reference/painless-api-reference/ToDoubleFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ToDoubleFunction]]++ToDoubleFunction++:: -* ++[[painless-api-reference-ToDoubleFunction-applyAsDouble-1]]double link:{java8-javadoc}/java/util/function/ToDoubleFunction.html#applyAsDouble%2Djava.lang.Object%2D[applyAsDouble](def)++ (link:{java9-javadoc}/java/util/function/ToDoubleFunction.html#applyAsDouble%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ToIntBiFunction.asciidoc b/docs/reference/painless-api-reference/ToIntBiFunction.asciidoc deleted file mode 100644 index 42cc396a1a989..0000000000000 --- a/docs/reference/painless-api-reference/ToIntBiFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ToIntBiFunction]]++ToIntBiFunction++:: -* ++[[painless-api-reference-ToIntBiFunction-applyAsInt-2]]int link:{java8-javadoc}/java/util/function/ToIntBiFunction.html#applyAsInt%2Djava.lang.Object%2Djava.lang.Object%2D[applyAsInt](def, def)++ (link:{java9-javadoc}/java/util/function/ToIntBiFunction.html#applyAsInt%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ToIntFunction.asciidoc b/docs/reference/painless-api-reference/ToIntFunction.asciidoc deleted file mode 100644 index c471de6b77dc4..0000000000000 --- a/docs/reference/painless-api-reference/ToIntFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ToIntFunction]]++ToIntFunction++:: -* ++[[painless-api-reference-ToIntFunction-applyAsInt-1]]int link:{java8-javadoc}/java/util/function/ToIntFunction.html#applyAsInt%2Djava.lang.Object%2D[applyAsInt](def)++ (link:{java9-javadoc}/java/util/function/ToIntFunction.html#applyAsInt%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ToLongBiFunction.asciidoc b/docs/reference/painless-api-reference/ToLongBiFunction.asciidoc deleted file mode 100644 index ffda1353210a8..0000000000000 --- a/docs/reference/painless-api-reference/ToLongBiFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ToLongBiFunction]]++ToLongBiFunction++:: -* ++[[painless-api-reference-ToLongBiFunction-applyAsLong-2]]long link:{java8-javadoc}/java/util/function/ToLongBiFunction.html#applyAsLong%2Djava.lang.Object%2Djava.lang.Object%2D[applyAsLong](def, def)++ (link:{java9-javadoc}/java/util/function/ToLongBiFunction.html#applyAsLong%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ToLongFunction.asciidoc b/docs/reference/painless-api-reference/ToLongFunction.asciidoc deleted file mode 100644 index 785c414f8c906..0000000000000 --- a/docs/reference/painless-api-reference/ToLongFunction.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ToLongFunction]]++ToLongFunction++:: -* ++[[painless-api-reference-ToLongFunction-applyAsLong-1]]long link:{java8-javadoc}/java/util/function/ToLongFunction.html#applyAsLong%2Djava.lang.Object%2D[applyAsLong](def)++ (link:{java9-javadoc}/java/util/function/ToLongFunction.html#applyAsLong%2Djava.lang.Object%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/TooManyListenersException.asciidoc b/docs/reference/painless-api-reference/TooManyListenersException.asciidoc deleted file mode 100644 index 5a2d3d57b314d..0000000000000 --- a/docs/reference/painless-api-reference/TooManyListenersException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TooManyListenersException]]++TooManyListenersException++:: -* ++[[painless-api-reference-TooManyListenersException-TooManyListenersException-0]]link:{java8-javadoc}/java/util/TooManyListenersException.html#TooManyListenersException%2D%2D[TooManyListenersException]()++ (link:{java9-javadoc}/java/util/TooManyListenersException.html#TooManyListenersException%2D%2D[java 9]) -* ++[[painless-api-reference-TooManyListenersException-TooManyListenersException-1]]link:{java8-javadoc}/java/util/TooManyListenersException.html#TooManyListenersException%2Djava.lang.String%2D[TooManyListenersException](<>)++ (link:{java9-javadoc}/java/util/TooManyListenersException.html#TooManyListenersException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/TreeMap.asciidoc b/docs/reference/painless-api-reference/TreeMap.asciidoc deleted file mode 100644 index f91bae69162f6..0000000000000 --- a/docs/reference/painless-api-reference/TreeMap.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TreeMap]]++TreeMap++:: -* ++[[painless-api-reference-TreeMap-TreeMap-0]]link:{java8-javadoc}/java/util/TreeMap.html#TreeMap%2D%2D[TreeMap]()++ (link:{java9-javadoc}/java/util/TreeMap.html#TreeMap%2D%2D[java 9]) -* ++[[painless-api-reference-TreeMap-TreeMap-1]]link:{java8-javadoc}/java/util/TreeMap.html#TreeMap%2Djava.util.Comparator%2D[TreeMap](<>)++ (link:{java9-javadoc}/java/util/TreeMap.html#TreeMap%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-TreeMap-clone-0]]def link:{java8-javadoc}/java/util/TreeMap.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/TreeMap.html#clone%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/TreeSet.asciidoc b/docs/reference/painless-api-reference/TreeSet.asciidoc deleted file mode 100644 index 84e8a339fe3bf..0000000000000 --- a/docs/reference/painless-api-reference/TreeSet.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TreeSet]]++TreeSet++:: -* ++[[painless-api-reference-TreeSet-TreeSet-0]]link:{java8-javadoc}/java/util/TreeSet.html#TreeSet%2D%2D[TreeSet]()++ (link:{java9-javadoc}/java/util/TreeSet.html#TreeSet%2D%2D[java 9]) -* ++[[painless-api-reference-TreeSet-TreeSet-1]]link:{java8-javadoc}/java/util/TreeSet.html#TreeSet%2Djava.util.Comparator%2D[TreeSet](<>)++ (link:{java9-javadoc}/java/util/TreeSet.html#TreeSet%2Djava.util.Comparator%2D[java 9]) -* ++[[painless-api-reference-TreeSet-clone-0]]def link:{java8-javadoc}/java/util/TreeSet.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/TreeSet.html#clone%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/TypeNotPresentException.asciidoc b/docs/reference/painless-api-reference/TypeNotPresentException.asciidoc deleted file mode 100644 index cecb588d83b17..0000000000000 --- a/docs/reference/painless-api-reference/TypeNotPresentException.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-TypeNotPresentException]]++TypeNotPresentException++:: -* ++[[painless-api-reference-TypeNotPresentException-typeName-0]]<> link:{java8-javadoc}/java/lang/TypeNotPresentException.html#typeName%2D%2D[typeName]()++ (link:{java9-javadoc}/java/lang/TypeNotPresentException.html#typeName%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/UUID.asciidoc b/docs/reference/painless-api-reference/UUID.asciidoc deleted file mode 100644 index a47620f9bc1d6..0000000000000 --- a/docs/reference/painless-api-reference/UUID.asciidoc +++ /dev/null @@ -1,18 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-UUID]]++UUID++:: -* ++[[painless-api-reference-UUID-fromString-1]]static <> link:{java8-javadoc}/java/util/UUID.html#fromString%2Djava.lang.String%2D[fromString](<>)++ (link:{java9-javadoc}/java/util/UUID.html#fromString%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-UUID-nameUUIDFromBytes-1]]static <> link:{java8-javadoc}/java/util/UUID.html#nameUUIDFromBytes%2Dbyte:A%2D[nameUUIDFromBytes](byte[])++ (link:{java9-javadoc}/java/util/UUID.html#nameUUIDFromBytes%2Dbyte:A%2D[java 9]) -* ++[[painless-api-reference-UUID-UUID-2]]link:{java8-javadoc}/java/util/UUID.html#UUID%2Dlong%2Dlong%2D[UUID](long, long)++ (link:{java9-javadoc}/java/util/UUID.html#UUID%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-UUID-clockSequence-0]]int link:{java8-javadoc}/java/util/UUID.html#clockSequence%2D%2D[clockSequence]()++ (link:{java9-javadoc}/java/util/UUID.html#clockSequence%2D%2D[java 9]) -* ++[[painless-api-reference-UUID-compareTo-1]]int link:{java8-javadoc}/java/util/UUID.html#compareTo%2Djava.util.UUID%2D[compareTo](<>)++ (link:{java9-javadoc}/java/util/UUID.html#compareTo%2Djava.util.UUID%2D[java 9]) -* ++[[painless-api-reference-UUID-getLeastSignificantBits-0]]long link:{java8-javadoc}/java/util/UUID.html#getLeastSignificantBits%2D%2D[getLeastSignificantBits]()++ (link:{java9-javadoc}/java/util/UUID.html#getLeastSignificantBits%2D%2D[java 9]) -* ++[[painless-api-reference-UUID-getMostSignificantBits-0]]long link:{java8-javadoc}/java/util/UUID.html#getMostSignificantBits%2D%2D[getMostSignificantBits]()++ (link:{java9-javadoc}/java/util/UUID.html#getMostSignificantBits%2D%2D[java 9]) -* ++[[painless-api-reference-UUID-node-0]]long link:{java8-javadoc}/java/util/UUID.html#node%2D%2D[node]()++ (link:{java9-javadoc}/java/util/UUID.html#node%2D%2D[java 9]) -* ++[[painless-api-reference-UUID-timestamp-0]]long link:{java8-javadoc}/java/util/UUID.html#timestamp%2D%2D[timestamp]()++ (link:{java9-javadoc}/java/util/UUID.html#timestamp%2D%2D[java 9]) -* ++[[painless-api-reference-UUID-variant-0]]int link:{java8-javadoc}/java/util/UUID.html#variant%2D%2D[variant]()++ (link:{java9-javadoc}/java/util/UUID.html#variant%2D%2D[java 9]) -* ++[[painless-api-reference-UUID-version-0]]int link:{java8-javadoc}/java/util/UUID.html#version%2D%2D[version]()++ (link:{java9-javadoc}/java/util/UUID.html#version%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/UnaryOperator.asciidoc b/docs/reference/painless-api-reference/UnaryOperator.asciidoc deleted file mode 100644 index fb7c6ed70d25d..0000000000000 --- a/docs/reference/painless-api-reference/UnaryOperator.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-UnaryOperator]]++UnaryOperator++:: -* ++[[painless-api-reference-UnaryOperator-identity-0]]static <> link:{java8-javadoc}/java/util/function/UnaryOperator.html#identity%2D%2D[identity]()++ (link:{java9-javadoc}/java/util/function/UnaryOperator.html#identity%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/UnknownFormatConversionException.asciidoc b/docs/reference/painless-api-reference/UnknownFormatConversionException.asciidoc deleted file mode 100644 index 95dd348f422c1..0000000000000 --- a/docs/reference/painless-api-reference/UnknownFormatConversionException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-UnknownFormatConversionException]]++UnknownFormatConversionException++:: -* ++[[painless-api-reference-UnknownFormatConversionException-UnknownFormatConversionException-1]]link:{java8-javadoc}/java/util/UnknownFormatConversionException.html#UnknownFormatConversionException%2Djava.lang.String%2D[UnknownFormatConversionException](<>)++ (link:{java9-javadoc}/java/util/UnknownFormatConversionException.html#UnknownFormatConversionException%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-UnknownFormatConversionException-getConversion-0]]<> link:{java8-javadoc}/java/util/UnknownFormatConversionException.html#getConversion%2D%2D[getConversion]()++ (link:{java9-javadoc}/java/util/UnknownFormatConversionException.html#getConversion%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/UnknownFormatFlagsException.asciidoc b/docs/reference/painless-api-reference/UnknownFormatFlagsException.asciidoc deleted file mode 100644 index f14d986c05b38..0000000000000 --- a/docs/reference/painless-api-reference/UnknownFormatFlagsException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-UnknownFormatFlagsException]]++UnknownFormatFlagsException++:: -* ++[[painless-api-reference-UnknownFormatFlagsException-UnknownFormatFlagsException-1]]link:{java8-javadoc}/java/util/UnknownFormatFlagsException.html#UnknownFormatFlagsException%2Djava.lang.String%2D[UnknownFormatFlagsException](<>)++ (link:{java9-javadoc}/java/util/UnknownFormatFlagsException.html#UnknownFormatFlagsException%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-UnknownFormatFlagsException-getFlags-0]]<> link:{java8-javadoc}/java/util/UnknownFormatFlagsException.html#getFlags%2D%2D[getFlags]()++ (link:{java9-javadoc}/java/util/UnknownFormatFlagsException.html#getFlags%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/UnsupportedOperationException.asciidoc b/docs/reference/painless-api-reference/UnsupportedOperationException.asciidoc deleted file mode 100644 index ad9cbdb1478bf..0000000000000 --- a/docs/reference/painless-api-reference/UnsupportedOperationException.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-UnsupportedOperationException]]++UnsupportedOperationException++:: -* ++[[painless-api-reference-UnsupportedOperationException-UnsupportedOperationException-0]]link:{java8-javadoc}/java/lang/UnsupportedOperationException.html#UnsupportedOperationException%2D%2D[UnsupportedOperationException]()++ (link:{java9-javadoc}/java/lang/UnsupportedOperationException.html#UnsupportedOperationException%2D%2D[java 9]) -* ++[[painless-api-reference-UnsupportedOperationException-UnsupportedOperationException-1]]link:{java8-javadoc}/java/lang/UnsupportedOperationException.html#UnsupportedOperationException%2Djava.lang.String%2D[UnsupportedOperationException](<>)++ (link:{java9-javadoc}/java/lang/UnsupportedOperationException.html#UnsupportedOperationException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/UnsupportedTemporalTypeException.asciidoc b/docs/reference/painless-api-reference/UnsupportedTemporalTypeException.asciidoc deleted file mode 100644 index 96e4c1d840220..0000000000000 --- a/docs/reference/painless-api-reference/UnsupportedTemporalTypeException.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-UnsupportedTemporalTypeException]]++UnsupportedTemporalTypeException++:: -* ++[[painless-api-reference-UnsupportedTemporalTypeException-UnsupportedTemporalTypeException-1]]link:{java8-javadoc}/java/time/temporal/UnsupportedTemporalTypeException.html#UnsupportedTemporalTypeException%2Djava.lang.String%2D[UnsupportedTemporalTypeException](<>)++ (link:{java9-javadoc}/java/time/temporal/UnsupportedTemporalTypeException.html#UnsupportedTemporalTypeException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ValueRange.asciidoc b/docs/reference/painless-api-reference/ValueRange.asciidoc deleted file mode 100644 index 409d155f9546a..0000000000000 --- a/docs/reference/painless-api-reference/ValueRange.asciidoc +++ /dev/null @@ -1,20 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ValueRange]]++ValueRange++:: -* ++[[painless-api-reference-ValueRange-of-2]]static <> link:{java8-javadoc}/java/time/temporal/ValueRange.html#of%2Dlong%2Dlong%2D[of](long, long)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#of%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ValueRange-of-3]]static <> link:{java8-javadoc}/java/time/temporal/ValueRange.html#of%2Dlong%2Dlong%2Dlong%2D[of](long, long, long)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#of%2Dlong%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ValueRange-of-4]]static <> link:{java8-javadoc}/java/time/temporal/ValueRange.html#of%2Dlong%2Dlong%2Dlong%2Dlong%2D[of](long, long, long, long)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#of%2Dlong%2Dlong%2Dlong%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ValueRange-checkValidIntValue-2]]int link:{java8-javadoc}/java/time/temporal/ValueRange.html#checkValidIntValue%2Dlong%2Djava.time.temporal.TemporalField%2D[checkValidIntValue](long, <>)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#checkValidIntValue%2Dlong%2Djava.time.temporal.TemporalField%2D[java 9]) -* ++[[painless-api-reference-ValueRange-checkValidValue-2]]long link:{java8-javadoc}/java/time/temporal/ValueRange.html#checkValidValue%2Dlong%2Djava.time.temporal.TemporalField%2D[checkValidValue](long, <>)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#checkValidValue%2Dlong%2Djava.time.temporal.TemporalField%2D[java 9]) -* ++[[painless-api-reference-ValueRange-getLargestMinimum-0]]long link:{java8-javadoc}/java/time/temporal/ValueRange.html#getLargestMinimum%2D%2D[getLargestMinimum]()++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#getLargestMinimum%2D%2D[java 9]) -* ++[[painless-api-reference-ValueRange-getMaximum-0]]long link:{java8-javadoc}/java/time/temporal/ValueRange.html#getMaximum%2D%2D[getMaximum]()++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#getMaximum%2D%2D[java 9]) -* ++[[painless-api-reference-ValueRange-getMinimum-0]]long link:{java8-javadoc}/java/time/temporal/ValueRange.html#getMinimum%2D%2D[getMinimum]()++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#getMinimum%2D%2D[java 9]) -* ++[[painless-api-reference-ValueRange-getSmallestMaximum-0]]long link:{java8-javadoc}/java/time/temporal/ValueRange.html#getSmallestMaximum%2D%2D[getSmallestMaximum]()++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#getSmallestMaximum%2D%2D[java 9]) -* ++[[painless-api-reference-ValueRange-isFixed-0]]boolean link:{java8-javadoc}/java/time/temporal/ValueRange.html#isFixed%2D%2D[isFixed]()++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#isFixed%2D%2D[java 9]) -* ++[[painless-api-reference-ValueRange-isIntValue-0]]boolean link:{java8-javadoc}/java/time/temporal/ValueRange.html#isIntValue%2D%2D[isIntValue]()++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#isIntValue%2D%2D[java 9]) -* ++[[painless-api-reference-ValueRange-isValidIntValue-1]]boolean link:{java8-javadoc}/java/time/temporal/ValueRange.html#isValidIntValue%2Dlong%2D[isValidIntValue](long)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#isValidIntValue%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ValueRange-isValidValue-1]]boolean link:{java8-javadoc}/java/time/temporal/ValueRange.html#isValidValue%2Dlong%2D[isValidValue](long)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#isValidValue%2Dlong%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Vector.asciidoc b/docs/reference/painless-api-reference/Vector.asciidoc deleted file mode 100644 index b1d9ed88b6f9a..0000000000000 --- a/docs/reference/painless-api-reference/Vector.asciidoc +++ /dev/null @@ -1,22 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Vector]]++Vector++:: -* ++[[painless-api-reference-Vector-Vector-0]]link:{java8-javadoc}/java/util/Vector.html#Vector%2D%2D[Vector]()++ (link:{java9-javadoc}/java/util/Vector.html#Vector%2D%2D[java 9]) -* ++[[painless-api-reference-Vector-Vector-1]]link:{java8-javadoc}/java/util/Vector.html#Vector%2Djava.util.Collection%2D[Vector](<>)++ (link:{java9-javadoc}/java/util/Vector.html#Vector%2Djava.util.Collection%2D[java 9]) -* ++[[painless-api-reference-Vector-addElement-1]]void link:{java8-javadoc}/java/util/Vector.html#addElement%2Djava.lang.Object%2D[addElement](def)++ (link:{java9-javadoc}/java/util/Vector.html#addElement%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Vector-clone-0]]def link:{java8-javadoc}/java/util/Vector.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/Vector.html#clone%2D%2D[java 9]) -* ++[[painless-api-reference-Vector-copyInto-1]]void link:{java8-javadoc}/java/util/Vector.html#copyInto%2Djava.lang.Object:A%2D[copyInto](<>[])++ (link:{java9-javadoc}/java/util/Vector.html#copyInto%2Djava.lang.Object:A%2D[java 9]) -* ++[[painless-api-reference-Vector-elementAt-1]]def link:{java8-javadoc}/java/util/Vector.html#elementAt%2Dint%2D[elementAt](int)++ (link:{java9-javadoc}/java/util/Vector.html#elementAt%2Dint%2D[java 9]) -* ++[[painless-api-reference-Vector-elements-0]]<> link:{java8-javadoc}/java/util/Vector.html#elements%2D%2D[elements]()++ (link:{java9-javadoc}/java/util/Vector.html#elements%2D%2D[java 9]) -* ++[[painless-api-reference-Vector-firstElement-0]]def link:{java8-javadoc}/java/util/Vector.html#firstElement%2D%2D[firstElement]()++ (link:{java9-javadoc}/java/util/Vector.html#firstElement%2D%2D[java 9]) -* ++[[painless-api-reference-Vector-insertElementAt-2]]void link:{java8-javadoc}/java/util/Vector.html#insertElementAt%2Djava.lang.Object%2Dint%2D[insertElementAt](def, int)++ (link:{java9-javadoc}/java/util/Vector.html#insertElementAt%2Djava.lang.Object%2Dint%2D[java 9]) -* ++[[painless-api-reference-Vector-lastElement-0]]def link:{java8-javadoc}/java/util/Vector.html#lastElement%2D%2D[lastElement]()++ (link:{java9-javadoc}/java/util/Vector.html#lastElement%2D%2D[java 9]) -* ++[[painless-api-reference-Vector-lastIndexOf-2]]int link:{java8-javadoc}/java/util/Vector.html#lastIndexOf%2Djava.lang.Object%2Dint%2D[lastIndexOf](def, int)++ (link:{java9-javadoc}/java/util/Vector.html#lastIndexOf%2Djava.lang.Object%2Dint%2D[java 9]) -* ++[[painless-api-reference-Vector-removeAllElements-0]]void link:{java8-javadoc}/java/util/Vector.html#removeAllElements%2D%2D[removeAllElements]()++ (link:{java9-javadoc}/java/util/Vector.html#removeAllElements%2D%2D[java 9]) -* ++[[painless-api-reference-Vector-removeElement-1]]boolean link:{java8-javadoc}/java/util/Vector.html#removeElement%2Djava.lang.Object%2D[removeElement](def)++ (link:{java9-javadoc}/java/util/Vector.html#removeElement%2Djava.lang.Object%2D[java 9]) -* ++[[painless-api-reference-Vector-removeElementAt-1]]void link:{java8-javadoc}/java/util/Vector.html#removeElementAt%2Dint%2D[removeElementAt](int)++ (link:{java9-javadoc}/java/util/Vector.html#removeElementAt%2Dint%2D[java 9]) -* ++[[painless-api-reference-Vector-setElementAt-2]]void link:{java8-javadoc}/java/util/Vector.html#setElementAt%2Djava.lang.Object%2Dint%2D[setElementAt](def, int)++ (link:{java9-javadoc}/java/util/Vector.html#setElementAt%2Djava.lang.Object%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/WeekFields.asciidoc b/docs/reference/painless-api-reference/WeekFields.asciidoc deleted file mode 100644 index 330b2e98d2e7a..0000000000000 --- a/docs/reference/painless-api-reference/WeekFields.asciidoc +++ /dev/null @@ -1,19 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-WeekFields]]++WeekFields++:: -** [[painless-api-reference-WeekFields-ISO]]static <> link:{java8-javadoc}/java/time/temporal/WeekFields.html#ISO[ISO] (link:{java9-javadoc}/java/time/temporal/WeekFields.html#ISO[java 9]) -** [[painless-api-reference-WeekFields-SUNDAY_START]]static <> link:{java8-javadoc}/java/time/temporal/WeekFields.html#SUNDAY_START[SUNDAY_START] (link:{java9-javadoc}/java/time/temporal/WeekFields.html#SUNDAY_START[java 9]) -** [[painless-api-reference-WeekFields-WEEK_BASED_YEARS]]static <> link:{java8-javadoc}/java/time/temporal/WeekFields.html#WEEK_BASED_YEARS[WEEK_BASED_YEARS] (link:{java9-javadoc}/java/time/temporal/WeekFields.html#WEEK_BASED_YEARS[java 9]) -* ++[[painless-api-reference-WeekFields-of-1]]static <> link:{java8-javadoc}/java/time/temporal/WeekFields.html#of%2Djava.util.Locale%2D[of](<>)++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#of%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-WeekFields-of-2]]static <> link:{java8-javadoc}/java/time/temporal/WeekFields.html#of%2Djava.time.DayOfWeek%2Dint%2D[of](<>, int)++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#of%2Djava.time.DayOfWeek%2Dint%2D[java 9]) -* ++[[painless-api-reference-WeekFields-dayOfWeek-0]]<> link:{java8-javadoc}/java/time/temporal/WeekFields.html#dayOfWeek%2D%2D[dayOfWeek]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#dayOfWeek%2D%2D[java 9]) -* ++[[painless-api-reference-WeekFields-getFirstDayOfWeek-0]]<> link:{java8-javadoc}/java/time/temporal/WeekFields.html#getFirstDayOfWeek%2D%2D[getFirstDayOfWeek]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#getFirstDayOfWeek%2D%2D[java 9]) -* ++[[painless-api-reference-WeekFields-getMinimalDaysInFirstWeek-0]]int link:{java8-javadoc}/java/time/temporal/WeekFields.html#getMinimalDaysInFirstWeek%2D%2D[getMinimalDaysInFirstWeek]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#getMinimalDaysInFirstWeek%2D%2D[java 9]) -* ++[[painless-api-reference-WeekFields-weekBasedYear-0]]<> link:{java8-javadoc}/java/time/temporal/WeekFields.html#weekBasedYear%2D%2D[weekBasedYear]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#weekBasedYear%2D%2D[java 9]) -* ++[[painless-api-reference-WeekFields-weekOfMonth-0]]<> link:{java8-javadoc}/java/time/temporal/WeekFields.html#weekOfMonth%2D%2D[weekOfMonth]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#weekOfMonth%2D%2D[java 9]) -* ++[[painless-api-reference-WeekFields-weekOfWeekBasedYear-0]]<> link:{java8-javadoc}/java/time/temporal/WeekFields.html#weekOfWeekBasedYear%2D%2D[weekOfWeekBasedYear]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#weekOfWeekBasedYear%2D%2D[java 9]) -* ++[[painless-api-reference-WeekFields-weekOfYear-0]]<> link:{java8-javadoc}/java/time/temporal/WeekFields.html#weekOfYear%2D%2D[weekOfYear]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#weekOfYear%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/Year.asciidoc b/docs/reference/painless-api-reference/Year.asciidoc deleted file mode 100644 index e3800991039d6..0000000000000 --- a/docs/reference/painless-api-reference/Year.asciidoc +++ /dev/null @@ -1,32 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-Year]]++Year++:: -** [[painless-api-reference-Year-MAX_VALUE]]static int link:{java8-javadoc}/java/time/Year.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/time/Year.html#MAX_VALUE[java 9]) -** [[painless-api-reference-Year-MIN_VALUE]]static int link:{java8-javadoc}/java/time/Year.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/time/Year.html#MIN_VALUE[java 9]) -* ++[[painless-api-reference-Year-from-1]]static <> link:{java8-javadoc}/java/time/Year.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/Year.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-Year-isLeap-1]]static boolean link:{java8-javadoc}/java/time/Year.html#isLeap%2Dlong%2D[isLeap](long)++ (link:{java9-javadoc}/java/time/Year.html#isLeap%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Year-of-1]]static <> link:{java8-javadoc}/java/time/Year.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/Year.html#of%2Dint%2D[java 9]) -* ++[[painless-api-reference-Year-parse-1]]static <> link:{java8-javadoc}/java/time/Year.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/Year.html#parse%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-Year-parse-2]]static <> link:{java8-javadoc}/java/time/Year.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/Year.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-Year-atDay-1]]<> link:{java8-javadoc}/java/time/Year.html#atDay%2Dint%2D[atDay](int)++ (link:{java9-javadoc}/java/time/Year.html#atDay%2Dint%2D[java 9]) -* ++[[painless-api-reference-Year-atMonth-1]]<> link:{java8-javadoc}/java/time/Year.html#atMonth%2Dint%2D[atMonth](int)++ (link:{java9-javadoc}/java/time/Year.html#atMonth%2Dint%2D[java 9]) -* ++[[painless-api-reference-Year-atMonthDay-1]]<> link:{java8-javadoc}/java/time/Year.html#atMonthDay%2Djava.time.MonthDay%2D[atMonthDay](<>)++ (link:{java9-javadoc}/java/time/Year.html#atMonthDay%2Djava.time.MonthDay%2D[java 9]) -* ++[[painless-api-reference-Year-compareTo-1]]int link:{java8-javadoc}/java/time/Year.html#compareTo%2Djava.time.Year%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/Year.html#compareTo%2Djava.time.Year%2D[java 9]) -* ++[[painless-api-reference-Year-format-1]]<> link:{java8-javadoc}/java/time/Year.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/Year.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-Year-getValue-0]]int link:{java8-javadoc}/java/time/Year.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/Year.html#getValue%2D%2D[java 9]) -* ++[[painless-api-reference-Year-isAfter-1]]boolean link:{java8-javadoc}/java/time/Year.html#isAfter%2Djava.time.Year%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/Year.html#isAfter%2Djava.time.Year%2D[java 9]) -* ++[[painless-api-reference-Year-isLeap-0]]boolean link:{java8-javadoc}/java/time/Year.html#isLeap%2D%2D[isLeap]()++ (link:{java9-javadoc}/java/time/Year.html#isLeap%2D%2D[java 9]) -* ++[[painless-api-reference-Year-isValidMonthDay-1]]boolean link:{java8-javadoc}/java/time/Year.html#isValidMonthDay%2Djava.time.MonthDay%2D[isValidMonthDay](<>)++ (link:{java9-javadoc}/java/time/Year.html#isValidMonthDay%2Djava.time.MonthDay%2D[java 9]) -* ++[[painless-api-reference-Year-length-0]]int link:{java8-javadoc}/java/time/Year.html#length%2D%2D[length]()++ (link:{java9-javadoc}/java/time/Year.html#length%2D%2D[java 9]) -* ++[[painless-api-reference-Year-minus-1]]<> link:{java8-javadoc}/java/time/Year.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/Year.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-Year-minus-2]]<> link:{java8-javadoc}/java/time/Year.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/Year.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-Year-minusYears-1]]<> link:{java8-javadoc}/java/time/Year.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/Year.html#minusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Year-plus-1]]<> link:{java8-javadoc}/java/time/Year.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/Year.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-Year-plus-2]]<> link:{java8-javadoc}/java/time/Year.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/Year.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-Year-plusYears-1]]<> link:{java8-javadoc}/java/time/Year.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/Year.html#plusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-Year-with-1]]<> link:{java8-javadoc}/java/time/Year.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/Year.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-Year-with-2]]<> link:{java8-javadoc}/java/time/Year.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/Year.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/YearMonth.asciidoc b/docs/reference/painless-api-reference/YearMonth.asciidoc deleted file mode 100644 index 5573b16c0640c..0000000000000 --- a/docs/reference/painless-api-reference/YearMonth.asciidoc +++ /dev/null @@ -1,36 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-YearMonth]]++YearMonth++:: -* ++[[painless-api-reference-YearMonth-from-1]]static <> link:{java8-javadoc}/java/time/YearMonth.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-YearMonth-of-2]]static <> link:{java8-javadoc}/java/time/YearMonth.html#of%2Dint%2Dint%2D[of](int, int)++ (link:{java9-javadoc}/java/time/YearMonth.html#of%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-YearMonth-parse-1]]static <> link:{java8-javadoc}/java/time/YearMonth.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#parse%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-YearMonth-parse-2]]static <> link:{java8-javadoc}/java/time/YearMonth.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/YearMonth.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-YearMonth-atDay-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#atDay%2Dint%2D[atDay](int)++ (link:{java9-javadoc}/java/time/YearMonth.html#atDay%2Dint%2D[java 9]) -* ++[[painless-api-reference-YearMonth-atEndOfMonth-0]]<> link:{java8-javadoc}/java/time/YearMonth.html#atEndOfMonth%2D%2D[atEndOfMonth]()++ (link:{java9-javadoc}/java/time/YearMonth.html#atEndOfMonth%2D%2D[java 9]) -* ++[[painless-api-reference-YearMonth-compareTo-1]]int link:{java8-javadoc}/java/time/YearMonth.html#compareTo%2Djava.time.YearMonth%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#compareTo%2Djava.time.YearMonth%2D[java 9]) -* ++[[painless-api-reference-YearMonth-format-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-YearMonth-getMonth-0]]<> link:{java8-javadoc}/java/time/YearMonth.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/YearMonth.html#getMonth%2D%2D[java 9]) -* ++[[painless-api-reference-YearMonth-getMonthValue-0]]int link:{java8-javadoc}/java/time/YearMonth.html#getMonthValue%2D%2D[getMonthValue]()++ (link:{java9-javadoc}/java/time/YearMonth.html#getMonthValue%2D%2D[java 9]) -* ++[[painless-api-reference-YearMonth-getYear-0]]int link:{java8-javadoc}/java/time/YearMonth.html#getYear%2D%2D[getYear]()++ (link:{java9-javadoc}/java/time/YearMonth.html#getYear%2D%2D[java 9]) -* ++[[painless-api-reference-YearMonth-isAfter-1]]boolean link:{java8-javadoc}/java/time/YearMonth.html#isAfter%2Djava.time.YearMonth%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#isAfter%2Djava.time.YearMonth%2D[java 9]) -* ++[[painless-api-reference-YearMonth-isBefore-1]]boolean link:{java8-javadoc}/java/time/YearMonth.html#isBefore%2Djava.time.YearMonth%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#isBefore%2Djava.time.YearMonth%2D[java 9]) -* ++[[painless-api-reference-YearMonth-isLeapYear-0]]boolean link:{java8-javadoc}/java/time/YearMonth.html#isLeapYear%2D%2D[isLeapYear]()++ (link:{java9-javadoc}/java/time/YearMonth.html#isLeapYear%2D%2D[java 9]) -* ++[[painless-api-reference-YearMonth-isValidDay-1]]boolean link:{java8-javadoc}/java/time/YearMonth.html#isValidDay%2Dint%2D[isValidDay](int)++ (link:{java9-javadoc}/java/time/YearMonth.html#isValidDay%2Dint%2D[java 9]) -* ++[[painless-api-reference-YearMonth-lengthOfMonth-0]]int link:{java8-javadoc}/java/time/YearMonth.html#lengthOfMonth%2D%2D[lengthOfMonth]()++ (link:{java9-javadoc}/java/time/YearMonth.html#lengthOfMonth%2D%2D[java 9]) -* ++[[painless-api-reference-YearMonth-lengthOfYear-0]]int link:{java8-javadoc}/java/time/YearMonth.html#lengthOfYear%2D%2D[lengthOfYear]()++ (link:{java9-javadoc}/java/time/YearMonth.html#lengthOfYear%2D%2D[java 9]) -* ++[[painless-api-reference-YearMonth-minus-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-YearMonth-minus-2]]<> link:{java8-javadoc}/java/time/YearMonth.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/YearMonth.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-YearMonth-minusMonths-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#minusMonths%2Dlong%2D[minusMonths](long)++ (link:{java9-javadoc}/java/time/YearMonth.html#minusMonths%2Dlong%2D[java 9]) -* ++[[painless-api-reference-YearMonth-minusYears-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/YearMonth.html#minusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-YearMonth-plus-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-YearMonth-plus-2]]<> link:{java8-javadoc}/java/time/YearMonth.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/YearMonth.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-YearMonth-plusMonths-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#plusMonths%2Dlong%2D[plusMonths](long)++ (link:{java9-javadoc}/java/time/YearMonth.html#plusMonths%2Dlong%2D[java 9]) -* ++[[painless-api-reference-YearMonth-plusYears-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/YearMonth.html#plusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-YearMonth-with-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-YearMonth-with-2]]<> link:{java8-javadoc}/java/time/YearMonth.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/YearMonth.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* ++[[painless-api-reference-YearMonth-withMonth-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#withMonth%2Dint%2D[withMonth](int)++ (link:{java9-javadoc}/java/time/YearMonth.html#withMonth%2Dint%2D[java 9]) -* ++[[painless-api-reference-YearMonth-withYear-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#withYear%2Dint%2D[withYear](int)++ (link:{java9-javadoc}/java/time/YearMonth.html#withYear%2Dint%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ZoneId.asciidoc b/docs/reference/painless-api-reference/ZoneId.asciidoc deleted file mode 100644 index fe31c9df00bb1..0000000000000 --- a/docs/reference/painless-api-reference/ZoneId.asciidoc +++ /dev/null @@ -1,18 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ZoneId]]++ZoneId++:: -** [[painless-api-reference-ZoneId-SHORT_IDS]]static <> link:{java8-javadoc}/java/time/ZoneId.html#SHORT_IDS[SHORT_IDS] (link:{java9-javadoc}/java/time/ZoneId.html#SHORT_IDS[java 9]) -* ++[[painless-api-reference-ZoneId-from-1]]static <> link:{java8-javadoc}/java/time/ZoneId.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/ZoneId.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-ZoneId-getAvailableZoneIds-0]]static <> link:{java8-javadoc}/java/time/ZoneId.html#getAvailableZoneIds%2D%2D[getAvailableZoneIds]()++ (link:{java9-javadoc}/java/time/ZoneId.html#getAvailableZoneIds%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneId-of-1]]static <> link:{java8-javadoc}/java/time/ZoneId.html#of%2Djava.lang.String%2D[of](<>)++ (link:{java9-javadoc}/java/time/ZoneId.html#of%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-ZoneId-of-2]]static <> link:{java8-javadoc}/java/time/ZoneId.html#of%2Djava.lang.String%2Djava.util.Map%2D[of](<>, <>)++ (link:{java9-javadoc}/java/time/ZoneId.html#of%2Djava.lang.String%2Djava.util.Map%2D[java 9]) -* ++[[painless-api-reference-ZoneId-ofOffset-2]]static <> link:{java8-javadoc}/java/time/ZoneId.html#ofOffset%2Djava.lang.String%2Djava.time.ZoneOffset%2D[ofOffset](<>, <>)++ (link:{java9-javadoc}/java/time/ZoneId.html#ofOffset%2Djava.lang.String%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-ZoneId-systemDefault-0]]static <> link:{java8-javadoc}/java/time/ZoneId.html#systemDefault%2D%2D[systemDefault]()++ (link:{java9-javadoc}/java/time/ZoneId.html#systemDefault%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneId-getDisplayName-2]]<> link:{java8-javadoc}/java/time/ZoneId.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[getDisplayName](<>, <>)++ (link:{java9-javadoc}/java/time/ZoneId.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[java 9]) -* ++[[painless-api-reference-ZoneId-getId-0]]<> link:{java8-javadoc}/java/time/ZoneId.html#getId%2D%2D[getId]()++ (link:{java9-javadoc}/java/time/ZoneId.html#getId%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneId-getRules-0]]<> link:{java8-javadoc}/java/time/ZoneId.html#getRules%2D%2D[getRules]()++ (link:{java9-javadoc}/java/time/ZoneId.html#getRules%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneId-normalized-0]]<> link:{java8-javadoc}/java/time/ZoneId.html#normalized%2D%2D[normalized]()++ (link:{java9-javadoc}/java/time/ZoneId.html#normalized%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ZoneOffset.asciidoc b/docs/reference/painless-api-reference/ZoneOffset.asciidoc deleted file mode 100644 index 84e8530f5938b..0000000000000 --- a/docs/reference/painless-api-reference/ZoneOffset.asciidoc +++ /dev/null @@ -1,17 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ZoneOffset]]++ZoneOffset++:: -** [[painless-api-reference-ZoneOffset-MAX]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#MAX[MAX] (link:{java9-javadoc}/java/time/ZoneOffset.html#MAX[java 9]) -** [[painless-api-reference-ZoneOffset-MIN]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#MIN[MIN] (link:{java9-javadoc}/java/time/ZoneOffset.html#MIN[java 9]) -** [[painless-api-reference-ZoneOffset-UTC]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#UTC[UTC] (link:{java9-javadoc}/java/time/ZoneOffset.html#UTC[java 9]) -* ++[[painless-api-reference-ZoneOffset-from-1]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/ZoneOffset.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-ZoneOffset-of-1]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#of%2Djava.lang.String%2D[of](<>)++ (link:{java9-javadoc}/java/time/ZoneOffset.html#of%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-ZoneOffset-ofHours-1]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#ofHours%2Dint%2D[ofHours](int)++ (link:{java9-javadoc}/java/time/ZoneOffset.html#ofHours%2Dint%2D[java 9]) -* ++[[painless-api-reference-ZoneOffset-ofHoursMinutes-2]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#ofHoursMinutes%2Dint%2Dint%2D[ofHoursMinutes](int, int)++ (link:{java9-javadoc}/java/time/ZoneOffset.html#ofHoursMinutes%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-ZoneOffset-ofHoursMinutesSeconds-3]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#ofHoursMinutesSeconds%2Dint%2Dint%2Dint%2D[ofHoursMinutesSeconds](int, int, int)++ (link:{java9-javadoc}/java/time/ZoneOffset.html#ofHoursMinutesSeconds%2Dint%2Dint%2Dint%2D[java 9]) -* ++[[painless-api-reference-ZoneOffset-ofTotalSeconds-1]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#ofTotalSeconds%2Dint%2D[ofTotalSeconds](int)++ (link:{java9-javadoc}/java/time/ZoneOffset.html#ofTotalSeconds%2Dint%2D[java 9]) -* ++[[painless-api-reference-ZoneOffset-getTotalSeconds-0]]int link:{java8-javadoc}/java/time/ZoneOffset.html#getTotalSeconds%2D%2D[getTotalSeconds]()++ (link:{java9-javadoc}/java/time/ZoneOffset.html#getTotalSeconds%2D%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ZoneOffsetTransition.asciidoc b/docs/reference/painless-api-reference/ZoneOffsetTransition.asciidoc deleted file mode 100644 index 43be4f56ee4b3..0000000000000 --- a/docs/reference/painless-api-reference/ZoneOffsetTransition.asciidoc +++ /dev/null @@ -1,19 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ZoneOffsetTransition]]++ZoneOffsetTransition++:: -* ++[[painless-api-reference-ZoneOffsetTransition-of-3]]static <> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#of%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2D[of](<>, <>, <>)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#of%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransition-compareTo-1]]int link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#compareTo%2Djava.time.zone.ZoneOffsetTransition%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#compareTo%2Djava.time.zone.ZoneOffsetTransition%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransition-getDateTimeAfter-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#getDateTimeAfter%2D%2D[getDateTimeAfter]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#getDateTimeAfter%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransition-getDateTimeBefore-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#getDateTimeBefore%2D%2D[getDateTimeBefore]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#getDateTimeBefore%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransition-getDuration-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#getDuration%2D%2D[getDuration]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#getDuration%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransition-getInstant-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#getInstant%2D%2D[getInstant]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#getInstant%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransition-getOffsetAfter-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#getOffsetAfter%2D%2D[getOffsetAfter]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#getOffsetAfter%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransition-getOffsetBefore-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#getOffsetBefore%2D%2D[getOffsetBefore]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#getOffsetBefore%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransition-isGap-0]]boolean link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#isGap%2D%2D[isGap]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#isGap%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransition-isOverlap-0]]boolean link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#isOverlap%2D%2D[isOverlap]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#isOverlap%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransition-isValidOffset-1]]boolean link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#isValidOffset%2Djava.time.ZoneOffset%2D[isValidOffset](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#isValidOffset%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransition-toEpochSecond-0]]long link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#toEpochSecond%2D%2D[toEpochSecond]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#toEpochSecond%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ZoneOffsetTransitionRule.TimeDefinition.asciidoc b/docs/reference/painless-api-reference/ZoneOffsetTransitionRule.TimeDefinition.asciidoc deleted file mode 100644 index 45fb25e0f6e73..0000000000000 --- a/docs/reference/painless-api-reference/ZoneOffsetTransitionRule.TimeDefinition.asciidoc +++ /dev/null @@ -1,13 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition]]++ZoneOffsetTransitionRule.TimeDefinition++:: -** [[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition-STANDARD]]static <> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#STANDARD[STANDARD] (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#STANDARD[java 9]) -** [[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition-UTC]]static <> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#UTC[UTC] (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#UTC[java 9]) -** [[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition-WALL]]static <> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#WALL[WALL] (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#WALL[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition-valueOf-1]]static <> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#valueOf%2Djava.lang.String%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition-values-0]]static <>[] link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#values%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition-createDateTime-3]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#createDateTime%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2D[createDateTime](<>, <>, <>)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#createDateTime%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ZoneOffsetTransitionRule.asciidoc b/docs/reference/painless-api-reference/ZoneOffsetTransitionRule.asciidoc deleted file mode 100644 index 8e84bff01722b..0000000000000 --- a/docs/reference/painless-api-reference/ZoneOffsetTransitionRule.asciidoc +++ /dev/null @@ -1,18 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ZoneOffsetTransitionRule]]++ZoneOffsetTransitionRule++:: -* ++[[painless-api-reference-ZoneOffsetTransitionRule-of-9]]static <> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#of%2Djava.time.Month%2Dint%2Djava.time.DayOfWeek%2Djava.time.LocalTime%2Dboolean%2Djava.time.zone.ZoneOffsetTransitionRule$TimeDefinition%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2D[of](<>, int, <>, <>, boolean, <>, <>, <>, <>)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#of%2Djava.time.Month%2Dint%2Djava.time.DayOfWeek%2Djava.time.LocalTime%2Dboolean%2Djava.time.zone.ZoneOffsetTransitionRule$TimeDefinition%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransitionRule-createTransition-1]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#createTransition%2Dint%2D[createTransition](int)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#createTransition%2Dint%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransitionRule-getDayOfMonthIndicator-0]]int link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getDayOfMonthIndicator%2D%2D[getDayOfMonthIndicator]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getDayOfMonthIndicator%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransitionRule-getDayOfWeek-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getDayOfWeek%2D%2D[getDayOfWeek]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getDayOfWeek%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransitionRule-getLocalTime-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getLocalTime%2D%2D[getLocalTime]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getLocalTime%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransitionRule-getMonth-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getMonth%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransitionRule-getOffsetAfter-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getOffsetAfter%2D%2D[getOffsetAfter]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getOffsetAfter%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransitionRule-getOffsetBefore-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getOffsetBefore%2D%2D[getOffsetBefore]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getOffsetBefore%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransitionRule-getStandardOffset-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getStandardOffset%2D%2D[getStandardOffset]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getStandardOffset%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransitionRule-getTimeDefinition-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getTimeDefinition%2D%2D[getTimeDefinition]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getTimeDefinition%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneOffsetTransitionRule-isMidnightEndOfDay-0]]boolean link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#isMidnightEndOfDay%2D%2D[isMidnightEndOfDay]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#isMidnightEndOfDay%2D%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ZoneRules.asciidoc b/docs/reference/painless-api-reference/ZoneRules.asciidoc deleted file mode 100644 index 6c3104a7e02ab..0000000000000 --- a/docs/reference/painless-api-reference/ZoneRules.asciidoc +++ /dev/null @@ -1,21 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ZoneRules]]++ZoneRules++:: -* ++[[painless-api-reference-ZoneRules-of-1]]static <> link:{java8-javadoc}/java/time/zone/ZoneRules.html#of%2Djava.time.ZoneOffset%2D[of](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#of%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-ZoneRules-of-5]]static <> link:{java8-javadoc}/java/time/zone/ZoneRules.html#of%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2Djava.util.List%2Djava.util.List%2Djava.util.List%2D[of](<>, <>, <>, <>, <>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#of%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2Djava.util.List%2Djava.util.List%2Djava.util.List%2D[java 9]) -* ++[[painless-api-reference-ZoneRules-getDaylightSavings-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getDaylightSavings%2Djava.time.Instant%2D[getDaylightSavings](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getDaylightSavings%2Djava.time.Instant%2D[java 9]) -* ++[[painless-api-reference-ZoneRules-getOffset-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getOffset%2Djava.time.Instant%2D[getOffset](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getOffset%2Djava.time.Instant%2D[java 9]) -* ++[[painless-api-reference-ZoneRules-getStandardOffset-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getStandardOffset%2Djava.time.Instant%2D[getStandardOffset](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getStandardOffset%2Djava.time.Instant%2D[java 9]) -* ++[[painless-api-reference-ZoneRules-getTransition-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getTransition%2Djava.time.LocalDateTime%2D[getTransition](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getTransition%2Djava.time.LocalDateTime%2D[java 9]) -* ++[[painless-api-reference-ZoneRules-getTransitionRules-0]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getTransitionRules%2D%2D[getTransitionRules]()++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getTransitionRules%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneRules-getTransitions-0]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getTransitions%2D%2D[getTransitions]()++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getTransitions%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneRules-getValidOffsets-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getValidOffsets%2Djava.time.LocalDateTime%2D[getValidOffsets](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getValidOffsets%2Djava.time.LocalDateTime%2D[java 9]) -* ++[[painless-api-reference-ZoneRules-isDaylightSavings-1]]boolean link:{java8-javadoc}/java/time/zone/ZoneRules.html#isDaylightSavings%2Djava.time.Instant%2D[isDaylightSavings](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#isDaylightSavings%2Djava.time.Instant%2D[java 9]) -* ++[[painless-api-reference-ZoneRules-isFixedOffset-0]]boolean link:{java8-javadoc}/java/time/zone/ZoneRules.html#isFixedOffset%2D%2D[isFixedOffset]()++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#isFixedOffset%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneRules-isValidOffset-2]]boolean link:{java8-javadoc}/java/time/zone/ZoneRules.html#isValidOffset%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2D[isValidOffset](<>, <>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#isValidOffset%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-ZoneRules-nextTransition-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#nextTransition%2Djava.time.Instant%2D[nextTransition](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#nextTransition%2Djava.time.Instant%2D[java 9]) -* ++[[painless-api-reference-ZoneRules-previousTransition-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#previousTransition%2Djava.time.Instant%2D[previousTransition](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#previousTransition%2Djava.time.Instant%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ZoneRulesException.asciidoc b/docs/reference/painless-api-reference/ZoneRulesException.asciidoc deleted file mode 100644 index 8bb3ef0cc98b0..0000000000000 --- a/docs/reference/painless-api-reference/ZoneRulesException.asciidoc +++ /dev/null @@ -1,8 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ZoneRulesException]]++ZoneRulesException++:: -* ++[[painless-api-reference-ZoneRulesException-ZoneRulesException-1]]link:{java8-javadoc}/java/time/zone/ZoneRulesException.html#ZoneRulesException%2Djava.lang.String%2D[ZoneRulesException](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRulesException.html#ZoneRulesException%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/ZoneRulesProvider.asciidoc b/docs/reference/painless-api-reference/ZoneRulesProvider.asciidoc deleted file mode 100644 index be33f691f370e..0000000000000 --- a/docs/reference/painless-api-reference/ZoneRulesProvider.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ZoneRulesProvider]]++ZoneRulesProvider++:: -* ++[[painless-api-reference-ZoneRulesProvider-getAvailableZoneIds-0]]static <> link:{java8-javadoc}/java/time/zone/ZoneRulesProvider.html#getAvailableZoneIds%2D%2D[getAvailableZoneIds]()++ (link:{java9-javadoc}/java/time/zone/ZoneRulesProvider.html#getAvailableZoneIds%2D%2D[java 9]) -* ++[[painless-api-reference-ZoneRulesProvider-getRules-2]]static <> link:{java8-javadoc}/java/time/zone/ZoneRulesProvider.html#getRules%2Djava.lang.String%2Dboolean%2D[getRules](<>, boolean)++ (link:{java9-javadoc}/java/time/zone/ZoneRulesProvider.html#getRules%2Djava.lang.String%2Dboolean%2D[java 9]) -* ++[[painless-api-reference-ZoneRulesProvider-getVersions-1]]static <> link:{java8-javadoc}/java/time/zone/ZoneRulesProvider.html#getVersions%2Djava.lang.String%2D[getVersions](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRulesProvider.html#getVersions%2Djava.lang.String%2D[java 9]) -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/ZonedDateTime.asciidoc b/docs/reference/painless-api-reference/ZonedDateTime.asciidoc deleted file mode 100644 index e8ba7501c6c20..0000000000000 --- a/docs/reference/painless-api-reference/ZonedDateTime.asciidoc +++ /dev/null @@ -1,66 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-ZonedDateTime]]++ZonedDateTime++:: -* ++[[painless-api-reference-ZonedDateTime-from-1]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-of-2]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#of%2Djava.time.LocalDateTime%2Djava.time.ZoneId%2D[of](<>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#of%2Djava.time.LocalDateTime%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-of-3]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#of%2Djava.time.LocalDate%2Djava.time.LocalTime%2Djava.time.ZoneId%2D[of](<>, <>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#of%2Djava.time.LocalDate%2Djava.time.LocalTime%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-of-8]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Djava.time.ZoneId%2D[of](int, int, int, int, int, int, int, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-ofInstant-2]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[ofInstant](<>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-ofInstant-3]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#ofInstant%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneId%2D[ofInstant](<>, <>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#ofInstant%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-ofLocal-3]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#ofLocal%2Djava.time.LocalDateTime%2Djava.time.ZoneId%2Djava.time.ZoneOffset%2D[ofLocal](<>, <>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#ofLocal%2Djava.time.LocalDateTime%2Djava.time.ZoneId%2Djava.time.ZoneOffset%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-ofStrict-3]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#ofStrict%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneId%2D[ofStrict](<>, <>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#ofStrict%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-parse-1]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#parse%2Djava.lang.CharSequence%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-parse-2]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-getDayOfMonth-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getDayOfMonth%2D%2D[getDayOfMonth]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getDayOfMonth%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-getDayOfWeek-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#getDayOfWeek%2D%2D[getDayOfWeek]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getDayOfWeek%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-getDayOfYear-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getDayOfYear%2D%2D[getDayOfYear]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getDayOfYear%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-getHour-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getHour%2D%2D[getHour]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getHour%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-getMinute-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getMinute%2D%2D[getMinute]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getMinute%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-getMonth-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getMonth%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-getMonthValue-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getMonthValue%2D%2D[getMonthValue]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getMonthValue%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-getNano-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getNano%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-getSecond-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getSecond%2D%2D[getSecond]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getSecond%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-getYear-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getYear%2D%2D[getYear]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getYear%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-minus-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-minus-2]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-minusDays-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusDays%2Dlong%2D[minusDays](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusDays%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-minusHours-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusHours%2Dlong%2D[minusHours](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusHours%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-minusMinutes-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusMinutes%2Dlong%2D[minusMinutes](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusMinutes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-minusMonths-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusMonths%2Dlong%2D[minusMonths](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusMonths%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-minusNanos-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-minusSeconds-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-minusWeeks-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusWeeks%2Dlong%2D[minusWeeks](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusWeeks%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-minusYears-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-plus-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-plus-2]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-plusDays-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusDays%2Dlong%2D[plusDays](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusDays%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-plusHours-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusHours%2Dlong%2D[plusHours](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusHours%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-plusMinutes-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusMinutes%2Dlong%2D[plusMinutes](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusMinutes%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-plusMonths-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusMonths%2Dlong%2D[plusMonths](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusMonths%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-plusNanos-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusNanos%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-plusSeconds-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusSeconds%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-plusWeeks-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusWeeks%2Dlong%2D[plusWeeks](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusWeeks%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-plusYears-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusYears%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-toLocalDate-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#toLocalDate%2D%2D[toLocalDate]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#toLocalDate%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-toLocalDateTime-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#toLocalDateTime%2D%2D[toLocalDateTime]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#toLocalDateTime%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-toOffsetDateTime-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#toOffsetDateTime%2D%2D[toOffsetDateTime]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#toOffsetDateTime%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-truncatedTo-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[truncatedTo](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-with-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-with-2]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-withDayOfMonth-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withDayOfMonth%2Dint%2D[withDayOfMonth](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withDayOfMonth%2Dint%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-withDayOfYear-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withDayOfYear%2Dint%2D[withDayOfYear](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withDayOfYear%2Dint%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-withEarlierOffsetAtOverlap-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withEarlierOffsetAtOverlap%2D%2D[withEarlierOffsetAtOverlap]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withEarlierOffsetAtOverlap%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-withFixedOffsetZone-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withFixedOffsetZone%2D%2D[withFixedOffsetZone]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withFixedOffsetZone%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-withHour-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withHour%2Dint%2D[withHour](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withHour%2Dint%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-withLaterOffsetAtOverlap-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withLaterOffsetAtOverlap%2D%2D[withLaterOffsetAtOverlap]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withLaterOffsetAtOverlap%2D%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-withMinute-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withMinute%2Dint%2D[withMinute](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withMinute%2Dint%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-withMonth-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withMonth%2Dint%2D[withMonth](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withMonth%2Dint%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-withNano-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withNano%2Dint%2D[withNano](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withNano%2Dint%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-withSecond-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withSecond%2Dint%2D[withSecond](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withSecond%2Dint%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-withYear-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withYear%2Dint%2D[withYear](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withYear%2Dint%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-withZoneSameInstant-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withZoneSameInstant%2Djava.time.ZoneId%2D[withZoneSameInstant](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withZoneSameInstant%2Djava.time.ZoneId%2D[java 9]) -* ++[[painless-api-reference-ZonedDateTime-withZoneSameLocal-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withZoneSameLocal%2Djava.time.ZoneId%2D[withZoneSameLocal](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withZoneSameLocal%2Djava.time.ZoneId%2D[java 9]) -* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/index.asciidoc b/docs/reference/painless-api-reference/index.asciidoc deleted file mode 100644 index 51e6271548410..0000000000000 --- a/docs/reference/painless-api-reference/index.asciidoc +++ /dev/null @@ -1,338 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -include::AbstractChronology.asciidoc[] -include::AbstractCollection.asciidoc[] -include::AbstractList.asciidoc[] -include::AbstractMap.asciidoc[] -include::AbstractMap.SimpleEntry.asciidoc[] -include::AbstractMap.SimpleImmutableEntry.asciidoc[] -include::AbstractQueue.asciidoc[] -include::AbstractSequentialList.asciidoc[] -include::AbstractSet.asciidoc[] -include::Annotation.asciidoc[] -include::Appendable.asciidoc[] -include::ArithmeticException.asciidoc[] -include::ArrayDeque.asciidoc[] -include::ArrayIndexOutOfBoundsException.asciidoc[] -include::ArrayList.asciidoc[] -include::ArrayStoreException.asciidoc[] -include::Arrays.asciidoc[] -include::AttributedCharacterIterator.asciidoc[] -include::AttributedCharacterIterator.Attribute.asciidoc[] -include::AttributedString.asciidoc[] -include::Base64.asciidoc[] -include::Base64.Decoder.asciidoc[] -include::Base64.Encoder.asciidoc[] -include::BaseStream.asciidoc[] -include::BiConsumer.asciidoc[] -include::BiFunction.asciidoc[] -include::BiPredicate.asciidoc[] -include::Bidi.asciidoc[] -include::BigDecimal.asciidoc[] -include::BigInteger.asciidoc[] -include::BinaryOperator.asciidoc[] -include::BitSet.asciidoc[] -include::Boolean.asciidoc[] -include::BooleanSupplier.asciidoc[] -include::BreakIterator.asciidoc[] -include::Byte.asciidoc[] -include::BytesRef.asciidoc[] -include::Calendar.asciidoc[] -include::Calendar.Builder.asciidoc[] -include::CharSequence.asciidoc[] -include::Character.asciidoc[] -include::Character.Subset.asciidoc[] -include::Character.UnicodeBlock.asciidoc[] -include::Character.UnicodeScript.asciidoc[] -include::CharacterIterator.asciidoc[] -include::ChoiceFormat.asciidoc[] -include::ChronoField.asciidoc[] -include::ChronoLocalDate.asciidoc[] -include::ChronoLocalDateTime.asciidoc[] -include::ChronoPeriod.asciidoc[] -include::ChronoUnit.asciidoc[] -include::ChronoZonedDateTime.asciidoc[] -include::Chronology.asciidoc[] -include::ClassCastException.asciidoc[] -include::ClassNotFoundException.asciidoc[] -include::Clock.asciidoc[] -include::CloneNotSupportedException.asciidoc[] -include::CollationElementIterator.asciidoc[] -include::CollationKey.asciidoc[] -include::Collator.asciidoc[] -include::Collection.asciidoc[] -include::Collections.asciidoc[] -include::Collector.asciidoc[] -include::Collector.Characteristics.asciidoc[] -include::Collectors.asciidoc[] -include::Comparable.asciidoc[] -include::Comparator.asciidoc[] -include::ConcurrentModificationException.asciidoc[] -include::Consumer.asciidoc[] -include::Currency.asciidoc[] -include::Date.asciidoc[] -include::DateFormat.asciidoc[] -include::DateFormat.Field.asciidoc[] -include::DateFormatSymbols.asciidoc[] -include::DateTimeException.asciidoc[] -include::DateTimeFormatter.asciidoc[] -include::DateTimeFormatterBuilder.asciidoc[] -include::DateTimeParseException.asciidoc[] -include::DayOfWeek.asciidoc[] -include::Debug.asciidoc[] -include::DecimalFormat.asciidoc[] -include::DecimalFormatSymbols.asciidoc[] -include::DecimalStyle.asciidoc[] -include::Deque.asciidoc[] -include::Dictionary.asciidoc[] -include::Double.asciidoc[] -include::DoubleBinaryOperator.asciidoc[] -include::DoubleConsumer.asciidoc[] -include::DoubleFunction.asciidoc[] -include::DoublePredicate.asciidoc[] -include::DoubleStream.asciidoc[] -include::DoubleStream.Builder.asciidoc[] -include::DoubleSummaryStatistics.asciidoc[] -include::DoubleSupplier.asciidoc[] -include::DoubleToIntFunction.asciidoc[] -include::DoubleToLongFunction.asciidoc[] -include::DoubleUnaryOperator.asciidoc[] -include::DuplicateFormatFlagsException.asciidoc[] -include::Duration.asciidoc[] -include::EmptyStackException.asciidoc[] -include::Enum.asciidoc[] -include::EnumConstantNotPresentException.asciidoc[] -include::Enumeration.asciidoc[] -include::Era.asciidoc[] -include::EventListener.asciidoc[] -include::EventListenerProxy.asciidoc[] -include::EventObject.asciidoc[] -include::Exception.asciidoc[] -include::FieldPosition.asciidoc[] -include::Float.asciidoc[] -include::Format.asciidoc[] -include::Format.Field.asciidoc[] -include::FormatFlagsConversionMismatchException.asciidoc[] -include::FormatStyle.asciidoc[] -include::Formattable.asciidoc[] -include::FormattableFlags.asciidoc[] -include::Formatter.asciidoc[] -include::Formatter.BigDecimalLayoutForm.asciidoc[] -include::FormatterClosedException.asciidoc[] -include::Function.asciidoc[] -include::GregorianCalendar.asciidoc[] -include::HashMap.asciidoc[] -include::HashSet.asciidoc[] -include::Hashtable.asciidoc[] -include::HijrahChronology.asciidoc[] -include::HijrahDate.asciidoc[] -include::HijrahEra.asciidoc[] -include::IdentityHashMap.asciidoc[] -include::IllegalAccessException.asciidoc[] -include::IllegalArgumentException.asciidoc[] -include::IllegalFormatCodePointException.asciidoc[] -include::IllegalFormatConversionException.asciidoc[] -include::IllegalFormatException.asciidoc[] -include::IllegalFormatFlagsException.asciidoc[] -include::IllegalFormatPrecisionException.asciidoc[] -include::IllegalFormatWidthException.asciidoc[] -include::IllegalMonitorStateException.asciidoc[] -include::IllegalStateException.asciidoc[] -include::IllegalThreadStateException.asciidoc[] -include::IllformedLocaleException.asciidoc[] -include::IndexOutOfBoundsException.asciidoc[] -include::InputMismatchException.asciidoc[] -include::Instant.asciidoc[] -include::InstantiationException.asciidoc[] -include::IntBinaryOperator.asciidoc[] -include::IntConsumer.asciidoc[] -include::IntFunction.asciidoc[] -include::IntPredicate.asciidoc[] -include::IntStream.asciidoc[] -include::IntStream.Builder.asciidoc[] -include::IntSummaryStatistics.asciidoc[] -include::IntSupplier.asciidoc[] -include::IntToDoubleFunction.asciidoc[] -include::IntToLongFunction.asciidoc[] -include::IntUnaryOperator.asciidoc[] -include::Integer.asciidoc[] -include::InterruptedException.asciidoc[] -include::IsoChronology.asciidoc[] -include::IsoEra.asciidoc[] -include::IsoFields.asciidoc[] -include::Iterable.asciidoc[] -include::Iterator.asciidoc[] -include::JapaneseChronology.asciidoc[] -include::JapaneseDate.asciidoc[] -include::JapaneseEra.asciidoc[] -include::JulianFields.asciidoc[] -include::LinkedHashMap.asciidoc[] -include::LinkedHashSet.asciidoc[] -include::LinkedList.asciidoc[] -include::List.asciidoc[] -include::ListIterator.asciidoc[] -include::LocalDate.asciidoc[] -include::LocalDateTime.asciidoc[] -include::LocalTime.asciidoc[] -include::Locale.asciidoc[] -include::Locale.Builder.asciidoc[] -include::Locale.Category.asciidoc[] -include::Locale.FilteringMode.asciidoc[] -include::Locale.LanguageRange.asciidoc[] -include::Long.asciidoc[] -include::LongBinaryOperator.asciidoc[] -include::LongConsumer.asciidoc[] -include::LongFunction.asciidoc[] -include::LongPredicate.asciidoc[] -include::LongStream.asciidoc[] -include::LongStream.Builder.asciidoc[] -include::LongSummaryStatistics.asciidoc[] -include::LongSupplier.asciidoc[] -include::LongToDoubleFunction.asciidoc[] -include::LongToIntFunction.asciidoc[] -include::LongUnaryOperator.asciidoc[] -include::Map.asciidoc[] -include::Map.Entry.asciidoc[] -include::Matcher.asciidoc[] -include::Math.asciidoc[] -include::MathContext.asciidoc[] -include::MessageFormat.asciidoc[] -include::MessageFormat.Field.asciidoc[] -include::MinguoChronology.asciidoc[] -include::MinguoDate.asciidoc[] -include::MinguoEra.asciidoc[] -include::MissingFormatArgumentException.asciidoc[] -include::MissingFormatWidthException.asciidoc[] -include::MissingResourceException.asciidoc[] -include::Month.asciidoc[] -include::MonthDay.asciidoc[] -include::NavigableMap.asciidoc[] -include::NavigableSet.asciidoc[] -include::NegativeArraySizeException.asciidoc[] -include::NoSuchElementException.asciidoc[] -include::NoSuchFieldException.asciidoc[] -include::NoSuchMethodException.asciidoc[] -include::Normalizer.asciidoc[] -include::Normalizer.Form.asciidoc[] -include::NullPointerException.asciidoc[] -include::Number.asciidoc[] -include::NumberFormat.asciidoc[] -include::NumberFormat.Field.asciidoc[] -include::NumberFormatException.asciidoc[] -include::ObjDoubleConsumer.asciidoc[] -include::ObjIntConsumer.asciidoc[] -include::ObjLongConsumer.asciidoc[] -include::Object.asciidoc[] -include::Objects.asciidoc[] -include::Observable.asciidoc[] -include::Observer.asciidoc[] -include::OffsetDateTime.asciidoc[] -include::OffsetTime.asciidoc[] -include::Optional.asciidoc[] -include::OptionalDouble.asciidoc[] -include::OptionalInt.asciidoc[] -include::OptionalLong.asciidoc[] -include::ParseException.asciidoc[] -include::ParsePosition.asciidoc[] -include::Pattern.asciidoc[] -include::Period.asciidoc[] -include::Predicate.asciidoc[] -include::PrimitiveIterator.asciidoc[] -include::PrimitiveIterator.OfDouble.asciidoc[] -include::PrimitiveIterator.OfInt.asciidoc[] -include::PrimitiveIterator.OfLong.asciidoc[] -include::PriorityQueue.asciidoc[] -include::Queue.asciidoc[] -include::Random.asciidoc[] -include::RandomAccess.asciidoc[] -include::ReflectiveOperationException.asciidoc[] -include::ResolverStyle.asciidoc[] -include::RoundingMode.asciidoc[] -include::RuleBasedCollator.asciidoc[] -include::RuntimeException.asciidoc[] -include::SecurityException.asciidoc[] -include::Set.asciidoc[] -include::Short.asciidoc[] -include::SignStyle.asciidoc[] -include::SimpleDateFormat.asciidoc[] -include::SimpleTimeZone.asciidoc[] -include::SortedMap.asciidoc[] -include::SortedSet.asciidoc[] -include::Spliterator.asciidoc[] -include::Spliterator.OfDouble.asciidoc[] -include::Spliterator.OfInt.asciidoc[] -include::Spliterator.OfLong.asciidoc[] -include::Spliterator.OfPrimitive.asciidoc[] -include::Spliterators.asciidoc[] -include::Stack.asciidoc[] -include::StackTraceElement.asciidoc[] -include::Stream.asciidoc[] -include::Stream.Builder.asciidoc[] -include::StrictMath.asciidoc[] -include::String.asciidoc[] -include::StringBuffer.asciidoc[] -include::StringBuilder.asciidoc[] -include::StringCharacterIterator.asciidoc[] -include::StringIndexOutOfBoundsException.asciidoc[] -include::StringJoiner.asciidoc[] -include::StringTokenizer.asciidoc[] -include::Supplier.asciidoc[] -include::System.asciidoc[] -include::Temporal.asciidoc[] -include::TemporalAccessor.asciidoc[] -include::TemporalAdjuster.asciidoc[] -include::TemporalAdjusters.asciidoc[] -include::TemporalAmount.asciidoc[] -include::TemporalField.asciidoc[] -include::TemporalQueries.asciidoc[] -include::TemporalQuery.asciidoc[] -include::TemporalUnit.asciidoc[] -include::TextStyle.asciidoc[] -include::ThaiBuddhistChronology.asciidoc[] -include::ThaiBuddhistDate.asciidoc[] -include::ThaiBuddhistEra.asciidoc[] -include::TimeZone.asciidoc[] -include::ToDoubleBiFunction.asciidoc[] -include::ToDoubleFunction.asciidoc[] -include::ToIntBiFunction.asciidoc[] -include::ToIntFunction.asciidoc[] -include::ToLongBiFunction.asciidoc[] -include::ToLongFunction.asciidoc[] -include::TooManyListenersException.asciidoc[] -include::TreeMap.asciidoc[] -include::TreeSet.asciidoc[] -include::TypeNotPresentException.asciidoc[] -include::UUID.asciidoc[] -include::UnaryOperator.asciidoc[] -include::UnknownFormatConversionException.asciidoc[] -include::UnknownFormatFlagsException.asciidoc[] -include::UnsupportedOperationException.asciidoc[] -include::UnsupportedTemporalTypeException.asciidoc[] -include::ValueRange.asciidoc[] -include::Vector.asciidoc[] -include::WeekFields.asciidoc[] -include::Year.asciidoc[] -include::YearMonth.asciidoc[] -include::ZoneId.asciidoc[] -include::ZoneOffset.asciidoc[] -include::ZoneOffsetTransition.asciidoc[] -include::ZoneOffsetTransitionRule.asciidoc[] -include::ZoneOffsetTransitionRule.TimeDefinition.asciidoc[] -include::ZoneRules.asciidoc[] -include::ZoneRulesException.asciidoc[] -include::ZoneRulesProvider.asciidoc[] -include::ZonedDateTime.asciidoc[] -include::org.elasticsearch.common.geo.GeoPoint.asciidoc[] -include::org.elasticsearch.index.fielddata.ScriptDocValues.Booleans.asciidoc[] -include::org.elasticsearch.index.fielddata.ScriptDocValues.BytesRefs.asciidoc[] -include::org.elasticsearch.index.fielddata.ScriptDocValues.Doubles.asciidoc[] -include::org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints.asciidoc[] -include::org.elasticsearch.index.fielddata.ScriptDocValues.Longs.asciidoc[] -include::org.elasticsearch.index.fielddata.ScriptDocValues.Strings.asciidoc[] -include::org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues.asciidoc[] -include::org.elasticsearch.painless.FeatureTest.asciidoc[] -include::org.joda.time.ReadableDateTime.asciidoc[] -include::org.joda.time.ReadableInstant.asciidoc[] diff --git a/docs/reference/painless-api-reference/org.elasticsearch.common.geo.GeoPoint.asciidoc b/docs/reference/painless-api-reference/org.elasticsearch.common.geo.GeoPoint.asciidoc deleted file mode 100644 index 2a593d0ec6314..0000000000000 --- a/docs/reference/painless-api-reference/org.elasticsearch.common.geo.GeoPoint.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-org-elasticsearch-common-geo-GeoPoint]]++org.elasticsearch.common.geo.GeoPoint++:: -* ++[[painless-api-reference-org-elasticsearch-common-geo-GeoPoint-getLat-0]]double link:{elasticsearch-javadoc}/org/elasticsearch/common/geo/GeoPoint.html#getLat%2D%2D[getLat]()++ -* ++[[painless-api-reference-org-elasticsearch-common-geo-GeoPoint-getLon-0]]double link:{elasticsearch-javadoc}/org/elasticsearch/common/geo/GeoPoint.html#getLon%2D%2D[getLon]()++ -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Booleans.asciidoc b/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Booleans.asciidoc deleted file mode 100644 index 12aaed4003d97..0000000000000 --- a/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Booleans.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Booleans]]++org.elasticsearch.index.fielddata.ScriptDocValues.Booleans++:: -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Booleans-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Booleans.html#get%2Dint%2D[get](int)++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Booleans-getValue-0]]boolean link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Booleans.html#getValue%2D%2D[getValue]()++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Booleans-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Booleans.html#getValues%2D%2D[getValues]()++ -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.BytesRefs.asciidoc b/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.BytesRefs.asciidoc deleted file mode 100644 index fe11f96874941..0000000000000 --- a/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.BytesRefs.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-BytesRefs]]++org.elasticsearch.index.fielddata.ScriptDocValues.BytesRefs++:: -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-BytesRefs-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$BytesRefs.html#get%2Dint%2D[get](int)++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-BytesRefs-getValue-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$BytesRefs.html#getValue%2D%2D[getValue]()++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-BytesRefs-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$BytesRefs.html#getValues%2D%2D[getValues]()++ -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Doubles.asciidoc b/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Doubles.asciidoc deleted file mode 100644 index 92d4307241483..0000000000000 --- a/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Doubles.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Doubles]]++org.elasticsearch.index.fielddata.ScriptDocValues.Doubles++:: -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Doubles-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Doubles.html#get%2Dint%2D[get](int)++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Doubles-getValue-0]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Doubles.html#getValue%2D%2D[getValue]()++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Doubles-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Doubles.html#getValues%2D%2D[getValues]()++ -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints.asciidoc b/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints.asciidoc deleted file mode 100644 index e55afb2559430..0000000000000 --- a/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints.asciidoc +++ /dev/null @@ -1,20 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints]]++org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints++:: -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-arcDistance-2]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#arcDistance%2Ddouble%2Ddouble%2D[arcDistance](double, double)++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-arcDistanceWithDefault-3]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#arcDistanceWithDefault%2Ddouble%2Ddouble%2Ddouble%2D[arcDistanceWithDefault](double, double, double)++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-geohashDistance-1]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#geohashDistance%2Djava.lang.String%2D[geohashDistance](<>)++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-geohashDistanceWithDefault-2]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#geohashDistanceWithDefault%2Djava.lang.String%2Ddouble%2D[geohashDistanceWithDefault](<>, double)++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#get%2Dint%2D[get](int)++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-getLat-0]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#getLat%2D%2D[getLat]()++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-getLats-0]]double[] link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#getLats%2D%2D[getLats]()++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-getLon-0]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#getLon%2D%2D[getLon]()++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-getLons-0]]double[] link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#getLons%2D%2D[getLons]()++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-getValue-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#getValue%2D%2D[getValue]()++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#getValues%2D%2D[getValues]()++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-planeDistance-2]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#planeDistance%2Ddouble%2Ddouble%2D[planeDistance](double, double)++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-planeDistanceWithDefault-3]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#planeDistanceWithDefault%2Ddouble%2Ddouble%2Ddouble%2D[planeDistanceWithDefault](double, double, double)++ -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Longs.asciidoc b/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Longs.asciidoc deleted file mode 100644 index 85e6e9289ef5a..0000000000000 --- a/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Longs.asciidoc +++ /dev/null @@ -1,12 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Longs]]++org.elasticsearch.index.fielddata.ScriptDocValues.Longs++:: -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Longs-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Longs.html#get%2Dint%2D[get](int)++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Longs-getDate-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Longs.html#getDate%2D%2D[getDate]()++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Longs-getDates-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Longs.html#getDates%2D%2D[getDates]()++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Longs-getValue-0]]long link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Longs.html#getValue%2D%2D[getValue]()++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Longs-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Longs.html#getValues%2D%2D[getValues]()++ -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Strings.asciidoc b/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Strings.asciidoc deleted file mode 100644 index c15c152cd65ba..0000000000000 --- a/docs/reference/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Strings.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Strings]]++org.elasticsearch.index.fielddata.ScriptDocValues.Strings++:: -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Strings-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Strings.html#get%2Dint%2D[get](int)++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Strings-getValue-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Strings.html#getValue%2D%2D[getValue]()++ -* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Strings-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Strings.html#getValues%2D%2D[getValues]()++ -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues.asciidoc b/docs/reference/painless-api-reference/org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues.asciidoc deleted file mode 100644 index e6ce5d5a57a27..0000000000000 --- a/docs/reference/painless-api-reference/org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues.asciidoc +++ /dev/null @@ -1,10 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-org-elasticsearch-index-mapper-IpFieldMapper-IpFieldType-IpScriptDocValues]]++org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues++:: -* ++[[painless-api-reference-org-elasticsearch-index-mapper-IpFieldMapper-IpFieldType-IpScriptDocValues-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/mapper/IpFieldMapper$IpFieldType$IpScriptDocValues.html#get%2Dint%2D[get](int)++ -* ++[[painless-api-reference-org-elasticsearch-index-mapper-IpFieldMapper-IpFieldType-IpScriptDocValues-getValue-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/mapper/IpFieldMapper$IpFieldType$IpScriptDocValues.html#getValue%2D%2D[getValue]()++ -* ++[[painless-api-reference-org-elasticsearch-index-mapper-IpFieldMapper-IpFieldType-IpScriptDocValues-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/mapper/IpFieldMapper$IpFieldType$IpScriptDocValues.html#getValues%2D%2D[getValues]()++ -* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/org.elasticsearch.painless.FeatureTest.asciidoc b/docs/reference/painless-api-reference/org.elasticsearch.painless.FeatureTest.asciidoc deleted file mode 100644 index 40fdec50793a9..0000000000000 --- a/docs/reference/painless-api-reference/org.elasticsearch.painless.FeatureTest.asciidoc +++ /dev/null @@ -1,16 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-org-elasticsearch-painless-FeatureTest]]++org.elasticsearch.painless.FeatureTest++:: -* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-overloadedStatic-0]]static boolean link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#overloadedStatic%2D%2D[overloadedStatic]()++ -* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-overloadedStatic-1]]static boolean link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#overloadedStatic%2Dboolean%2D[overloadedStatic](boolean)++ -* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-org.elasticsearch.painless.FeatureTest-0]]link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#org.elasticsearch.painless.FeatureTest%2D%2D[org.elasticsearch.painless.FeatureTest]()++ -* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-org.elasticsearch.painless.FeatureTest-2]]link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#org.elasticsearch.painless.FeatureTest%2Dint%2Dint%2D[org.elasticsearch.painless.FeatureTest](int, int)++ -* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-getX-0]]int link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#getX%2D%2D[getX]()++ -* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-getY-0]]int link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#getY%2D%2D[getY]()++ -* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-setX-1]]void link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#setX%2Dint%2D[setX](int)++ -* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-setY-1]]void link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#setY%2Dint%2D[setY](int)++ -* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-twoFunctionsOfX-2]]<> link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#twoFunctionsOfX%2Djava.util.function.Function%2Djava.util.function.Function%2D[twoFunctionsOfX](<>, <>)++ -* Inherits methods from ++<>++ diff --git a/docs/reference/painless-api-reference/org.joda.time.ReadableDateTime.asciidoc b/docs/reference/painless-api-reference/org.joda.time.ReadableDateTime.asciidoc deleted file mode 100644 index a7a0a17c1adb8..0000000000000 --- a/docs/reference/painless-api-reference/org.joda.time.ReadableDateTime.asciidoc +++ /dev/null @@ -1,27 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-org-joda-time-ReadableDateTime]]++org.joda.time.ReadableDateTime++:: -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getCenturyOfEra-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getCenturyOfEra%2D%2D[getCenturyOfEra]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getDayOfMonth-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getDayOfMonth%2D%2D[getDayOfMonth]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getDayOfWeek-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getDayOfWeek%2D%2D[getDayOfWeek]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getDayOfYear-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getDayOfYear%2D%2D[getDayOfYear]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getEra-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getEra%2D%2D[getEra]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getHourOfDay-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getHourOfDay%2D%2D[getHourOfDay]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getMillisOfDay-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getMillisOfDay%2D%2D[getMillisOfDay]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getMillisOfSecond-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getMillisOfSecond%2D%2D[getMillisOfSecond]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getMinuteOfDay-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getMinuteOfDay%2D%2D[getMinuteOfDay]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getMinuteOfHour-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getMinuteOfHour%2D%2D[getMinuteOfHour]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getMonthOfYear-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getMonthOfYear%2D%2D[getMonthOfYear]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getSecondOfDay-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getSecondOfDay%2D%2D[getSecondOfDay]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getSecondOfMinute-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getSecondOfMinute%2D%2D[getSecondOfMinute]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getWeekOfWeekyear-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getWeekOfWeekyear%2D%2D[getWeekOfWeekyear]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getWeekyear-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getWeekyear%2D%2D[getWeekyear]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getYear-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getYear%2D%2D[getYear]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getYearOfCentury-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getYearOfCentury%2D%2D[getYearOfCentury]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getYearOfEra-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getYearOfEra%2D%2D[getYearOfEra]()++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-toString-1]]<> link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#toString%2Djava.lang.String%2D[toString](<>)++ -* ++[[painless-api-reference-org-joda-time-ReadableDateTime-toString-2]]<> link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#toString%2Djava.lang.String%2Djava.util.Locale%2D[toString](<>, <>)++ -* Inherits methods from ++<>++, ++<>++ diff --git a/docs/reference/painless-api-reference/org.joda.time.ReadableInstant.asciidoc b/docs/reference/painless-api-reference/org.joda.time.ReadableInstant.asciidoc deleted file mode 100644 index 96d1f61a81f55..0000000000000 --- a/docs/reference/painless-api-reference/org.joda.time.ReadableInstant.asciidoc +++ /dev/null @@ -1,14 +0,0 @@ -//// -Automatically generated by PainlessDocGenerator. Do not edit. -Rebuild by running `gradle generatePainlessApi`. -//// - -[[painless-api-reference-org-joda-time-ReadableInstant]]++org.joda.time.ReadableInstant++:: -* ++[[painless-api-reference-org-joda-time-ReadableInstant-equals-1]]boolean link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#equals%2Djava.lang.Object%2D[equals](<>)++ -* ++[[painless-api-reference-org-joda-time-ReadableInstant-getMillis-0]]long link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#getMillis%2D%2D[getMillis]()++ -* ++[[painless-api-reference-org-joda-time-ReadableInstant-hashCode-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#hashCode%2D%2D[hashCode]()++ -* ++[[painless-api-reference-org-joda-time-ReadableInstant-isAfter-1]]boolean link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#isAfter%2Dorg.joda.time.ReadableInstant%2D[isAfter](<>)++ -* ++[[painless-api-reference-org-joda-time-ReadableInstant-isBefore-1]]boolean link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#isBefore%2Dorg.joda.time.ReadableInstant%2D[isBefore](<>)++ -* ++[[painless-api-reference-org-joda-time-ReadableInstant-isEqual-1]]boolean link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#isEqual%2Dorg.joda.time.ReadableInstant%2D[isEqual](<>)++ -* ++[[painless-api-reference-org-joda-time-ReadableInstant-toString-0]]<> link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#toString%2D%2D[toString]()++ -* Inherits methods from ++<>++ diff --git a/docs/reference/redirects.asciidoc b/docs/reference/redirects.asciidoc index 0644b5498128b..2ee6bc7b88a0b 100644 --- a/docs/reference/redirects.asciidoc +++ b/docs/reference/redirects.asciidoc @@ -473,3 +473,22 @@ Native scripts have been replaced with writing custom `ScriptEngine` backends (s === Advanced scripting Using `_index` in scripts has been replaced with writing `ScriptEngine` backends (see <>). + +[role="exclude",id="modules-scripting-painless-syntax"] +=== Painless Syntax + +See the +{painless-ref}/painless-language-spec.html[Painless Language Specification] +in the guide to the {painless-ref}/index.html[Painless Scripting Language]. + +[role="exclude",id="modules-scripting-painless-debugging"] +=== Painless Debugging + +See {painless-ref}/painless-debugging.html[Painless Debugging] in the +guide to the {painless-ref}/index.html[Painless Scripting Language]. + +[role="exclude",id="painless-api-reference"] +=== Painless API Reference + +See the {painless-ref}/painless-api-reference.html[Painless API Reference] in +the guide to the {painless-ref}/index.html[Painless Scripting Language]. \ No newline at end of file From 5ac2ddd2beb859d41266d92cbef63ea524ddc7b9 Mon Sep 17 00:00:00 2001 From: debadair Date: Fri, 12 May 2017 16:17:06 -0700 Subject: [PATCH 382/619] [DOCS] Setting up separate Painless book. --- docs/painless/index.asciidoc | 45 + docs/painless/painless-api-reference.asciidoc | 17 + .../AbstractChronology.asciidoc | 7 + .../AbstractCollection.asciidoc | 7 + .../AbstractList.asciidoc | 7 + .../AbstractMap.SimpleEntry.asciidoc | 9 + .../AbstractMap.SimpleImmutableEntry.asciidoc | 9 + .../AbstractMap.asciidoc | 7 + .../AbstractQueue.asciidoc | 7 + .../AbstractSequentialList.asciidoc | 7 + .../AbstractSet.asciidoc | 7 + .../Annotation.asciidoc | 9 + .../Appendable.asciidoc | 8 + .../ArithmeticException.asciidoc | 9 + .../ArrayDeque.asciidoc | 10 + .../ArrayIndexOutOfBoundsException.asciidoc | 9 + .../painless-api-reference/ArrayList.asciidoc | 11 + .../ArrayStoreException.asciidoc | 9 + .../painless-api-reference/Arrays.asciidoc | 11 + ...ibutedCharacterIterator.Attribute.asciidoc | 10 + .../AttributedCharacterIterator.asciidoc | 14 + .../AttributedString.asciidoc | 15 + .../Base64.Decoder.asciidoc | 9 + .../Base64.Encoder.asciidoc | 10 + .../painless-api-reference/Base64.asciidoc | 14 + .../BaseStream.asciidoc | 13 + .../BiConsumer.asciidoc | 9 + .../BiFunction.asciidoc | 9 + .../BiPredicate.asciidoc | 11 + .../painless-api-reference/Bidi.asciidoc | 28 + .../BigDecimal.asciidoc | 57 + .../BigInteger.asciidoc | 50 + .../BinaryOperator.asciidoc | 9 + .../painless-api-reference/BitSet.asciidoc | 34 + .../painless-api-reference/Boolean.asciidoc | 19 + .../BooleanSupplier.asciidoc | 8 + .../BreakIterator.asciidoc | 29 + .../painless-api-reference/Byte.asciidoc | 22 + .../painless-api-reference/BytesRef.asciidoc | 12 + .../Calendar.Builder.asciidoc | 21 + .../painless-api-reference/Calendar.asciidoc | 102 + .../CharSequence.asciidoc | 15 + .../Character.Subset.asciidoc | 7 + .../Character.UnicodeBlock.asciidoc | 229 +++ .../Character.UnicodeScript.asciidoc | 114 ++ .../painless-api-reference/Character.asciidoc | 125 ++ .../CharacterIterator.asciidoc | 18 + .../ChoiceFormat.asciidoc | 17 + .../ChronoField.asciidoc | 41 + .../ChronoLocalDate.asciidoc | 31 + .../ChronoLocalDateTime.asciidoc | 29 + .../ChronoPeriod.asciidoc | 20 + .../ChronoUnit.asciidoc | 25 + .../ChronoZonedDateTime.asciidoc | 35 + .../Chronology.asciidoc | 33 + .../ClassCastException.asciidoc | 9 + .../ClassNotFoundException.asciidoc | 9 + .../painless-api-reference/Clock.asciidoc | 13 + .../CloneNotSupportedException.asciidoc | 9 + .../CollationElementIterator.asciidoc | 18 + .../CollationKey.asciidoc | 10 + .../painless-api-reference/Collator.asciidoc | 24 + .../Collection.asciidoc | 28 + .../Collections.asciidoc | 59 + .../Collector.Characteristics.asciidoc | 12 + .../painless-api-reference/Collector.asciidoc | 14 + .../Collectors.asciidoc | 38 + .../Comparable.asciidoc | 8 + .../Comparator.asciidoc | 24 + .../ConcurrentModificationException.asciidoc | 9 + .../painless-api-reference/Consumer.asciidoc | 9 + .../painless-api-reference/Currency.asciidoc | 16 + .../painless-api-reference/Date.asciidoc | 16 + .../DateFormat.Field.asciidoc | 27 + .../DateFormat.asciidoc | 51 + .../DateFormatSymbols.asciidoc | 30 + .../DateTimeException.asciidoc | 8 + .../DateTimeFormatter.asciidoc | 50 + .../DateTimeFormatterBuilder.asciidoc | 44 + .../DateTimeParseException.asciidoc | 10 + .../painless-api-reference/DayOfWeek.asciidoc | 22 + .../painless-api-reference/Debug.asciidoc | 8 + .../DecimalFormat.asciidoc | 32 + .../DecimalFormatSymbols.asciidoc | 43 + .../DecimalStyle.asciidoc | 19 + .../painless-api-reference/Deque.asciidoc | 25 + .../Dictionary.asciidoc | 14 + .../painless-api-reference/Double.asciidoc | 35 + .../DoubleBinaryOperator.asciidoc | 8 + .../DoubleConsumer.asciidoc | 9 + .../DoubleFunction.asciidoc | 8 + .../DoublePredicate.asciidoc | 11 + .../DoubleStream.Builder.asciidoc | 9 + .../DoubleStream.asciidoc | 43 + .../DoubleSummaryStatistics.asciidoc | 14 + .../DoubleSupplier.asciidoc | 8 + .../DoubleToIntFunction.asciidoc | 8 + .../DoubleToLongFunction.asciidoc | 8 + .../DoubleUnaryOperator.asciidoc | 11 + .../DuplicateFormatFlagsException.asciidoc | 9 + .../painless-api-reference/Duration.asciidoc | 51 + .../EmptyStackException.asciidoc | 8 + .../painless-api-reference/Enum.asciidoc | 10 + .../EnumConstantNotPresentException.asciidoc | 8 + .../Enumeration.asciidoc | 9 + .../painless-api-reference/Era.asciidoc | 9 + .../EventListener.asciidoc | 7 + .../EventListenerProxy.asciidoc | 8 + .../EventObject.asciidoc | 9 + .../painless-api-reference/Exception.asciidoc | 12 + .../FieldPosition.asciidoc | 15 + .../painless-api-reference/Float.asciidoc | 35 + .../Format.Field.asciidoc | 7 + .../painless-api-reference/Format.asciidoc | 13 + ...tFlagsConversionMismatchException.asciidoc | 10 + .../FormatStyle.asciidoc | 13 + .../Formattable.asciidoc | 8 + .../FormattableFlags.asciidoc | 10 + .../Formatter.BigDecimalLayoutForm.asciidoc | 9 + .../painless-api-reference/Formatter.asciidoc | 14 + .../FormatterClosedException.asciidoc | 8 + .../painless-api-reference/Function.asciidoc | 11 + .../GregorianCalendar.asciidoc | 20 + .../painless-api-reference/HashMap.asciidoc | 10 + .../painless-api-reference/HashSet.asciidoc | 10 + .../painless-api-reference/Hashtable.asciidoc | 10 + .../HijrahChronology.asciidoc | 16 + .../HijrahDate.asciidoc | 18 + .../painless-api-reference/HijrahEra.asciidoc | 12 + .../IdentityHashMap.asciidoc | 10 + .../IllegalAccessException.asciidoc | 9 + .../IllegalArgumentException.asciidoc | 9 + .../IllegalFormatCodePointException.asciidoc | 9 + .../IllegalFormatConversionException.asciidoc | 8 + .../IllegalFormatException.asciidoc | 7 + .../IllegalFormatFlagsException.asciidoc | 9 + .../IllegalFormatPrecisionException.asciidoc | 9 + .../IllegalFormatWidthException.asciidoc | 9 + .../IllegalMonitorStateException.asciidoc | 9 + .../IllegalStateException.asciidoc | 9 + .../IllegalThreadStateException.asciidoc | 9 + .../IllformedLocaleException.asciidoc | 11 + .../IndexOutOfBoundsException.asciidoc | 9 + .../InputMismatchException.asciidoc | 9 + .../painless-api-reference/Instant.asciidoc | 36 + .../InstantiationException.asciidoc | 9 + .../IntBinaryOperator.asciidoc | 8 + .../IntConsumer.asciidoc | 9 + .../IntFunction.asciidoc | 8 + .../IntPredicate.asciidoc | 11 + .../IntStream.Builder.asciidoc | 9 + .../painless-api-reference/IntStream.asciidoc | 47 + .../IntSummaryStatistics.asciidoc | 14 + .../IntSupplier.asciidoc | 8 + .../IntToDoubleFunction.asciidoc | 8 + .../IntToLongFunction.asciidoc | 8 + .../IntUnaryOperator.asciidoc | 11 + .../painless-api-reference/Integer.asciidoc | 44 + .../InterruptedException.asciidoc | 9 + .../IsoChronology.asciidoc | 20 + .../painless-api-reference/IsoEra.asciidoc | 13 + .../painless-api-reference/IsoFields.asciidoc | 13 + .../painless-api-reference/Iterable.asciidoc | 21 + .../painless-api-reference/Iterator.asciidoc | 11 + .../JapaneseChronology.asciidoc | 16 + .../JapaneseDate.asciidoc | 17 + .../JapaneseEra.asciidoc | 15 + .../JulianFields.asciidoc | 10 + .../LinkedHashMap.asciidoc | 9 + .../LinkedHashSet.asciidoc | 9 + .../LinkedList.asciidoc | 10 + .../painless-api-reference/List.asciidoc | 22 + .../ListIterator.asciidoc | 12 + .../painless-api-reference/LocalDate.asciidoc | 47 + .../LocalDateTime.asciidoc | 61 + .../painless-api-reference/LocalTime.asciidoc | 50 + .../Locale.Builder.asciidoc | 21 + .../Locale.Category.asciidoc | 11 + .../Locale.FilteringMode.asciidoc | 14 + .../Locale.LanguageRange.asciidoc | 16 + .../painless-api-reference/Locale.asciidoc | 69 + .../painless-api-reference/Long.asciidoc | 44 + .../LongBinaryOperator.asciidoc | 8 + .../LongConsumer.asciidoc | 9 + .../LongFunction.asciidoc | 8 + .../LongPredicate.asciidoc | 11 + .../LongStream.Builder.asciidoc | 9 + .../LongStream.asciidoc | 46 + .../LongSummaryStatistics.asciidoc | 14 + .../LongSupplier.asciidoc | 8 + .../LongToDoubleFunction.asciidoc | 8 + .../LongToIntFunction.asciidoc | 8 + .../LongUnaryOperator.asciidoc | 11 + .../painless-api-reference/Map.Entry.asciidoc | 16 + .../painless-api-reference/Map.asciidoc | 42 + .../painless-api-reference/Matcher.asciidoc | 34 + .../painless-api-reference/Math.asciidoc | 46 + .../MathContext.asciidoc | 15 + .../MessageFormat.Field.asciidoc | 8 + .../MessageFormat.asciidoc | 20 + .../MinguoChronology.asciidoc | 16 + .../MinguoDate.asciidoc | 17 + .../painless-api-reference/MinguoEra.asciidoc | 13 + .../MissingFormatArgumentException.asciidoc | 9 + .../MissingFormatWidthException.asciidoc | 9 + .../MissingResourceException.asciidoc | 10 + .../painless-api-reference/Month.asciidoc | 32 + .../painless-api-reference/MonthDay.asciidoc | 23 + .../NavigableMap.asciidoc | 24 + .../NavigableSet.asciidoc | 18 + .../NegativeArraySizeException.asciidoc | 9 + .../NoSuchElementException.asciidoc | 9 + .../NoSuchFieldException.asciidoc | 9 + .../NoSuchMethodException.asciidoc | 9 + .../Normalizer.Form.asciidoc | 13 + .../Normalizer.asciidoc | 9 + .../NullPointerException.asciidoc | 9 + .../painless-api-reference/Number.asciidoc | 13 + .../NumberFormat.Field.asciidoc | 18 + .../NumberFormat.asciidoc | 38 + .../NumberFormatException.asciidoc | 9 + .../ObjDoubleConsumer.asciidoc | 8 + .../ObjIntConsumer.asciidoc | 8 + .../ObjLongConsumer.asciidoc | 8 + .../painless-api-reference/Object.asciidoc | 9 + .../painless-api-reference/Objects.asciidoc | 18 + .../Observable.asciidoc | 15 + .../painless-api-reference/Observer.asciidoc | 8 + .../OffsetDateTime.asciidoc | 75 + .../OffsetTime.asciidoc | 47 + .../painless-api-reference/Optional.asciidoc | 19 + .../OptionalDouble.asciidoc | 15 + .../OptionalInt.asciidoc | 15 + .../OptionalLong.asciidoc | 15 + .../ParseException.asciidoc | 9 + .../ParsePosition.asciidoc | 12 + .../painless-api-reference/Pattern.asciidoc | 15 + .../painless-api-reference/Period.asciidoc | 35 + .../painless-api-reference/Predicate.asciidoc | 12 + .../PrimitiveIterator.OfDouble.asciidoc | 9 + .../PrimitiveIterator.OfInt.asciidoc | 9 + .../PrimitiveIterator.OfLong.asciidoc | 9 + .../PrimitiveIterator.asciidoc | 8 + .../PriorityQueue.asciidoc | 9 + .../painless-api-reference/Queue.asciidoc | 12 + .../painless-api-reference/Random.asciidoc | 24 + .../RandomAccess.asciidoc | 7 + .../ReflectiveOperationException.asciidoc | 9 + .../ResolverStyle.asciidoc | 12 + .../RoundingMode.asciidoc | 17 + .../RuleBasedCollator.asciidoc | 10 + .../RuntimeException.asciidoc | 9 + .../SecurityException.asciidoc | 9 + .../painless-api-reference/Set.asciidoc | 10 + .../painless-api-reference/Short.asciidoc | 23 + .../painless-api-reference/SignStyle.asciidoc | 14 + .../SimpleDateFormat.asciidoc | 18 + .../SimpleTimeZone.asciidoc | 23 + .../painless-api-reference/SortedMap.asciidoc | 13 + .../painless-api-reference/SortedSet.asciidoc | 13 + .../Spliterator.OfDouble.asciidoc | 8 + .../Spliterator.OfInt.asciidoc | 8 + .../Spliterator.OfLong.asciidoc | 8 + .../Spliterator.OfPrimitive.asciidoc | 10 + .../Spliterator.asciidoc | 23 + .../Spliterators.asciidoc | 15 + .../painless-api-reference/Stack.asciidoc | 13 + .../StackTraceElement.asciidoc | 13 + .../Stream.Builder.asciidoc | 9 + .../painless-api-reference/Stream.asciidoc | 43 + .../StrictMath.asciidoc | 46 + .../painless-api-reference/String.asciidoc | 46 + .../StringBuffer.asciidoc | 31 + .../StringBuilder.asciidoc | 31 + .../StringCharacterIterator.asciidoc | 11 + .../StringIndexOutOfBoundsException.asciidoc | 9 + .../StringJoiner.asciidoc | 13 + .../StringTokenizer.asciidoc | 14 + .../painless-api-reference/Supplier.asciidoc | 8 + .../painless-api-reference/System.asciidoc | 10 + .../painless-api-reference/Temporal.asciidoc | 14 + .../TemporalAccessor.asciidoc | 12 + .../TemporalAdjuster.asciidoc | 8 + .../TemporalAdjusters.asciidoc | 21 + .../TemporalAmount.asciidoc | 11 + .../TemporalField.asciidoc | 19 + .../TemporalQueries.asciidoc | 14 + .../TemporalQuery.asciidoc | 8 + .../TemporalUnit.asciidoc | 15 + .../painless-api-reference/TextStyle.asciidoc | 18 + .../ThaiBuddhistChronology.asciidoc | 16 + .../ThaiBuddhistDate.asciidoc | 17 + .../ThaiBuddhistEra.asciidoc | 13 + .../painless-api-reference/TimeZone.asciidoc | 29 + .../ToDoubleBiFunction.asciidoc | 8 + .../ToDoubleFunction.asciidoc | 8 + .../ToIntBiFunction.asciidoc | 8 + .../ToIntFunction.asciidoc | 8 + .../ToLongBiFunction.asciidoc | 8 + .../ToLongFunction.asciidoc | 8 + .../TooManyListenersException.asciidoc | 9 + .../painless-api-reference/TreeMap.asciidoc | 10 + .../painless-api-reference/TreeSet.asciidoc | 10 + .../TypeNotPresentException.asciidoc | 8 + .../painless-api-reference/UUID.asciidoc | 18 + .../UnaryOperator.asciidoc | 8 + .../UnknownFormatConversionException.asciidoc | 9 + .../UnknownFormatFlagsException.asciidoc | 9 + .../UnsupportedOperationException.asciidoc | 9 + .../UnsupportedTemporalTypeException.asciidoc | 8 + .../ValueRange.asciidoc | 20 + .../painless-api-reference/Vector.asciidoc | 22 + .../WeekFields.asciidoc | 19 + .../painless-api-reference/Year.asciidoc | 32 + .../painless-api-reference/YearMonth.asciidoc | 36 + .../painless-api-reference/ZoneId.asciidoc | 18 + .../ZoneOffset.asciidoc | 17 + .../ZoneOffsetTransition.asciidoc | 19 + ...fsetTransitionRule.TimeDefinition.asciidoc | 13 + .../ZoneOffsetTransitionRule.asciidoc | 18 + .../painless-api-reference/ZoneRules.asciidoc | 21 + .../ZoneRulesException.asciidoc | 8 + .../ZoneRulesProvider.asciidoc | 10 + .../ZonedDateTime.asciidoc | 66 + .../painless-api-reference/index.asciidoc | 338 ++++ ...elasticsearch.common.geo.GeoPoint.asciidoc | 9 + ...ielddata.ScriptDocValues.Booleans.asciidoc | 10 + ...elddata.ScriptDocValues.BytesRefs.asciidoc | 10 + ...fielddata.ScriptDocValues.Doubles.asciidoc | 10 + ...elddata.ScriptDocValues.GeoPoints.asciidoc | 20 + ...x.fielddata.ScriptDocValues.Longs.asciidoc | 12 + ...fielddata.ScriptDocValues.Strings.asciidoc | 10 + ...per.IpFieldType.IpScriptDocValues.asciidoc | 10 + ...lasticsearch.painless.FeatureTest.asciidoc | 16 + .../org.joda.time.ReadableDateTime.asciidoc | 27 + .../org.joda.time.ReadableInstant.asciidoc | 14 + docs/painless/painless-debugging.asciidoc | 96 + docs/painless/painless-description.asciidoc | 24 + .../painless-getting-started.asciidoc | 362 ++++ docs/painless/painless-lang-spec.asciidoc | 73 + docs/painless/painless-literals.asciidoc | 94 + docs/painless/painless-operators.asciidoc | 1774 +++++++++++++++++ docs/painless/painless-syntax.asciidoc | 119 ++ docs/painless/painless-types.asciidoc | 442 ++++ docs/painless/painless-variables.asciidoc | 123 ++ docs/painless/painless-xref.asciidoc | 2 + 346 files changed, 9514 insertions(+) create mode 100644 docs/painless/index.asciidoc create mode 100644 docs/painless/painless-api-reference.asciidoc create mode 100644 docs/painless/painless-api-reference/AbstractChronology.asciidoc create mode 100644 docs/painless/painless-api-reference/AbstractCollection.asciidoc create mode 100644 docs/painless/painless-api-reference/AbstractList.asciidoc create mode 100644 docs/painless/painless-api-reference/AbstractMap.SimpleEntry.asciidoc create mode 100644 docs/painless/painless-api-reference/AbstractMap.SimpleImmutableEntry.asciidoc create mode 100644 docs/painless/painless-api-reference/AbstractMap.asciidoc create mode 100644 docs/painless/painless-api-reference/AbstractQueue.asciidoc create mode 100644 docs/painless/painless-api-reference/AbstractSequentialList.asciidoc create mode 100644 docs/painless/painless-api-reference/AbstractSet.asciidoc create mode 100644 docs/painless/painless-api-reference/Annotation.asciidoc create mode 100644 docs/painless/painless-api-reference/Appendable.asciidoc create mode 100644 docs/painless/painless-api-reference/ArithmeticException.asciidoc create mode 100644 docs/painless/painless-api-reference/ArrayDeque.asciidoc create mode 100644 docs/painless/painless-api-reference/ArrayIndexOutOfBoundsException.asciidoc create mode 100644 docs/painless/painless-api-reference/ArrayList.asciidoc create mode 100644 docs/painless/painless-api-reference/ArrayStoreException.asciidoc create mode 100644 docs/painless/painless-api-reference/Arrays.asciidoc create mode 100644 docs/painless/painless-api-reference/AttributedCharacterIterator.Attribute.asciidoc create mode 100644 docs/painless/painless-api-reference/AttributedCharacterIterator.asciidoc create mode 100644 docs/painless/painless-api-reference/AttributedString.asciidoc create mode 100644 docs/painless/painless-api-reference/Base64.Decoder.asciidoc create mode 100644 docs/painless/painless-api-reference/Base64.Encoder.asciidoc create mode 100644 docs/painless/painless-api-reference/Base64.asciidoc create mode 100644 docs/painless/painless-api-reference/BaseStream.asciidoc create mode 100644 docs/painless/painless-api-reference/BiConsumer.asciidoc create mode 100644 docs/painless/painless-api-reference/BiFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/BiPredicate.asciidoc create mode 100644 docs/painless/painless-api-reference/Bidi.asciidoc create mode 100644 docs/painless/painless-api-reference/BigDecimal.asciidoc create mode 100644 docs/painless/painless-api-reference/BigInteger.asciidoc create mode 100644 docs/painless/painless-api-reference/BinaryOperator.asciidoc create mode 100644 docs/painless/painless-api-reference/BitSet.asciidoc create mode 100644 docs/painless/painless-api-reference/Boolean.asciidoc create mode 100644 docs/painless/painless-api-reference/BooleanSupplier.asciidoc create mode 100644 docs/painless/painless-api-reference/BreakIterator.asciidoc create mode 100644 docs/painless/painless-api-reference/Byte.asciidoc create mode 100644 docs/painless/painless-api-reference/BytesRef.asciidoc create mode 100644 docs/painless/painless-api-reference/Calendar.Builder.asciidoc create mode 100644 docs/painless/painless-api-reference/Calendar.asciidoc create mode 100644 docs/painless/painless-api-reference/CharSequence.asciidoc create mode 100644 docs/painless/painless-api-reference/Character.Subset.asciidoc create mode 100644 docs/painless/painless-api-reference/Character.UnicodeBlock.asciidoc create mode 100644 docs/painless/painless-api-reference/Character.UnicodeScript.asciidoc create mode 100644 docs/painless/painless-api-reference/Character.asciidoc create mode 100644 docs/painless/painless-api-reference/CharacterIterator.asciidoc create mode 100644 docs/painless/painless-api-reference/ChoiceFormat.asciidoc create mode 100644 docs/painless/painless-api-reference/ChronoField.asciidoc create mode 100644 docs/painless/painless-api-reference/ChronoLocalDate.asciidoc create mode 100644 docs/painless/painless-api-reference/ChronoLocalDateTime.asciidoc create mode 100644 docs/painless/painless-api-reference/ChronoPeriod.asciidoc create mode 100644 docs/painless/painless-api-reference/ChronoUnit.asciidoc create mode 100644 docs/painless/painless-api-reference/ChronoZonedDateTime.asciidoc create mode 100644 docs/painless/painless-api-reference/Chronology.asciidoc create mode 100644 docs/painless/painless-api-reference/ClassCastException.asciidoc create mode 100644 docs/painless/painless-api-reference/ClassNotFoundException.asciidoc create mode 100644 docs/painless/painless-api-reference/Clock.asciidoc create mode 100644 docs/painless/painless-api-reference/CloneNotSupportedException.asciidoc create mode 100644 docs/painless/painless-api-reference/CollationElementIterator.asciidoc create mode 100644 docs/painless/painless-api-reference/CollationKey.asciidoc create mode 100644 docs/painless/painless-api-reference/Collator.asciidoc create mode 100644 docs/painless/painless-api-reference/Collection.asciidoc create mode 100644 docs/painless/painless-api-reference/Collections.asciidoc create mode 100644 docs/painless/painless-api-reference/Collector.Characteristics.asciidoc create mode 100644 docs/painless/painless-api-reference/Collector.asciidoc create mode 100644 docs/painless/painless-api-reference/Collectors.asciidoc create mode 100644 docs/painless/painless-api-reference/Comparable.asciidoc create mode 100644 docs/painless/painless-api-reference/Comparator.asciidoc create mode 100644 docs/painless/painless-api-reference/ConcurrentModificationException.asciidoc create mode 100644 docs/painless/painless-api-reference/Consumer.asciidoc create mode 100644 docs/painless/painless-api-reference/Currency.asciidoc create mode 100644 docs/painless/painless-api-reference/Date.asciidoc create mode 100644 docs/painless/painless-api-reference/DateFormat.Field.asciidoc create mode 100644 docs/painless/painless-api-reference/DateFormat.asciidoc create mode 100644 docs/painless/painless-api-reference/DateFormatSymbols.asciidoc create mode 100644 docs/painless/painless-api-reference/DateTimeException.asciidoc create mode 100644 docs/painless/painless-api-reference/DateTimeFormatter.asciidoc create mode 100644 docs/painless/painless-api-reference/DateTimeFormatterBuilder.asciidoc create mode 100644 docs/painless/painless-api-reference/DateTimeParseException.asciidoc create mode 100644 docs/painless/painless-api-reference/DayOfWeek.asciidoc create mode 100644 docs/painless/painless-api-reference/Debug.asciidoc create mode 100644 docs/painless/painless-api-reference/DecimalFormat.asciidoc create mode 100644 docs/painless/painless-api-reference/DecimalFormatSymbols.asciidoc create mode 100644 docs/painless/painless-api-reference/DecimalStyle.asciidoc create mode 100644 docs/painless/painless-api-reference/Deque.asciidoc create mode 100644 docs/painless/painless-api-reference/Dictionary.asciidoc create mode 100644 docs/painless/painless-api-reference/Double.asciidoc create mode 100644 docs/painless/painless-api-reference/DoubleBinaryOperator.asciidoc create mode 100644 docs/painless/painless-api-reference/DoubleConsumer.asciidoc create mode 100644 docs/painless/painless-api-reference/DoubleFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/DoublePredicate.asciidoc create mode 100644 docs/painless/painless-api-reference/DoubleStream.Builder.asciidoc create mode 100644 docs/painless/painless-api-reference/DoubleStream.asciidoc create mode 100644 docs/painless/painless-api-reference/DoubleSummaryStatistics.asciidoc create mode 100644 docs/painless/painless-api-reference/DoubleSupplier.asciidoc create mode 100644 docs/painless/painless-api-reference/DoubleToIntFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/DoubleToLongFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/DoubleUnaryOperator.asciidoc create mode 100644 docs/painless/painless-api-reference/DuplicateFormatFlagsException.asciidoc create mode 100644 docs/painless/painless-api-reference/Duration.asciidoc create mode 100644 docs/painless/painless-api-reference/EmptyStackException.asciidoc create mode 100644 docs/painless/painless-api-reference/Enum.asciidoc create mode 100644 docs/painless/painless-api-reference/EnumConstantNotPresentException.asciidoc create mode 100644 docs/painless/painless-api-reference/Enumeration.asciidoc create mode 100644 docs/painless/painless-api-reference/Era.asciidoc create mode 100644 docs/painless/painless-api-reference/EventListener.asciidoc create mode 100644 docs/painless/painless-api-reference/EventListenerProxy.asciidoc create mode 100644 docs/painless/painless-api-reference/EventObject.asciidoc create mode 100644 docs/painless/painless-api-reference/Exception.asciidoc create mode 100644 docs/painless/painless-api-reference/FieldPosition.asciidoc create mode 100644 docs/painless/painless-api-reference/Float.asciidoc create mode 100644 docs/painless/painless-api-reference/Format.Field.asciidoc create mode 100644 docs/painless/painless-api-reference/Format.asciidoc create mode 100644 docs/painless/painless-api-reference/FormatFlagsConversionMismatchException.asciidoc create mode 100644 docs/painless/painless-api-reference/FormatStyle.asciidoc create mode 100644 docs/painless/painless-api-reference/Formattable.asciidoc create mode 100644 docs/painless/painless-api-reference/FormattableFlags.asciidoc create mode 100644 docs/painless/painless-api-reference/Formatter.BigDecimalLayoutForm.asciidoc create mode 100644 docs/painless/painless-api-reference/Formatter.asciidoc create mode 100644 docs/painless/painless-api-reference/FormatterClosedException.asciidoc create mode 100644 docs/painless/painless-api-reference/Function.asciidoc create mode 100644 docs/painless/painless-api-reference/GregorianCalendar.asciidoc create mode 100644 docs/painless/painless-api-reference/HashMap.asciidoc create mode 100644 docs/painless/painless-api-reference/HashSet.asciidoc create mode 100644 docs/painless/painless-api-reference/Hashtable.asciidoc create mode 100644 docs/painless/painless-api-reference/HijrahChronology.asciidoc create mode 100644 docs/painless/painless-api-reference/HijrahDate.asciidoc create mode 100644 docs/painless/painless-api-reference/HijrahEra.asciidoc create mode 100644 docs/painless/painless-api-reference/IdentityHashMap.asciidoc create mode 100644 docs/painless/painless-api-reference/IllegalAccessException.asciidoc create mode 100644 docs/painless/painless-api-reference/IllegalArgumentException.asciidoc create mode 100644 docs/painless/painless-api-reference/IllegalFormatCodePointException.asciidoc create mode 100644 docs/painless/painless-api-reference/IllegalFormatConversionException.asciidoc create mode 100644 docs/painless/painless-api-reference/IllegalFormatException.asciidoc create mode 100644 docs/painless/painless-api-reference/IllegalFormatFlagsException.asciidoc create mode 100644 docs/painless/painless-api-reference/IllegalFormatPrecisionException.asciidoc create mode 100644 docs/painless/painless-api-reference/IllegalFormatWidthException.asciidoc create mode 100644 docs/painless/painless-api-reference/IllegalMonitorStateException.asciidoc create mode 100644 docs/painless/painless-api-reference/IllegalStateException.asciidoc create mode 100644 docs/painless/painless-api-reference/IllegalThreadStateException.asciidoc create mode 100644 docs/painless/painless-api-reference/IllformedLocaleException.asciidoc create mode 100644 docs/painless/painless-api-reference/IndexOutOfBoundsException.asciidoc create mode 100644 docs/painless/painless-api-reference/InputMismatchException.asciidoc create mode 100644 docs/painless/painless-api-reference/Instant.asciidoc create mode 100644 docs/painless/painless-api-reference/InstantiationException.asciidoc create mode 100644 docs/painless/painless-api-reference/IntBinaryOperator.asciidoc create mode 100644 docs/painless/painless-api-reference/IntConsumer.asciidoc create mode 100644 docs/painless/painless-api-reference/IntFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/IntPredicate.asciidoc create mode 100644 docs/painless/painless-api-reference/IntStream.Builder.asciidoc create mode 100644 docs/painless/painless-api-reference/IntStream.asciidoc create mode 100644 docs/painless/painless-api-reference/IntSummaryStatistics.asciidoc create mode 100644 docs/painless/painless-api-reference/IntSupplier.asciidoc create mode 100644 docs/painless/painless-api-reference/IntToDoubleFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/IntToLongFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/IntUnaryOperator.asciidoc create mode 100644 docs/painless/painless-api-reference/Integer.asciidoc create mode 100644 docs/painless/painless-api-reference/InterruptedException.asciidoc create mode 100644 docs/painless/painless-api-reference/IsoChronology.asciidoc create mode 100644 docs/painless/painless-api-reference/IsoEra.asciidoc create mode 100644 docs/painless/painless-api-reference/IsoFields.asciidoc create mode 100644 docs/painless/painless-api-reference/Iterable.asciidoc create mode 100644 docs/painless/painless-api-reference/Iterator.asciidoc create mode 100644 docs/painless/painless-api-reference/JapaneseChronology.asciidoc create mode 100644 docs/painless/painless-api-reference/JapaneseDate.asciidoc create mode 100644 docs/painless/painless-api-reference/JapaneseEra.asciidoc create mode 100644 docs/painless/painless-api-reference/JulianFields.asciidoc create mode 100644 docs/painless/painless-api-reference/LinkedHashMap.asciidoc create mode 100644 docs/painless/painless-api-reference/LinkedHashSet.asciidoc create mode 100644 docs/painless/painless-api-reference/LinkedList.asciidoc create mode 100644 docs/painless/painless-api-reference/List.asciidoc create mode 100644 docs/painless/painless-api-reference/ListIterator.asciidoc create mode 100644 docs/painless/painless-api-reference/LocalDate.asciidoc create mode 100644 docs/painless/painless-api-reference/LocalDateTime.asciidoc create mode 100644 docs/painless/painless-api-reference/LocalTime.asciidoc create mode 100644 docs/painless/painless-api-reference/Locale.Builder.asciidoc create mode 100644 docs/painless/painless-api-reference/Locale.Category.asciidoc create mode 100644 docs/painless/painless-api-reference/Locale.FilteringMode.asciidoc create mode 100644 docs/painless/painless-api-reference/Locale.LanguageRange.asciidoc create mode 100644 docs/painless/painless-api-reference/Locale.asciidoc create mode 100644 docs/painless/painless-api-reference/Long.asciidoc create mode 100644 docs/painless/painless-api-reference/LongBinaryOperator.asciidoc create mode 100644 docs/painless/painless-api-reference/LongConsumer.asciidoc create mode 100644 docs/painless/painless-api-reference/LongFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/LongPredicate.asciidoc create mode 100644 docs/painless/painless-api-reference/LongStream.Builder.asciidoc create mode 100644 docs/painless/painless-api-reference/LongStream.asciidoc create mode 100644 docs/painless/painless-api-reference/LongSummaryStatistics.asciidoc create mode 100644 docs/painless/painless-api-reference/LongSupplier.asciidoc create mode 100644 docs/painless/painless-api-reference/LongToDoubleFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/LongToIntFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/LongUnaryOperator.asciidoc create mode 100644 docs/painless/painless-api-reference/Map.Entry.asciidoc create mode 100644 docs/painless/painless-api-reference/Map.asciidoc create mode 100644 docs/painless/painless-api-reference/Matcher.asciidoc create mode 100644 docs/painless/painless-api-reference/Math.asciidoc create mode 100644 docs/painless/painless-api-reference/MathContext.asciidoc create mode 100644 docs/painless/painless-api-reference/MessageFormat.Field.asciidoc create mode 100644 docs/painless/painless-api-reference/MessageFormat.asciidoc create mode 100644 docs/painless/painless-api-reference/MinguoChronology.asciidoc create mode 100644 docs/painless/painless-api-reference/MinguoDate.asciidoc create mode 100644 docs/painless/painless-api-reference/MinguoEra.asciidoc create mode 100644 docs/painless/painless-api-reference/MissingFormatArgumentException.asciidoc create mode 100644 docs/painless/painless-api-reference/MissingFormatWidthException.asciidoc create mode 100644 docs/painless/painless-api-reference/MissingResourceException.asciidoc create mode 100644 docs/painless/painless-api-reference/Month.asciidoc create mode 100644 docs/painless/painless-api-reference/MonthDay.asciidoc create mode 100644 docs/painless/painless-api-reference/NavigableMap.asciidoc create mode 100644 docs/painless/painless-api-reference/NavigableSet.asciidoc create mode 100644 docs/painless/painless-api-reference/NegativeArraySizeException.asciidoc create mode 100644 docs/painless/painless-api-reference/NoSuchElementException.asciidoc create mode 100644 docs/painless/painless-api-reference/NoSuchFieldException.asciidoc create mode 100644 docs/painless/painless-api-reference/NoSuchMethodException.asciidoc create mode 100644 docs/painless/painless-api-reference/Normalizer.Form.asciidoc create mode 100644 docs/painless/painless-api-reference/Normalizer.asciidoc create mode 100644 docs/painless/painless-api-reference/NullPointerException.asciidoc create mode 100644 docs/painless/painless-api-reference/Number.asciidoc create mode 100644 docs/painless/painless-api-reference/NumberFormat.Field.asciidoc create mode 100644 docs/painless/painless-api-reference/NumberFormat.asciidoc create mode 100644 docs/painless/painless-api-reference/NumberFormatException.asciidoc create mode 100644 docs/painless/painless-api-reference/ObjDoubleConsumer.asciidoc create mode 100644 docs/painless/painless-api-reference/ObjIntConsumer.asciidoc create mode 100644 docs/painless/painless-api-reference/ObjLongConsumer.asciidoc create mode 100644 docs/painless/painless-api-reference/Object.asciidoc create mode 100644 docs/painless/painless-api-reference/Objects.asciidoc create mode 100644 docs/painless/painless-api-reference/Observable.asciidoc create mode 100644 docs/painless/painless-api-reference/Observer.asciidoc create mode 100644 docs/painless/painless-api-reference/OffsetDateTime.asciidoc create mode 100644 docs/painless/painless-api-reference/OffsetTime.asciidoc create mode 100644 docs/painless/painless-api-reference/Optional.asciidoc create mode 100644 docs/painless/painless-api-reference/OptionalDouble.asciidoc create mode 100644 docs/painless/painless-api-reference/OptionalInt.asciidoc create mode 100644 docs/painless/painless-api-reference/OptionalLong.asciidoc create mode 100644 docs/painless/painless-api-reference/ParseException.asciidoc create mode 100644 docs/painless/painless-api-reference/ParsePosition.asciidoc create mode 100644 docs/painless/painless-api-reference/Pattern.asciidoc create mode 100644 docs/painless/painless-api-reference/Period.asciidoc create mode 100644 docs/painless/painless-api-reference/Predicate.asciidoc create mode 100644 docs/painless/painless-api-reference/PrimitiveIterator.OfDouble.asciidoc create mode 100644 docs/painless/painless-api-reference/PrimitiveIterator.OfInt.asciidoc create mode 100644 docs/painless/painless-api-reference/PrimitiveIterator.OfLong.asciidoc create mode 100644 docs/painless/painless-api-reference/PrimitiveIterator.asciidoc create mode 100644 docs/painless/painless-api-reference/PriorityQueue.asciidoc create mode 100644 docs/painless/painless-api-reference/Queue.asciidoc create mode 100644 docs/painless/painless-api-reference/Random.asciidoc create mode 100644 docs/painless/painless-api-reference/RandomAccess.asciidoc create mode 100644 docs/painless/painless-api-reference/ReflectiveOperationException.asciidoc create mode 100644 docs/painless/painless-api-reference/ResolverStyle.asciidoc create mode 100644 docs/painless/painless-api-reference/RoundingMode.asciidoc create mode 100644 docs/painless/painless-api-reference/RuleBasedCollator.asciidoc create mode 100644 docs/painless/painless-api-reference/RuntimeException.asciidoc create mode 100644 docs/painless/painless-api-reference/SecurityException.asciidoc create mode 100644 docs/painless/painless-api-reference/Set.asciidoc create mode 100644 docs/painless/painless-api-reference/Short.asciidoc create mode 100644 docs/painless/painless-api-reference/SignStyle.asciidoc create mode 100644 docs/painless/painless-api-reference/SimpleDateFormat.asciidoc create mode 100644 docs/painless/painless-api-reference/SimpleTimeZone.asciidoc create mode 100644 docs/painless/painless-api-reference/SortedMap.asciidoc create mode 100644 docs/painless/painless-api-reference/SortedSet.asciidoc create mode 100644 docs/painless/painless-api-reference/Spliterator.OfDouble.asciidoc create mode 100644 docs/painless/painless-api-reference/Spliterator.OfInt.asciidoc create mode 100644 docs/painless/painless-api-reference/Spliterator.OfLong.asciidoc create mode 100644 docs/painless/painless-api-reference/Spliterator.OfPrimitive.asciidoc create mode 100644 docs/painless/painless-api-reference/Spliterator.asciidoc create mode 100644 docs/painless/painless-api-reference/Spliterators.asciidoc create mode 100644 docs/painless/painless-api-reference/Stack.asciidoc create mode 100644 docs/painless/painless-api-reference/StackTraceElement.asciidoc create mode 100644 docs/painless/painless-api-reference/Stream.Builder.asciidoc create mode 100644 docs/painless/painless-api-reference/Stream.asciidoc create mode 100644 docs/painless/painless-api-reference/StrictMath.asciidoc create mode 100644 docs/painless/painless-api-reference/String.asciidoc create mode 100644 docs/painless/painless-api-reference/StringBuffer.asciidoc create mode 100644 docs/painless/painless-api-reference/StringBuilder.asciidoc create mode 100644 docs/painless/painless-api-reference/StringCharacterIterator.asciidoc create mode 100644 docs/painless/painless-api-reference/StringIndexOutOfBoundsException.asciidoc create mode 100644 docs/painless/painless-api-reference/StringJoiner.asciidoc create mode 100644 docs/painless/painless-api-reference/StringTokenizer.asciidoc create mode 100644 docs/painless/painless-api-reference/Supplier.asciidoc create mode 100644 docs/painless/painless-api-reference/System.asciidoc create mode 100644 docs/painless/painless-api-reference/Temporal.asciidoc create mode 100644 docs/painless/painless-api-reference/TemporalAccessor.asciidoc create mode 100644 docs/painless/painless-api-reference/TemporalAdjuster.asciidoc create mode 100644 docs/painless/painless-api-reference/TemporalAdjusters.asciidoc create mode 100644 docs/painless/painless-api-reference/TemporalAmount.asciidoc create mode 100644 docs/painless/painless-api-reference/TemporalField.asciidoc create mode 100644 docs/painless/painless-api-reference/TemporalQueries.asciidoc create mode 100644 docs/painless/painless-api-reference/TemporalQuery.asciidoc create mode 100644 docs/painless/painless-api-reference/TemporalUnit.asciidoc create mode 100644 docs/painless/painless-api-reference/TextStyle.asciidoc create mode 100644 docs/painless/painless-api-reference/ThaiBuddhistChronology.asciidoc create mode 100644 docs/painless/painless-api-reference/ThaiBuddhistDate.asciidoc create mode 100644 docs/painless/painless-api-reference/ThaiBuddhistEra.asciidoc create mode 100644 docs/painless/painless-api-reference/TimeZone.asciidoc create mode 100644 docs/painless/painless-api-reference/ToDoubleBiFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/ToDoubleFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/ToIntBiFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/ToIntFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/ToLongBiFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/ToLongFunction.asciidoc create mode 100644 docs/painless/painless-api-reference/TooManyListenersException.asciidoc create mode 100644 docs/painless/painless-api-reference/TreeMap.asciidoc create mode 100644 docs/painless/painless-api-reference/TreeSet.asciidoc create mode 100644 docs/painless/painless-api-reference/TypeNotPresentException.asciidoc create mode 100644 docs/painless/painless-api-reference/UUID.asciidoc create mode 100644 docs/painless/painless-api-reference/UnaryOperator.asciidoc create mode 100644 docs/painless/painless-api-reference/UnknownFormatConversionException.asciidoc create mode 100644 docs/painless/painless-api-reference/UnknownFormatFlagsException.asciidoc create mode 100644 docs/painless/painless-api-reference/UnsupportedOperationException.asciidoc create mode 100644 docs/painless/painless-api-reference/UnsupportedTemporalTypeException.asciidoc create mode 100644 docs/painless/painless-api-reference/ValueRange.asciidoc create mode 100644 docs/painless/painless-api-reference/Vector.asciidoc create mode 100644 docs/painless/painless-api-reference/WeekFields.asciidoc create mode 100644 docs/painless/painless-api-reference/Year.asciidoc create mode 100644 docs/painless/painless-api-reference/YearMonth.asciidoc create mode 100644 docs/painless/painless-api-reference/ZoneId.asciidoc create mode 100644 docs/painless/painless-api-reference/ZoneOffset.asciidoc create mode 100644 docs/painless/painless-api-reference/ZoneOffsetTransition.asciidoc create mode 100644 docs/painless/painless-api-reference/ZoneOffsetTransitionRule.TimeDefinition.asciidoc create mode 100644 docs/painless/painless-api-reference/ZoneOffsetTransitionRule.asciidoc create mode 100644 docs/painless/painless-api-reference/ZoneRules.asciidoc create mode 100644 docs/painless/painless-api-reference/ZoneRulesException.asciidoc create mode 100644 docs/painless/painless-api-reference/ZoneRulesProvider.asciidoc create mode 100644 docs/painless/painless-api-reference/ZonedDateTime.asciidoc create mode 100644 docs/painless/painless-api-reference/index.asciidoc create mode 100644 docs/painless/painless-api-reference/org.elasticsearch.common.geo.GeoPoint.asciidoc create mode 100644 docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Booleans.asciidoc create mode 100644 docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.BytesRefs.asciidoc create mode 100644 docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Doubles.asciidoc create mode 100644 docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints.asciidoc create mode 100644 docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Longs.asciidoc create mode 100644 docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Strings.asciidoc create mode 100644 docs/painless/painless-api-reference/org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues.asciidoc create mode 100644 docs/painless/painless-api-reference/org.elasticsearch.painless.FeatureTest.asciidoc create mode 100644 docs/painless/painless-api-reference/org.joda.time.ReadableDateTime.asciidoc create mode 100644 docs/painless/painless-api-reference/org.joda.time.ReadableInstant.asciidoc create mode 100644 docs/painless/painless-debugging.asciidoc create mode 100644 docs/painless/painless-description.asciidoc create mode 100644 docs/painless/painless-getting-started.asciidoc create mode 100644 docs/painless/painless-lang-spec.asciidoc create mode 100644 docs/painless/painless-literals.asciidoc create mode 100644 docs/painless/painless-operators.asciidoc create mode 100644 docs/painless/painless-syntax.asciidoc create mode 100644 docs/painless/painless-types.asciidoc create mode 100644 docs/painless/painless-variables.asciidoc create mode 100644 docs/painless/painless-xref.asciidoc diff --git a/docs/painless/index.asciidoc b/docs/painless/index.asciidoc new file mode 100644 index 0000000000000..1e3d68c536435 --- /dev/null +++ b/docs/painless/index.asciidoc @@ -0,0 +1,45 @@ +[[painless]] += Painless Scripting Language + +:ref: https://www.elastic.co/guide/en/elasticsearch/reference + +include::../Versions.asciidoc[] + +include::painless-getting-started.asciidoc[] + +// include::painless-examples.asciidoc[] + +// include::painless-design.asciidoc[] + +include::painless-lang-spec.asciidoc[] + +include::painless-syntax.asciidoc[] + +include::painless-api-reference.asciidoc[] + +//// +Proposed Outline (WIP) +Getting Started with Painless + Accessing Doc Values + Updating Fields + Working with Dates + Using Regular Expressions + Debugging Painless Scripts + +Example Scripts + Using Painless in Script Fields + Using Painless in Watches + Using Painless in Function Score Queries + Using Painless in Script Queries + Using Painless When Updating Docs + Using Painless When Reindexing + +How Painless Works + Painless Architecture + Dispatching Functions + +Painless Language Specification +Painless API +//// + +Painless API Reference diff --git a/docs/painless/painless-api-reference.asciidoc b/docs/painless/painless-api-reference.asciidoc new file mode 100644 index 0000000000000..1bda6d890c859 --- /dev/null +++ b/docs/painless/painless-api-reference.asciidoc @@ -0,0 +1,17 @@ +["appendix",id="painless-api-reference"] += Painless API Reference + +Painless has a strict whitelist for methods and +classes to make sure that all painless scripts are secure and fast. Most of +these methods are exposed directly from the JRE while others are part of +Elasticsearch or Painless itself. Below is a list of all available methods +grouped under the classes on which you can call them. Clicking on the method +name takes you to the documentation for the method. + +NOTE: Methods defined in the JRE also have a `(java 9)` link which can be used +to see the method's documentation in Java 9 while clicking on the method's name +goes to the Java 8 documentation. Usually these aren't different but it is +worth going to the version that matches the version of Java you are using to +run Elasticsearch just in case. + +include::painless-api-reference/index.asciidoc[] diff --git a/docs/painless/painless-api-reference/AbstractChronology.asciidoc b/docs/painless/painless-api-reference/AbstractChronology.asciidoc new file mode 100644 index 0000000000000..3cc90ee65cde2 --- /dev/null +++ b/docs/painless/painless-api-reference/AbstractChronology.asciidoc @@ -0,0 +1,7 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-AbstractChronology]]++AbstractChronology++:: +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/AbstractCollection.asciidoc b/docs/painless/painless-api-reference/AbstractCollection.asciidoc new file mode 100644 index 0000000000000..313c327ac07cc --- /dev/null +++ b/docs/painless/painless-api-reference/AbstractCollection.asciidoc @@ -0,0 +1,7 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-AbstractCollection]]++AbstractCollection++:: +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/AbstractList.asciidoc b/docs/painless/painless-api-reference/AbstractList.asciidoc new file mode 100644 index 0000000000000..9ce7c9a683001 --- /dev/null +++ b/docs/painless/painless-api-reference/AbstractList.asciidoc @@ -0,0 +1,7 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-AbstractList]]++AbstractList++:: +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/AbstractMap.SimpleEntry.asciidoc b/docs/painless/painless-api-reference/AbstractMap.SimpleEntry.asciidoc new file mode 100644 index 0000000000000..327939e009d5e --- /dev/null +++ b/docs/painless/painless-api-reference/AbstractMap.SimpleEntry.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-AbstractMap-SimpleEntry]]++AbstractMap.SimpleEntry++:: +* ++[[painless-api-reference-AbstractMap-SimpleEntry-AbstractMap.SimpleEntry-1]]link:{java8-javadoc}/java/util/AbstractMap$SimpleEntry.html#AbstractMap.SimpleEntry%2Djava.util.Map$Entry%2D[AbstractMap.SimpleEntry](<>)++ (link:{java9-javadoc}/java/util/AbstractMap$SimpleEntry.html#AbstractMap.SimpleEntry%2Djava.util.Map$Entry%2D[java 9]) +* ++[[painless-api-reference-AbstractMap-SimpleEntry-AbstractMap.SimpleEntry-2]]link:{java8-javadoc}/java/util/AbstractMap$SimpleEntry.html#AbstractMap.SimpleEntry%2Djava.lang.Object%2Djava.lang.Object%2D[AbstractMap.SimpleEntry](def, def)++ (link:{java9-javadoc}/java/util/AbstractMap$SimpleEntry.html#AbstractMap.SimpleEntry%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/AbstractMap.SimpleImmutableEntry.asciidoc b/docs/painless/painless-api-reference/AbstractMap.SimpleImmutableEntry.asciidoc new file mode 100644 index 0000000000000..94a4b0c307aa2 --- /dev/null +++ b/docs/painless/painless-api-reference/AbstractMap.SimpleImmutableEntry.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-AbstractMap-SimpleImmutableEntry]]++AbstractMap.SimpleImmutableEntry++:: +* ++[[painless-api-reference-AbstractMap-SimpleImmutableEntry-AbstractMap.SimpleImmutableEntry-1]]link:{java8-javadoc}/java/util/AbstractMap$SimpleImmutableEntry.html#AbstractMap.SimpleImmutableEntry%2Djava.util.Map$Entry%2D[AbstractMap.SimpleImmutableEntry](<>)++ (link:{java9-javadoc}/java/util/AbstractMap$SimpleImmutableEntry.html#AbstractMap.SimpleImmutableEntry%2Djava.util.Map$Entry%2D[java 9]) +* ++[[painless-api-reference-AbstractMap-SimpleImmutableEntry-AbstractMap.SimpleImmutableEntry-2]]link:{java8-javadoc}/java/util/AbstractMap$SimpleImmutableEntry.html#AbstractMap.SimpleImmutableEntry%2Djava.lang.Object%2Djava.lang.Object%2D[AbstractMap.SimpleImmutableEntry](def, def)++ (link:{java9-javadoc}/java/util/AbstractMap$SimpleImmutableEntry.html#AbstractMap.SimpleImmutableEntry%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/AbstractMap.asciidoc b/docs/painless/painless-api-reference/AbstractMap.asciidoc new file mode 100644 index 0000000000000..ea3be0d125a98 --- /dev/null +++ b/docs/painless/painless-api-reference/AbstractMap.asciidoc @@ -0,0 +1,7 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-AbstractMap]]++AbstractMap++:: +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/AbstractQueue.asciidoc b/docs/painless/painless-api-reference/AbstractQueue.asciidoc new file mode 100644 index 0000000000000..37fc53bd73d65 --- /dev/null +++ b/docs/painless/painless-api-reference/AbstractQueue.asciidoc @@ -0,0 +1,7 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-AbstractQueue]]++AbstractQueue++:: +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/AbstractSequentialList.asciidoc b/docs/painless/painless-api-reference/AbstractSequentialList.asciidoc new file mode 100644 index 0000000000000..822b203851648 --- /dev/null +++ b/docs/painless/painless-api-reference/AbstractSequentialList.asciidoc @@ -0,0 +1,7 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-AbstractSequentialList]]++AbstractSequentialList++:: +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/AbstractSet.asciidoc b/docs/painless/painless-api-reference/AbstractSet.asciidoc new file mode 100644 index 0000000000000..7baa3a58f7995 --- /dev/null +++ b/docs/painless/painless-api-reference/AbstractSet.asciidoc @@ -0,0 +1,7 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-AbstractSet]]++AbstractSet++:: +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Annotation.asciidoc b/docs/painless/painless-api-reference/Annotation.asciidoc new file mode 100644 index 0000000000000..33252543951b8 --- /dev/null +++ b/docs/painless/painless-api-reference/Annotation.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Annotation]]++Annotation++:: +* ++[[painless-api-reference-Annotation-Annotation-1]]link:{java8-javadoc}/java/text/Annotation.html#Annotation%2Djava.lang.Object%2D[Annotation](<>)++ (link:{java9-javadoc}/java/text/Annotation.html#Annotation%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Annotation-getValue-0]]def link:{java8-javadoc}/java/text/Annotation.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/text/Annotation.html#getValue%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Appendable.asciidoc b/docs/painless/painless-api-reference/Appendable.asciidoc new file mode 100644 index 0000000000000..61fcbaf5a6052 --- /dev/null +++ b/docs/painless/painless-api-reference/Appendable.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Appendable]]++Appendable++:: +* ++[[painless-api-reference-Appendable-append-3]]<> link:{java8-javadoc}/java/lang/Appendable.html#append%2Djava.lang.CharSequence%2Dint%2Dint%2D[append](<>, int, int)++ (link:{java9-javadoc}/java/lang/Appendable.html#append%2Djava.lang.CharSequence%2Dint%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ArithmeticException.asciidoc b/docs/painless/painless-api-reference/ArithmeticException.asciidoc new file mode 100644 index 0000000000000..f486289320570 --- /dev/null +++ b/docs/painless/painless-api-reference/ArithmeticException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ArithmeticException]]++ArithmeticException++:: +* ++[[painless-api-reference-ArithmeticException-ArithmeticException-0]]link:{java8-javadoc}/java/lang/ArithmeticException.html#ArithmeticException%2D%2D[ArithmeticException]()++ (link:{java9-javadoc}/java/lang/ArithmeticException.html#ArithmeticException%2D%2D[java 9]) +* ++[[painless-api-reference-ArithmeticException-ArithmeticException-1]]link:{java8-javadoc}/java/lang/ArithmeticException.html#ArithmeticException%2Djava.lang.String%2D[ArithmeticException](<>)++ (link:{java9-javadoc}/java/lang/ArithmeticException.html#ArithmeticException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ArrayDeque.asciidoc b/docs/painless/painless-api-reference/ArrayDeque.asciidoc new file mode 100644 index 0000000000000..668a86185e690 --- /dev/null +++ b/docs/painless/painless-api-reference/ArrayDeque.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ArrayDeque]]++ArrayDeque++:: +* ++[[painless-api-reference-ArrayDeque-ArrayDeque-0]]link:{java8-javadoc}/java/util/ArrayDeque.html#ArrayDeque%2D%2D[ArrayDeque]()++ (link:{java9-javadoc}/java/util/ArrayDeque.html#ArrayDeque%2D%2D[java 9]) +* ++[[painless-api-reference-ArrayDeque-ArrayDeque-1]]link:{java8-javadoc}/java/util/ArrayDeque.html#ArrayDeque%2Djava.util.Collection%2D[ArrayDeque](<>)++ (link:{java9-javadoc}/java/util/ArrayDeque.html#ArrayDeque%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-ArrayDeque-clone-0]]<> link:{java8-javadoc}/java/util/ArrayDeque.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/ArrayDeque.html#clone%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ArrayIndexOutOfBoundsException.asciidoc b/docs/painless/painless-api-reference/ArrayIndexOutOfBoundsException.asciidoc new file mode 100644 index 0000000000000..49b13361163f4 --- /dev/null +++ b/docs/painless/painless-api-reference/ArrayIndexOutOfBoundsException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ArrayIndexOutOfBoundsException]]++ArrayIndexOutOfBoundsException++:: +* ++[[painless-api-reference-ArrayIndexOutOfBoundsException-ArrayIndexOutOfBoundsException-0]]link:{java8-javadoc}/java/lang/ArrayIndexOutOfBoundsException.html#ArrayIndexOutOfBoundsException%2D%2D[ArrayIndexOutOfBoundsException]()++ (link:{java9-javadoc}/java/lang/ArrayIndexOutOfBoundsException.html#ArrayIndexOutOfBoundsException%2D%2D[java 9]) +* ++[[painless-api-reference-ArrayIndexOutOfBoundsException-ArrayIndexOutOfBoundsException-1]]link:{java8-javadoc}/java/lang/ArrayIndexOutOfBoundsException.html#ArrayIndexOutOfBoundsException%2Djava.lang.String%2D[ArrayIndexOutOfBoundsException](<>)++ (link:{java9-javadoc}/java/lang/ArrayIndexOutOfBoundsException.html#ArrayIndexOutOfBoundsException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ArrayList.asciidoc b/docs/painless/painless-api-reference/ArrayList.asciidoc new file mode 100644 index 0000000000000..90386a3742cf7 --- /dev/null +++ b/docs/painless/painless-api-reference/ArrayList.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ArrayList]]++ArrayList++:: +* ++[[painless-api-reference-ArrayList-ArrayList-0]]link:{java8-javadoc}/java/util/ArrayList.html#ArrayList%2D%2D[ArrayList]()++ (link:{java9-javadoc}/java/util/ArrayList.html#ArrayList%2D%2D[java 9]) +* ++[[painless-api-reference-ArrayList-ArrayList-1]]link:{java8-javadoc}/java/util/ArrayList.html#ArrayList%2Djava.util.Collection%2D[ArrayList](<>)++ (link:{java9-javadoc}/java/util/ArrayList.html#ArrayList%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-ArrayList-clone-0]]def link:{java8-javadoc}/java/util/ArrayList.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/ArrayList.html#clone%2D%2D[java 9]) +* ++[[painless-api-reference-ArrayList-trimToSize-0]]void link:{java8-javadoc}/java/util/ArrayList.html#trimToSize%2D%2D[trimToSize]()++ (link:{java9-javadoc}/java/util/ArrayList.html#trimToSize%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ArrayStoreException.asciidoc b/docs/painless/painless-api-reference/ArrayStoreException.asciidoc new file mode 100644 index 0000000000000..29f7a536eadf9 --- /dev/null +++ b/docs/painless/painless-api-reference/ArrayStoreException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ArrayStoreException]]++ArrayStoreException++:: +* ++[[painless-api-reference-ArrayStoreException-ArrayStoreException-0]]link:{java8-javadoc}/java/lang/ArrayStoreException.html#ArrayStoreException%2D%2D[ArrayStoreException]()++ (link:{java9-javadoc}/java/lang/ArrayStoreException.html#ArrayStoreException%2D%2D[java 9]) +* ++[[painless-api-reference-ArrayStoreException-ArrayStoreException-1]]link:{java8-javadoc}/java/lang/ArrayStoreException.html#ArrayStoreException%2Djava.lang.String%2D[ArrayStoreException](<>)++ (link:{java9-javadoc}/java/lang/ArrayStoreException.html#ArrayStoreException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Arrays.asciidoc b/docs/painless/painless-api-reference/Arrays.asciidoc new file mode 100644 index 0000000000000..97598d64d5790 --- /dev/null +++ b/docs/painless/painless-api-reference/Arrays.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Arrays]]++Arrays++:: +* ++[[painless-api-reference-Arrays-asList-1]]static <> link:{java8-javadoc}/java/util/Arrays.html#asList%2Djava.lang.Object:A%2D[asList](<>[])++ (link:{java9-javadoc}/java/util/Arrays.html#asList%2Djava.lang.Object:A%2D[java 9]) +* ++[[painless-api-reference-Arrays-deepEquals-2]]static boolean link:{java8-javadoc}/java/util/Arrays.html#deepEquals%2Djava.lang.Object:A%2Djava.lang.Object:A%2D[deepEquals](<>[], <>[])++ (link:{java9-javadoc}/java/util/Arrays.html#deepEquals%2Djava.lang.Object:A%2Djava.lang.Object:A%2D[java 9]) +* ++[[painless-api-reference-Arrays-deepHashCode-1]]static int link:{java8-javadoc}/java/util/Arrays.html#deepHashCode%2Djava.lang.Object:A%2D[deepHashCode](<>[])++ (link:{java9-javadoc}/java/util/Arrays.html#deepHashCode%2Djava.lang.Object:A%2D[java 9]) +* ++[[painless-api-reference-Arrays-deepToString-1]]static <> link:{java8-javadoc}/java/util/Arrays.html#deepToString%2Djava.lang.Object:A%2D[deepToString](<>[])++ (link:{java9-javadoc}/java/util/Arrays.html#deepToString%2Djava.lang.Object:A%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/AttributedCharacterIterator.Attribute.asciidoc b/docs/painless/painless-api-reference/AttributedCharacterIterator.Attribute.asciidoc new file mode 100644 index 0000000000000..56ccfbe6aabc1 --- /dev/null +++ b/docs/painless/painless-api-reference/AttributedCharacterIterator.Attribute.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-AttributedCharacterIterator-Attribute]]++AttributedCharacterIterator.Attribute++:: +** [[painless-api-reference-AttributedCharacterIterator-Attribute-INPUT_METHOD_SEGMENT]]static <> link:{java8-javadoc}/java/text/AttributedCharacterIterator$Attribute.html#INPUT_METHOD_SEGMENT[INPUT_METHOD_SEGMENT] (link:{java9-javadoc}/java/text/AttributedCharacterIterator$Attribute.html#INPUT_METHOD_SEGMENT[java 9]) +** [[painless-api-reference-AttributedCharacterIterator-Attribute-LANGUAGE]]static <> link:{java8-javadoc}/java/text/AttributedCharacterIterator$Attribute.html#LANGUAGE[LANGUAGE] (link:{java9-javadoc}/java/text/AttributedCharacterIterator$Attribute.html#LANGUAGE[java 9]) +** [[painless-api-reference-AttributedCharacterIterator-Attribute-READING]]static <> link:{java8-javadoc}/java/text/AttributedCharacterIterator$Attribute.html#READING[READING] (link:{java9-javadoc}/java/text/AttributedCharacterIterator$Attribute.html#READING[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/AttributedCharacterIterator.asciidoc b/docs/painless/painless-api-reference/AttributedCharacterIterator.asciidoc new file mode 100644 index 0000000000000..a71d3906fa167 --- /dev/null +++ b/docs/painless/painless-api-reference/AttributedCharacterIterator.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-AttributedCharacterIterator]]++AttributedCharacterIterator++:: +* ++[[painless-api-reference-AttributedCharacterIterator-getAllAttributeKeys-0]]<> link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getAllAttributeKeys%2D%2D[getAllAttributeKeys]()++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getAllAttributeKeys%2D%2D[java 9]) +* ++[[painless-api-reference-AttributedCharacterIterator-getAttribute-1]]def link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getAttribute%2Djava.text.AttributedCharacterIterator$Attribute%2D[getAttribute](<>)++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getAttribute%2Djava.text.AttributedCharacterIterator$Attribute%2D[java 9]) +* ++[[painless-api-reference-AttributedCharacterIterator-getAttributes-0]]<> link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getAttributes%2D%2D[getAttributes]()++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getAttributes%2D%2D[java 9]) +* ++[[painless-api-reference-AttributedCharacterIterator-getRunLimit-0]]int link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getRunLimit%2D%2D[getRunLimit]()++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getRunLimit%2D%2D[java 9]) +* ++[[painless-api-reference-AttributedCharacterIterator-getRunLimit-1]]int link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getRunLimit%2Djava.util.Set%2D[getRunLimit](<>)++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getRunLimit%2Djava.util.Set%2D[java 9]) +* ++[[painless-api-reference-AttributedCharacterIterator-getRunStart-0]]int link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getRunStart%2D%2D[getRunStart]()++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getRunStart%2D%2D[java 9]) +* ++[[painless-api-reference-AttributedCharacterIterator-getRunStart-1]]int link:{java8-javadoc}/java/text/AttributedCharacterIterator.html#getRunStart%2Djava.util.Set%2D[getRunStart](<>)++ (link:{java9-javadoc}/java/text/AttributedCharacterIterator.html#getRunStart%2Djava.util.Set%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/AttributedString.asciidoc b/docs/painless/painless-api-reference/AttributedString.asciidoc new file mode 100644 index 0000000000000..0393838a73717 --- /dev/null +++ b/docs/painless/painless-api-reference/AttributedString.asciidoc @@ -0,0 +1,15 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-AttributedString]]++AttributedString++:: +* ++[[painless-api-reference-AttributedString-AttributedString-1]]link:{java8-javadoc}/java/text/AttributedString.html#AttributedString%2Djava.lang.String%2D[AttributedString](<>)++ (link:{java9-javadoc}/java/text/AttributedString.html#AttributedString%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-AttributedString-AttributedString-2]]link:{java8-javadoc}/java/text/AttributedString.html#AttributedString%2Djava.lang.String%2Djava.util.Map%2D[AttributedString](<>, <>)++ (link:{java9-javadoc}/java/text/AttributedString.html#AttributedString%2Djava.lang.String%2Djava.util.Map%2D[java 9]) +* ++[[painless-api-reference-AttributedString-addAttribute-2]]void link:{java8-javadoc}/java/text/AttributedString.html#addAttribute%2Djava.text.AttributedCharacterIterator$Attribute%2Djava.lang.Object%2D[addAttribute](<>, <>)++ (link:{java9-javadoc}/java/text/AttributedString.html#addAttribute%2Djava.text.AttributedCharacterIterator$Attribute%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-AttributedString-addAttribute-4]]void link:{java8-javadoc}/java/text/AttributedString.html#addAttribute%2Djava.text.AttributedCharacterIterator$Attribute%2Djava.lang.Object%2Dint%2Dint%2D[addAttribute](<>, <>, int, int)++ (link:{java9-javadoc}/java/text/AttributedString.html#addAttribute%2Djava.text.AttributedCharacterIterator$Attribute%2Djava.lang.Object%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-AttributedString-addAttributes-3]]void link:{java8-javadoc}/java/text/AttributedString.html#addAttributes%2Djava.util.Map%2Dint%2Dint%2D[addAttributes](<>, int, int)++ (link:{java9-javadoc}/java/text/AttributedString.html#addAttributes%2Djava.util.Map%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-AttributedString-getIterator-0]]<> link:{java8-javadoc}/java/text/AttributedString.html#getIterator%2D%2D[getIterator]()++ (link:{java9-javadoc}/java/text/AttributedString.html#getIterator%2D%2D[java 9]) +* ++[[painless-api-reference-AttributedString-getIterator-1]]<> link:{java8-javadoc}/java/text/AttributedString.html#getIterator%2Djava.text.AttributedCharacterIterator$Attribute:A%2D[getIterator](<>[])++ (link:{java9-javadoc}/java/text/AttributedString.html#getIterator%2Djava.text.AttributedCharacterIterator$Attribute:A%2D[java 9]) +* ++[[painless-api-reference-AttributedString-getIterator-3]]<> link:{java8-javadoc}/java/text/AttributedString.html#getIterator%2Djava.text.AttributedCharacterIterator$Attribute:A%2Dint%2Dint%2D[getIterator](<>[], int, int)++ (link:{java9-javadoc}/java/text/AttributedString.html#getIterator%2Djava.text.AttributedCharacterIterator$Attribute:A%2Dint%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Base64.Decoder.asciidoc b/docs/painless/painless-api-reference/Base64.Decoder.asciidoc new file mode 100644 index 0000000000000..3b3acd16e61ef --- /dev/null +++ b/docs/painless/painless-api-reference/Base64.Decoder.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Base64-Decoder]]++Base64.Decoder++:: +* ++[[painless-api-reference-Base64-Decoder-decode-1]]byte[] link:{java8-javadoc}/java/util/Base64$Decoder.html#decode%2Djava.lang.String%2D[decode](<>)++ (link:{java9-javadoc}/java/util/Base64$Decoder.html#decode%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Base64-Decoder-decode-2]]int link:{java8-javadoc}/java/util/Base64$Decoder.html#decode%2Dbyte:A%2Dbyte:A%2D[decode](byte[], byte[])++ (link:{java9-javadoc}/java/util/Base64$Decoder.html#decode%2Dbyte:A%2Dbyte:A%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Base64.Encoder.asciidoc b/docs/painless/painless-api-reference/Base64.Encoder.asciidoc new file mode 100644 index 0000000000000..bc632fc79950e --- /dev/null +++ b/docs/painless/painless-api-reference/Base64.Encoder.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Base64-Encoder]]++Base64.Encoder++:: +* ++[[painless-api-reference-Base64-Encoder-encode-2]]int link:{java8-javadoc}/java/util/Base64$Encoder.html#encode%2Dbyte:A%2Dbyte:A%2D[encode](byte[], byte[])++ (link:{java9-javadoc}/java/util/Base64$Encoder.html#encode%2Dbyte:A%2Dbyte:A%2D[java 9]) +* ++[[painless-api-reference-Base64-Encoder-encodeToString-1]]<> link:{java8-javadoc}/java/util/Base64$Encoder.html#encodeToString%2Dbyte:A%2D[encodeToString](byte[])++ (link:{java9-javadoc}/java/util/Base64$Encoder.html#encodeToString%2Dbyte:A%2D[java 9]) +* ++[[painless-api-reference-Base64-Encoder-withoutPadding-0]]<> link:{java8-javadoc}/java/util/Base64$Encoder.html#withoutPadding%2D%2D[withoutPadding]()++ (link:{java9-javadoc}/java/util/Base64$Encoder.html#withoutPadding%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Base64.asciidoc b/docs/painless/painless-api-reference/Base64.asciidoc new file mode 100644 index 0000000000000..795f538d7d479 --- /dev/null +++ b/docs/painless/painless-api-reference/Base64.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Base64]]++Base64++:: +* ++[[painless-api-reference-Base64-getDecoder-0]]static <> link:{java8-javadoc}/java/util/Base64.html#getDecoder%2D%2D[getDecoder]()++ (link:{java9-javadoc}/java/util/Base64.html#getDecoder%2D%2D[java 9]) +* ++[[painless-api-reference-Base64-getEncoder-0]]static <> link:{java8-javadoc}/java/util/Base64.html#getEncoder%2D%2D[getEncoder]()++ (link:{java9-javadoc}/java/util/Base64.html#getEncoder%2D%2D[java 9]) +* ++[[painless-api-reference-Base64-getMimeDecoder-0]]static <> link:{java8-javadoc}/java/util/Base64.html#getMimeDecoder%2D%2D[getMimeDecoder]()++ (link:{java9-javadoc}/java/util/Base64.html#getMimeDecoder%2D%2D[java 9]) +* ++[[painless-api-reference-Base64-getMimeEncoder-0]]static <> link:{java8-javadoc}/java/util/Base64.html#getMimeEncoder%2D%2D[getMimeEncoder]()++ (link:{java9-javadoc}/java/util/Base64.html#getMimeEncoder%2D%2D[java 9]) +* ++[[painless-api-reference-Base64-getMimeEncoder-2]]static <> link:{java8-javadoc}/java/util/Base64.html#getMimeEncoder%2Dint%2Dbyte:A%2D[getMimeEncoder](int, byte[])++ (link:{java9-javadoc}/java/util/Base64.html#getMimeEncoder%2Dint%2Dbyte:A%2D[java 9]) +* ++[[painless-api-reference-Base64-getUrlDecoder-0]]static <> link:{java8-javadoc}/java/util/Base64.html#getUrlDecoder%2D%2D[getUrlDecoder]()++ (link:{java9-javadoc}/java/util/Base64.html#getUrlDecoder%2D%2D[java 9]) +* ++[[painless-api-reference-Base64-getUrlEncoder-0]]static <> link:{java8-javadoc}/java/util/Base64.html#getUrlEncoder%2D%2D[getUrlEncoder]()++ (link:{java9-javadoc}/java/util/Base64.html#getUrlEncoder%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/BaseStream.asciidoc b/docs/painless/painless-api-reference/BaseStream.asciidoc new file mode 100644 index 0000000000000..680e9fff71d79 --- /dev/null +++ b/docs/painless/painless-api-reference/BaseStream.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-BaseStream]]++BaseStream++:: +* ++[[painless-api-reference-BaseStream-close-0]]void link:{java8-javadoc}/java/util/stream/BaseStream.html#close%2D%2D[close]()++ (link:{java9-javadoc}/java/util/stream/BaseStream.html#close%2D%2D[java 9]) +* ++[[painless-api-reference-BaseStream-isParallel-0]]boolean link:{java8-javadoc}/java/util/stream/BaseStream.html#isParallel%2D%2D[isParallel]()++ (link:{java9-javadoc}/java/util/stream/BaseStream.html#isParallel%2D%2D[java 9]) +* ++[[painless-api-reference-BaseStream-iterator-0]]<> link:{java8-javadoc}/java/util/stream/BaseStream.html#iterator%2D%2D[iterator]()++ (link:{java9-javadoc}/java/util/stream/BaseStream.html#iterator%2D%2D[java 9]) +* ++[[painless-api-reference-BaseStream-sequential-0]]<> link:{java8-javadoc}/java/util/stream/BaseStream.html#sequential%2D%2D[sequential]()++ (link:{java9-javadoc}/java/util/stream/BaseStream.html#sequential%2D%2D[java 9]) +* ++[[painless-api-reference-BaseStream-spliterator-0]]<> link:{java8-javadoc}/java/util/stream/BaseStream.html#spliterator%2D%2D[spliterator]()++ (link:{java9-javadoc}/java/util/stream/BaseStream.html#spliterator%2D%2D[java 9]) +* ++[[painless-api-reference-BaseStream-unordered-0]]<> link:{java8-javadoc}/java/util/stream/BaseStream.html#unordered%2D%2D[unordered]()++ (link:{java9-javadoc}/java/util/stream/BaseStream.html#unordered%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/BiConsumer.asciidoc b/docs/painless/painless-api-reference/BiConsumer.asciidoc new file mode 100644 index 0000000000000..a13bbdbd300e2 --- /dev/null +++ b/docs/painless/painless-api-reference/BiConsumer.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-BiConsumer]]++BiConsumer++:: +* ++[[painless-api-reference-BiConsumer-accept-2]]void link:{java8-javadoc}/java/util/function/BiConsumer.html#accept%2Djava.lang.Object%2Djava.lang.Object%2D[accept](def, def)++ (link:{java9-javadoc}/java/util/function/BiConsumer.html#accept%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-BiConsumer-andThen-1]]<> link:{java8-javadoc}/java/util/function/BiConsumer.html#andThen%2Djava.util.function.BiConsumer%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/BiConsumer.html#andThen%2Djava.util.function.BiConsumer%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/BiFunction.asciidoc b/docs/painless/painless-api-reference/BiFunction.asciidoc new file mode 100644 index 0000000000000..88b316300321e --- /dev/null +++ b/docs/painless/painless-api-reference/BiFunction.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-BiFunction]]++BiFunction++:: +* ++[[painless-api-reference-BiFunction-andThen-1]]<> link:{java8-javadoc}/java/util/function/BiFunction.html#andThen%2Djava.util.function.Function%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/BiFunction.html#andThen%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-BiFunction-apply-2]]def link:{java8-javadoc}/java/util/function/BiFunction.html#apply%2Djava.lang.Object%2Djava.lang.Object%2D[apply](def, def)++ (link:{java9-javadoc}/java/util/function/BiFunction.html#apply%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/BiPredicate.asciidoc b/docs/painless/painless-api-reference/BiPredicate.asciidoc new file mode 100644 index 0000000000000..7961800814cc6 --- /dev/null +++ b/docs/painless/painless-api-reference/BiPredicate.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-BiPredicate]]++BiPredicate++:: +* ++[[painless-api-reference-BiPredicate-and-1]]<> link:{java8-javadoc}/java/util/function/BiPredicate.html#and%2Djava.util.function.BiPredicate%2D[and](<>)++ (link:{java9-javadoc}/java/util/function/BiPredicate.html#and%2Djava.util.function.BiPredicate%2D[java 9]) +* ++[[painless-api-reference-BiPredicate-negate-0]]<> link:{java8-javadoc}/java/util/function/BiPredicate.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/util/function/BiPredicate.html#negate%2D%2D[java 9]) +* ++[[painless-api-reference-BiPredicate-or-1]]<> link:{java8-javadoc}/java/util/function/BiPredicate.html#or%2Djava.util.function.BiPredicate%2D[or](<>)++ (link:{java9-javadoc}/java/util/function/BiPredicate.html#or%2Djava.util.function.BiPredicate%2D[java 9]) +* ++[[painless-api-reference-BiPredicate-test-2]]boolean link:{java8-javadoc}/java/util/function/BiPredicate.html#test%2Djava.lang.Object%2Djava.lang.Object%2D[test](def, def)++ (link:{java9-javadoc}/java/util/function/BiPredicate.html#test%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Bidi.asciidoc b/docs/painless/painless-api-reference/Bidi.asciidoc new file mode 100644 index 0000000000000..cf6272dd49cf5 --- /dev/null +++ b/docs/painless/painless-api-reference/Bidi.asciidoc @@ -0,0 +1,28 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Bidi]]++Bidi++:: +** [[painless-api-reference-Bidi-DIRECTION_DEFAULT_LEFT_TO_RIGHT]]static int link:{java8-javadoc}/java/text/Bidi.html#DIRECTION_DEFAULT_LEFT_TO_RIGHT[DIRECTION_DEFAULT_LEFT_TO_RIGHT] (link:{java9-javadoc}/java/text/Bidi.html#DIRECTION_DEFAULT_LEFT_TO_RIGHT[java 9]) +** [[painless-api-reference-Bidi-DIRECTION_DEFAULT_RIGHT_TO_LEFT]]static int link:{java8-javadoc}/java/text/Bidi.html#DIRECTION_DEFAULT_RIGHT_TO_LEFT[DIRECTION_DEFAULT_RIGHT_TO_LEFT] (link:{java9-javadoc}/java/text/Bidi.html#DIRECTION_DEFAULT_RIGHT_TO_LEFT[java 9]) +** [[painless-api-reference-Bidi-DIRECTION_LEFT_TO_RIGHT]]static int link:{java8-javadoc}/java/text/Bidi.html#DIRECTION_LEFT_TO_RIGHT[DIRECTION_LEFT_TO_RIGHT] (link:{java9-javadoc}/java/text/Bidi.html#DIRECTION_LEFT_TO_RIGHT[java 9]) +** [[painless-api-reference-Bidi-DIRECTION_RIGHT_TO_LEFT]]static int link:{java8-javadoc}/java/text/Bidi.html#DIRECTION_RIGHT_TO_LEFT[DIRECTION_RIGHT_TO_LEFT] (link:{java9-javadoc}/java/text/Bidi.html#DIRECTION_RIGHT_TO_LEFT[java 9]) +* ++[[painless-api-reference-Bidi-reorderVisually-5]]static void link:{java8-javadoc}/java/text/Bidi.html#reorderVisually%2Dbyte:A%2Dint%2Djava.lang.Object:A%2Dint%2Dint%2D[reorderVisually](byte[], int, <>[], int, int)++ (link:{java9-javadoc}/java/text/Bidi.html#reorderVisually%2Dbyte:A%2Dint%2Djava.lang.Object:A%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Bidi-requiresBidi-3]]static boolean link:{java8-javadoc}/java/text/Bidi.html#requiresBidi%2Dchar:A%2Dint%2Dint%2D[requiresBidi](char[], int, int)++ (link:{java9-javadoc}/java/text/Bidi.html#requiresBidi%2Dchar:A%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Bidi-Bidi-1]]link:{java8-javadoc}/java/text/Bidi.html#Bidi%2Djava.text.AttributedCharacterIterator%2D[Bidi](<>)++ (link:{java9-javadoc}/java/text/Bidi.html#Bidi%2Djava.text.AttributedCharacterIterator%2D[java 9]) +* ++[[painless-api-reference-Bidi-Bidi-2]]link:{java8-javadoc}/java/text/Bidi.html#Bidi%2Djava.lang.String%2Dint%2D[Bidi](<>, int)++ (link:{java9-javadoc}/java/text/Bidi.html#Bidi%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-Bidi-Bidi-6]]link:{java8-javadoc}/java/text/Bidi.html#Bidi%2Dchar:A%2Dint%2Dbyte:A%2Dint%2Dint%2Dint%2D[Bidi](char[], int, byte[], int, int, int)++ (link:{java9-javadoc}/java/text/Bidi.html#Bidi%2Dchar:A%2Dint%2Dbyte:A%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Bidi-baseIsLeftToRight-0]]boolean link:{java8-javadoc}/java/text/Bidi.html#baseIsLeftToRight%2D%2D[baseIsLeftToRight]()++ (link:{java9-javadoc}/java/text/Bidi.html#baseIsLeftToRight%2D%2D[java 9]) +* ++[[painless-api-reference-Bidi-createLineBidi-2]]<> link:{java8-javadoc}/java/text/Bidi.html#createLineBidi%2Dint%2Dint%2D[createLineBidi](int, int)++ (link:{java9-javadoc}/java/text/Bidi.html#createLineBidi%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Bidi-getBaseLevel-0]]int link:{java8-javadoc}/java/text/Bidi.html#getBaseLevel%2D%2D[getBaseLevel]()++ (link:{java9-javadoc}/java/text/Bidi.html#getBaseLevel%2D%2D[java 9]) +* ++[[painless-api-reference-Bidi-getLength-0]]int link:{java8-javadoc}/java/text/Bidi.html#getLength%2D%2D[getLength]()++ (link:{java9-javadoc}/java/text/Bidi.html#getLength%2D%2D[java 9]) +* ++[[painless-api-reference-Bidi-getLevelAt-1]]int link:{java8-javadoc}/java/text/Bidi.html#getLevelAt%2Dint%2D[getLevelAt](int)++ (link:{java9-javadoc}/java/text/Bidi.html#getLevelAt%2Dint%2D[java 9]) +* ++[[painless-api-reference-Bidi-getRunCount-0]]int link:{java8-javadoc}/java/text/Bidi.html#getRunCount%2D%2D[getRunCount]()++ (link:{java9-javadoc}/java/text/Bidi.html#getRunCount%2D%2D[java 9]) +* ++[[painless-api-reference-Bidi-getRunLevel-1]]int link:{java8-javadoc}/java/text/Bidi.html#getRunLevel%2Dint%2D[getRunLevel](int)++ (link:{java9-javadoc}/java/text/Bidi.html#getRunLevel%2Dint%2D[java 9]) +* ++[[painless-api-reference-Bidi-getRunLimit-1]]int link:{java8-javadoc}/java/text/Bidi.html#getRunLimit%2Dint%2D[getRunLimit](int)++ (link:{java9-javadoc}/java/text/Bidi.html#getRunLimit%2Dint%2D[java 9]) +* ++[[painless-api-reference-Bidi-getRunStart-1]]int link:{java8-javadoc}/java/text/Bidi.html#getRunStart%2Dint%2D[getRunStart](int)++ (link:{java9-javadoc}/java/text/Bidi.html#getRunStart%2Dint%2D[java 9]) +* ++[[painless-api-reference-Bidi-isLeftToRight-0]]boolean link:{java8-javadoc}/java/text/Bidi.html#isLeftToRight%2D%2D[isLeftToRight]()++ (link:{java9-javadoc}/java/text/Bidi.html#isLeftToRight%2D%2D[java 9]) +* ++[[painless-api-reference-Bidi-isMixed-0]]boolean link:{java8-javadoc}/java/text/Bidi.html#isMixed%2D%2D[isMixed]()++ (link:{java9-javadoc}/java/text/Bidi.html#isMixed%2D%2D[java 9]) +* ++[[painless-api-reference-Bidi-isRightToLeft-0]]boolean link:{java8-javadoc}/java/text/Bidi.html#isRightToLeft%2D%2D[isRightToLeft]()++ (link:{java9-javadoc}/java/text/Bidi.html#isRightToLeft%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/BigDecimal.asciidoc b/docs/painless/painless-api-reference/BigDecimal.asciidoc new file mode 100644 index 0000000000000..ab8a78f35c15b --- /dev/null +++ b/docs/painless/painless-api-reference/BigDecimal.asciidoc @@ -0,0 +1,57 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-BigDecimal]]++BigDecimal++:: +** [[painless-api-reference-BigDecimal-ONE]]static <> link:{java8-javadoc}/java/math/BigDecimal.html#ONE[ONE] (link:{java9-javadoc}/java/math/BigDecimal.html#ONE[java 9]) +** [[painless-api-reference-BigDecimal-TEN]]static <> link:{java8-javadoc}/java/math/BigDecimal.html#TEN[TEN] (link:{java9-javadoc}/java/math/BigDecimal.html#TEN[java 9]) +** [[painless-api-reference-BigDecimal-ZERO]]static <> link:{java8-javadoc}/java/math/BigDecimal.html#ZERO[ZERO] (link:{java9-javadoc}/java/math/BigDecimal.html#ZERO[java 9]) +* ++[[painless-api-reference-BigDecimal-valueOf-1]]static <> link:{java8-javadoc}/java/math/BigDecimal.html#valueOf%2Ddouble%2D[valueOf](double)++ (link:{java9-javadoc}/java/math/BigDecimal.html#valueOf%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-BigDecimal-1]]link:{java8-javadoc}/java/math/BigDecimal.html#BigDecimal%2Djava.lang.String%2D[BigDecimal](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#BigDecimal%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-BigDecimal-2]]link:{java8-javadoc}/java/math/BigDecimal.html#BigDecimal%2Djava.lang.String%2Djava.math.MathContext%2D[BigDecimal](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#BigDecimal%2Djava.lang.String%2Djava.math.MathContext%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-abs-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#abs%2D%2D[abs]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#abs%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-abs-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#abs%2Djava.math.MathContext%2D[abs](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#abs%2Djava.math.MathContext%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-add-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#add%2Djava.math.BigDecimal%2D[add](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#add%2Djava.math.BigDecimal%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-add-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#add%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[add](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#add%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-byteValueExact-0]]byte link:{java8-javadoc}/java/math/BigDecimal.html#byteValueExact%2D%2D[byteValueExact]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#byteValueExact%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-compareTo-1]]int link:{java8-javadoc}/java/math/BigDecimal.html#compareTo%2Djava.math.BigDecimal%2D[compareTo](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#compareTo%2Djava.math.BigDecimal%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-divide-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#divide%2Djava.math.BigDecimal%2D[divide](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#divide%2Djava.math.BigDecimal%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-divide-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#divide%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[divide](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#divide%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-divideAndRemainder-1]]<>[] link:{java8-javadoc}/java/math/BigDecimal.html#divideAndRemainder%2Djava.math.BigDecimal%2D[divideAndRemainder](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#divideAndRemainder%2Djava.math.BigDecimal%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-divideAndRemainder-2]]<>[] link:{java8-javadoc}/java/math/BigDecimal.html#divideAndRemainder%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[divideAndRemainder](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#divideAndRemainder%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-divideToIntegralValue-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#divideToIntegralValue%2Djava.math.BigDecimal%2D[divideToIntegralValue](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#divideToIntegralValue%2Djava.math.BigDecimal%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-divideToIntegralValue-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#divideToIntegralValue%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[divideToIntegralValue](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#divideToIntegralValue%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-intValueExact-0]]int link:{java8-javadoc}/java/math/BigDecimal.html#intValueExact%2D%2D[intValueExact]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#intValueExact%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-longValueExact-0]]long link:{java8-javadoc}/java/math/BigDecimal.html#longValueExact%2D%2D[longValueExact]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#longValueExact%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-max-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#max%2Djava.math.BigDecimal%2D[max](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#max%2Djava.math.BigDecimal%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-min-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#min%2Djava.math.BigDecimal%2D[min](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#min%2Djava.math.BigDecimal%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-movePointLeft-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#movePointLeft%2Dint%2D[movePointLeft](int)++ (link:{java9-javadoc}/java/math/BigDecimal.html#movePointLeft%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-movePointRight-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#movePointRight%2Dint%2D[movePointRight](int)++ (link:{java9-javadoc}/java/math/BigDecimal.html#movePointRight%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-multiply-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#multiply%2Djava.math.BigDecimal%2D[multiply](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#multiply%2Djava.math.BigDecimal%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-multiply-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#multiply%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[multiply](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#multiply%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-negate-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#negate%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-negate-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#negate%2Djava.math.MathContext%2D[negate](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#negate%2Djava.math.MathContext%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-plus-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#plus%2D%2D[plus]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#plus%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-plus-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#plus%2Djava.math.MathContext%2D[plus](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#plus%2Djava.math.MathContext%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-pow-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#pow%2Dint%2D[pow](int)++ (link:{java9-javadoc}/java/math/BigDecimal.html#pow%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-pow-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#pow%2Dint%2Djava.math.MathContext%2D[pow](int, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#pow%2Dint%2Djava.math.MathContext%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-precision-0]]int link:{java8-javadoc}/java/math/BigDecimal.html#precision%2D%2D[precision]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#precision%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-remainder-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#remainder%2Djava.math.BigDecimal%2D[remainder](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#remainder%2Djava.math.BigDecimal%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-remainder-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#remainder%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[remainder](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#remainder%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-round-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#round%2Djava.math.MathContext%2D[round](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#round%2Djava.math.MathContext%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-scale-0]]int link:{java8-javadoc}/java/math/BigDecimal.html#scale%2D%2D[scale]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#scale%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-scaleByPowerOfTen-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#scaleByPowerOfTen%2Dint%2D[scaleByPowerOfTen](int)++ (link:{java9-javadoc}/java/math/BigDecimal.html#scaleByPowerOfTen%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-setScale-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#setScale%2Dint%2D[setScale](int)++ (link:{java9-javadoc}/java/math/BigDecimal.html#setScale%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-setScale-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#setScale%2Dint%2Djava.math.RoundingMode%2D[setScale](int, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#setScale%2Dint%2Djava.math.RoundingMode%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-shortValueExact-0]]short link:{java8-javadoc}/java/math/BigDecimal.html#shortValueExact%2D%2D[shortValueExact]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#shortValueExact%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-signum-0]]int link:{java8-javadoc}/java/math/BigDecimal.html#signum%2D%2D[signum]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#signum%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-stripTrailingZeros-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#stripTrailingZeros%2D%2D[stripTrailingZeros]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#stripTrailingZeros%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-subtract-1]]<> link:{java8-javadoc}/java/math/BigDecimal.html#subtract%2Djava.math.BigDecimal%2D[subtract](<>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#subtract%2Djava.math.BigDecimal%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-subtract-2]]<> link:{java8-javadoc}/java/math/BigDecimal.html#subtract%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[subtract](<>, <>)++ (link:{java9-javadoc}/java/math/BigDecimal.html#subtract%2Djava.math.BigDecimal%2Djava.math.MathContext%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-toBigInteger-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#toBigInteger%2D%2D[toBigInteger]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#toBigInteger%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-toBigIntegerExact-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#toBigIntegerExact%2D%2D[toBigIntegerExact]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#toBigIntegerExact%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-toEngineeringString-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#toEngineeringString%2D%2D[toEngineeringString]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#toEngineeringString%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-toPlainString-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#toPlainString%2D%2D[toPlainString]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#toPlainString%2D%2D[java 9]) +* ++[[painless-api-reference-BigDecimal-ulp-0]]<> link:{java8-javadoc}/java/math/BigDecimal.html#ulp%2D%2D[ulp]()++ (link:{java9-javadoc}/java/math/BigDecimal.html#ulp%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/BigInteger.asciidoc b/docs/painless/painless-api-reference/BigInteger.asciidoc new file mode 100644 index 0000000000000..796355cadd3b7 --- /dev/null +++ b/docs/painless/painless-api-reference/BigInteger.asciidoc @@ -0,0 +1,50 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-BigInteger]]++BigInteger++:: +** [[painless-api-reference-BigInteger-ONE]]static <> link:{java8-javadoc}/java/math/BigInteger.html#ONE[ONE] (link:{java9-javadoc}/java/math/BigInteger.html#ONE[java 9]) +** [[painless-api-reference-BigInteger-TEN]]static <> link:{java8-javadoc}/java/math/BigInteger.html#TEN[TEN] (link:{java9-javadoc}/java/math/BigInteger.html#TEN[java 9]) +** [[painless-api-reference-BigInteger-ZERO]]static <> link:{java8-javadoc}/java/math/BigInteger.html#ZERO[ZERO] (link:{java9-javadoc}/java/math/BigInteger.html#ZERO[java 9]) +* ++[[painless-api-reference-BigInteger-valueOf-1]]static <> link:{java8-javadoc}/java/math/BigInteger.html#valueOf%2Dlong%2D[valueOf](long)++ (link:{java9-javadoc}/java/math/BigInteger.html#valueOf%2Dlong%2D[java 9]) +* ++[[painless-api-reference-BigInteger-BigInteger-1]]link:{java8-javadoc}/java/math/BigInteger.html#BigInteger%2Djava.lang.String%2D[BigInteger](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#BigInteger%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-BigInteger-BigInteger-2]]link:{java8-javadoc}/java/math/BigInteger.html#BigInteger%2Djava.lang.String%2Dint%2D[BigInteger](<>, int)++ (link:{java9-javadoc}/java/math/BigInteger.html#BigInteger%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigInteger-abs-0]]<> link:{java8-javadoc}/java/math/BigInteger.html#abs%2D%2D[abs]()++ (link:{java9-javadoc}/java/math/BigInteger.html#abs%2D%2D[java 9]) +* ++[[painless-api-reference-BigInteger-add-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#add%2Djava.math.BigInteger%2D[add](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#add%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-and-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#and%2Djava.math.BigInteger%2D[and](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#and%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-andNot-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#andNot%2Djava.math.BigInteger%2D[andNot](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#andNot%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-bitCount-0]]int link:{java8-javadoc}/java/math/BigInteger.html#bitCount%2D%2D[bitCount]()++ (link:{java9-javadoc}/java/math/BigInteger.html#bitCount%2D%2D[java 9]) +* ++[[painless-api-reference-BigInteger-bitLength-0]]int link:{java8-javadoc}/java/math/BigInteger.html#bitLength%2D%2D[bitLength]()++ (link:{java9-javadoc}/java/math/BigInteger.html#bitLength%2D%2D[java 9]) +* ++[[painless-api-reference-BigInteger-byteValueExact-0]]byte link:{java8-javadoc}/java/math/BigInteger.html#byteValueExact%2D%2D[byteValueExact]()++ (link:{java9-javadoc}/java/math/BigInteger.html#byteValueExact%2D%2D[java 9]) +* ++[[painless-api-reference-BigInteger-clearBit-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#clearBit%2Dint%2D[clearBit](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#clearBit%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigInteger-compareTo-1]]int link:{java8-javadoc}/java/math/BigInteger.html#compareTo%2Djava.math.BigInteger%2D[compareTo](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#compareTo%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-divide-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#divide%2Djava.math.BigInteger%2D[divide](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#divide%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-divideAndRemainder-1]]<>[] link:{java8-javadoc}/java/math/BigInteger.html#divideAndRemainder%2Djava.math.BigInteger%2D[divideAndRemainder](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#divideAndRemainder%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-flipBit-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#flipBit%2Dint%2D[flipBit](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#flipBit%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigInteger-gcd-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#gcd%2Djava.math.BigInteger%2D[gcd](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#gcd%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-getLowestSetBit-0]]int link:{java8-javadoc}/java/math/BigInteger.html#getLowestSetBit%2D%2D[getLowestSetBit]()++ (link:{java9-javadoc}/java/math/BigInteger.html#getLowestSetBit%2D%2D[java 9]) +* ++[[painless-api-reference-BigInteger-intValueExact-0]]int link:{java8-javadoc}/java/math/BigInteger.html#intValueExact%2D%2D[intValueExact]()++ (link:{java9-javadoc}/java/math/BigInteger.html#intValueExact%2D%2D[java 9]) +* ++[[painless-api-reference-BigInteger-longValueExact-0]]long link:{java8-javadoc}/java/math/BigInteger.html#longValueExact%2D%2D[longValueExact]()++ (link:{java9-javadoc}/java/math/BigInteger.html#longValueExact%2D%2D[java 9]) +* ++[[painless-api-reference-BigInteger-max-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#max%2Djava.math.BigInteger%2D[max](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#max%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-min-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#min%2Djava.math.BigInteger%2D[min](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#min%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-mod-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#mod%2Djava.math.BigInteger%2D[mod](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#mod%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-modInverse-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#modInverse%2Djava.math.BigInteger%2D[modInverse](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#modInverse%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-modPow-2]]<> link:{java8-javadoc}/java/math/BigInteger.html#modPow%2Djava.math.BigInteger%2Djava.math.BigInteger%2D[modPow](<>, <>)++ (link:{java9-javadoc}/java/math/BigInteger.html#modPow%2Djava.math.BigInteger%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-multiply-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#multiply%2Djava.math.BigInteger%2D[multiply](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#multiply%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-negate-0]]<> link:{java8-javadoc}/java/math/BigInteger.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/math/BigInteger.html#negate%2D%2D[java 9]) +* ++[[painless-api-reference-BigInteger-not-0]]<> link:{java8-javadoc}/java/math/BigInteger.html#not%2D%2D[not]()++ (link:{java9-javadoc}/java/math/BigInteger.html#not%2D%2D[java 9]) +* ++[[painless-api-reference-BigInteger-or-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#or%2Djava.math.BigInteger%2D[or](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#or%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-pow-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#pow%2Dint%2D[pow](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#pow%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigInteger-remainder-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#remainder%2Djava.math.BigInteger%2D[remainder](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#remainder%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-setBit-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#setBit%2Dint%2D[setBit](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#setBit%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigInteger-shiftLeft-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#shiftLeft%2Dint%2D[shiftLeft](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#shiftLeft%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigInteger-shiftRight-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#shiftRight%2Dint%2D[shiftRight](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#shiftRight%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigInteger-shortValueExact-0]]short link:{java8-javadoc}/java/math/BigInteger.html#shortValueExact%2D%2D[shortValueExact]()++ (link:{java9-javadoc}/java/math/BigInteger.html#shortValueExact%2D%2D[java 9]) +* ++[[painless-api-reference-BigInteger-signum-0]]int link:{java8-javadoc}/java/math/BigInteger.html#signum%2D%2D[signum]()++ (link:{java9-javadoc}/java/math/BigInteger.html#signum%2D%2D[java 9]) +* ++[[painless-api-reference-BigInteger-subtract-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#subtract%2Djava.math.BigInteger%2D[subtract](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#subtract%2Djava.math.BigInteger%2D[java 9]) +* ++[[painless-api-reference-BigInteger-testBit-1]]boolean link:{java8-javadoc}/java/math/BigInteger.html#testBit%2Dint%2D[testBit](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#testBit%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigInteger-toByteArray-0]]byte[] link:{java8-javadoc}/java/math/BigInteger.html#toByteArray%2D%2D[toByteArray]()++ (link:{java9-javadoc}/java/math/BigInteger.html#toByteArray%2D%2D[java 9]) +* ++[[painless-api-reference-BigInteger-toString-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#toString%2Dint%2D[toString](int)++ (link:{java9-javadoc}/java/math/BigInteger.html#toString%2Dint%2D[java 9]) +* ++[[painless-api-reference-BigInteger-xor-1]]<> link:{java8-javadoc}/java/math/BigInteger.html#xor%2Djava.math.BigInteger%2D[xor](<>)++ (link:{java9-javadoc}/java/math/BigInteger.html#xor%2Djava.math.BigInteger%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/BinaryOperator.asciidoc b/docs/painless/painless-api-reference/BinaryOperator.asciidoc new file mode 100644 index 0000000000000..c7e53be47f720 --- /dev/null +++ b/docs/painless/painless-api-reference/BinaryOperator.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-BinaryOperator]]++BinaryOperator++:: +* ++[[painless-api-reference-BinaryOperator-maxBy-1]]static <> link:{java8-javadoc}/java/util/function/BinaryOperator.html#maxBy%2Djava.util.Comparator%2D[maxBy](<>)++ (link:{java9-javadoc}/java/util/function/BinaryOperator.html#maxBy%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-BinaryOperator-minBy-1]]static <> link:{java8-javadoc}/java/util/function/BinaryOperator.html#minBy%2Djava.util.Comparator%2D[minBy](<>)++ (link:{java9-javadoc}/java/util/function/BinaryOperator.html#minBy%2Djava.util.Comparator%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/BitSet.asciidoc b/docs/painless/painless-api-reference/BitSet.asciidoc new file mode 100644 index 0000000000000..158f64675d93e --- /dev/null +++ b/docs/painless/painless-api-reference/BitSet.asciidoc @@ -0,0 +1,34 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-BitSet]]++BitSet++:: +* ++[[painless-api-reference-BitSet-valueOf-1]]static <> link:{java8-javadoc}/java/util/BitSet.html#valueOf%2Dlong:A%2D[valueOf](long[])++ (link:{java9-javadoc}/java/util/BitSet.html#valueOf%2Dlong:A%2D[java 9]) +* ++[[painless-api-reference-BitSet-BitSet-0]]link:{java8-javadoc}/java/util/BitSet.html#BitSet%2D%2D[BitSet]()++ (link:{java9-javadoc}/java/util/BitSet.html#BitSet%2D%2D[java 9]) +* ++[[painless-api-reference-BitSet-BitSet-1]]link:{java8-javadoc}/java/util/BitSet.html#BitSet%2Dint%2D[BitSet](int)++ (link:{java9-javadoc}/java/util/BitSet.html#BitSet%2Dint%2D[java 9]) +* ++[[painless-api-reference-BitSet-and-1]]void link:{java8-javadoc}/java/util/BitSet.html#and%2Djava.util.BitSet%2D[and](<>)++ (link:{java9-javadoc}/java/util/BitSet.html#and%2Djava.util.BitSet%2D[java 9]) +* ++[[painless-api-reference-BitSet-andNot-1]]void link:{java8-javadoc}/java/util/BitSet.html#andNot%2Djava.util.BitSet%2D[andNot](<>)++ (link:{java9-javadoc}/java/util/BitSet.html#andNot%2Djava.util.BitSet%2D[java 9]) +* ++[[painless-api-reference-BitSet-cardinality-0]]int link:{java8-javadoc}/java/util/BitSet.html#cardinality%2D%2D[cardinality]()++ (link:{java9-javadoc}/java/util/BitSet.html#cardinality%2D%2D[java 9]) +* ++[[painless-api-reference-BitSet-clear-0]]void link:{java8-javadoc}/java/util/BitSet.html#clear%2D%2D[clear]()++ (link:{java9-javadoc}/java/util/BitSet.html#clear%2D%2D[java 9]) +* ++[[painless-api-reference-BitSet-clear-1]]void link:{java8-javadoc}/java/util/BitSet.html#clear%2Dint%2D[clear](int)++ (link:{java9-javadoc}/java/util/BitSet.html#clear%2Dint%2D[java 9]) +* ++[[painless-api-reference-BitSet-clear-2]]void link:{java8-javadoc}/java/util/BitSet.html#clear%2Dint%2Dint%2D[clear](int, int)++ (link:{java9-javadoc}/java/util/BitSet.html#clear%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-BitSet-clone-0]]def link:{java8-javadoc}/java/util/BitSet.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/BitSet.html#clone%2D%2D[java 9]) +* ++[[painless-api-reference-BitSet-flip-1]]void link:{java8-javadoc}/java/util/BitSet.html#flip%2Dint%2D[flip](int)++ (link:{java9-javadoc}/java/util/BitSet.html#flip%2Dint%2D[java 9]) +* ++[[painless-api-reference-BitSet-flip-2]]void link:{java8-javadoc}/java/util/BitSet.html#flip%2Dint%2Dint%2D[flip](int, int)++ (link:{java9-javadoc}/java/util/BitSet.html#flip%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-BitSet-intersects-1]]boolean link:{java8-javadoc}/java/util/BitSet.html#intersects%2Djava.util.BitSet%2D[intersects](<>)++ (link:{java9-javadoc}/java/util/BitSet.html#intersects%2Djava.util.BitSet%2D[java 9]) +* ++[[painless-api-reference-BitSet-isEmpty-0]]boolean link:{java8-javadoc}/java/util/BitSet.html#isEmpty%2D%2D[isEmpty]()++ (link:{java9-javadoc}/java/util/BitSet.html#isEmpty%2D%2D[java 9]) +* ++[[painless-api-reference-BitSet-length-0]]int link:{java8-javadoc}/java/util/BitSet.html#length%2D%2D[length]()++ (link:{java9-javadoc}/java/util/BitSet.html#length%2D%2D[java 9]) +* ++[[painless-api-reference-BitSet-nextClearBit-1]]int link:{java8-javadoc}/java/util/BitSet.html#nextClearBit%2Dint%2D[nextClearBit](int)++ (link:{java9-javadoc}/java/util/BitSet.html#nextClearBit%2Dint%2D[java 9]) +* ++[[painless-api-reference-BitSet-nextSetBit-1]]int link:{java8-javadoc}/java/util/BitSet.html#nextSetBit%2Dint%2D[nextSetBit](int)++ (link:{java9-javadoc}/java/util/BitSet.html#nextSetBit%2Dint%2D[java 9]) +* ++[[painless-api-reference-BitSet-or-1]]void link:{java8-javadoc}/java/util/BitSet.html#or%2Djava.util.BitSet%2D[or](<>)++ (link:{java9-javadoc}/java/util/BitSet.html#or%2Djava.util.BitSet%2D[java 9]) +* ++[[painless-api-reference-BitSet-previousClearBit-1]]int link:{java8-javadoc}/java/util/BitSet.html#previousClearBit%2Dint%2D[previousClearBit](int)++ (link:{java9-javadoc}/java/util/BitSet.html#previousClearBit%2Dint%2D[java 9]) +* ++[[painless-api-reference-BitSet-previousSetBit-1]]int link:{java8-javadoc}/java/util/BitSet.html#previousSetBit%2Dint%2D[previousSetBit](int)++ (link:{java9-javadoc}/java/util/BitSet.html#previousSetBit%2Dint%2D[java 9]) +* ++[[painless-api-reference-BitSet-set-1]]void link:{java8-javadoc}/java/util/BitSet.html#set%2Dint%2D[set](int)++ (link:{java9-javadoc}/java/util/BitSet.html#set%2Dint%2D[java 9]) +* ++[[painless-api-reference-BitSet-set-2]]void link:{java8-javadoc}/java/util/BitSet.html#set%2Dint%2Dint%2D[set](int, int)++ (link:{java9-javadoc}/java/util/BitSet.html#set%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-BitSet-set-3]]void link:{java8-javadoc}/java/util/BitSet.html#set%2Dint%2Dint%2Dboolean%2D[set](int, int, boolean)++ (link:{java9-javadoc}/java/util/BitSet.html#set%2Dint%2Dint%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-BitSet-size-0]]int link:{java8-javadoc}/java/util/BitSet.html#size%2D%2D[size]()++ (link:{java9-javadoc}/java/util/BitSet.html#size%2D%2D[java 9]) +* ++[[painless-api-reference-BitSet-toByteArray-0]]byte[] link:{java8-javadoc}/java/util/BitSet.html#toByteArray%2D%2D[toByteArray]()++ (link:{java9-javadoc}/java/util/BitSet.html#toByteArray%2D%2D[java 9]) +* ++[[painless-api-reference-BitSet-toLongArray-0]]long[] link:{java8-javadoc}/java/util/BitSet.html#toLongArray%2D%2D[toLongArray]()++ (link:{java9-javadoc}/java/util/BitSet.html#toLongArray%2D%2D[java 9]) +* ++[[painless-api-reference-BitSet-xor-1]]void link:{java8-javadoc}/java/util/BitSet.html#xor%2Djava.util.BitSet%2D[xor](<>)++ (link:{java9-javadoc}/java/util/BitSet.html#xor%2Djava.util.BitSet%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Boolean.asciidoc b/docs/painless/painless-api-reference/Boolean.asciidoc new file mode 100644 index 0000000000000..47000e58fa828 --- /dev/null +++ b/docs/painless/painless-api-reference/Boolean.asciidoc @@ -0,0 +1,19 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Boolean]]++Boolean++:: +** [[painless-api-reference-Boolean-FALSE]]static <> link:{java8-javadoc}/java/lang/Boolean.html#FALSE[FALSE] (link:{java9-javadoc}/java/lang/Boolean.html#FALSE[java 9]) +** [[painless-api-reference-Boolean-TRUE]]static <> link:{java8-javadoc}/java/lang/Boolean.html#TRUE[TRUE] (link:{java9-javadoc}/java/lang/Boolean.html#TRUE[java 9]) +* ++[[painless-api-reference-Boolean-compare-2]]static int link:{java8-javadoc}/java/lang/Boolean.html#compare%2Dboolean%2Dboolean%2D[compare](boolean, boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#compare%2Dboolean%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-Boolean-hashCode-1]]static int link:{java8-javadoc}/java/lang/Boolean.html#hashCode%2Dboolean%2D[hashCode](boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#hashCode%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-Boolean-logicalAnd-2]]static boolean link:{java8-javadoc}/java/lang/Boolean.html#logicalAnd%2Dboolean%2Dboolean%2D[logicalAnd](boolean, boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#logicalAnd%2Dboolean%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-Boolean-logicalOr-2]]static boolean link:{java8-javadoc}/java/lang/Boolean.html#logicalOr%2Dboolean%2Dboolean%2D[logicalOr](boolean, boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#logicalOr%2Dboolean%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-Boolean-logicalXor-2]]static boolean link:{java8-javadoc}/java/lang/Boolean.html#logicalXor%2Dboolean%2Dboolean%2D[logicalXor](boolean, boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#logicalXor%2Dboolean%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-Boolean-parseBoolean-1]]static boolean link:{java8-javadoc}/java/lang/Boolean.html#parseBoolean%2Djava.lang.String%2D[parseBoolean](<>)++ (link:{java9-javadoc}/java/lang/Boolean.html#parseBoolean%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Boolean-toString-1]]static <> link:{java8-javadoc}/java/lang/Boolean.html#toString%2Dboolean%2D[toString](boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#toString%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-Boolean-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Boolean.html#valueOf%2Dboolean%2D[valueOf](boolean)++ (link:{java9-javadoc}/java/lang/Boolean.html#valueOf%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-Boolean-booleanValue-0]]boolean link:{java8-javadoc}/java/lang/Boolean.html#booleanValue%2D%2D[booleanValue]()++ (link:{java9-javadoc}/java/lang/Boolean.html#booleanValue%2D%2D[java 9]) +* ++[[painless-api-reference-Boolean-compareTo-1]]int link:{java8-javadoc}/java/lang/Boolean.html#compareTo%2Djava.lang.Boolean%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Boolean.html#compareTo%2Djava.lang.Boolean%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/BooleanSupplier.asciidoc b/docs/painless/painless-api-reference/BooleanSupplier.asciidoc new file mode 100644 index 0000000000000..9a34d1caf2d99 --- /dev/null +++ b/docs/painless/painless-api-reference/BooleanSupplier.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-BooleanSupplier]]++BooleanSupplier++:: +* ++[[painless-api-reference-BooleanSupplier-getAsBoolean-0]]boolean link:{java8-javadoc}/java/util/function/BooleanSupplier.html#getAsBoolean%2D%2D[getAsBoolean]()++ (link:{java9-javadoc}/java/util/function/BooleanSupplier.html#getAsBoolean%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/BreakIterator.asciidoc b/docs/painless/painless-api-reference/BreakIterator.asciidoc new file mode 100644 index 0000000000000..0e830859c457f --- /dev/null +++ b/docs/painless/painless-api-reference/BreakIterator.asciidoc @@ -0,0 +1,29 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-BreakIterator]]++BreakIterator++:: +** [[painless-api-reference-BreakIterator-DONE]]static int link:{java8-javadoc}/java/text/BreakIterator.html#DONE[DONE] (link:{java9-javadoc}/java/text/BreakIterator.html#DONE[java 9]) +* ++[[painless-api-reference-BreakIterator-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/text/BreakIterator.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#getAvailableLocales%2D%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-getCharacterInstance-0]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getCharacterInstance%2D%2D[getCharacterInstance]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#getCharacterInstance%2D%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-getCharacterInstance-1]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getCharacterInstance%2Djava.util.Locale%2D[getCharacterInstance](<>)++ (link:{java9-javadoc}/java/text/BreakIterator.html#getCharacterInstance%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-getLineInstance-0]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getLineInstance%2D%2D[getLineInstance]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#getLineInstance%2D%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-getLineInstance-1]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getLineInstance%2Djava.util.Locale%2D[getLineInstance](<>)++ (link:{java9-javadoc}/java/text/BreakIterator.html#getLineInstance%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-getSentenceInstance-0]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getSentenceInstance%2D%2D[getSentenceInstance]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#getSentenceInstance%2D%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-getSentenceInstance-1]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getSentenceInstance%2Djava.util.Locale%2D[getSentenceInstance](<>)++ (link:{java9-javadoc}/java/text/BreakIterator.html#getSentenceInstance%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-getWordInstance-0]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getWordInstance%2D%2D[getWordInstance]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#getWordInstance%2D%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-getWordInstance-1]]static <> link:{java8-javadoc}/java/text/BreakIterator.html#getWordInstance%2Djava.util.Locale%2D[getWordInstance](<>)++ (link:{java9-javadoc}/java/text/BreakIterator.html#getWordInstance%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-clone-0]]def link:{java8-javadoc}/java/text/BreakIterator.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#clone%2D%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-current-0]]int link:{java8-javadoc}/java/text/BreakIterator.html#current%2D%2D[current]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#current%2D%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-first-0]]int link:{java8-javadoc}/java/text/BreakIterator.html#first%2D%2D[first]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#first%2D%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-following-1]]int link:{java8-javadoc}/java/text/BreakIterator.html#following%2Dint%2D[following](int)++ (link:{java9-javadoc}/java/text/BreakIterator.html#following%2Dint%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-getText-0]]<> link:{java8-javadoc}/java/text/BreakIterator.html#getText%2D%2D[getText]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#getText%2D%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-isBoundary-1]]boolean link:{java8-javadoc}/java/text/BreakIterator.html#isBoundary%2Dint%2D[isBoundary](int)++ (link:{java9-javadoc}/java/text/BreakIterator.html#isBoundary%2Dint%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-last-0]]int link:{java8-javadoc}/java/text/BreakIterator.html#last%2D%2D[last]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#last%2D%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-next-0]]int link:{java8-javadoc}/java/text/BreakIterator.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#next%2D%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-next-1]]int link:{java8-javadoc}/java/text/BreakIterator.html#next%2Dint%2D[next](int)++ (link:{java9-javadoc}/java/text/BreakIterator.html#next%2Dint%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-preceding-1]]int link:{java8-javadoc}/java/text/BreakIterator.html#preceding%2Dint%2D[preceding](int)++ (link:{java9-javadoc}/java/text/BreakIterator.html#preceding%2Dint%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-previous-0]]int link:{java8-javadoc}/java/text/BreakIterator.html#previous%2D%2D[previous]()++ (link:{java9-javadoc}/java/text/BreakIterator.html#previous%2D%2D[java 9]) +* ++[[painless-api-reference-BreakIterator-setText-1]]void link:{java8-javadoc}/java/text/BreakIterator.html#setText%2Djava.lang.String%2D[setText](<>)++ (link:{java9-javadoc}/java/text/BreakIterator.html#setText%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Byte.asciidoc b/docs/painless/painless-api-reference/Byte.asciidoc new file mode 100644 index 0000000000000..2fc42b6362540 --- /dev/null +++ b/docs/painless/painless-api-reference/Byte.asciidoc @@ -0,0 +1,22 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Byte]]++Byte++:: +** [[painless-api-reference-Byte-BYTES]]static int link:{java8-javadoc}/java/lang/Byte.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Byte.html#BYTES[java 9]) +** [[painless-api-reference-Byte-MAX_VALUE]]static byte link:{java8-javadoc}/java/lang/Byte.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Byte.html#MAX_VALUE[java 9]) +** [[painless-api-reference-Byte-MIN_VALUE]]static byte link:{java8-javadoc}/java/lang/Byte.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Byte.html#MIN_VALUE[java 9]) +** [[painless-api-reference-Byte-SIZE]]static int link:{java8-javadoc}/java/lang/Byte.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Byte.html#SIZE[java 9]) +* ++[[painless-api-reference-Byte-compare-2]]static int link:{java8-javadoc}/java/lang/Byte.html#compare%2Dbyte%2Dbyte%2D[compare](byte, byte)++ (link:{java9-javadoc}/java/lang/Byte.html#compare%2Dbyte%2Dbyte%2D[java 9]) +* ++[[painless-api-reference-Byte-decode-1]]static <> link:{java8-javadoc}/java/lang/Byte.html#decode%2Djava.lang.String%2D[decode](<>)++ (link:{java9-javadoc}/java/lang/Byte.html#decode%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Byte-hashCode-1]]static int link:{java8-javadoc}/java/lang/Byte.html#hashCode%2Dbyte%2D[hashCode](byte)++ (link:{java9-javadoc}/java/lang/Byte.html#hashCode%2Dbyte%2D[java 9]) +* ++[[painless-api-reference-Byte-parseByte-1]]static byte link:{java8-javadoc}/java/lang/Byte.html#parseByte%2Djava.lang.String%2D[parseByte](<>)++ (link:{java9-javadoc}/java/lang/Byte.html#parseByte%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Byte-parseByte-2]]static byte link:{java8-javadoc}/java/lang/Byte.html#parseByte%2Djava.lang.String%2Dint%2D[parseByte](<>, int)++ (link:{java9-javadoc}/java/lang/Byte.html#parseByte%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-Byte-toString-1]]static <> link:{java8-javadoc}/java/lang/Byte.html#toString%2Dbyte%2D[toString](byte)++ (link:{java9-javadoc}/java/lang/Byte.html#toString%2Dbyte%2D[java 9]) +* ++[[painless-api-reference-Byte-toUnsignedInt-1]]static int link:{java8-javadoc}/java/lang/Byte.html#toUnsignedInt%2Dbyte%2D[toUnsignedInt](byte)++ (link:{java9-javadoc}/java/lang/Byte.html#toUnsignedInt%2Dbyte%2D[java 9]) +* ++[[painless-api-reference-Byte-toUnsignedLong-1]]static long link:{java8-javadoc}/java/lang/Byte.html#toUnsignedLong%2Dbyte%2D[toUnsignedLong](byte)++ (link:{java9-javadoc}/java/lang/Byte.html#toUnsignedLong%2Dbyte%2D[java 9]) +* ++[[painless-api-reference-Byte-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Byte.html#valueOf%2Dbyte%2D[valueOf](byte)++ (link:{java9-javadoc}/java/lang/Byte.html#valueOf%2Dbyte%2D[java 9]) +* ++[[painless-api-reference-Byte-valueOf-2]]static <> link:{java8-javadoc}/java/lang/Byte.html#valueOf%2Djava.lang.String%2Dint%2D[valueOf](<>, int)++ (link:{java9-javadoc}/java/lang/Byte.html#valueOf%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-Byte-compareTo-1]]int link:{java8-javadoc}/java/lang/Byte.html#compareTo%2Djava.lang.Byte%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Byte.html#compareTo%2Djava.lang.Byte%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/BytesRef.asciidoc b/docs/painless/painless-api-reference/BytesRef.asciidoc new file mode 100644 index 0000000000000..6114a5625fcae --- /dev/null +++ b/docs/painless/painless-api-reference/BytesRef.asciidoc @@ -0,0 +1,12 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-BytesRef]]++BytesRef++:: +** [[painless-api-reference-BytesRef-bytes]]byte[] link:{lucene-core-javadoc}/org/apache/lucene/util/BytesRef.html#bytes[bytes] +** [[painless-api-reference-BytesRef-length]]int link:{lucene-core-javadoc}/org/apache/lucene/util/BytesRef.html#length[length] +** [[painless-api-reference-BytesRef-offset]]int link:{lucene-core-javadoc}/org/apache/lucene/util/BytesRef.html#offset[offset] +* ++[[painless-api-reference-BytesRef-bytesEquals-1]]boolean link:{lucene-core-javadoc}/org/apache/lucene/util/BytesRef.html#bytesEquals%2Dorg.apache.lucene.util.BytesRef%2D[bytesEquals](<>)++ +* ++[[painless-api-reference-BytesRef-utf8ToString-0]]<> link:{lucene-core-javadoc}/org/apache/lucene/util/BytesRef.html#utf8ToString%2D%2D[utf8ToString]()++ +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Calendar.Builder.asciidoc b/docs/painless/painless-api-reference/Calendar.Builder.asciidoc new file mode 100644 index 0000000000000..2fd4cafa03637 --- /dev/null +++ b/docs/painless/painless-api-reference/Calendar.Builder.asciidoc @@ -0,0 +1,21 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Calendar-Builder]]++Calendar.Builder++:: +* ++[[painless-api-reference-Calendar-Builder-Calendar.Builder-0]]link:{java8-javadoc}/java/util/Calendar$Builder.html#Calendar.Builder%2D%2D[Calendar.Builder]()++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#Calendar.Builder%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-Builder-build-0]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#build%2D%2D[build]()++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#build%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-Builder-set-2]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#set%2Dint%2Dint%2D[set](int, int)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#set%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-Builder-setCalendarType-1]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setCalendarType%2Djava.lang.String%2D[setCalendarType](<>)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setCalendarType%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Calendar-Builder-setDate-3]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setDate%2Dint%2Dint%2Dint%2D[setDate](int, int, int)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setDate%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-Builder-setFields-1]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setFields%2Dint:A%2D[setFields](int[])++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setFields%2Dint:A%2D[java 9]) +* ++[[painless-api-reference-Calendar-Builder-setInstant-1]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setInstant%2Dlong%2D[setInstant](long)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setInstant%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Calendar-Builder-setLenient-1]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setLenient%2Dboolean%2D[setLenient](boolean)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setLenient%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-Calendar-Builder-setLocale-1]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setLocale%2Djava.util.Locale%2D[setLocale](<>)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setLocale%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Calendar-Builder-setTimeOfDay-3]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setTimeOfDay%2Dint%2Dint%2Dint%2D[setTimeOfDay](int, int, int)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setTimeOfDay%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-Builder-setTimeOfDay-4]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setTimeOfDay%2Dint%2Dint%2Dint%2Dint%2D[setTimeOfDay](int, int, int, int)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setTimeOfDay%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-Builder-setTimeZone-1]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setTimeZone%2Djava.util.TimeZone%2D[setTimeZone](<>)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setTimeZone%2Djava.util.TimeZone%2D[java 9]) +* ++[[painless-api-reference-Calendar-Builder-setWeekDate-3]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setWeekDate%2Dint%2Dint%2Dint%2D[setWeekDate](int, int, int)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setWeekDate%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-Builder-setWeekDefinition-2]]<> link:{java8-javadoc}/java/util/Calendar$Builder.html#setWeekDefinition%2Dint%2Dint%2D[setWeekDefinition](int, int)++ (link:{java9-javadoc}/java/util/Calendar$Builder.html#setWeekDefinition%2Dint%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Calendar.asciidoc b/docs/painless/painless-api-reference/Calendar.asciidoc new file mode 100644 index 0000000000000..e1a489d8c87d1 --- /dev/null +++ b/docs/painless/painless-api-reference/Calendar.asciidoc @@ -0,0 +1,102 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Calendar]]++Calendar++:: +** [[painless-api-reference-Calendar-ALL_STYLES]]static int link:{java8-javadoc}/java/util/Calendar.html#ALL_STYLES[ALL_STYLES] (link:{java9-javadoc}/java/util/Calendar.html#ALL_STYLES[java 9]) +** [[painless-api-reference-Calendar-AM]]static int link:{java8-javadoc}/java/util/Calendar.html#AM[AM] (link:{java9-javadoc}/java/util/Calendar.html#AM[java 9]) +** [[painless-api-reference-Calendar-AM_PM]]static int link:{java8-javadoc}/java/util/Calendar.html#AM_PM[AM_PM] (link:{java9-javadoc}/java/util/Calendar.html#AM_PM[java 9]) +** [[painless-api-reference-Calendar-APRIL]]static int link:{java8-javadoc}/java/util/Calendar.html#APRIL[APRIL] (link:{java9-javadoc}/java/util/Calendar.html#APRIL[java 9]) +** [[painless-api-reference-Calendar-AUGUST]]static int link:{java8-javadoc}/java/util/Calendar.html#AUGUST[AUGUST] (link:{java9-javadoc}/java/util/Calendar.html#AUGUST[java 9]) +** [[painless-api-reference-Calendar-DATE]]static int link:{java8-javadoc}/java/util/Calendar.html#DATE[DATE] (link:{java9-javadoc}/java/util/Calendar.html#DATE[java 9]) +** [[painless-api-reference-Calendar-DAY_OF_MONTH]]static int link:{java8-javadoc}/java/util/Calendar.html#DAY_OF_MONTH[DAY_OF_MONTH] (link:{java9-javadoc}/java/util/Calendar.html#DAY_OF_MONTH[java 9]) +** [[painless-api-reference-Calendar-DAY_OF_WEEK]]static int link:{java8-javadoc}/java/util/Calendar.html#DAY_OF_WEEK[DAY_OF_WEEK] (link:{java9-javadoc}/java/util/Calendar.html#DAY_OF_WEEK[java 9]) +** [[painless-api-reference-Calendar-DAY_OF_WEEK_IN_MONTH]]static int link:{java8-javadoc}/java/util/Calendar.html#DAY_OF_WEEK_IN_MONTH[DAY_OF_WEEK_IN_MONTH] (link:{java9-javadoc}/java/util/Calendar.html#DAY_OF_WEEK_IN_MONTH[java 9]) +** [[painless-api-reference-Calendar-DAY_OF_YEAR]]static int link:{java8-javadoc}/java/util/Calendar.html#DAY_OF_YEAR[DAY_OF_YEAR] (link:{java9-javadoc}/java/util/Calendar.html#DAY_OF_YEAR[java 9]) +** [[painless-api-reference-Calendar-DECEMBER]]static int link:{java8-javadoc}/java/util/Calendar.html#DECEMBER[DECEMBER] (link:{java9-javadoc}/java/util/Calendar.html#DECEMBER[java 9]) +** [[painless-api-reference-Calendar-DST_OFFSET]]static int link:{java8-javadoc}/java/util/Calendar.html#DST_OFFSET[DST_OFFSET] (link:{java9-javadoc}/java/util/Calendar.html#DST_OFFSET[java 9]) +** [[painless-api-reference-Calendar-ERA]]static int link:{java8-javadoc}/java/util/Calendar.html#ERA[ERA] (link:{java9-javadoc}/java/util/Calendar.html#ERA[java 9]) +** [[painless-api-reference-Calendar-FEBRUARY]]static int link:{java8-javadoc}/java/util/Calendar.html#FEBRUARY[FEBRUARY] (link:{java9-javadoc}/java/util/Calendar.html#FEBRUARY[java 9]) +** [[painless-api-reference-Calendar-FIELD_COUNT]]static int link:{java8-javadoc}/java/util/Calendar.html#FIELD_COUNT[FIELD_COUNT] (link:{java9-javadoc}/java/util/Calendar.html#FIELD_COUNT[java 9]) +** [[painless-api-reference-Calendar-FRIDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#FRIDAY[FRIDAY] (link:{java9-javadoc}/java/util/Calendar.html#FRIDAY[java 9]) +** [[painless-api-reference-Calendar-HOUR]]static int link:{java8-javadoc}/java/util/Calendar.html#HOUR[HOUR] (link:{java9-javadoc}/java/util/Calendar.html#HOUR[java 9]) +** [[painless-api-reference-Calendar-HOUR_OF_DAY]]static int link:{java8-javadoc}/java/util/Calendar.html#HOUR_OF_DAY[HOUR_OF_DAY] (link:{java9-javadoc}/java/util/Calendar.html#HOUR_OF_DAY[java 9]) +** [[painless-api-reference-Calendar-JANUARY]]static int link:{java8-javadoc}/java/util/Calendar.html#JANUARY[JANUARY] (link:{java9-javadoc}/java/util/Calendar.html#JANUARY[java 9]) +** [[painless-api-reference-Calendar-JULY]]static int link:{java8-javadoc}/java/util/Calendar.html#JULY[JULY] (link:{java9-javadoc}/java/util/Calendar.html#JULY[java 9]) +** [[painless-api-reference-Calendar-JUNE]]static int link:{java8-javadoc}/java/util/Calendar.html#JUNE[JUNE] (link:{java9-javadoc}/java/util/Calendar.html#JUNE[java 9]) +** [[painless-api-reference-Calendar-LONG]]static int link:{java8-javadoc}/java/util/Calendar.html#LONG[LONG] (link:{java9-javadoc}/java/util/Calendar.html#LONG[java 9]) +** [[painless-api-reference-Calendar-LONG_FORMAT]]static int link:{java8-javadoc}/java/util/Calendar.html#LONG_FORMAT[LONG_FORMAT] (link:{java9-javadoc}/java/util/Calendar.html#LONG_FORMAT[java 9]) +** [[painless-api-reference-Calendar-LONG_STANDALONE]]static int link:{java8-javadoc}/java/util/Calendar.html#LONG_STANDALONE[LONG_STANDALONE] (link:{java9-javadoc}/java/util/Calendar.html#LONG_STANDALONE[java 9]) +** [[painless-api-reference-Calendar-MARCH]]static int link:{java8-javadoc}/java/util/Calendar.html#MARCH[MARCH] (link:{java9-javadoc}/java/util/Calendar.html#MARCH[java 9]) +** [[painless-api-reference-Calendar-MAY]]static int link:{java8-javadoc}/java/util/Calendar.html#MAY[MAY] (link:{java9-javadoc}/java/util/Calendar.html#MAY[java 9]) +** [[painless-api-reference-Calendar-MILLISECOND]]static int link:{java8-javadoc}/java/util/Calendar.html#MILLISECOND[MILLISECOND] (link:{java9-javadoc}/java/util/Calendar.html#MILLISECOND[java 9]) +** [[painless-api-reference-Calendar-MINUTE]]static int link:{java8-javadoc}/java/util/Calendar.html#MINUTE[MINUTE] (link:{java9-javadoc}/java/util/Calendar.html#MINUTE[java 9]) +** [[painless-api-reference-Calendar-MONDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#MONDAY[MONDAY] (link:{java9-javadoc}/java/util/Calendar.html#MONDAY[java 9]) +** [[painless-api-reference-Calendar-MONTH]]static int link:{java8-javadoc}/java/util/Calendar.html#MONTH[MONTH] (link:{java9-javadoc}/java/util/Calendar.html#MONTH[java 9]) +** [[painless-api-reference-Calendar-NARROW_FORMAT]]static int link:{java8-javadoc}/java/util/Calendar.html#NARROW_FORMAT[NARROW_FORMAT] (link:{java9-javadoc}/java/util/Calendar.html#NARROW_FORMAT[java 9]) +** [[painless-api-reference-Calendar-NARROW_STANDALONE]]static int link:{java8-javadoc}/java/util/Calendar.html#NARROW_STANDALONE[NARROW_STANDALONE] (link:{java9-javadoc}/java/util/Calendar.html#NARROW_STANDALONE[java 9]) +** [[painless-api-reference-Calendar-NOVEMBER]]static int link:{java8-javadoc}/java/util/Calendar.html#NOVEMBER[NOVEMBER] (link:{java9-javadoc}/java/util/Calendar.html#NOVEMBER[java 9]) +** [[painless-api-reference-Calendar-OCTOBER]]static int link:{java8-javadoc}/java/util/Calendar.html#OCTOBER[OCTOBER] (link:{java9-javadoc}/java/util/Calendar.html#OCTOBER[java 9]) +** [[painless-api-reference-Calendar-PM]]static int link:{java8-javadoc}/java/util/Calendar.html#PM[PM] (link:{java9-javadoc}/java/util/Calendar.html#PM[java 9]) +** [[painless-api-reference-Calendar-SATURDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#SATURDAY[SATURDAY] (link:{java9-javadoc}/java/util/Calendar.html#SATURDAY[java 9]) +** [[painless-api-reference-Calendar-SECOND]]static int link:{java8-javadoc}/java/util/Calendar.html#SECOND[SECOND] (link:{java9-javadoc}/java/util/Calendar.html#SECOND[java 9]) +** [[painless-api-reference-Calendar-SEPTEMBER]]static int link:{java8-javadoc}/java/util/Calendar.html#SEPTEMBER[SEPTEMBER] (link:{java9-javadoc}/java/util/Calendar.html#SEPTEMBER[java 9]) +** [[painless-api-reference-Calendar-SHORT]]static int link:{java8-javadoc}/java/util/Calendar.html#SHORT[SHORT] (link:{java9-javadoc}/java/util/Calendar.html#SHORT[java 9]) +** [[painless-api-reference-Calendar-SHORT_FORMAT]]static int link:{java8-javadoc}/java/util/Calendar.html#SHORT_FORMAT[SHORT_FORMAT] (link:{java9-javadoc}/java/util/Calendar.html#SHORT_FORMAT[java 9]) +** [[painless-api-reference-Calendar-SHORT_STANDALONE]]static int link:{java8-javadoc}/java/util/Calendar.html#SHORT_STANDALONE[SHORT_STANDALONE] (link:{java9-javadoc}/java/util/Calendar.html#SHORT_STANDALONE[java 9]) +** [[painless-api-reference-Calendar-SUNDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#SUNDAY[SUNDAY] (link:{java9-javadoc}/java/util/Calendar.html#SUNDAY[java 9]) +** [[painless-api-reference-Calendar-THURSDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#THURSDAY[THURSDAY] (link:{java9-javadoc}/java/util/Calendar.html#THURSDAY[java 9]) +** [[painless-api-reference-Calendar-TUESDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#TUESDAY[TUESDAY] (link:{java9-javadoc}/java/util/Calendar.html#TUESDAY[java 9]) +** [[painless-api-reference-Calendar-UNDECIMBER]]static int link:{java8-javadoc}/java/util/Calendar.html#UNDECIMBER[UNDECIMBER] (link:{java9-javadoc}/java/util/Calendar.html#UNDECIMBER[java 9]) +** [[painless-api-reference-Calendar-WEDNESDAY]]static int link:{java8-javadoc}/java/util/Calendar.html#WEDNESDAY[WEDNESDAY] (link:{java9-javadoc}/java/util/Calendar.html#WEDNESDAY[java 9]) +** [[painless-api-reference-Calendar-WEEK_OF_MONTH]]static int link:{java8-javadoc}/java/util/Calendar.html#WEEK_OF_MONTH[WEEK_OF_MONTH] (link:{java9-javadoc}/java/util/Calendar.html#WEEK_OF_MONTH[java 9]) +** [[painless-api-reference-Calendar-WEEK_OF_YEAR]]static int link:{java8-javadoc}/java/util/Calendar.html#WEEK_OF_YEAR[WEEK_OF_YEAR] (link:{java9-javadoc}/java/util/Calendar.html#WEEK_OF_YEAR[java 9]) +** [[painless-api-reference-Calendar-YEAR]]static int link:{java8-javadoc}/java/util/Calendar.html#YEAR[YEAR] (link:{java9-javadoc}/java/util/Calendar.html#YEAR[java 9]) +** [[painless-api-reference-Calendar-ZONE_OFFSET]]static int link:{java8-javadoc}/java/util/Calendar.html#ZONE_OFFSET[ZONE_OFFSET] (link:{java9-javadoc}/java/util/Calendar.html#ZONE_OFFSET[java 9]) +* ++[[painless-api-reference-Calendar-getAvailableCalendarTypes-0]]static <> link:{java8-javadoc}/java/util/Calendar.html#getAvailableCalendarTypes%2D%2D[getAvailableCalendarTypes]()++ (link:{java9-javadoc}/java/util/Calendar.html#getAvailableCalendarTypes%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/util/Calendar.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/util/Calendar.html#getAvailableLocales%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-getInstance-0]]static <> link:{java8-javadoc}/java/util/Calendar.html#getInstance%2D%2D[getInstance]()++ (link:{java9-javadoc}/java/util/Calendar.html#getInstance%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-getInstance-1]]static <> link:{java8-javadoc}/java/util/Calendar.html#getInstance%2Djava.util.TimeZone%2D[getInstance](<>)++ (link:{java9-javadoc}/java/util/Calendar.html#getInstance%2Djava.util.TimeZone%2D[java 9]) +* ++[[painless-api-reference-Calendar-getInstance-2]]static <> link:{java8-javadoc}/java/util/Calendar.html#getInstance%2Djava.util.TimeZone%2Djava.util.Locale%2D[getInstance](<>, <>)++ (link:{java9-javadoc}/java/util/Calendar.html#getInstance%2Djava.util.TimeZone%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Calendar-add-2]]void link:{java8-javadoc}/java/util/Calendar.html#add%2Dint%2Dint%2D[add](int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#add%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-after-1]]boolean link:{java8-javadoc}/java/util/Calendar.html#after%2Djava.lang.Object%2D[after](<>)++ (link:{java9-javadoc}/java/util/Calendar.html#after%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Calendar-before-1]]boolean link:{java8-javadoc}/java/util/Calendar.html#before%2Djava.lang.Object%2D[before](<>)++ (link:{java9-javadoc}/java/util/Calendar.html#before%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Calendar-clear-0]]void link:{java8-javadoc}/java/util/Calendar.html#clear%2D%2D[clear]()++ (link:{java9-javadoc}/java/util/Calendar.html#clear%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-clear-1]]void link:{java8-javadoc}/java/util/Calendar.html#clear%2Dint%2D[clear](int)++ (link:{java9-javadoc}/java/util/Calendar.html#clear%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-clone-0]]def link:{java8-javadoc}/java/util/Calendar.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/Calendar.html#clone%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-compareTo-1]]int link:{java8-javadoc}/java/util/Calendar.html#compareTo%2Djava.util.Calendar%2D[compareTo](<>)++ (link:{java9-javadoc}/java/util/Calendar.html#compareTo%2Djava.util.Calendar%2D[java 9]) +* ++[[painless-api-reference-Calendar-get-1]]int link:{java8-javadoc}/java/util/Calendar.html#get%2Dint%2D[get](int)++ (link:{java9-javadoc}/java/util/Calendar.html#get%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-getActualMaximum-1]]int link:{java8-javadoc}/java/util/Calendar.html#getActualMaximum%2Dint%2D[getActualMaximum](int)++ (link:{java9-javadoc}/java/util/Calendar.html#getActualMaximum%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-getActualMinimum-1]]int link:{java8-javadoc}/java/util/Calendar.html#getActualMinimum%2Dint%2D[getActualMinimum](int)++ (link:{java9-javadoc}/java/util/Calendar.html#getActualMinimum%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-getCalendarType-0]]<> link:{java8-javadoc}/java/util/Calendar.html#getCalendarType%2D%2D[getCalendarType]()++ (link:{java9-javadoc}/java/util/Calendar.html#getCalendarType%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-getDisplayName-3]]<> link:{java8-javadoc}/java/util/Calendar.html#getDisplayName%2Dint%2Dint%2Djava.util.Locale%2D[getDisplayName](int, int, <>)++ (link:{java9-javadoc}/java/util/Calendar.html#getDisplayName%2Dint%2Dint%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Calendar-getDisplayNames-3]]<> link:{java8-javadoc}/java/util/Calendar.html#getDisplayNames%2Dint%2Dint%2Djava.util.Locale%2D[getDisplayNames](int, int, <>)++ (link:{java9-javadoc}/java/util/Calendar.html#getDisplayNames%2Dint%2Dint%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Calendar-getFirstDayOfWeek-0]]int link:{java8-javadoc}/java/util/Calendar.html#getFirstDayOfWeek%2D%2D[getFirstDayOfWeek]()++ (link:{java9-javadoc}/java/util/Calendar.html#getFirstDayOfWeek%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-getGreatestMinimum-1]]int link:{java8-javadoc}/java/util/Calendar.html#getGreatestMinimum%2Dint%2D[getGreatestMinimum](int)++ (link:{java9-javadoc}/java/util/Calendar.html#getGreatestMinimum%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-getLeastMaximum-1]]int link:{java8-javadoc}/java/util/Calendar.html#getLeastMaximum%2Dint%2D[getLeastMaximum](int)++ (link:{java9-javadoc}/java/util/Calendar.html#getLeastMaximum%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-getMaximum-1]]int link:{java8-javadoc}/java/util/Calendar.html#getMaximum%2Dint%2D[getMaximum](int)++ (link:{java9-javadoc}/java/util/Calendar.html#getMaximum%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-getMinimalDaysInFirstWeek-0]]int link:{java8-javadoc}/java/util/Calendar.html#getMinimalDaysInFirstWeek%2D%2D[getMinimalDaysInFirstWeek]()++ (link:{java9-javadoc}/java/util/Calendar.html#getMinimalDaysInFirstWeek%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-getMinimum-1]]int link:{java8-javadoc}/java/util/Calendar.html#getMinimum%2Dint%2D[getMinimum](int)++ (link:{java9-javadoc}/java/util/Calendar.html#getMinimum%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-getTime-0]]<> link:{java8-javadoc}/java/util/Calendar.html#getTime%2D%2D[getTime]()++ (link:{java9-javadoc}/java/util/Calendar.html#getTime%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-getTimeInMillis-0]]long link:{java8-javadoc}/java/util/Calendar.html#getTimeInMillis%2D%2D[getTimeInMillis]()++ (link:{java9-javadoc}/java/util/Calendar.html#getTimeInMillis%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-getTimeZone-0]]<> link:{java8-javadoc}/java/util/Calendar.html#getTimeZone%2D%2D[getTimeZone]()++ (link:{java9-javadoc}/java/util/Calendar.html#getTimeZone%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-getWeekYear-0]]int link:{java8-javadoc}/java/util/Calendar.html#getWeekYear%2D%2D[getWeekYear]()++ (link:{java9-javadoc}/java/util/Calendar.html#getWeekYear%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-getWeeksInWeekYear-0]]int link:{java8-javadoc}/java/util/Calendar.html#getWeeksInWeekYear%2D%2D[getWeeksInWeekYear]()++ (link:{java9-javadoc}/java/util/Calendar.html#getWeeksInWeekYear%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-isLenient-0]]boolean link:{java8-javadoc}/java/util/Calendar.html#isLenient%2D%2D[isLenient]()++ (link:{java9-javadoc}/java/util/Calendar.html#isLenient%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-isSet-1]]boolean link:{java8-javadoc}/java/util/Calendar.html#isSet%2Dint%2D[isSet](int)++ (link:{java9-javadoc}/java/util/Calendar.html#isSet%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-isWeekDateSupported-0]]boolean link:{java8-javadoc}/java/util/Calendar.html#isWeekDateSupported%2D%2D[isWeekDateSupported]()++ (link:{java9-javadoc}/java/util/Calendar.html#isWeekDateSupported%2D%2D[java 9]) +* ++[[painless-api-reference-Calendar-roll-2]]void link:{java8-javadoc}/java/util/Calendar.html#roll%2Dint%2Dint%2D[roll](int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#roll%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-set-2]]void link:{java8-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2D[set](int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-set-3]]void link:{java8-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2Dint%2D[set](int, int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-set-5]]void link:{java8-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2Dint%2Dint%2Dint%2D[set](int, int, int, int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-set-6]]void link:{java8-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[set](int, int, int, int, int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#set%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-setFirstDayOfWeek-1]]void link:{java8-javadoc}/java/util/Calendar.html#setFirstDayOfWeek%2Dint%2D[setFirstDayOfWeek](int)++ (link:{java9-javadoc}/java/util/Calendar.html#setFirstDayOfWeek%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-setLenient-1]]void link:{java8-javadoc}/java/util/Calendar.html#setLenient%2Dboolean%2D[setLenient](boolean)++ (link:{java9-javadoc}/java/util/Calendar.html#setLenient%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-Calendar-setMinimalDaysInFirstWeek-1]]void link:{java8-javadoc}/java/util/Calendar.html#setMinimalDaysInFirstWeek%2Dint%2D[setMinimalDaysInFirstWeek](int)++ (link:{java9-javadoc}/java/util/Calendar.html#setMinimalDaysInFirstWeek%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-setTime-1]]void link:{java8-javadoc}/java/util/Calendar.html#setTime%2Djava.util.Date%2D[setTime](<>)++ (link:{java9-javadoc}/java/util/Calendar.html#setTime%2Djava.util.Date%2D[java 9]) +* ++[[painless-api-reference-Calendar-setTimeInMillis-1]]void link:{java8-javadoc}/java/util/Calendar.html#setTimeInMillis%2Dlong%2D[setTimeInMillis](long)++ (link:{java9-javadoc}/java/util/Calendar.html#setTimeInMillis%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Calendar-setTimeZone-1]]void link:{java8-javadoc}/java/util/Calendar.html#setTimeZone%2Djava.util.TimeZone%2D[setTimeZone](<>)++ (link:{java9-javadoc}/java/util/Calendar.html#setTimeZone%2Djava.util.TimeZone%2D[java 9]) +* ++[[painless-api-reference-Calendar-setWeekDate-3]]void link:{java8-javadoc}/java/util/Calendar.html#setWeekDate%2Dint%2Dint%2Dint%2D[setWeekDate](int, int, int)++ (link:{java9-javadoc}/java/util/Calendar.html#setWeekDate%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Calendar-toInstant-0]]<> link:{java8-javadoc}/java/util/Calendar.html#toInstant%2D%2D[toInstant]()++ (link:{java9-javadoc}/java/util/Calendar.html#toInstant%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/CharSequence.asciidoc b/docs/painless/painless-api-reference/CharSequence.asciidoc new file mode 100644 index 0000000000000..2618a22881e02 --- /dev/null +++ b/docs/painless/painless-api-reference/CharSequence.asciidoc @@ -0,0 +1,15 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-CharSequence]]++CharSequence++:: +* ++[[painless-api-reference-CharSequence-charAt-1]]char link:{java8-javadoc}/java/lang/CharSequence.html#charAt%2Dint%2D[charAt](int)++ (link:{java9-javadoc}/java/lang/CharSequence.html#charAt%2Dint%2D[java 9]) +* ++[[painless-api-reference-CharSequence-chars-0]]<> link:{java8-javadoc}/java/lang/CharSequence.html#chars%2D%2D[chars]()++ (link:{java9-javadoc}/java/lang/CharSequence.html#chars%2D%2D[java 9]) +* ++[[painless-api-reference-CharSequence-codePoints-0]]<> link:{java8-javadoc}/java/lang/CharSequence.html#codePoints%2D%2D[codePoints]()++ (link:{java9-javadoc}/java/lang/CharSequence.html#codePoints%2D%2D[java 9]) +* ++[[painless-api-reference-CharSequence-length-0]]int link:{java8-javadoc}/java/lang/CharSequence.html#length%2D%2D[length]()++ (link:{java9-javadoc}/java/lang/CharSequence.html#length%2D%2D[java 9]) +* ++[[painless-api-reference-CharSequence-replaceAll-2]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#replaceAll%2Djava.lang.CharSequence%2Djava.util.regex.Pattern%2Djava.util.function.Function%2D[replaceAll](<>, <>)++ +* ++[[painless-api-reference-CharSequence-replaceFirst-2]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#replaceFirst%2Djava.lang.CharSequence%2Djava.util.regex.Pattern%2Djava.util.function.Function%2D[replaceFirst](<>, <>)++ +* ++[[painless-api-reference-CharSequence-subSequence-2]]<> link:{java8-javadoc}/java/lang/CharSequence.html#subSequence%2Dint%2Dint%2D[subSequence](int, int)++ (link:{java9-javadoc}/java/lang/CharSequence.html#subSequence%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-CharSequence-toString-0]]<> link:{java8-javadoc}/java/lang/CharSequence.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/lang/CharSequence.html#toString%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Character.Subset.asciidoc b/docs/painless/painless-api-reference/Character.Subset.asciidoc new file mode 100644 index 0000000000000..e8dff322e6e27 --- /dev/null +++ b/docs/painless/painless-api-reference/Character.Subset.asciidoc @@ -0,0 +1,7 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Character-Subset]]++Character.Subset++:: +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Character.UnicodeBlock.asciidoc b/docs/painless/painless-api-reference/Character.UnicodeBlock.asciidoc new file mode 100644 index 0000000000000..b06cc70397a23 --- /dev/null +++ b/docs/painless/painless-api-reference/Character.UnicodeBlock.asciidoc @@ -0,0 +1,229 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Character-UnicodeBlock]]++Character.UnicodeBlock++:: +** [[painless-api-reference-Character-UnicodeBlock-AEGEAN_NUMBERS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#AEGEAN_NUMBERS[AEGEAN_NUMBERS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#AEGEAN_NUMBERS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ALCHEMICAL_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ALCHEMICAL_SYMBOLS[ALCHEMICAL_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ALCHEMICAL_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ALPHABETIC_PRESENTATION_FORMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ALPHABETIC_PRESENTATION_FORMS[ALPHABETIC_PRESENTATION_FORMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ALPHABETIC_PRESENTATION_FORMS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ANCIENT_GREEK_MUSICAL_NOTATION]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ANCIENT_GREEK_MUSICAL_NOTATION[ANCIENT_GREEK_MUSICAL_NOTATION] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ANCIENT_GREEK_MUSICAL_NOTATION[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ANCIENT_GREEK_NUMBERS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ANCIENT_GREEK_NUMBERS[ANCIENT_GREEK_NUMBERS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ANCIENT_GREEK_NUMBERS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ANCIENT_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ANCIENT_SYMBOLS[ANCIENT_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ANCIENT_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ARABIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC[ARABIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ARABIC_EXTENDED_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_EXTENDED_A[ARABIC_EXTENDED_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_EXTENDED_A[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS[ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ARABIC_PRESENTATION_FORMS_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_PRESENTATION_FORMS_A[ARABIC_PRESENTATION_FORMS_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_PRESENTATION_FORMS_A[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ARABIC_PRESENTATION_FORMS_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_PRESENTATION_FORMS_B[ARABIC_PRESENTATION_FORMS_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_PRESENTATION_FORMS_B[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ARABIC_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_SUPPLEMENT[ARABIC_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARABIC_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ARMENIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARMENIAN[ARMENIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARMENIAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ARROWS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ARROWS[ARROWS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ARROWS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-AVESTAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#AVESTAN[AVESTAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#AVESTAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BALINESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BALINESE[BALINESE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BALINESE[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BAMUM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BAMUM[BAMUM] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BAMUM[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BAMUM_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BAMUM_SUPPLEMENT[BAMUM_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BAMUM_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BASIC_LATIN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BASIC_LATIN[BASIC_LATIN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BASIC_LATIN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BATAK]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BATAK[BATAK] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BATAK[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BENGALI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BENGALI[BENGALI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BENGALI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BLOCK_ELEMENTS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BLOCK_ELEMENTS[BLOCK_ELEMENTS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BLOCK_ELEMENTS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BOPOMOFO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BOPOMOFO[BOPOMOFO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BOPOMOFO[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BOPOMOFO_EXTENDED]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BOPOMOFO_EXTENDED[BOPOMOFO_EXTENDED] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BOPOMOFO_EXTENDED[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BOX_DRAWING]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BOX_DRAWING[BOX_DRAWING] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BOX_DRAWING[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BRAHMI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BRAHMI[BRAHMI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BRAHMI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BRAILLE_PATTERNS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BRAILLE_PATTERNS[BRAILLE_PATTERNS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BRAILLE_PATTERNS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BUGINESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BUGINESE[BUGINESE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BUGINESE[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BUHID]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BUHID[BUHID] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BUHID[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-BYZANTINE_MUSICAL_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#BYZANTINE_MUSICAL_SYMBOLS[BYZANTINE_MUSICAL_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#BYZANTINE_MUSICAL_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CARIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CARIAN[CARIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CARIAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CHAKMA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CHAKMA[CHAKMA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CHAKMA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CHAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CHAM[CHAM] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CHAM[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CHEROKEE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CHEROKEE[CHEROKEE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CHEROKEE[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CJK_COMPATIBILITY]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY[CJK_COMPATIBILITY] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CJK_COMPATIBILITY_FORMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY_FORMS[CJK_COMPATIBILITY_FORMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY_FORMS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CJK_COMPATIBILITY_IDEOGRAPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY_IDEOGRAPHS[CJK_COMPATIBILITY_IDEOGRAPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY_IDEOGRAPHS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT[CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CJK_RADICALS_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_RADICALS_SUPPLEMENT[CJK_RADICALS_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_RADICALS_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CJK_STROKES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_STROKES[CJK_STROKES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_STROKES[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CJK_SYMBOLS_AND_PUNCTUATION]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_SYMBOLS_AND_PUNCTUATION[CJK_SYMBOLS_AND_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_SYMBOLS_AND_PUNCTUATION[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CJK_UNIFIED_IDEOGRAPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS[CJK_UNIFIED_IDEOGRAPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A[CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B[CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C[CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D[CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-COMBINING_DIACRITICAL_MARKS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_DIACRITICAL_MARKS[COMBINING_DIACRITICAL_MARKS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_DIACRITICAL_MARKS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-COMBINING_DIACRITICAL_MARKS_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_DIACRITICAL_MARKS_SUPPLEMENT[COMBINING_DIACRITICAL_MARKS_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_DIACRITICAL_MARKS_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-COMBINING_HALF_MARKS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_HALF_MARKS[COMBINING_HALF_MARKS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_HALF_MARKS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-COMBINING_MARKS_FOR_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_MARKS_FOR_SYMBOLS[COMBINING_MARKS_FOR_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COMBINING_MARKS_FOR_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-COMMON_INDIC_NUMBER_FORMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COMMON_INDIC_NUMBER_FORMS[COMMON_INDIC_NUMBER_FORMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COMMON_INDIC_NUMBER_FORMS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CONTROL_PICTURES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CONTROL_PICTURES[CONTROL_PICTURES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CONTROL_PICTURES[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-COPTIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COPTIC[COPTIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COPTIC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-COUNTING_ROD_NUMERALS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#COUNTING_ROD_NUMERALS[COUNTING_ROD_NUMERALS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#COUNTING_ROD_NUMERALS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CUNEIFORM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CUNEIFORM[CUNEIFORM] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CUNEIFORM[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CUNEIFORM_NUMBERS_AND_PUNCTUATION]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CUNEIFORM_NUMBERS_AND_PUNCTUATION[CUNEIFORM_NUMBERS_AND_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CUNEIFORM_NUMBERS_AND_PUNCTUATION[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CURRENCY_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CURRENCY_SYMBOLS[CURRENCY_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CURRENCY_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CYPRIOT_SYLLABARY]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CYPRIOT_SYLLABARY[CYPRIOT_SYLLABARY] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CYPRIOT_SYLLABARY[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CYRILLIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC[CYRILLIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CYRILLIC_EXTENDED_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC_EXTENDED_A[CYRILLIC_EXTENDED_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC_EXTENDED_A[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CYRILLIC_EXTENDED_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC_EXTENDED_B[CYRILLIC_EXTENDED_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC_EXTENDED_B[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-CYRILLIC_SUPPLEMENTARY]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC_SUPPLEMENTARY[CYRILLIC_SUPPLEMENTARY] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#CYRILLIC_SUPPLEMENTARY[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-DESERET]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#DESERET[DESERET] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#DESERET[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-DEVANAGARI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#DEVANAGARI[DEVANAGARI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#DEVANAGARI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-DEVANAGARI_EXTENDED]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#DEVANAGARI_EXTENDED[DEVANAGARI_EXTENDED] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#DEVANAGARI_EXTENDED[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-DINGBATS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#DINGBATS[DINGBATS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#DINGBATS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-DOMINO_TILES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#DOMINO_TILES[DOMINO_TILES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#DOMINO_TILES[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-EGYPTIAN_HIEROGLYPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#EGYPTIAN_HIEROGLYPHS[EGYPTIAN_HIEROGLYPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#EGYPTIAN_HIEROGLYPHS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-EMOTICONS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#EMOTICONS[EMOTICONS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#EMOTICONS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ENCLOSED_ALPHANUMERICS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_ALPHANUMERICS[ENCLOSED_ALPHANUMERICS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_ALPHANUMERICS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ENCLOSED_ALPHANUMERIC_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_ALPHANUMERIC_SUPPLEMENT[ENCLOSED_ALPHANUMERIC_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_ALPHANUMERIC_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ENCLOSED_CJK_LETTERS_AND_MONTHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_CJK_LETTERS_AND_MONTHS[ENCLOSED_CJK_LETTERS_AND_MONTHS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_CJK_LETTERS_AND_MONTHS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ENCLOSED_IDEOGRAPHIC_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_IDEOGRAPHIC_SUPPLEMENT[ENCLOSED_IDEOGRAPHIC_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ENCLOSED_IDEOGRAPHIC_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ETHIOPIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC[ETHIOPIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ETHIOPIC_EXTENDED]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC_EXTENDED[ETHIOPIC_EXTENDED] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC_EXTENDED[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ETHIOPIC_EXTENDED_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC_EXTENDED_A[ETHIOPIC_EXTENDED_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC_EXTENDED_A[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ETHIOPIC_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC_SUPPLEMENT[ETHIOPIC_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ETHIOPIC_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-GENERAL_PUNCTUATION]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GENERAL_PUNCTUATION[GENERAL_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GENERAL_PUNCTUATION[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-GEOMETRIC_SHAPES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GEOMETRIC_SHAPES[GEOMETRIC_SHAPES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GEOMETRIC_SHAPES[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-GEORGIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GEORGIAN[GEORGIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GEORGIAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-GEORGIAN_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GEORGIAN_SUPPLEMENT[GEORGIAN_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GEORGIAN_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-GLAGOLITIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GLAGOLITIC[GLAGOLITIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GLAGOLITIC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-GOTHIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GOTHIC[GOTHIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GOTHIC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-GREEK]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GREEK[GREEK] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GREEK[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-GREEK_EXTENDED]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GREEK_EXTENDED[GREEK_EXTENDED] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GREEK_EXTENDED[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-GUJARATI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GUJARATI[GUJARATI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GUJARATI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-GURMUKHI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#GURMUKHI[GURMUKHI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#GURMUKHI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-HALFWIDTH_AND_FULLWIDTH_FORMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HALFWIDTH_AND_FULLWIDTH_FORMS[HALFWIDTH_AND_FULLWIDTH_FORMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HALFWIDTH_AND_FULLWIDTH_FORMS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-HANGUL_COMPATIBILITY_JAMO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_COMPATIBILITY_JAMO[HANGUL_COMPATIBILITY_JAMO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_COMPATIBILITY_JAMO[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-HANGUL_JAMO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_JAMO[HANGUL_JAMO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_JAMO[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-HANGUL_JAMO_EXTENDED_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_JAMO_EXTENDED_A[HANGUL_JAMO_EXTENDED_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_JAMO_EXTENDED_A[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-HANGUL_JAMO_EXTENDED_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_JAMO_EXTENDED_B[HANGUL_JAMO_EXTENDED_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_JAMO_EXTENDED_B[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-HANGUL_SYLLABLES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_SYLLABLES[HANGUL_SYLLABLES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HANGUL_SYLLABLES[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-HANUNOO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HANUNOO[HANUNOO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HANUNOO[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-HEBREW]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HEBREW[HEBREW] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HEBREW[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-HIGH_PRIVATE_USE_SURROGATES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HIGH_PRIVATE_USE_SURROGATES[HIGH_PRIVATE_USE_SURROGATES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HIGH_PRIVATE_USE_SURROGATES[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-HIGH_SURROGATES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HIGH_SURROGATES[HIGH_SURROGATES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HIGH_SURROGATES[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-HIRAGANA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#HIRAGANA[HIRAGANA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#HIRAGANA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-IDEOGRAPHIC_DESCRIPTION_CHARACTERS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#IDEOGRAPHIC_DESCRIPTION_CHARACTERS[IDEOGRAPHIC_DESCRIPTION_CHARACTERS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#IDEOGRAPHIC_DESCRIPTION_CHARACTERS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-IMPERIAL_ARAMAIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#IMPERIAL_ARAMAIC[IMPERIAL_ARAMAIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#IMPERIAL_ARAMAIC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-INSCRIPTIONAL_PAHLAVI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#INSCRIPTIONAL_PAHLAVI[INSCRIPTIONAL_PAHLAVI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#INSCRIPTIONAL_PAHLAVI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-INSCRIPTIONAL_PARTHIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#INSCRIPTIONAL_PARTHIAN[INSCRIPTIONAL_PARTHIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#INSCRIPTIONAL_PARTHIAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-IPA_EXTENSIONS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#IPA_EXTENSIONS[IPA_EXTENSIONS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#IPA_EXTENSIONS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-JAVANESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#JAVANESE[JAVANESE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#JAVANESE[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-KAITHI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KAITHI[KAITHI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KAITHI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-KANA_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KANA_SUPPLEMENT[KANA_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KANA_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-KANBUN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KANBUN[KANBUN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KANBUN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-KANGXI_RADICALS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KANGXI_RADICALS[KANGXI_RADICALS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KANGXI_RADICALS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-KANNADA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KANNADA[KANNADA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KANNADA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-KATAKANA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KATAKANA[KATAKANA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KATAKANA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-KATAKANA_PHONETIC_EXTENSIONS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KATAKANA_PHONETIC_EXTENSIONS[KATAKANA_PHONETIC_EXTENSIONS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KATAKANA_PHONETIC_EXTENSIONS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-KAYAH_LI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KAYAH_LI[KAYAH_LI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KAYAH_LI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-KHAROSHTHI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KHAROSHTHI[KHAROSHTHI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KHAROSHTHI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-KHMER]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KHMER[KHMER] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KHMER[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-KHMER_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#KHMER_SYMBOLS[KHMER_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#KHMER_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LAO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LAO[LAO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LAO[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LATIN_1_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_1_SUPPLEMENT[LATIN_1_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_1_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LATIN_EXTENDED_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_A[LATIN_EXTENDED_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_A[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LATIN_EXTENDED_ADDITIONAL]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_ADDITIONAL[LATIN_EXTENDED_ADDITIONAL] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_ADDITIONAL[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LATIN_EXTENDED_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_B[LATIN_EXTENDED_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_B[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LATIN_EXTENDED_C]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_C[LATIN_EXTENDED_C] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_C[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LATIN_EXTENDED_D]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_D[LATIN_EXTENDED_D] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LATIN_EXTENDED_D[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LEPCHA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LEPCHA[LEPCHA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LEPCHA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LETTERLIKE_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LETTERLIKE_SYMBOLS[LETTERLIKE_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LETTERLIKE_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LIMBU]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LIMBU[LIMBU] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LIMBU[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LINEAR_B_IDEOGRAMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LINEAR_B_IDEOGRAMS[LINEAR_B_IDEOGRAMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LINEAR_B_IDEOGRAMS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LINEAR_B_SYLLABARY]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LINEAR_B_SYLLABARY[LINEAR_B_SYLLABARY] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LINEAR_B_SYLLABARY[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LISU]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LISU[LISU] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LISU[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LOW_SURROGATES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LOW_SURROGATES[LOW_SURROGATES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LOW_SURROGATES[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LYCIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LYCIAN[LYCIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LYCIAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-LYDIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#LYDIAN[LYDIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#LYDIAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MAHJONG_TILES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MAHJONG_TILES[MAHJONG_TILES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MAHJONG_TILES[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MALAYALAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MALAYALAM[MALAYALAM] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MALAYALAM[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MANDAIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MANDAIC[MANDAIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MANDAIC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MATHEMATICAL_ALPHANUMERIC_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MATHEMATICAL_ALPHANUMERIC_SYMBOLS[MATHEMATICAL_ALPHANUMERIC_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MATHEMATICAL_ALPHANUMERIC_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MATHEMATICAL_OPERATORS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MATHEMATICAL_OPERATORS[MATHEMATICAL_OPERATORS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MATHEMATICAL_OPERATORS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MEETEI_MAYEK]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MEETEI_MAYEK[MEETEI_MAYEK] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MEETEI_MAYEK[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MEETEI_MAYEK_EXTENSIONS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MEETEI_MAYEK_EXTENSIONS[MEETEI_MAYEK_EXTENSIONS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MEETEI_MAYEK_EXTENSIONS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MEROITIC_CURSIVE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MEROITIC_CURSIVE[MEROITIC_CURSIVE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MEROITIC_CURSIVE[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MEROITIC_HIEROGLYPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MEROITIC_HIEROGLYPHS[MEROITIC_HIEROGLYPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MEROITIC_HIEROGLYPHS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MIAO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MIAO[MIAO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MIAO[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A[MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B[MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MISCELLANEOUS_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_SYMBOLS[MISCELLANEOUS_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MISCELLANEOUS_SYMBOLS_AND_ARROWS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_SYMBOLS_AND_ARROWS[MISCELLANEOUS_SYMBOLS_AND_ARROWS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_SYMBOLS_AND_ARROWS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS[MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MISCELLANEOUS_TECHNICAL]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_TECHNICAL[MISCELLANEOUS_TECHNICAL] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MISCELLANEOUS_TECHNICAL[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MODIFIER_TONE_LETTERS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MODIFIER_TONE_LETTERS[MODIFIER_TONE_LETTERS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MODIFIER_TONE_LETTERS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MONGOLIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MONGOLIAN[MONGOLIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MONGOLIAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MUSICAL_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MUSICAL_SYMBOLS[MUSICAL_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MUSICAL_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MYANMAR]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MYANMAR[MYANMAR] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MYANMAR[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-MYANMAR_EXTENDED_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#MYANMAR_EXTENDED_A[MYANMAR_EXTENDED_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#MYANMAR_EXTENDED_A[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-NEW_TAI_LUE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#NEW_TAI_LUE[NEW_TAI_LUE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#NEW_TAI_LUE[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-NKO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#NKO[NKO] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#NKO[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-NUMBER_FORMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#NUMBER_FORMS[NUMBER_FORMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#NUMBER_FORMS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-OGHAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OGHAM[OGHAM] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OGHAM[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-OLD_ITALIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_ITALIC[OLD_ITALIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_ITALIC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-OLD_PERSIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_PERSIAN[OLD_PERSIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_PERSIAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-OLD_SOUTH_ARABIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_SOUTH_ARABIAN[OLD_SOUTH_ARABIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_SOUTH_ARABIAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-OLD_TURKIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_TURKIC[OLD_TURKIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OLD_TURKIC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-OL_CHIKI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OL_CHIKI[OL_CHIKI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OL_CHIKI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-OPTICAL_CHARACTER_RECOGNITION]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OPTICAL_CHARACTER_RECOGNITION[OPTICAL_CHARACTER_RECOGNITION] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OPTICAL_CHARACTER_RECOGNITION[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-ORIYA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#ORIYA[ORIYA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#ORIYA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-OSMANYA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#OSMANYA[OSMANYA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#OSMANYA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-PHAGS_PA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PHAGS_PA[PHAGS_PA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PHAGS_PA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-PHAISTOS_DISC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PHAISTOS_DISC[PHAISTOS_DISC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PHAISTOS_DISC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-PHOENICIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PHOENICIAN[PHOENICIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PHOENICIAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-PHONETIC_EXTENSIONS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PHONETIC_EXTENSIONS[PHONETIC_EXTENSIONS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PHONETIC_EXTENSIONS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-PHONETIC_EXTENSIONS_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PHONETIC_EXTENSIONS_SUPPLEMENT[PHONETIC_EXTENSIONS_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PHONETIC_EXTENSIONS_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-PLAYING_CARDS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PLAYING_CARDS[PLAYING_CARDS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PLAYING_CARDS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-PRIVATE_USE_AREA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#PRIVATE_USE_AREA[PRIVATE_USE_AREA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#PRIVATE_USE_AREA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-REJANG]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#REJANG[REJANG] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#REJANG[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-RUMI_NUMERAL_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#RUMI_NUMERAL_SYMBOLS[RUMI_NUMERAL_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#RUMI_NUMERAL_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-RUNIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#RUNIC[RUNIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#RUNIC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SAMARITAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SAMARITAN[SAMARITAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SAMARITAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SAURASHTRA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SAURASHTRA[SAURASHTRA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SAURASHTRA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SHARADA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SHARADA[SHARADA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SHARADA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SHAVIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SHAVIAN[SHAVIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SHAVIAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SINHALA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SINHALA[SINHALA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SINHALA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SMALL_FORM_VARIANTS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SMALL_FORM_VARIANTS[SMALL_FORM_VARIANTS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SMALL_FORM_VARIANTS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SORA_SOMPENG]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SORA_SOMPENG[SORA_SOMPENG] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SORA_SOMPENG[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SPACING_MODIFIER_LETTERS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SPACING_MODIFIER_LETTERS[SPACING_MODIFIER_LETTERS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SPACING_MODIFIER_LETTERS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SPECIALS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SPECIALS[SPECIALS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SPECIALS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SUNDANESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUNDANESE[SUNDANESE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUNDANESE[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SUNDANESE_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUNDANESE_SUPPLEMENT[SUNDANESE_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUNDANESE_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SUPERSCRIPTS_AND_SUBSCRIPTS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPERSCRIPTS_AND_SUBSCRIPTS[SUPERSCRIPTS_AND_SUBSCRIPTS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPERSCRIPTS_AND_SUBSCRIPTS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SUPPLEMENTAL_ARROWS_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_ARROWS_A[SUPPLEMENTAL_ARROWS_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_ARROWS_A[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SUPPLEMENTAL_ARROWS_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_ARROWS_B[SUPPLEMENTAL_ARROWS_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_ARROWS_B[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SUPPLEMENTAL_MATHEMATICAL_OPERATORS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_MATHEMATICAL_OPERATORS[SUPPLEMENTAL_MATHEMATICAL_OPERATORS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_MATHEMATICAL_OPERATORS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SUPPLEMENTAL_PUNCTUATION]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_PUNCTUATION[SUPPLEMENTAL_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTAL_PUNCTUATION[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SUPPLEMENTARY_PRIVATE_USE_AREA_A]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTARY_PRIVATE_USE_AREA_A[SUPPLEMENTARY_PRIVATE_USE_AREA_A] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTARY_PRIVATE_USE_AREA_A[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SUPPLEMENTARY_PRIVATE_USE_AREA_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTARY_PRIVATE_USE_AREA_B[SUPPLEMENTARY_PRIVATE_USE_AREA_B] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SUPPLEMENTARY_PRIVATE_USE_AREA_B[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SYLOTI_NAGRI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SYLOTI_NAGRI[SYLOTI_NAGRI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SYLOTI_NAGRI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-SYRIAC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#SYRIAC[SYRIAC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#SYRIAC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-TAGALOG]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAGALOG[TAGALOG] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAGALOG[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-TAGBANWA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAGBANWA[TAGBANWA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAGBANWA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-TAGS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAGS[TAGS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAGS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-TAI_LE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_LE[TAI_LE] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_LE[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-TAI_THAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_THAM[TAI_THAM] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_THAM[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-TAI_VIET]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_VIET[TAI_VIET] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_VIET[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-TAI_XUAN_JING_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_XUAN_JING_SYMBOLS[TAI_XUAN_JING_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAI_XUAN_JING_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-TAKRI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAKRI[TAKRI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAKRI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-TAMIL]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TAMIL[TAMIL] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TAMIL[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-TELUGU]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TELUGU[TELUGU] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TELUGU[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-THAANA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#THAANA[THAANA] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#THAANA[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-THAI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#THAI[THAI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#THAI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-TIBETAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TIBETAN[TIBETAN] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TIBETAN[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-TIFINAGH]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TIFINAGH[TIFINAGH] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TIFINAGH[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-TRANSPORT_AND_MAP_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#TRANSPORT_AND_MAP_SYMBOLS[TRANSPORT_AND_MAP_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#TRANSPORT_AND_MAP_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-UGARITIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#UGARITIC[UGARITIC] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#UGARITIC[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS[UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED[UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-VAI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#VAI[VAI] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#VAI[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-VARIATION_SELECTORS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#VARIATION_SELECTORS[VARIATION_SELECTORS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#VARIATION_SELECTORS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-VARIATION_SELECTORS_SUPPLEMENT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#VARIATION_SELECTORS_SUPPLEMENT[VARIATION_SELECTORS_SUPPLEMENT] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#VARIATION_SELECTORS_SUPPLEMENT[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-VEDIC_EXTENSIONS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#VEDIC_EXTENSIONS[VEDIC_EXTENSIONS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#VEDIC_EXTENSIONS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-VERTICAL_FORMS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#VERTICAL_FORMS[VERTICAL_FORMS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#VERTICAL_FORMS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-YIJING_HEXAGRAM_SYMBOLS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#YIJING_HEXAGRAM_SYMBOLS[YIJING_HEXAGRAM_SYMBOLS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#YIJING_HEXAGRAM_SYMBOLS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-YI_RADICALS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#YI_RADICALS[YI_RADICALS] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#YI_RADICALS[java 9]) +** [[painless-api-reference-Character-UnicodeBlock-YI_SYLLABLES]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#YI_SYLLABLES[YI_SYLLABLES] (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#YI_SYLLABLES[java 9]) +* ++[[painless-api-reference-Character-UnicodeBlock-forName-1]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#forName%2Djava.lang.String%2D[forName](<>)++ (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#forName%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Character-UnicodeBlock-of-1]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeBlock.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/lang/Character$UnicodeBlock.html#of%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Character.UnicodeScript.asciidoc b/docs/painless/painless-api-reference/Character.UnicodeScript.asciidoc new file mode 100644 index 0000000000000..cc189b1ee1e2f --- /dev/null +++ b/docs/painless/painless-api-reference/Character.UnicodeScript.asciidoc @@ -0,0 +1,114 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Character-UnicodeScript]]++Character.UnicodeScript++:: +** [[painless-api-reference-Character-UnicodeScript-ARABIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#ARABIC[ARABIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#ARABIC[java 9]) +** [[painless-api-reference-Character-UnicodeScript-ARMENIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#ARMENIAN[ARMENIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#ARMENIAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-AVESTAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#AVESTAN[AVESTAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#AVESTAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-BALINESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BALINESE[BALINESE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BALINESE[java 9]) +** [[painless-api-reference-Character-UnicodeScript-BAMUM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BAMUM[BAMUM] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BAMUM[java 9]) +** [[painless-api-reference-Character-UnicodeScript-BATAK]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BATAK[BATAK] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BATAK[java 9]) +** [[painless-api-reference-Character-UnicodeScript-BENGALI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BENGALI[BENGALI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BENGALI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-BOPOMOFO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BOPOMOFO[BOPOMOFO] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BOPOMOFO[java 9]) +** [[painless-api-reference-Character-UnicodeScript-BRAHMI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BRAHMI[BRAHMI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BRAHMI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-BRAILLE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BRAILLE[BRAILLE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BRAILLE[java 9]) +** [[painless-api-reference-Character-UnicodeScript-BUGINESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BUGINESE[BUGINESE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BUGINESE[java 9]) +** [[painless-api-reference-Character-UnicodeScript-BUHID]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#BUHID[BUHID] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#BUHID[java 9]) +** [[painless-api-reference-Character-UnicodeScript-CANADIAN_ABORIGINAL]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CANADIAN_ABORIGINAL[CANADIAN_ABORIGINAL] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CANADIAN_ABORIGINAL[java 9]) +** [[painless-api-reference-Character-UnicodeScript-CARIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CARIAN[CARIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CARIAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-CHAKMA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CHAKMA[CHAKMA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CHAKMA[java 9]) +** [[painless-api-reference-Character-UnicodeScript-CHAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CHAM[CHAM] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CHAM[java 9]) +** [[painless-api-reference-Character-UnicodeScript-CHEROKEE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CHEROKEE[CHEROKEE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CHEROKEE[java 9]) +** [[painless-api-reference-Character-UnicodeScript-COMMON]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#COMMON[COMMON] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#COMMON[java 9]) +** [[painless-api-reference-Character-UnicodeScript-COPTIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#COPTIC[COPTIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#COPTIC[java 9]) +** [[painless-api-reference-Character-UnicodeScript-CUNEIFORM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CUNEIFORM[CUNEIFORM] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CUNEIFORM[java 9]) +** [[painless-api-reference-Character-UnicodeScript-CYPRIOT]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CYPRIOT[CYPRIOT] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CYPRIOT[java 9]) +** [[painless-api-reference-Character-UnicodeScript-CYRILLIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#CYRILLIC[CYRILLIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#CYRILLIC[java 9]) +** [[painless-api-reference-Character-UnicodeScript-DESERET]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#DESERET[DESERET] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#DESERET[java 9]) +** [[painless-api-reference-Character-UnicodeScript-DEVANAGARI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#DEVANAGARI[DEVANAGARI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#DEVANAGARI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-EGYPTIAN_HIEROGLYPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#EGYPTIAN_HIEROGLYPHS[EGYPTIAN_HIEROGLYPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#EGYPTIAN_HIEROGLYPHS[java 9]) +** [[painless-api-reference-Character-UnicodeScript-ETHIOPIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#ETHIOPIC[ETHIOPIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#ETHIOPIC[java 9]) +** [[painless-api-reference-Character-UnicodeScript-GEORGIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#GEORGIAN[GEORGIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#GEORGIAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-GLAGOLITIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#GLAGOLITIC[GLAGOLITIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#GLAGOLITIC[java 9]) +** [[painless-api-reference-Character-UnicodeScript-GOTHIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#GOTHIC[GOTHIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#GOTHIC[java 9]) +** [[painless-api-reference-Character-UnicodeScript-GREEK]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#GREEK[GREEK] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#GREEK[java 9]) +** [[painless-api-reference-Character-UnicodeScript-GUJARATI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#GUJARATI[GUJARATI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#GUJARATI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-GURMUKHI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#GURMUKHI[GURMUKHI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#GURMUKHI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-HAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#HAN[HAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#HAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-HANGUL]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#HANGUL[HANGUL] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#HANGUL[java 9]) +** [[painless-api-reference-Character-UnicodeScript-HANUNOO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#HANUNOO[HANUNOO] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#HANUNOO[java 9]) +** [[painless-api-reference-Character-UnicodeScript-HEBREW]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#HEBREW[HEBREW] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#HEBREW[java 9]) +** [[painless-api-reference-Character-UnicodeScript-HIRAGANA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#HIRAGANA[HIRAGANA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#HIRAGANA[java 9]) +** [[painless-api-reference-Character-UnicodeScript-IMPERIAL_ARAMAIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#IMPERIAL_ARAMAIC[IMPERIAL_ARAMAIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#IMPERIAL_ARAMAIC[java 9]) +** [[painless-api-reference-Character-UnicodeScript-INHERITED]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#INHERITED[INHERITED] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#INHERITED[java 9]) +** [[painless-api-reference-Character-UnicodeScript-INSCRIPTIONAL_PAHLAVI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#INSCRIPTIONAL_PAHLAVI[INSCRIPTIONAL_PAHLAVI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#INSCRIPTIONAL_PAHLAVI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-INSCRIPTIONAL_PARTHIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#INSCRIPTIONAL_PARTHIAN[INSCRIPTIONAL_PARTHIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#INSCRIPTIONAL_PARTHIAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-JAVANESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#JAVANESE[JAVANESE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#JAVANESE[java 9]) +** [[painless-api-reference-Character-UnicodeScript-KAITHI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#KAITHI[KAITHI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#KAITHI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-KANNADA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#KANNADA[KANNADA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#KANNADA[java 9]) +** [[painless-api-reference-Character-UnicodeScript-KATAKANA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#KATAKANA[KATAKANA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#KATAKANA[java 9]) +** [[painless-api-reference-Character-UnicodeScript-KAYAH_LI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#KAYAH_LI[KAYAH_LI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#KAYAH_LI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-KHAROSHTHI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#KHAROSHTHI[KHAROSHTHI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#KHAROSHTHI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-KHMER]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#KHMER[KHMER] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#KHMER[java 9]) +** [[painless-api-reference-Character-UnicodeScript-LAO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LAO[LAO] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LAO[java 9]) +** [[painless-api-reference-Character-UnicodeScript-LATIN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LATIN[LATIN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LATIN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-LEPCHA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LEPCHA[LEPCHA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LEPCHA[java 9]) +** [[painless-api-reference-Character-UnicodeScript-LIMBU]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LIMBU[LIMBU] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LIMBU[java 9]) +** [[painless-api-reference-Character-UnicodeScript-LINEAR_B]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LINEAR_B[LINEAR_B] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LINEAR_B[java 9]) +** [[painless-api-reference-Character-UnicodeScript-LISU]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LISU[LISU] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LISU[java 9]) +** [[painless-api-reference-Character-UnicodeScript-LYCIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LYCIAN[LYCIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LYCIAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-LYDIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#LYDIAN[LYDIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#LYDIAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-MALAYALAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MALAYALAM[MALAYALAM] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MALAYALAM[java 9]) +** [[painless-api-reference-Character-UnicodeScript-MANDAIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MANDAIC[MANDAIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MANDAIC[java 9]) +** [[painless-api-reference-Character-UnicodeScript-MEETEI_MAYEK]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MEETEI_MAYEK[MEETEI_MAYEK] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MEETEI_MAYEK[java 9]) +** [[painless-api-reference-Character-UnicodeScript-MEROITIC_CURSIVE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MEROITIC_CURSIVE[MEROITIC_CURSIVE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MEROITIC_CURSIVE[java 9]) +** [[painless-api-reference-Character-UnicodeScript-MEROITIC_HIEROGLYPHS]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MEROITIC_HIEROGLYPHS[MEROITIC_HIEROGLYPHS] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MEROITIC_HIEROGLYPHS[java 9]) +** [[painless-api-reference-Character-UnicodeScript-MIAO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MIAO[MIAO] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MIAO[java 9]) +** [[painless-api-reference-Character-UnicodeScript-MONGOLIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MONGOLIAN[MONGOLIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MONGOLIAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-MYANMAR]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#MYANMAR[MYANMAR] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#MYANMAR[java 9]) +** [[painless-api-reference-Character-UnicodeScript-NEW_TAI_LUE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#NEW_TAI_LUE[NEW_TAI_LUE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#NEW_TAI_LUE[java 9]) +** [[painless-api-reference-Character-UnicodeScript-NKO]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#NKO[NKO] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#NKO[java 9]) +** [[painless-api-reference-Character-UnicodeScript-OGHAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OGHAM[OGHAM] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OGHAM[java 9]) +** [[painless-api-reference-Character-UnicodeScript-OLD_ITALIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OLD_ITALIC[OLD_ITALIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OLD_ITALIC[java 9]) +** [[painless-api-reference-Character-UnicodeScript-OLD_PERSIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OLD_PERSIAN[OLD_PERSIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OLD_PERSIAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-OLD_SOUTH_ARABIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OLD_SOUTH_ARABIAN[OLD_SOUTH_ARABIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OLD_SOUTH_ARABIAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-OLD_TURKIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OLD_TURKIC[OLD_TURKIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OLD_TURKIC[java 9]) +** [[painless-api-reference-Character-UnicodeScript-OL_CHIKI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OL_CHIKI[OL_CHIKI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OL_CHIKI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-ORIYA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#ORIYA[ORIYA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#ORIYA[java 9]) +** [[painless-api-reference-Character-UnicodeScript-OSMANYA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#OSMANYA[OSMANYA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#OSMANYA[java 9]) +** [[painless-api-reference-Character-UnicodeScript-PHAGS_PA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#PHAGS_PA[PHAGS_PA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#PHAGS_PA[java 9]) +** [[painless-api-reference-Character-UnicodeScript-PHOENICIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#PHOENICIAN[PHOENICIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#PHOENICIAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-REJANG]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#REJANG[REJANG] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#REJANG[java 9]) +** [[painless-api-reference-Character-UnicodeScript-RUNIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#RUNIC[RUNIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#RUNIC[java 9]) +** [[painless-api-reference-Character-UnicodeScript-SAMARITAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SAMARITAN[SAMARITAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SAMARITAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-SAURASHTRA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SAURASHTRA[SAURASHTRA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SAURASHTRA[java 9]) +** [[painless-api-reference-Character-UnicodeScript-SHARADA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SHARADA[SHARADA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SHARADA[java 9]) +** [[painless-api-reference-Character-UnicodeScript-SHAVIAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SHAVIAN[SHAVIAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SHAVIAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-SINHALA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SINHALA[SINHALA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SINHALA[java 9]) +** [[painless-api-reference-Character-UnicodeScript-SORA_SOMPENG]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SORA_SOMPENG[SORA_SOMPENG] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SORA_SOMPENG[java 9]) +** [[painless-api-reference-Character-UnicodeScript-SUNDANESE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SUNDANESE[SUNDANESE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SUNDANESE[java 9]) +** [[painless-api-reference-Character-UnicodeScript-SYLOTI_NAGRI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SYLOTI_NAGRI[SYLOTI_NAGRI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SYLOTI_NAGRI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-SYRIAC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#SYRIAC[SYRIAC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#SYRIAC[java 9]) +** [[painless-api-reference-Character-UnicodeScript-TAGALOG]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAGALOG[TAGALOG] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAGALOG[java 9]) +** [[painless-api-reference-Character-UnicodeScript-TAGBANWA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAGBANWA[TAGBANWA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAGBANWA[java 9]) +** [[painless-api-reference-Character-UnicodeScript-TAI_LE]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAI_LE[TAI_LE] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAI_LE[java 9]) +** [[painless-api-reference-Character-UnicodeScript-TAI_THAM]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAI_THAM[TAI_THAM] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAI_THAM[java 9]) +** [[painless-api-reference-Character-UnicodeScript-TAI_VIET]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAI_VIET[TAI_VIET] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAI_VIET[java 9]) +** [[painless-api-reference-Character-UnicodeScript-TAKRI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAKRI[TAKRI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAKRI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-TAMIL]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TAMIL[TAMIL] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TAMIL[java 9]) +** [[painless-api-reference-Character-UnicodeScript-TELUGU]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TELUGU[TELUGU] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TELUGU[java 9]) +** [[painless-api-reference-Character-UnicodeScript-THAANA]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#THAANA[THAANA] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#THAANA[java 9]) +** [[painless-api-reference-Character-UnicodeScript-THAI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#THAI[THAI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#THAI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-TIBETAN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TIBETAN[TIBETAN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TIBETAN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-TIFINAGH]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#TIFINAGH[TIFINAGH] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#TIFINAGH[java 9]) +** [[painless-api-reference-Character-UnicodeScript-UGARITIC]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#UGARITIC[UGARITIC] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#UGARITIC[java 9]) +** [[painless-api-reference-Character-UnicodeScript-UNKNOWN]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#UNKNOWN[UNKNOWN] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#UNKNOWN[java 9]) +** [[painless-api-reference-Character-UnicodeScript-VAI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#VAI[VAI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#VAI[java 9]) +** [[painless-api-reference-Character-UnicodeScript-YI]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#YI[YI] (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#YI[java 9]) +* ++[[painless-api-reference-Character-UnicodeScript-forName-1]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#forName%2Djava.lang.String%2D[forName](<>)++ (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#forName%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Character-UnicodeScript-of-1]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#of%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-UnicodeScript-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Character-UnicodeScript-values-0]]static <>[] link:{java8-javadoc}/java/lang/Character$UnicodeScript.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/lang/Character$UnicodeScript.html#values%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Character.asciidoc b/docs/painless/painless-api-reference/Character.asciidoc new file mode 100644 index 0000000000000..929a4ea3c5ec1 --- /dev/null +++ b/docs/painless/painless-api-reference/Character.asciidoc @@ -0,0 +1,125 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Character]]++Character++:: +** [[painless-api-reference-Character-BYTES]]static int link:{java8-javadoc}/java/lang/Character.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Character.html#BYTES[java 9]) +** [[painless-api-reference-Character-COMBINING_SPACING_MARK]]static byte link:{java8-javadoc}/java/lang/Character.html#COMBINING_SPACING_MARK[COMBINING_SPACING_MARK] (link:{java9-javadoc}/java/lang/Character.html#COMBINING_SPACING_MARK[java 9]) +** [[painless-api-reference-Character-CONNECTOR_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#CONNECTOR_PUNCTUATION[CONNECTOR_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#CONNECTOR_PUNCTUATION[java 9]) +** [[painless-api-reference-Character-CONTROL]]static byte link:{java8-javadoc}/java/lang/Character.html#CONTROL[CONTROL] (link:{java9-javadoc}/java/lang/Character.html#CONTROL[java 9]) +** [[painless-api-reference-Character-CURRENCY_SYMBOL]]static byte link:{java8-javadoc}/java/lang/Character.html#CURRENCY_SYMBOL[CURRENCY_SYMBOL] (link:{java9-javadoc}/java/lang/Character.html#CURRENCY_SYMBOL[java 9]) +** [[painless-api-reference-Character-DASH_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#DASH_PUNCTUATION[DASH_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#DASH_PUNCTUATION[java 9]) +** [[painless-api-reference-Character-DECIMAL_DIGIT_NUMBER]]static byte link:{java8-javadoc}/java/lang/Character.html#DECIMAL_DIGIT_NUMBER[DECIMAL_DIGIT_NUMBER] (link:{java9-javadoc}/java/lang/Character.html#DECIMAL_DIGIT_NUMBER[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_ARABIC_NUMBER]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_ARABIC_NUMBER[DIRECTIONALITY_ARABIC_NUMBER] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_ARABIC_NUMBER[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_BOUNDARY_NEUTRAL]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_BOUNDARY_NEUTRAL[DIRECTIONALITY_BOUNDARY_NEUTRAL] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_BOUNDARY_NEUTRAL[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_COMMON_NUMBER_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_COMMON_NUMBER_SEPARATOR[DIRECTIONALITY_COMMON_NUMBER_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_COMMON_NUMBER_SEPARATOR[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_EUROPEAN_NUMBER]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_EUROPEAN_NUMBER[DIRECTIONALITY_EUROPEAN_NUMBER] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_EUROPEAN_NUMBER[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR[DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR[DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_LEFT_TO_RIGHT]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_LEFT_TO_RIGHT[DIRECTIONALITY_LEFT_TO_RIGHT] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_LEFT_TO_RIGHT[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING[DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE[DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_NONSPACING_MARK]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_NONSPACING_MARK[DIRECTIONALITY_NONSPACING_MARK] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_NONSPACING_MARK[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_OTHER_NEUTRALS]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_OTHER_NEUTRALS[DIRECTIONALITY_OTHER_NEUTRALS] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_OTHER_NEUTRALS[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_PARAGRAPH_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_PARAGRAPH_SEPARATOR[DIRECTIONALITY_PARAGRAPH_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_PARAGRAPH_SEPARATOR[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_POP_DIRECTIONAL_FORMAT]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_POP_DIRECTIONAL_FORMAT[DIRECTIONALITY_POP_DIRECTIONAL_FORMAT] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_POP_DIRECTIONAL_FORMAT[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_RIGHT_TO_LEFT]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT[DIRECTIONALITY_RIGHT_TO_LEFT] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC[DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING[DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE[DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_SEGMENT_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_SEGMENT_SEPARATOR[DIRECTIONALITY_SEGMENT_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_SEGMENT_SEPARATOR[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_UNDEFINED]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_UNDEFINED[DIRECTIONALITY_UNDEFINED] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_UNDEFINED[java 9]) +** [[painless-api-reference-Character-DIRECTIONALITY_WHITESPACE]]static byte link:{java8-javadoc}/java/lang/Character.html#DIRECTIONALITY_WHITESPACE[DIRECTIONALITY_WHITESPACE] (link:{java9-javadoc}/java/lang/Character.html#DIRECTIONALITY_WHITESPACE[java 9]) +** [[painless-api-reference-Character-ENCLOSING_MARK]]static byte link:{java8-javadoc}/java/lang/Character.html#ENCLOSING_MARK[ENCLOSING_MARK] (link:{java9-javadoc}/java/lang/Character.html#ENCLOSING_MARK[java 9]) +** [[painless-api-reference-Character-END_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#END_PUNCTUATION[END_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#END_PUNCTUATION[java 9]) +** [[painless-api-reference-Character-FINAL_QUOTE_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#FINAL_QUOTE_PUNCTUATION[FINAL_QUOTE_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#FINAL_QUOTE_PUNCTUATION[java 9]) +** [[painless-api-reference-Character-FORMAT]]static byte link:{java8-javadoc}/java/lang/Character.html#FORMAT[FORMAT] (link:{java9-javadoc}/java/lang/Character.html#FORMAT[java 9]) +** [[painless-api-reference-Character-INITIAL_QUOTE_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#INITIAL_QUOTE_PUNCTUATION[INITIAL_QUOTE_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#INITIAL_QUOTE_PUNCTUATION[java 9]) +** [[painless-api-reference-Character-LETTER_NUMBER]]static byte link:{java8-javadoc}/java/lang/Character.html#LETTER_NUMBER[LETTER_NUMBER] (link:{java9-javadoc}/java/lang/Character.html#LETTER_NUMBER[java 9]) +** [[painless-api-reference-Character-LINE_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#LINE_SEPARATOR[LINE_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#LINE_SEPARATOR[java 9]) +** [[painless-api-reference-Character-LOWERCASE_LETTER]]static byte link:{java8-javadoc}/java/lang/Character.html#LOWERCASE_LETTER[LOWERCASE_LETTER] (link:{java9-javadoc}/java/lang/Character.html#LOWERCASE_LETTER[java 9]) +** [[painless-api-reference-Character-MATH_SYMBOL]]static byte link:{java8-javadoc}/java/lang/Character.html#MATH_SYMBOL[MATH_SYMBOL] (link:{java9-javadoc}/java/lang/Character.html#MATH_SYMBOL[java 9]) +** [[painless-api-reference-Character-MAX_CODE_POINT]]static int link:{java8-javadoc}/java/lang/Character.html#MAX_CODE_POINT[MAX_CODE_POINT] (link:{java9-javadoc}/java/lang/Character.html#MAX_CODE_POINT[java 9]) +** [[painless-api-reference-Character-MAX_HIGH_SURROGATE]]static char link:{java8-javadoc}/java/lang/Character.html#MAX_HIGH_SURROGATE[MAX_HIGH_SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#MAX_HIGH_SURROGATE[java 9]) +** [[painless-api-reference-Character-MAX_LOW_SURROGATE]]static char link:{java8-javadoc}/java/lang/Character.html#MAX_LOW_SURROGATE[MAX_LOW_SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#MAX_LOW_SURROGATE[java 9]) +** [[painless-api-reference-Character-MAX_RADIX]]static int link:{java8-javadoc}/java/lang/Character.html#MAX_RADIX[MAX_RADIX] (link:{java9-javadoc}/java/lang/Character.html#MAX_RADIX[java 9]) +** [[painless-api-reference-Character-MAX_SURROGATE]]static char link:{java8-javadoc}/java/lang/Character.html#MAX_SURROGATE[MAX_SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#MAX_SURROGATE[java 9]) +** [[painless-api-reference-Character-MAX_VALUE]]static char link:{java8-javadoc}/java/lang/Character.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Character.html#MAX_VALUE[java 9]) +** [[painless-api-reference-Character-MIN_CODE_POINT]]static char link:{java8-javadoc}/java/lang/Character.html#MIN_CODE_POINT[MIN_CODE_POINT] (link:{java9-javadoc}/java/lang/Character.html#MIN_CODE_POINT[java 9]) +** [[painless-api-reference-Character-MIN_HIGH_SURROGATE]]static char link:{java8-javadoc}/java/lang/Character.html#MIN_HIGH_SURROGATE[MIN_HIGH_SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#MIN_HIGH_SURROGATE[java 9]) +** [[painless-api-reference-Character-MIN_LOW_SURROGATE]]static char link:{java8-javadoc}/java/lang/Character.html#MIN_LOW_SURROGATE[MIN_LOW_SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#MIN_LOW_SURROGATE[java 9]) +** [[painless-api-reference-Character-MIN_RADIX]]static int link:{java8-javadoc}/java/lang/Character.html#MIN_RADIX[MIN_RADIX] (link:{java9-javadoc}/java/lang/Character.html#MIN_RADIX[java 9]) +** [[painless-api-reference-Character-MIN_SUPPLEMENTARY_CODE_POINT]]static int link:{java8-javadoc}/java/lang/Character.html#MIN_SUPPLEMENTARY_CODE_POINT[MIN_SUPPLEMENTARY_CODE_POINT] (link:{java9-javadoc}/java/lang/Character.html#MIN_SUPPLEMENTARY_CODE_POINT[java 9]) +** [[painless-api-reference-Character-MIN_SURROGATE]]static char link:{java8-javadoc}/java/lang/Character.html#MIN_SURROGATE[MIN_SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#MIN_SURROGATE[java 9]) +** [[painless-api-reference-Character-MIN_VALUE]]static char link:{java8-javadoc}/java/lang/Character.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Character.html#MIN_VALUE[java 9]) +** [[painless-api-reference-Character-MODIFIER_LETTER]]static byte link:{java8-javadoc}/java/lang/Character.html#MODIFIER_LETTER[MODIFIER_LETTER] (link:{java9-javadoc}/java/lang/Character.html#MODIFIER_LETTER[java 9]) +** [[painless-api-reference-Character-MODIFIER_SYMBOL]]static byte link:{java8-javadoc}/java/lang/Character.html#MODIFIER_SYMBOL[MODIFIER_SYMBOL] (link:{java9-javadoc}/java/lang/Character.html#MODIFIER_SYMBOL[java 9]) +** [[painless-api-reference-Character-NON_SPACING_MARK]]static byte link:{java8-javadoc}/java/lang/Character.html#NON_SPACING_MARK[NON_SPACING_MARK] (link:{java9-javadoc}/java/lang/Character.html#NON_SPACING_MARK[java 9]) +** [[painless-api-reference-Character-OTHER_LETTER]]static byte link:{java8-javadoc}/java/lang/Character.html#OTHER_LETTER[OTHER_LETTER] (link:{java9-javadoc}/java/lang/Character.html#OTHER_LETTER[java 9]) +** [[painless-api-reference-Character-OTHER_NUMBER]]static byte link:{java8-javadoc}/java/lang/Character.html#OTHER_NUMBER[OTHER_NUMBER] (link:{java9-javadoc}/java/lang/Character.html#OTHER_NUMBER[java 9]) +** [[painless-api-reference-Character-OTHER_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#OTHER_PUNCTUATION[OTHER_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#OTHER_PUNCTUATION[java 9]) +** [[painless-api-reference-Character-OTHER_SYMBOL]]static byte link:{java8-javadoc}/java/lang/Character.html#OTHER_SYMBOL[OTHER_SYMBOL] (link:{java9-javadoc}/java/lang/Character.html#OTHER_SYMBOL[java 9]) +** [[painless-api-reference-Character-PARAGRAPH_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#PARAGRAPH_SEPARATOR[PARAGRAPH_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#PARAGRAPH_SEPARATOR[java 9]) +** [[painless-api-reference-Character-PRIVATE_USE]]static byte link:{java8-javadoc}/java/lang/Character.html#PRIVATE_USE[PRIVATE_USE] (link:{java9-javadoc}/java/lang/Character.html#PRIVATE_USE[java 9]) +** [[painless-api-reference-Character-SIZE]]static int link:{java8-javadoc}/java/lang/Character.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Character.html#SIZE[java 9]) +** [[painless-api-reference-Character-SPACE_SEPARATOR]]static byte link:{java8-javadoc}/java/lang/Character.html#SPACE_SEPARATOR[SPACE_SEPARATOR] (link:{java9-javadoc}/java/lang/Character.html#SPACE_SEPARATOR[java 9]) +** [[painless-api-reference-Character-START_PUNCTUATION]]static byte link:{java8-javadoc}/java/lang/Character.html#START_PUNCTUATION[START_PUNCTUATION] (link:{java9-javadoc}/java/lang/Character.html#START_PUNCTUATION[java 9]) +** [[painless-api-reference-Character-SURROGATE]]static byte link:{java8-javadoc}/java/lang/Character.html#SURROGATE[SURROGATE] (link:{java9-javadoc}/java/lang/Character.html#SURROGATE[java 9]) +** [[painless-api-reference-Character-TITLECASE_LETTER]]static byte link:{java8-javadoc}/java/lang/Character.html#TITLECASE_LETTER[TITLECASE_LETTER] (link:{java9-javadoc}/java/lang/Character.html#TITLECASE_LETTER[java 9]) +** [[painless-api-reference-Character-UNASSIGNED]]static byte link:{java8-javadoc}/java/lang/Character.html#UNASSIGNED[UNASSIGNED] (link:{java9-javadoc}/java/lang/Character.html#UNASSIGNED[java 9]) +** [[painless-api-reference-Character-UPPERCASE_LETTER]]static byte link:{java8-javadoc}/java/lang/Character.html#UPPERCASE_LETTER[UPPERCASE_LETTER] (link:{java9-javadoc}/java/lang/Character.html#UPPERCASE_LETTER[java 9]) +* ++[[painless-api-reference-Character-charCount-1]]static int link:{java8-javadoc}/java/lang/Character.html#charCount%2Dint%2D[charCount](int)++ (link:{java9-javadoc}/java/lang/Character.html#charCount%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-codePointAt-2]]static int link:{java8-javadoc}/java/lang/Character.html#codePointAt%2Djava.lang.CharSequence%2Dint%2D[codePointAt](<>, int)++ (link:{java9-javadoc}/java/lang/Character.html#codePointAt%2Djava.lang.CharSequence%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-codePointAt-3]]static int link:{java8-javadoc}/java/lang/Character.html#codePointAt%2Dchar:A%2Dint%2Dint%2D[codePointAt](char[], int, int)++ (link:{java9-javadoc}/java/lang/Character.html#codePointAt%2Dchar:A%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-codePointBefore-2]]static int link:{java8-javadoc}/java/lang/Character.html#codePointBefore%2Djava.lang.CharSequence%2Dint%2D[codePointBefore](<>, int)++ (link:{java9-javadoc}/java/lang/Character.html#codePointBefore%2Djava.lang.CharSequence%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-codePointBefore-3]]static int link:{java8-javadoc}/java/lang/Character.html#codePointBefore%2Dchar:A%2Dint%2Dint%2D[codePointBefore](char[], int, int)++ (link:{java9-javadoc}/java/lang/Character.html#codePointBefore%2Dchar:A%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-codePointCount-3]]static int link:{java8-javadoc}/java/lang/Character.html#codePointCount%2Djava.lang.CharSequence%2Dint%2Dint%2D[codePointCount](<>, int, int)++ (link:{java9-javadoc}/java/lang/Character.html#codePointCount%2Djava.lang.CharSequence%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-compare-2]]static int link:{java8-javadoc}/java/lang/Character.html#compare%2Dchar%2Dchar%2D[compare](char, char)++ (link:{java9-javadoc}/java/lang/Character.html#compare%2Dchar%2Dchar%2D[java 9]) +* ++[[painless-api-reference-Character-digit-2]]static int link:{java8-javadoc}/java/lang/Character.html#digit%2Dint%2Dint%2D[digit](int, int)++ (link:{java9-javadoc}/java/lang/Character.html#digit%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-forDigit-2]]static char link:{java8-javadoc}/java/lang/Character.html#forDigit%2Dint%2Dint%2D[forDigit](int, int)++ (link:{java9-javadoc}/java/lang/Character.html#forDigit%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-getDirectionality-1]]static byte link:{java8-javadoc}/java/lang/Character.html#getDirectionality%2Dint%2D[getDirectionality](int)++ (link:{java9-javadoc}/java/lang/Character.html#getDirectionality%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-getName-1]]static <> link:{java8-javadoc}/java/lang/Character.html#getName%2Dint%2D[getName](int)++ (link:{java9-javadoc}/java/lang/Character.html#getName%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-getNumericValue-1]]static int link:{java8-javadoc}/java/lang/Character.html#getNumericValue%2Dint%2D[getNumericValue](int)++ (link:{java9-javadoc}/java/lang/Character.html#getNumericValue%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-getType-1]]static int link:{java8-javadoc}/java/lang/Character.html#getType%2Dint%2D[getType](int)++ (link:{java9-javadoc}/java/lang/Character.html#getType%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-hashCode-1]]static int link:{java8-javadoc}/java/lang/Character.html#hashCode%2Dchar%2D[hashCode](char)++ (link:{java9-javadoc}/java/lang/Character.html#hashCode%2Dchar%2D[java 9]) +* ++[[painless-api-reference-Character-highSurrogate-1]]static char link:{java8-javadoc}/java/lang/Character.html#highSurrogate%2Dint%2D[highSurrogate](int)++ (link:{java9-javadoc}/java/lang/Character.html#highSurrogate%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isAlphabetic-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isAlphabetic%2Dint%2D[isAlphabetic](int)++ (link:{java9-javadoc}/java/lang/Character.html#isAlphabetic%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isBmpCodePoint-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isBmpCodePoint%2Dint%2D[isBmpCodePoint](int)++ (link:{java9-javadoc}/java/lang/Character.html#isBmpCodePoint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isDefined-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isDefined%2Dint%2D[isDefined](int)++ (link:{java9-javadoc}/java/lang/Character.html#isDefined%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isDigit-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isDigit%2Dint%2D[isDigit](int)++ (link:{java9-javadoc}/java/lang/Character.html#isDigit%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isHighSurrogate-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isHighSurrogate%2Dchar%2D[isHighSurrogate](char)++ (link:{java9-javadoc}/java/lang/Character.html#isHighSurrogate%2Dchar%2D[java 9]) +* ++[[painless-api-reference-Character-isISOControl-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isISOControl%2Dint%2D[isISOControl](int)++ (link:{java9-javadoc}/java/lang/Character.html#isISOControl%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isIdentifierIgnorable-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isIdentifierIgnorable%2Dint%2D[isIdentifierIgnorable](int)++ (link:{java9-javadoc}/java/lang/Character.html#isIdentifierIgnorable%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isIdeographic-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isIdeographic%2Dint%2D[isIdeographic](int)++ (link:{java9-javadoc}/java/lang/Character.html#isIdeographic%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isJavaIdentifierPart-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isJavaIdentifierPart%2Dint%2D[isJavaIdentifierPart](int)++ (link:{java9-javadoc}/java/lang/Character.html#isJavaIdentifierPart%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isJavaIdentifierStart-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isJavaIdentifierStart%2Dint%2D[isJavaIdentifierStart](int)++ (link:{java9-javadoc}/java/lang/Character.html#isJavaIdentifierStart%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isLetter-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isLetter%2Dint%2D[isLetter](int)++ (link:{java9-javadoc}/java/lang/Character.html#isLetter%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isLetterOrDigit-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isLetterOrDigit%2Dint%2D[isLetterOrDigit](int)++ (link:{java9-javadoc}/java/lang/Character.html#isLetterOrDigit%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isLowerCase-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isLowerCase%2Dint%2D[isLowerCase](int)++ (link:{java9-javadoc}/java/lang/Character.html#isLowerCase%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isMirrored-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isMirrored%2Dint%2D[isMirrored](int)++ (link:{java9-javadoc}/java/lang/Character.html#isMirrored%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isSpaceChar-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isSpaceChar%2Dint%2D[isSpaceChar](int)++ (link:{java9-javadoc}/java/lang/Character.html#isSpaceChar%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isSupplementaryCodePoint-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isSupplementaryCodePoint%2Dint%2D[isSupplementaryCodePoint](int)++ (link:{java9-javadoc}/java/lang/Character.html#isSupplementaryCodePoint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isSurrogate-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isSurrogate%2Dchar%2D[isSurrogate](char)++ (link:{java9-javadoc}/java/lang/Character.html#isSurrogate%2Dchar%2D[java 9]) +* ++[[painless-api-reference-Character-isSurrogatePair-2]]static boolean link:{java8-javadoc}/java/lang/Character.html#isSurrogatePair%2Dchar%2Dchar%2D[isSurrogatePair](char, char)++ (link:{java9-javadoc}/java/lang/Character.html#isSurrogatePair%2Dchar%2Dchar%2D[java 9]) +* ++[[painless-api-reference-Character-isTitleCase-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isTitleCase%2Dint%2D[isTitleCase](int)++ (link:{java9-javadoc}/java/lang/Character.html#isTitleCase%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isUnicodeIdentifierPart-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isUnicodeIdentifierPart%2Dint%2D[isUnicodeIdentifierPart](int)++ (link:{java9-javadoc}/java/lang/Character.html#isUnicodeIdentifierPart%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isUnicodeIdentifierStart-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isUnicodeIdentifierStart%2Dint%2D[isUnicodeIdentifierStart](int)++ (link:{java9-javadoc}/java/lang/Character.html#isUnicodeIdentifierStart%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isUpperCase-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isUpperCase%2Dint%2D[isUpperCase](int)++ (link:{java9-javadoc}/java/lang/Character.html#isUpperCase%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isValidCodePoint-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isValidCodePoint%2Dint%2D[isValidCodePoint](int)++ (link:{java9-javadoc}/java/lang/Character.html#isValidCodePoint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-isWhitespace-1]]static boolean link:{java8-javadoc}/java/lang/Character.html#isWhitespace%2Dint%2D[isWhitespace](int)++ (link:{java9-javadoc}/java/lang/Character.html#isWhitespace%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-lowSurrogate-1]]static char link:{java8-javadoc}/java/lang/Character.html#lowSurrogate%2Dint%2D[lowSurrogate](int)++ (link:{java9-javadoc}/java/lang/Character.html#lowSurrogate%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-offsetByCodePoints-3]]static int link:{java8-javadoc}/java/lang/Character.html#offsetByCodePoints%2Djava.lang.CharSequence%2Dint%2Dint%2D[offsetByCodePoints](<>, int, int)++ (link:{java9-javadoc}/java/lang/Character.html#offsetByCodePoints%2Djava.lang.CharSequence%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-offsetByCodePoints-5]]static int link:{java8-javadoc}/java/lang/Character.html#offsetByCodePoints%2Dchar:A%2Dint%2Dint%2Dint%2Dint%2D[offsetByCodePoints](char[], int, int, int, int)++ (link:{java9-javadoc}/java/lang/Character.html#offsetByCodePoints%2Dchar:A%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-reverseBytes-1]]static char link:{java8-javadoc}/java/lang/Character.html#reverseBytes%2Dchar%2D[reverseBytes](char)++ (link:{java9-javadoc}/java/lang/Character.html#reverseBytes%2Dchar%2D[java 9]) +* ++[[painless-api-reference-Character-toChars-1]]static char[] link:{java8-javadoc}/java/lang/Character.html#toChars%2Dint%2D[toChars](int)++ (link:{java9-javadoc}/java/lang/Character.html#toChars%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-toChars-3]]static int link:{java8-javadoc}/java/lang/Character.html#toChars%2Dint%2Dchar:A%2Dint%2D[toChars](int, char[], int)++ (link:{java9-javadoc}/java/lang/Character.html#toChars%2Dint%2Dchar:A%2Dint%2D[java 9]) +* ++[[painless-api-reference-Character-toCodePoint-2]]static int link:{java8-javadoc}/java/lang/Character.html#toCodePoint%2Dchar%2Dchar%2D[toCodePoint](char, char)++ (link:{java9-javadoc}/java/lang/Character.html#toCodePoint%2Dchar%2Dchar%2D[java 9]) +* ++[[painless-api-reference-Character-toLowerCase-1]]static char link:{java8-javadoc}/java/lang/Character.html#toLowerCase%2Dchar%2D[toLowerCase](char)++ (link:{java9-javadoc}/java/lang/Character.html#toLowerCase%2Dchar%2D[java 9]) +* ++[[painless-api-reference-Character-toString-1]]static <> link:{java8-javadoc}/java/lang/Character.html#toString%2Dchar%2D[toString](char)++ (link:{java9-javadoc}/java/lang/Character.html#toString%2Dchar%2D[java 9]) +* ++[[painless-api-reference-Character-toTitleCase-1]]static char link:{java8-javadoc}/java/lang/Character.html#toTitleCase%2Dchar%2D[toTitleCase](char)++ (link:{java9-javadoc}/java/lang/Character.html#toTitleCase%2Dchar%2D[java 9]) +* ++[[painless-api-reference-Character-toUpperCase-1]]static char link:{java8-javadoc}/java/lang/Character.html#toUpperCase%2Dchar%2D[toUpperCase](char)++ (link:{java9-javadoc}/java/lang/Character.html#toUpperCase%2Dchar%2D[java 9]) +* ++[[painless-api-reference-Character-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Character.html#valueOf%2Dchar%2D[valueOf](char)++ (link:{java9-javadoc}/java/lang/Character.html#valueOf%2Dchar%2D[java 9]) +* ++[[painless-api-reference-Character-charValue-0]]char link:{java8-javadoc}/java/lang/Character.html#charValue%2D%2D[charValue]()++ (link:{java9-javadoc}/java/lang/Character.html#charValue%2D%2D[java 9]) +* ++[[painless-api-reference-Character-compareTo-1]]int link:{java8-javadoc}/java/lang/Character.html#compareTo%2Djava.lang.Character%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Character.html#compareTo%2Djava.lang.Character%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/CharacterIterator.asciidoc b/docs/painless/painless-api-reference/CharacterIterator.asciidoc new file mode 100644 index 0000000000000..1303e2d47396a --- /dev/null +++ b/docs/painless/painless-api-reference/CharacterIterator.asciidoc @@ -0,0 +1,18 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-CharacterIterator]]++CharacterIterator++:: +** [[painless-api-reference-CharacterIterator-DONE]]static char link:{java8-javadoc}/java/text/CharacterIterator.html#DONE[DONE] (link:{java9-javadoc}/java/text/CharacterIterator.html#DONE[java 9]) +* ++[[painless-api-reference-CharacterIterator-clone-0]]def link:{java8-javadoc}/java/text/CharacterIterator.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#clone%2D%2D[java 9]) +* ++[[painless-api-reference-CharacterIterator-current-0]]char link:{java8-javadoc}/java/text/CharacterIterator.html#current%2D%2D[current]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#current%2D%2D[java 9]) +* ++[[painless-api-reference-CharacterIterator-first-0]]char link:{java8-javadoc}/java/text/CharacterIterator.html#first%2D%2D[first]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#first%2D%2D[java 9]) +* ++[[painless-api-reference-CharacterIterator-getBeginIndex-0]]int link:{java8-javadoc}/java/text/CharacterIterator.html#getBeginIndex%2D%2D[getBeginIndex]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#getBeginIndex%2D%2D[java 9]) +* ++[[painless-api-reference-CharacterIterator-getEndIndex-0]]int link:{java8-javadoc}/java/text/CharacterIterator.html#getEndIndex%2D%2D[getEndIndex]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#getEndIndex%2D%2D[java 9]) +* ++[[painless-api-reference-CharacterIterator-getIndex-0]]int link:{java8-javadoc}/java/text/CharacterIterator.html#getIndex%2D%2D[getIndex]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#getIndex%2D%2D[java 9]) +* ++[[painless-api-reference-CharacterIterator-last-0]]char link:{java8-javadoc}/java/text/CharacterIterator.html#last%2D%2D[last]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#last%2D%2D[java 9]) +* ++[[painless-api-reference-CharacterIterator-next-0]]char link:{java8-javadoc}/java/text/CharacterIterator.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#next%2D%2D[java 9]) +* ++[[painless-api-reference-CharacterIterator-previous-0]]char link:{java8-javadoc}/java/text/CharacterIterator.html#previous%2D%2D[previous]()++ (link:{java9-javadoc}/java/text/CharacterIterator.html#previous%2D%2D[java 9]) +* ++[[painless-api-reference-CharacterIterator-setIndex-1]]char link:{java8-javadoc}/java/text/CharacterIterator.html#setIndex%2Dint%2D[setIndex](int)++ (link:{java9-javadoc}/java/text/CharacterIterator.html#setIndex%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ChoiceFormat.asciidoc b/docs/painless/painless-api-reference/ChoiceFormat.asciidoc new file mode 100644 index 0000000000000..426120a194f73 --- /dev/null +++ b/docs/painless/painless-api-reference/ChoiceFormat.asciidoc @@ -0,0 +1,17 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ChoiceFormat]]++ChoiceFormat++:: +* ++[[painless-api-reference-ChoiceFormat-nextDouble-1]]static double link:{java8-javadoc}/java/text/ChoiceFormat.html#nextDouble%2Ddouble%2D[nextDouble](double)++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#nextDouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-ChoiceFormat-nextDouble-2]]static double link:{java8-javadoc}/java/text/ChoiceFormat.html#nextDouble%2Ddouble%2Dboolean%2D[nextDouble](double, boolean)++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#nextDouble%2Ddouble%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-ChoiceFormat-previousDouble-1]]static double link:{java8-javadoc}/java/text/ChoiceFormat.html#previousDouble%2Ddouble%2D[previousDouble](double)++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#previousDouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-ChoiceFormat-ChoiceFormat-1]]link:{java8-javadoc}/java/text/ChoiceFormat.html#ChoiceFormat%2Djava.lang.String%2D[ChoiceFormat](<>)++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#ChoiceFormat%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-ChoiceFormat-ChoiceFormat-2]]link:{java8-javadoc}/java/text/ChoiceFormat.html#ChoiceFormat%2Ddouble:A%2Djava.lang.String:A%2D[ChoiceFormat](double[], <>[])++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#ChoiceFormat%2Ddouble:A%2Djava.lang.String:A%2D[java 9]) +* ++[[painless-api-reference-ChoiceFormat-applyPattern-1]]void link:{java8-javadoc}/java/text/ChoiceFormat.html#applyPattern%2Djava.lang.String%2D[applyPattern](<>)++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#applyPattern%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-ChoiceFormat-getFormats-0]]def[] link:{java8-javadoc}/java/text/ChoiceFormat.html#getFormats%2D%2D[getFormats]()++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#getFormats%2D%2D[java 9]) +* ++[[painless-api-reference-ChoiceFormat-getLimits-0]]double[] link:{java8-javadoc}/java/text/ChoiceFormat.html#getLimits%2D%2D[getLimits]()++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#getLimits%2D%2D[java 9]) +* ++[[painless-api-reference-ChoiceFormat-setChoices-2]]void link:{java8-javadoc}/java/text/ChoiceFormat.html#setChoices%2Ddouble:A%2Djava.lang.String:A%2D[setChoices](double[], <>[])++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#setChoices%2Ddouble:A%2Djava.lang.String:A%2D[java 9]) +* ++[[painless-api-reference-ChoiceFormat-toPattern-0]]<> link:{java8-javadoc}/java/text/ChoiceFormat.html#toPattern%2D%2D[toPattern]()++ (link:{java9-javadoc}/java/text/ChoiceFormat.html#toPattern%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ChronoField.asciidoc b/docs/painless/painless-api-reference/ChronoField.asciidoc new file mode 100644 index 0000000000000..33143a52eaf63 --- /dev/null +++ b/docs/painless/painless-api-reference/ChronoField.asciidoc @@ -0,0 +1,41 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ChronoField]]++ChronoField++:: +** [[painless-api-reference-ChronoField-ALIGNED_DAY_OF_WEEK_IN_MONTH]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_DAY_OF_WEEK_IN_MONTH[ALIGNED_DAY_OF_WEEK_IN_MONTH] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_DAY_OF_WEEK_IN_MONTH[java 9]) +** [[painless-api-reference-ChronoField-ALIGNED_DAY_OF_WEEK_IN_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_DAY_OF_WEEK_IN_YEAR[ALIGNED_DAY_OF_WEEK_IN_YEAR] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_DAY_OF_WEEK_IN_YEAR[java 9]) +** [[painless-api-reference-ChronoField-ALIGNED_WEEK_OF_MONTH]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_WEEK_OF_MONTH[ALIGNED_WEEK_OF_MONTH] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_WEEK_OF_MONTH[java 9]) +** [[painless-api-reference-ChronoField-ALIGNED_WEEK_OF_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_WEEK_OF_YEAR[ALIGNED_WEEK_OF_YEAR] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#ALIGNED_WEEK_OF_YEAR[java 9]) +** [[painless-api-reference-ChronoField-AMPM_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#AMPM_OF_DAY[AMPM_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#AMPM_OF_DAY[java 9]) +** [[painless-api-reference-ChronoField-CLOCK_HOUR_OF_AMPM]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#CLOCK_HOUR_OF_AMPM[CLOCK_HOUR_OF_AMPM] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#CLOCK_HOUR_OF_AMPM[java 9]) +** [[painless-api-reference-ChronoField-CLOCK_HOUR_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#CLOCK_HOUR_OF_DAY[CLOCK_HOUR_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#CLOCK_HOUR_OF_DAY[java 9]) +** [[painless-api-reference-ChronoField-DAY_OF_MONTH]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#DAY_OF_MONTH[DAY_OF_MONTH] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#DAY_OF_MONTH[java 9]) +** [[painless-api-reference-ChronoField-DAY_OF_WEEK]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#DAY_OF_WEEK[DAY_OF_WEEK] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#DAY_OF_WEEK[java 9]) +** [[painless-api-reference-ChronoField-DAY_OF_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#DAY_OF_YEAR[DAY_OF_YEAR] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#DAY_OF_YEAR[java 9]) +** [[painless-api-reference-ChronoField-EPOCH_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#EPOCH_DAY[EPOCH_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#EPOCH_DAY[java 9]) +** [[painless-api-reference-ChronoField-ERA]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#ERA[ERA] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#ERA[java 9]) +** [[painless-api-reference-ChronoField-HOUR_OF_AMPM]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#HOUR_OF_AMPM[HOUR_OF_AMPM] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#HOUR_OF_AMPM[java 9]) +** [[painless-api-reference-ChronoField-HOUR_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#HOUR_OF_DAY[HOUR_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#HOUR_OF_DAY[java 9]) +** [[painless-api-reference-ChronoField-INSTANT_SECONDS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#INSTANT_SECONDS[INSTANT_SECONDS] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#INSTANT_SECONDS[java 9]) +** [[painless-api-reference-ChronoField-MICRO_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MICRO_OF_DAY[MICRO_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MICRO_OF_DAY[java 9]) +** [[painless-api-reference-ChronoField-MICRO_OF_SECOND]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MICRO_OF_SECOND[MICRO_OF_SECOND] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MICRO_OF_SECOND[java 9]) +** [[painless-api-reference-ChronoField-MILLI_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MILLI_OF_DAY[MILLI_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MILLI_OF_DAY[java 9]) +** [[painless-api-reference-ChronoField-MILLI_OF_SECOND]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MILLI_OF_SECOND[MILLI_OF_SECOND] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MILLI_OF_SECOND[java 9]) +** [[painless-api-reference-ChronoField-MINUTE_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MINUTE_OF_DAY[MINUTE_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MINUTE_OF_DAY[java 9]) +** [[painless-api-reference-ChronoField-MINUTE_OF_HOUR]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MINUTE_OF_HOUR[MINUTE_OF_HOUR] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MINUTE_OF_HOUR[java 9]) +** [[painless-api-reference-ChronoField-MONTH_OF_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#MONTH_OF_YEAR[MONTH_OF_YEAR] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#MONTH_OF_YEAR[java 9]) +** [[painless-api-reference-ChronoField-NANO_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#NANO_OF_DAY[NANO_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#NANO_OF_DAY[java 9]) +** [[painless-api-reference-ChronoField-NANO_OF_SECOND]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#NANO_OF_SECOND[NANO_OF_SECOND] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#NANO_OF_SECOND[java 9]) +** [[painless-api-reference-ChronoField-OFFSET_SECONDS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#OFFSET_SECONDS[OFFSET_SECONDS] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#OFFSET_SECONDS[java 9]) +** [[painless-api-reference-ChronoField-PROLEPTIC_MONTH]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#PROLEPTIC_MONTH[PROLEPTIC_MONTH] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#PROLEPTIC_MONTH[java 9]) +** [[painless-api-reference-ChronoField-SECOND_OF_DAY]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#SECOND_OF_DAY[SECOND_OF_DAY] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#SECOND_OF_DAY[java 9]) +** [[painless-api-reference-ChronoField-SECOND_OF_MINUTE]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#SECOND_OF_MINUTE[SECOND_OF_MINUTE] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#SECOND_OF_MINUTE[java 9]) +** [[painless-api-reference-ChronoField-YEAR]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#YEAR[YEAR] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#YEAR[java 9]) +** [[painless-api-reference-ChronoField-YEAR_OF_ERA]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#YEAR_OF_ERA[YEAR_OF_ERA] (link:{java9-javadoc}/java/time/temporal/ChronoField.html#YEAR_OF_ERA[java 9]) +* ++[[painless-api-reference-ChronoField-valueOf-1]]static <> link:{java8-javadoc}/java/time/temporal/ChronoField.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/temporal/ChronoField.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-ChronoField-values-0]]static <>[] link:{java8-javadoc}/java/time/temporal/ChronoField.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/temporal/ChronoField.html#values%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoField-checkValidIntValue-1]]int link:{java8-javadoc}/java/time/temporal/ChronoField.html#checkValidIntValue%2Dlong%2D[checkValidIntValue](long)++ (link:{java9-javadoc}/java/time/temporal/ChronoField.html#checkValidIntValue%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ChronoField-checkValidValue-1]]long link:{java8-javadoc}/java/time/temporal/ChronoField.html#checkValidValue%2Dlong%2D[checkValidValue](long)++ (link:{java9-javadoc}/java/time/temporal/ChronoField.html#checkValidValue%2Dlong%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ChronoLocalDate.asciidoc b/docs/painless/painless-api-reference/ChronoLocalDate.asciidoc new file mode 100644 index 0000000000000..2c483c27bd8d9 --- /dev/null +++ b/docs/painless/painless-api-reference/ChronoLocalDate.asciidoc @@ -0,0 +1,31 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ChronoLocalDate]]++ChronoLocalDate++:: +* ++[[painless-api-reference-ChronoLocalDate-from-1]]static <> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-timeLineOrder-0]]static <> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#timeLineOrder%2D%2D[timeLineOrder]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#timeLineOrder%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-atTime-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#atTime%2Djava.time.LocalTime%2D[atTime](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#atTime%2Djava.time.LocalTime%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-compareTo-1]]int link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#compareTo%2Djava.time.chrono.ChronoLocalDate%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#compareTo%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-equals-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#equals%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-format-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#getChronology%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-getEra-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#getEra%2D%2D[getEra]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#getEra%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-hashCode-0]]int link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#hashCode%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-isAfter-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#isAfter%2Djava.time.chrono.ChronoLocalDate%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#isAfter%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-isBefore-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#isBefore%2Djava.time.chrono.ChronoLocalDate%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#isBefore%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-isEqual-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#isEqual%2Djava.time.chrono.ChronoLocalDate%2D[isEqual](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#isEqual%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-isLeapYear-0]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#isLeapYear%2D%2D[isLeapYear]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#isLeapYear%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-lengthOfMonth-0]]int link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#lengthOfMonth%2D%2D[lengthOfMonth]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#lengthOfMonth%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-lengthOfYear-0]]int link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#lengthOfYear%2D%2D[lengthOfYear]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#lengthOfYear%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-minus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-minus-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-plus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-plus-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-toEpochDay-0]]long link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#toEpochDay%2D%2D[toEpochDay]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#toEpochDay%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-toString-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#toString%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-until-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#until%2Djava.time.chrono.ChronoLocalDate%2D[until](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#until%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-with-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDate-with-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ChronoLocalDateTime.asciidoc b/docs/painless/painless-api-reference/ChronoLocalDateTime.asciidoc new file mode 100644 index 0000000000000..7b49712ee0985 --- /dev/null +++ b/docs/painless/painless-api-reference/ChronoLocalDateTime.asciidoc @@ -0,0 +1,29 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ChronoLocalDateTime]]++ChronoLocalDateTime++:: +* ++[[painless-api-reference-ChronoLocalDateTime-from-1]]static <> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-timeLineOrder-0]]static <> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#timeLineOrder%2D%2D[timeLineOrder]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#timeLineOrder%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-atZone-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#atZone%2Djava.time.ZoneId%2D[atZone](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#atZone%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-compareTo-1]]int link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#compareTo%2Djava.time.chrono.ChronoLocalDateTime%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#compareTo%2Djava.time.chrono.ChronoLocalDateTime%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-equals-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#equals%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-format-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#getChronology%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-hashCode-0]]int link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#hashCode%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-isAfter-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#isAfter%2Djava.time.chrono.ChronoLocalDateTime%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#isAfter%2Djava.time.chrono.ChronoLocalDateTime%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-isBefore-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#isBefore%2Djava.time.chrono.ChronoLocalDateTime%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#isBefore%2Djava.time.chrono.ChronoLocalDateTime%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-isEqual-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#isEqual%2Djava.time.chrono.ChronoLocalDateTime%2D[isEqual](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#isEqual%2Djava.time.chrono.ChronoLocalDateTime%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-minus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-minus-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-plus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-plus-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-toEpochSecond-1]]long link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toEpochSecond%2Djava.time.ZoneOffset%2D[toEpochSecond](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toEpochSecond%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-toInstant-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toInstant%2Djava.time.ZoneOffset%2D[toInstant](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toInstant%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-toLocalDate-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toLocalDate%2D%2D[toLocalDate]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toLocalDate%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-toLocalTime-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toLocalTime%2D%2D[toLocalTime]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toLocalTime%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-toString-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#toString%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-with-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-ChronoLocalDateTime-with-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoLocalDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/ChronoLocalDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ChronoPeriod.asciidoc b/docs/painless/painless-api-reference/ChronoPeriod.asciidoc new file mode 100644 index 0000000000000..1b15df31c7667 --- /dev/null +++ b/docs/painless/painless-api-reference/ChronoPeriod.asciidoc @@ -0,0 +1,20 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ChronoPeriod]]++ChronoPeriod++:: +* ++[[painless-api-reference-ChronoPeriod-between-2]]static <> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#between%2Djava.time.chrono.ChronoLocalDate%2Djava.time.chrono.ChronoLocalDate%2D[between](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#between%2Djava.time.chrono.ChronoLocalDate%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) +* ++[[painless-api-reference-ChronoPeriod-equals-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#equals%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-ChronoPeriod-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#getChronology%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoPeriod-getUnits-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#getUnits%2D%2D[getUnits]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#getUnits%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoPeriod-hashCode-0]]int link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#hashCode%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoPeriod-isNegative-0]]boolean link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#isNegative%2D%2D[isNegative]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#isNegative%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoPeriod-isZero-0]]boolean link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#isZero%2D%2D[isZero]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#isZero%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoPeriod-minus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-ChronoPeriod-multipliedBy-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#multipliedBy%2Dint%2D[multipliedBy](int)++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#multipliedBy%2Dint%2D[java 9]) +* ++[[painless-api-reference-ChronoPeriod-negated-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#negated%2D%2D[negated]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#negated%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoPeriod-normalized-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#normalized%2D%2D[normalized]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#normalized%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoPeriod-plus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-ChronoPeriod-toString-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoPeriod.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/chrono/ChronoPeriod.html#toString%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ChronoUnit.asciidoc b/docs/painless/painless-api-reference/ChronoUnit.asciidoc new file mode 100644 index 0000000000000..297fbf61835f9 --- /dev/null +++ b/docs/painless/painless-api-reference/ChronoUnit.asciidoc @@ -0,0 +1,25 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ChronoUnit]]++ChronoUnit++:: +** [[painless-api-reference-ChronoUnit-CENTURIES]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#CENTURIES[CENTURIES] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#CENTURIES[java 9]) +** [[painless-api-reference-ChronoUnit-DAYS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#DAYS[DAYS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#DAYS[java 9]) +** [[painless-api-reference-ChronoUnit-DECADES]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#DECADES[DECADES] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#DECADES[java 9]) +** [[painless-api-reference-ChronoUnit-ERAS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#ERAS[ERAS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#ERAS[java 9]) +** [[painless-api-reference-ChronoUnit-FOREVER]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#FOREVER[FOREVER] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#FOREVER[java 9]) +** [[painless-api-reference-ChronoUnit-HALF_DAYS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#HALF_DAYS[HALF_DAYS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#HALF_DAYS[java 9]) +** [[painless-api-reference-ChronoUnit-HOURS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#HOURS[HOURS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#HOURS[java 9]) +** [[painless-api-reference-ChronoUnit-MICROS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#MICROS[MICROS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#MICROS[java 9]) +** [[painless-api-reference-ChronoUnit-MILLENNIA]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#MILLENNIA[MILLENNIA] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#MILLENNIA[java 9]) +** [[painless-api-reference-ChronoUnit-MILLIS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#MILLIS[MILLIS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#MILLIS[java 9]) +** [[painless-api-reference-ChronoUnit-MINUTES]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#MINUTES[MINUTES] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#MINUTES[java 9]) +** [[painless-api-reference-ChronoUnit-MONTHS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#MONTHS[MONTHS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#MONTHS[java 9]) +** [[painless-api-reference-ChronoUnit-NANOS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#NANOS[NANOS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#NANOS[java 9]) +** [[painless-api-reference-ChronoUnit-SECONDS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#SECONDS[SECONDS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#SECONDS[java 9]) +** [[painless-api-reference-ChronoUnit-WEEKS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#WEEKS[WEEKS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#WEEKS[java 9]) +** [[painless-api-reference-ChronoUnit-YEARS]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#YEARS[YEARS] (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#YEARS[java 9]) +* ++[[painless-api-reference-ChronoUnit-valueOf-1]]static <> link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-ChronoUnit-values-0]]static <>[] link:{java8-javadoc}/java/time/temporal/ChronoUnit.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/temporal/ChronoUnit.html#values%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ChronoZonedDateTime.asciidoc b/docs/painless/painless-api-reference/ChronoZonedDateTime.asciidoc new file mode 100644 index 0000000000000..0684b8b541e02 --- /dev/null +++ b/docs/painless/painless-api-reference/ChronoZonedDateTime.asciidoc @@ -0,0 +1,35 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ChronoZonedDateTime]]++ChronoZonedDateTime++:: +* ++[[painless-api-reference-ChronoZonedDateTime-from-1]]static <> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-timeLineOrder-0]]static <> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#timeLineOrder%2D%2D[timeLineOrder]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#timeLineOrder%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-compareTo-1]]int link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#compareTo%2Djava.time.chrono.ChronoZonedDateTime%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#compareTo%2Djava.time.chrono.ChronoZonedDateTime%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-equals-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#equals%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-format-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#getChronology%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-getOffset-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#getOffset%2D%2D[getOffset]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#getOffset%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-getZone-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#getZone%2D%2D[getZone]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#getZone%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-hashCode-0]]int link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#hashCode%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-isAfter-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#isAfter%2Djava.time.chrono.ChronoZonedDateTime%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#isAfter%2Djava.time.chrono.ChronoZonedDateTime%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-isBefore-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#isBefore%2Djava.time.chrono.ChronoZonedDateTime%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#isBefore%2Djava.time.chrono.ChronoZonedDateTime%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-isEqual-1]]boolean link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#isEqual%2Djava.time.chrono.ChronoZonedDateTime%2D[isEqual](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#isEqual%2Djava.time.chrono.ChronoZonedDateTime%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-minus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-minus-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-plus-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-plus-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-toEpochSecond-0]]long link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toEpochSecond%2D%2D[toEpochSecond]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toEpochSecond%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-toInstant-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toInstant%2D%2D[toInstant]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toInstant%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-toLocalDate-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toLocalDate%2D%2D[toLocalDate]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toLocalDate%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-toLocalDateTime-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toLocalDateTime%2D%2D[toLocalDateTime]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toLocalDateTime%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-toLocalTime-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toLocalTime%2D%2D[toLocalTime]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toLocalTime%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-toString-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#toString%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-with-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-with-2]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-withEarlierOffsetAtOverlap-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withEarlierOffsetAtOverlap%2D%2D[withEarlierOffsetAtOverlap]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withEarlierOffsetAtOverlap%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-withLaterOffsetAtOverlap-0]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withLaterOffsetAtOverlap%2D%2D[withLaterOffsetAtOverlap]()++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withLaterOffsetAtOverlap%2D%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-withZoneSameInstant-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withZoneSameInstant%2Djava.time.ZoneId%2D[withZoneSameInstant](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withZoneSameInstant%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-ChronoZonedDateTime-withZoneSameLocal-1]]<> link:{java8-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withZoneSameLocal%2Djava.time.ZoneId%2D[withZoneSameLocal](<>)++ (link:{java9-javadoc}/java/time/chrono/ChronoZonedDateTime.html#withZoneSameLocal%2Djava.time.ZoneId%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Chronology.asciidoc b/docs/painless/painless-api-reference/Chronology.asciidoc new file mode 100644 index 0000000000000..42b395d930a40 --- /dev/null +++ b/docs/painless/painless-api-reference/Chronology.asciidoc @@ -0,0 +1,33 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Chronology]]++Chronology++:: +* ++[[painless-api-reference-Chronology-from-1]]static <> link:{java8-javadoc}/java/time/chrono/Chronology.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-Chronology-getAvailableChronologies-0]]static <> link:{java8-javadoc}/java/time/chrono/Chronology.html#getAvailableChronologies%2D%2D[getAvailableChronologies]()++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#getAvailableChronologies%2D%2D[java 9]) +* ++[[painless-api-reference-Chronology-of-1]]static <> link:{java8-javadoc}/java/time/chrono/Chronology.html#of%2Djava.lang.String%2D[of](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#of%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Chronology-ofLocale-1]]static <> link:{java8-javadoc}/java/time/chrono/Chronology.html#ofLocale%2Djava.util.Locale%2D[ofLocale](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#ofLocale%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Chronology-compareTo-1]]int link:{java8-javadoc}/java/time/chrono/Chronology.html#compareTo%2Djava.time.chrono.Chronology%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#compareTo%2Djava.time.chrono.Chronology%2D[java 9]) +* ++[[painless-api-reference-Chronology-date-1]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[date](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-Chronology-date-3]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#date%2Dint%2Dint%2Dint%2D[date](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#date%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Chronology-date-4]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[date](<>, int, int, int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Chronology-dateEpochDay-1]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#dateEpochDay%2Dlong%2D[dateEpochDay](long)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#dateEpochDay%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Chronology-dateYearDay-2]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#dateYearDay%2Dint%2Dint%2D[dateYearDay](int, int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#dateYearDay%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Chronology-dateYearDay-3]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[dateYearDay](<>, int, int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Chronology-equals-1]]boolean link:{java8-javadoc}/java/time/chrono/Chronology.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#equals%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Chronology-eraOf-1]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#eraOf%2Dint%2D[eraOf](int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#eraOf%2Dint%2D[java 9]) +* ++[[painless-api-reference-Chronology-eras-0]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#eras%2D%2D[eras]()++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#eras%2D%2D[java 9]) +* ++[[painless-api-reference-Chronology-getCalendarType-0]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#getCalendarType%2D%2D[getCalendarType]()++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#getCalendarType%2D%2D[java 9]) +* ++[[painless-api-reference-Chronology-getDisplayName-2]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[getDisplayName](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Chronology-getId-0]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#getId%2D%2D[getId]()++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#getId%2D%2D[java 9]) +* ++[[painless-api-reference-Chronology-hashCode-0]]int link:{java8-javadoc}/java/time/chrono/Chronology.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#hashCode%2D%2D[java 9]) +* ++[[painless-api-reference-Chronology-isLeapYear-1]]boolean link:{java8-javadoc}/java/time/chrono/Chronology.html#isLeapYear%2Dlong%2D[isLeapYear](long)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#isLeapYear%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Chronology-localDateTime-1]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#localDateTime%2Djava.time.temporal.TemporalAccessor%2D[localDateTime](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#localDateTime%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-Chronology-period-3]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#period%2Dint%2Dint%2Dint%2D[period](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#period%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Chronology-prolepticYear-2]]int link:{java8-javadoc}/java/time/chrono/Chronology.html#prolepticYear%2Djava.time.chrono.Era%2Dint%2D[prolepticYear](<>, int)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#prolepticYear%2Djava.time.chrono.Era%2Dint%2D[java 9]) +* ++[[painless-api-reference-Chronology-range-1]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#range%2Djava.time.temporal.ChronoField%2D[range](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#range%2Djava.time.temporal.ChronoField%2D[java 9]) +* ++[[painless-api-reference-Chronology-resolveDate-2]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[resolveDate](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[java 9]) +* ++[[painless-api-reference-Chronology-toString-0]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#toString%2D%2D[java 9]) +* ++[[painless-api-reference-Chronology-zonedDateTime-1]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#zonedDateTime%2Djava.time.temporal.TemporalAccessor%2D[zonedDateTime](<>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#zonedDateTime%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-Chronology-zonedDateTime-2]]<> link:{java8-javadoc}/java/time/chrono/Chronology.html#zonedDateTime%2Djava.time.Instant%2Djava.time.ZoneId%2D[zonedDateTime](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/Chronology.html#zonedDateTime%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) diff --git a/docs/painless/painless-api-reference/ClassCastException.asciidoc b/docs/painless/painless-api-reference/ClassCastException.asciidoc new file mode 100644 index 0000000000000..f837c66f9db11 --- /dev/null +++ b/docs/painless/painless-api-reference/ClassCastException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ClassCastException]]++ClassCastException++:: +* ++[[painless-api-reference-ClassCastException-ClassCastException-0]]link:{java8-javadoc}/java/lang/ClassCastException.html#ClassCastException%2D%2D[ClassCastException]()++ (link:{java9-javadoc}/java/lang/ClassCastException.html#ClassCastException%2D%2D[java 9]) +* ++[[painless-api-reference-ClassCastException-ClassCastException-1]]link:{java8-javadoc}/java/lang/ClassCastException.html#ClassCastException%2Djava.lang.String%2D[ClassCastException](<>)++ (link:{java9-javadoc}/java/lang/ClassCastException.html#ClassCastException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ClassNotFoundException.asciidoc b/docs/painless/painless-api-reference/ClassNotFoundException.asciidoc new file mode 100644 index 0000000000000..f4d0d63aa0605 --- /dev/null +++ b/docs/painless/painless-api-reference/ClassNotFoundException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ClassNotFoundException]]++ClassNotFoundException++:: +* ++[[painless-api-reference-ClassNotFoundException-ClassNotFoundException-0]]link:{java8-javadoc}/java/lang/ClassNotFoundException.html#ClassNotFoundException%2D%2D[ClassNotFoundException]()++ (link:{java9-javadoc}/java/lang/ClassNotFoundException.html#ClassNotFoundException%2D%2D[java 9]) +* ++[[painless-api-reference-ClassNotFoundException-ClassNotFoundException-1]]link:{java8-javadoc}/java/lang/ClassNotFoundException.html#ClassNotFoundException%2Djava.lang.String%2D[ClassNotFoundException](<>)++ (link:{java9-javadoc}/java/lang/ClassNotFoundException.html#ClassNotFoundException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Clock.asciidoc b/docs/painless/painless-api-reference/Clock.asciidoc new file mode 100644 index 0000000000000..c40a09a2fe92a --- /dev/null +++ b/docs/painless/painless-api-reference/Clock.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Clock]]++Clock++:: +* ++[[painless-api-reference-Clock-fixed-2]]static <> link:{java8-javadoc}/java/time/Clock.html#fixed%2Djava.time.Instant%2Djava.time.ZoneId%2D[fixed](<>, <>)++ (link:{java9-javadoc}/java/time/Clock.html#fixed%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-Clock-offset-2]]static <> link:{java8-javadoc}/java/time/Clock.html#offset%2Djava.time.Clock%2Djava.time.Duration%2D[offset](<>, <>)++ (link:{java9-javadoc}/java/time/Clock.html#offset%2Djava.time.Clock%2Djava.time.Duration%2D[java 9]) +* ++[[painless-api-reference-Clock-tick-2]]static <> link:{java8-javadoc}/java/time/Clock.html#tick%2Djava.time.Clock%2Djava.time.Duration%2D[tick](<>, <>)++ (link:{java9-javadoc}/java/time/Clock.html#tick%2Djava.time.Clock%2Djava.time.Duration%2D[java 9]) +* ++[[painless-api-reference-Clock-getZone-0]]<> link:{java8-javadoc}/java/time/Clock.html#getZone%2D%2D[getZone]()++ (link:{java9-javadoc}/java/time/Clock.html#getZone%2D%2D[java 9]) +* ++[[painless-api-reference-Clock-instant-0]]<> link:{java8-javadoc}/java/time/Clock.html#instant%2D%2D[instant]()++ (link:{java9-javadoc}/java/time/Clock.html#instant%2D%2D[java 9]) +* ++[[painless-api-reference-Clock-millis-0]]long link:{java8-javadoc}/java/time/Clock.html#millis%2D%2D[millis]()++ (link:{java9-javadoc}/java/time/Clock.html#millis%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/CloneNotSupportedException.asciidoc b/docs/painless/painless-api-reference/CloneNotSupportedException.asciidoc new file mode 100644 index 0000000000000..deeb87d599b5c --- /dev/null +++ b/docs/painless/painless-api-reference/CloneNotSupportedException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-CloneNotSupportedException]]++CloneNotSupportedException++:: +* ++[[painless-api-reference-CloneNotSupportedException-CloneNotSupportedException-0]]link:{java8-javadoc}/java/lang/CloneNotSupportedException.html#CloneNotSupportedException%2D%2D[CloneNotSupportedException]()++ (link:{java9-javadoc}/java/lang/CloneNotSupportedException.html#CloneNotSupportedException%2D%2D[java 9]) +* ++[[painless-api-reference-CloneNotSupportedException-CloneNotSupportedException-1]]link:{java8-javadoc}/java/lang/CloneNotSupportedException.html#CloneNotSupportedException%2Djava.lang.String%2D[CloneNotSupportedException](<>)++ (link:{java9-javadoc}/java/lang/CloneNotSupportedException.html#CloneNotSupportedException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/CollationElementIterator.asciidoc b/docs/painless/painless-api-reference/CollationElementIterator.asciidoc new file mode 100644 index 0000000000000..f277709123d8a --- /dev/null +++ b/docs/painless/painless-api-reference/CollationElementIterator.asciidoc @@ -0,0 +1,18 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-CollationElementIterator]]++CollationElementIterator++:: +** [[painless-api-reference-CollationElementIterator-NULLORDER]]static int link:{java8-javadoc}/java/text/CollationElementIterator.html#NULLORDER[NULLORDER] (link:{java9-javadoc}/java/text/CollationElementIterator.html#NULLORDER[java 9]) +* ++[[painless-api-reference-CollationElementIterator-primaryOrder-1]]static int link:{java8-javadoc}/java/text/CollationElementIterator.html#primaryOrder%2Dint%2D[primaryOrder](int)++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#primaryOrder%2Dint%2D[java 9]) +* ++[[painless-api-reference-CollationElementIterator-secondaryOrder-1]]static short link:{java8-javadoc}/java/text/CollationElementIterator.html#secondaryOrder%2Dint%2D[secondaryOrder](int)++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#secondaryOrder%2Dint%2D[java 9]) +* ++[[painless-api-reference-CollationElementIterator-tertiaryOrder-1]]static short link:{java8-javadoc}/java/text/CollationElementIterator.html#tertiaryOrder%2Dint%2D[tertiaryOrder](int)++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#tertiaryOrder%2Dint%2D[java 9]) +* ++[[painless-api-reference-CollationElementIterator-getMaxExpansion-1]]int link:{java8-javadoc}/java/text/CollationElementIterator.html#getMaxExpansion%2Dint%2D[getMaxExpansion](int)++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#getMaxExpansion%2Dint%2D[java 9]) +* ++[[painless-api-reference-CollationElementIterator-getOffset-0]]int link:{java8-javadoc}/java/text/CollationElementIterator.html#getOffset%2D%2D[getOffset]()++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#getOffset%2D%2D[java 9]) +* ++[[painless-api-reference-CollationElementIterator-next-0]]int link:{java8-javadoc}/java/text/CollationElementIterator.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#next%2D%2D[java 9]) +* ++[[painless-api-reference-CollationElementIterator-previous-0]]int link:{java8-javadoc}/java/text/CollationElementIterator.html#previous%2D%2D[previous]()++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#previous%2D%2D[java 9]) +* ++[[painless-api-reference-CollationElementIterator-reset-0]]void link:{java8-javadoc}/java/text/CollationElementIterator.html#reset%2D%2D[reset]()++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#reset%2D%2D[java 9]) +* ++[[painless-api-reference-CollationElementIterator-setOffset-1]]void link:{java8-javadoc}/java/text/CollationElementIterator.html#setOffset%2Dint%2D[setOffset](int)++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#setOffset%2Dint%2D[java 9]) +* ++[[painless-api-reference-CollationElementIterator-setText-1]]void link:{java8-javadoc}/java/text/CollationElementIterator.html#setText%2Djava.lang.String%2D[setText](<>)++ (link:{java9-javadoc}/java/text/CollationElementIterator.html#setText%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/CollationKey.asciidoc b/docs/painless/painless-api-reference/CollationKey.asciidoc new file mode 100644 index 0000000000000..adb6d29cf81bc --- /dev/null +++ b/docs/painless/painless-api-reference/CollationKey.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-CollationKey]]++CollationKey++:: +* ++[[painless-api-reference-CollationKey-compareTo-1]]int link:{java8-javadoc}/java/text/CollationKey.html#compareTo%2Djava.text.CollationKey%2D[compareTo](<>)++ (link:{java9-javadoc}/java/text/CollationKey.html#compareTo%2Djava.text.CollationKey%2D[java 9]) +* ++[[painless-api-reference-CollationKey-getSourceString-0]]<> link:{java8-javadoc}/java/text/CollationKey.html#getSourceString%2D%2D[getSourceString]()++ (link:{java9-javadoc}/java/text/CollationKey.html#getSourceString%2D%2D[java 9]) +* ++[[painless-api-reference-CollationKey-toByteArray-0]]byte[] link:{java8-javadoc}/java/text/CollationKey.html#toByteArray%2D%2D[toByteArray]()++ (link:{java9-javadoc}/java/text/CollationKey.html#toByteArray%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Collator.asciidoc b/docs/painless/painless-api-reference/Collator.asciidoc new file mode 100644 index 0000000000000..a14f29d986851 --- /dev/null +++ b/docs/painless/painless-api-reference/Collator.asciidoc @@ -0,0 +1,24 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Collator]]++Collator++:: +** [[painless-api-reference-Collator-CANONICAL_DECOMPOSITION]]static int link:{java8-javadoc}/java/text/Collator.html#CANONICAL_DECOMPOSITION[CANONICAL_DECOMPOSITION] (link:{java9-javadoc}/java/text/Collator.html#CANONICAL_DECOMPOSITION[java 9]) +** [[painless-api-reference-Collator-FULL_DECOMPOSITION]]static int link:{java8-javadoc}/java/text/Collator.html#FULL_DECOMPOSITION[FULL_DECOMPOSITION] (link:{java9-javadoc}/java/text/Collator.html#FULL_DECOMPOSITION[java 9]) +** [[painless-api-reference-Collator-IDENTICAL]]static int link:{java8-javadoc}/java/text/Collator.html#IDENTICAL[IDENTICAL] (link:{java9-javadoc}/java/text/Collator.html#IDENTICAL[java 9]) +** [[painless-api-reference-Collator-NO_DECOMPOSITION]]static int link:{java8-javadoc}/java/text/Collator.html#NO_DECOMPOSITION[NO_DECOMPOSITION] (link:{java9-javadoc}/java/text/Collator.html#NO_DECOMPOSITION[java 9]) +** [[painless-api-reference-Collator-PRIMARY]]static int link:{java8-javadoc}/java/text/Collator.html#PRIMARY[PRIMARY] (link:{java9-javadoc}/java/text/Collator.html#PRIMARY[java 9]) +** [[painless-api-reference-Collator-SECONDARY]]static int link:{java8-javadoc}/java/text/Collator.html#SECONDARY[SECONDARY] (link:{java9-javadoc}/java/text/Collator.html#SECONDARY[java 9]) +** [[painless-api-reference-Collator-TERTIARY]]static int link:{java8-javadoc}/java/text/Collator.html#TERTIARY[TERTIARY] (link:{java9-javadoc}/java/text/Collator.html#TERTIARY[java 9]) +* ++[[painless-api-reference-Collator-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/text/Collator.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/text/Collator.html#getAvailableLocales%2D%2D[java 9]) +* ++[[painless-api-reference-Collator-getInstance-0]]static <> link:{java8-javadoc}/java/text/Collator.html#getInstance%2D%2D[getInstance]()++ (link:{java9-javadoc}/java/text/Collator.html#getInstance%2D%2D[java 9]) +* ++[[painless-api-reference-Collator-getInstance-1]]static <> link:{java8-javadoc}/java/text/Collator.html#getInstance%2Djava.util.Locale%2D[getInstance](<>)++ (link:{java9-javadoc}/java/text/Collator.html#getInstance%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Collator-clone-0]]def link:{java8-javadoc}/java/text/Collator.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/text/Collator.html#clone%2D%2D[java 9]) +* ++[[painless-api-reference-Collator-equals-2]]boolean link:{java8-javadoc}/java/text/Collator.html#equals%2Djava.lang.String%2Djava.lang.String%2D[equals](<>, <>)++ (link:{java9-javadoc}/java/text/Collator.html#equals%2Djava.lang.String%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Collator-getCollationKey-1]]<> link:{java8-javadoc}/java/text/Collator.html#getCollationKey%2Djava.lang.String%2D[getCollationKey](<>)++ (link:{java9-javadoc}/java/text/Collator.html#getCollationKey%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Collator-getDecomposition-0]]int link:{java8-javadoc}/java/text/Collator.html#getDecomposition%2D%2D[getDecomposition]()++ (link:{java9-javadoc}/java/text/Collator.html#getDecomposition%2D%2D[java 9]) +* ++[[painless-api-reference-Collator-getStrength-0]]int link:{java8-javadoc}/java/text/Collator.html#getStrength%2D%2D[getStrength]()++ (link:{java9-javadoc}/java/text/Collator.html#getStrength%2D%2D[java 9]) +* ++[[painless-api-reference-Collator-setDecomposition-1]]void link:{java8-javadoc}/java/text/Collator.html#setDecomposition%2Dint%2D[setDecomposition](int)++ (link:{java9-javadoc}/java/text/Collator.html#setDecomposition%2Dint%2D[java 9]) +* ++[[painless-api-reference-Collator-setStrength-1]]void link:{java8-javadoc}/java/text/Collator.html#setStrength%2Dint%2D[setStrength](int)++ (link:{java9-javadoc}/java/text/Collator.html#setStrength%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Collection.asciidoc b/docs/painless/painless-api-reference/Collection.asciidoc new file mode 100644 index 0000000000000..e8faba23a6109 --- /dev/null +++ b/docs/painless/painless-api-reference/Collection.asciidoc @@ -0,0 +1,28 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Collection]]++Collection++:: +* ++[[painless-api-reference-Collection-add-1]]boolean link:{java8-javadoc}/java/util/Collection.html#add%2Djava.lang.Object%2D[add](def)++ (link:{java9-javadoc}/java/util/Collection.html#add%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Collection-addAll-1]]boolean link:{java8-javadoc}/java/util/Collection.html#addAll%2Djava.util.Collection%2D[addAll](<>)++ (link:{java9-javadoc}/java/util/Collection.html#addAll%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Collection-clear-0]]void link:{java8-javadoc}/java/util/Collection.html#clear%2D%2D[clear]()++ (link:{java9-javadoc}/java/util/Collection.html#clear%2D%2D[java 9]) +* ++[[painless-api-reference-Collection-collect-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#collect%2Djava.util.Collection%2Djava.util.function.Function%2D[collect](<>)++ +* ++[[painless-api-reference-Collection-collect-2]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#collect%2Djava.util.Collection%2Djava.util.Collection%2Djava.util.function.Function%2D[collect](<>, <>)++ +* ++[[painless-api-reference-Collection-contains-1]]boolean link:{java8-javadoc}/java/util/Collection.html#contains%2Djava.lang.Object%2D[contains](def)++ (link:{java9-javadoc}/java/util/Collection.html#contains%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Collection-containsAll-1]]boolean link:{java8-javadoc}/java/util/Collection.html#containsAll%2Djava.util.Collection%2D[containsAll](<>)++ (link:{java9-javadoc}/java/util/Collection.html#containsAll%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Collection-find-1]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#find%2Djava.util.Collection%2Djava.util.function.Predicate%2D[find](<>)++ +* ++[[painless-api-reference-Collection-findAll-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findAll%2Djava.util.Collection%2Djava.util.function.Predicate%2D[findAll](<>)++ +* ++[[painless-api-reference-Collection-findResult-1]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findResult%2Djava.util.Collection%2Djava.util.function.Function%2D[findResult](<>)++ +* ++[[painless-api-reference-Collection-findResult-2]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findResult%2Djava.util.Collection%2Djava.lang.Object%2Djava.util.function.Function%2D[findResult](def, <>)++ +* ++[[painless-api-reference-Collection-isEmpty-0]]boolean link:{java8-javadoc}/java/util/Collection.html#isEmpty%2D%2D[isEmpty]()++ (link:{java9-javadoc}/java/util/Collection.html#isEmpty%2D%2D[java 9]) +* ++[[painless-api-reference-Collection-removeAll-1]]boolean link:{java8-javadoc}/java/util/Collection.html#removeAll%2Djava.util.Collection%2D[removeAll](<>)++ (link:{java9-javadoc}/java/util/Collection.html#removeAll%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Collection-removeIf-1]]boolean link:{java8-javadoc}/java/util/Collection.html#removeIf%2Djava.util.function.Predicate%2D[removeIf](<>)++ (link:{java9-javadoc}/java/util/Collection.html#removeIf%2Djava.util.function.Predicate%2D[java 9]) +* ++[[painless-api-reference-Collection-retainAll-1]]boolean link:{java8-javadoc}/java/util/Collection.html#retainAll%2Djava.util.Collection%2D[retainAll](<>)++ (link:{java9-javadoc}/java/util/Collection.html#retainAll%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Collection-size-0]]int link:{java8-javadoc}/java/util/Collection.html#size%2D%2D[size]()++ (link:{java9-javadoc}/java/util/Collection.html#size%2D%2D[java 9]) +* ++[[painless-api-reference-Collection-split-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#split%2Djava.util.Collection%2Djava.util.function.Predicate%2D[split](<>)++ +* ++[[painless-api-reference-Collection-spliterator-0]]<> link:{java8-javadoc}/java/util/Collection.html#spliterator%2D%2D[spliterator]()++ (link:{java9-javadoc}/java/util/Collection.html#spliterator%2D%2D[java 9]) +* ++[[painless-api-reference-Collection-stream-0]]<> link:{java8-javadoc}/java/util/Collection.html#stream%2D%2D[stream]()++ (link:{java9-javadoc}/java/util/Collection.html#stream%2D%2D[java 9]) +* ++[[painless-api-reference-Collection-toArray-0]]def[] link:{java8-javadoc}/java/util/Collection.html#toArray%2D%2D[toArray]()++ (link:{java9-javadoc}/java/util/Collection.html#toArray%2D%2D[java 9]) +* ++[[painless-api-reference-Collection-toArray-1]]def[] link:{java8-javadoc}/java/util/Collection.html#toArray%2Djava.lang.Object:A%2D[toArray](def[])++ (link:{java9-javadoc}/java/util/Collection.html#toArray%2Djava.lang.Object:A%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Collections.asciidoc b/docs/painless/painless-api-reference/Collections.asciidoc new file mode 100644 index 0000000000000..db6d87e85210e --- /dev/null +++ b/docs/painless/painless-api-reference/Collections.asciidoc @@ -0,0 +1,59 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Collections]]++Collections++:: +** [[painless-api-reference-Collections-EMPTY_LIST]]static <> link:{java8-javadoc}/java/util/Collections.html#EMPTY_LIST[EMPTY_LIST] (link:{java9-javadoc}/java/util/Collections.html#EMPTY_LIST[java 9]) +** [[painless-api-reference-Collections-EMPTY_MAP]]static <> link:{java8-javadoc}/java/util/Collections.html#EMPTY_MAP[EMPTY_MAP] (link:{java9-javadoc}/java/util/Collections.html#EMPTY_MAP[java 9]) +** [[painless-api-reference-Collections-EMPTY_SET]]static <> link:{java8-javadoc}/java/util/Collections.html#EMPTY_SET[EMPTY_SET] (link:{java9-javadoc}/java/util/Collections.html#EMPTY_SET[java 9]) +* ++[[painless-api-reference-Collections-addAll-2]]static boolean link:{java8-javadoc}/java/util/Collections.html#addAll%2Djava.util.Collection%2Djava.lang.Object:A%2D[addAll](<>, def[])++ (link:{java9-javadoc}/java/util/Collections.html#addAll%2Djava.util.Collection%2Djava.lang.Object:A%2D[java 9]) +* ++[[painless-api-reference-Collections-asLifoQueue-1]]static <> link:{java8-javadoc}/java/util/Collections.html#asLifoQueue%2Djava.util.Deque%2D[asLifoQueue](<>)++ (link:{java9-javadoc}/java/util/Collections.html#asLifoQueue%2Djava.util.Deque%2D[java 9]) +* ++[[painless-api-reference-Collections-binarySearch-2]]static int link:{java8-javadoc}/java/util/Collections.html#binarySearch%2Djava.util.List%2Djava.lang.Object%2D[binarySearch](<>, def)++ (link:{java9-javadoc}/java/util/Collections.html#binarySearch%2Djava.util.List%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Collections-binarySearch-3]]static int link:{java8-javadoc}/java/util/Collections.html#binarySearch%2Djava.util.List%2Djava.lang.Object%2Djava.util.Comparator%2D[binarySearch](<>, def, <>)++ (link:{java9-javadoc}/java/util/Collections.html#binarySearch%2Djava.util.List%2Djava.lang.Object%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Collections-copy-2]]static void link:{java8-javadoc}/java/util/Collections.html#copy%2Djava.util.List%2Djava.util.List%2D[copy](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#copy%2Djava.util.List%2Djava.util.List%2D[java 9]) +* ++[[painless-api-reference-Collections-disjoint-2]]static boolean link:{java8-javadoc}/java/util/Collections.html#disjoint%2Djava.util.Collection%2Djava.util.Collection%2D[disjoint](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#disjoint%2Djava.util.Collection%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Collections-emptyEnumeration-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyEnumeration%2D%2D[emptyEnumeration]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyEnumeration%2D%2D[java 9]) +* ++[[painless-api-reference-Collections-emptyIterator-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyIterator%2D%2D[emptyIterator]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyIterator%2D%2D[java 9]) +* ++[[painless-api-reference-Collections-emptyList-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyList%2D%2D[emptyList]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyList%2D%2D[java 9]) +* ++[[painless-api-reference-Collections-emptyListIterator-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyListIterator%2D%2D[emptyListIterator]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyListIterator%2D%2D[java 9]) +* ++[[painless-api-reference-Collections-emptyMap-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyMap%2D%2D[emptyMap]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyMap%2D%2D[java 9]) +* ++[[painless-api-reference-Collections-emptyNavigableMap-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyNavigableMap%2D%2D[emptyNavigableMap]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyNavigableMap%2D%2D[java 9]) +* ++[[painless-api-reference-Collections-emptyNavigableSet-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptyNavigableSet%2D%2D[emptyNavigableSet]()++ (link:{java9-javadoc}/java/util/Collections.html#emptyNavigableSet%2D%2D[java 9]) +* ++[[painless-api-reference-Collections-emptySet-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptySet%2D%2D[emptySet]()++ (link:{java9-javadoc}/java/util/Collections.html#emptySet%2D%2D[java 9]) +* ++[[painless-api-reference-Collections-emptySortedMap-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptySortedMap%2D%2D[emptySortedMap]()++ (link:{java9-javadoc}/java/util/Collections.html#emptySortedMap%2D%2D[java 9]) +* ++[[painless-api-reference-Collections-emptySortedSet-0]]static <> link:{java8-javadoc}/java/util/Collections.html#emptySortedSet%2D%2D[emptySortedSet]()++ (link:{java9-javadoc}/java/util/Collections.html#emptySortedSet%2D%2D[java 9]) +* ++[[painless-api-reference-Collections-enumeration-1]]static <> link:{java8-javadoc}/java/util/Collections.html#enumeration%2Djava.util.Collection%2D[enumeration](<>)++ (link:{java9-javadoc}/java/util/Collections.html#enumeration%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Collections-fill-2]]static void link:{java8-javadoc}/java/util/Collections.html#fill%2Djava.util.List%2Djava.lang.Object%2D[fill](<>, def)++ (link:{java9-javadoc}/java/util/Collections.html#fill%2Djava.util.List%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Collections-frequency-2]]static int link:{java8-javadoc}/java/util/Collections.html#frequency%2Djava.util.Collection%2Djava.lang.Object%2D[frequency](<>, def)++ (link:{java9-javadoc}/java/util/Collections.html#frequency%2Djava.util.Collection%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Collections-indexOfSubList-2]]static int link:{java8-javadoc}/java/util/Collections.html#indexOfSubList%2Djava.util.List%2Djava.util.List%2D[indexOfSubList](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#indexOfSubList%2Djava.util.List%2Djava.util.List%2D[java 9]) +* ++[[painless-api-reference-Collections-lastIndexOfSubList-2]]static int link:{java8-javadoc}/java/util/Collections.html#lastIndexOfSubList%2Djava.util.List%2Djava.util.List%2D[lastIndexOfSubList](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#lastIndexOfSubList%2Djava.util.List%2Djava.util.List%2D[java 9]) +* ++[[painless-api-reference-Collections-list-1]]static <> link:{java8-javadoc}/java/util/Collections.html#list%2Djava.util.Enumeration%2D[list](<>)++ (link:{java9-javadoc}/java/util/Collections.html#list%2Djava.util.Enumeration%2D[java 9]) +* ++[[painless-api-reference-Collections-max-1]]static def link:{java8-javadoc}/java/util/Collections.html#max%2Djava.util.Collection%2D[max](<>)++ (link:{java9-javadoc}/java/util/Collections.html#max%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Collections-max-2]]static def link:{java8-javadoc}/java/util/Collections.html#max%2Djava.util.Collection%2Djava.util.Comparator%2D[max](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#max%2Djava.util.Collection%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Collections-min-1]]static def link:{java8-javadoc}/java/util/Collections.html#min%2Djava.util.Collection%2D[min](<>)++ (link:{java9-javadoc}/java/util/Collections.html#min%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Collections-min-2]]static def link:{java8-javadoc}/java/util/Collections.html#min%2Djava.util.Collection%2Djava.util.Comparator%2D[min](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#min%2Djava.util.Collection%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Collections-nCopies-2]]static <> link:{java8-javadoc}/java/util/Collections.html#nCopies%2Dint%2Djava.lang.Object%2D[nCopies](int, def)++ (link:{java9-javadoc}/java/util/Collections.html#nCopies%2Dint%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Collections-newSetFromMap-1]]static <> link:{java8-javadoc}/java/util/Collections.html#newSetFromMap%2Djava.util.Map%2D[newSetFromMap](<>)++ (link:{java9-javadoc}/java/util/Collections.html#newSetFromMap%2Djava.util.Map%2D[java 9]) +* ++[[painless-api-reference-Collections-replaceAll-3]]static boolean link:{java8-javadoc}/java/util/Collections.html#replaceAll%2Djava.util.List%2Djava.lang.Object%2Djava.lang.Object%2D[replaceAll](<>, def, def)++ (link:{java9-javadoc}/java/util/Collections.html#replaceAll%2Djava.util.List%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Collections-reverse-1]]static void link:{java8-javadoc}/java/util/Collections.html#reverse%2Djava.util.List%2D[reverse](<>)++ (link:{java9-javadoc}/java/util/Collections.html#reverse%2Djava.util.List%2D[java 9]) +* ++[[painless-api-reference-Collections-reverseOrder-0]]static <> link:{java8-javadoc}/java/util/Collections.html#reverseOrder%2D%2D[reverseOrder]()++ (link:{java9-javadoc}/java/util/Collections.html#reverseOrder%2D%2D[java 9]) +* ++[[painless-api-reference-Collections-reverseOrder-1]]static <> link:{java8-javadoc}/java/util/Collections.html#reverseOrder%2Djava.util.Comparator%2D[reverseOrder](<>)++ (link:{java9-javadoc}/java/util/Collections.html#reverseOrder%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Collections-rotate-2]]static void link:{java8-javadoc}/java/util/Collections.html#rotate%2Djava.util.List%2Dint%2D[rotate](<>, int)++ (link:{java9-javadoc}/java/util/Collections.html#rotate%2Djava.util.List%2Dint%2D[java 9]) +* ++[[painless-api-reference-Collections-shuffle-1]]static void link:{java8-javadoc}/java/util/Collections.html#shuffle%2Djava.util.List%2D[shuffle](<>)++ (link:{java9-javadoc}/java/util/Collections.html#shuffle%2Djava.util.List%2D[java 9]) +* ++[[painless-api-reference-Collections-shuffle-2]]static void link:{java8-javadoc}/java/util/Collections.html#shuffle%2Djava.util.List%2Djava.util.Random%2D[shuffle](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#shuffle%2Djava.util.List%2Djava.util.Random%2D[java 9]) +* ++[[painless-api-reference-Collections-singleton-1]]static <> link:{java8-javadoc}/java/util/Collections.html#singleton%2Djava.lang.Object%2D[singleton](def)++ (link:{java9-javadoc}/java/util/Collections.html#singleton%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Collections-singletonList-1]]static <> link:{java8-javadoc}/java/util/Collections.html#singletonList%2Djava.lang.Object%2D[singletonList](def)++ (link:{java9-javadoc}/java/util/Collections.html#singletonList%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Collections-singletonMap-2]]static <> link:{java8-javadoc}/java/util/Collections.html#singletonMap%2Djava.lang.Object%2Djava.lang.Object%2D[singletonMap](def, def)++ (link:{java9-javadoc}/java/util/Collections.html#singletonMap%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Collections-sort-1]]static void link:{java8-javadoc}/java/util/Collections.html#sort%2Djava.util.List%2D[sort](<>)++ (link:{java9-javadoc}/java/util/Collections.html#sort%2Djava.util.List%2D[java 9]) +* ++[[painless-api-reference-Collections-sort-2]]static void link:{java8-javadoc}/java/util/Collections.html#sort%2Djava.util.List%2Djava.util.Comparator%2D[sort](<>, <>)++ (link:{java9-javadoc}/java/util/Collections.html#sort%2Djava.util.List%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Collections-swap-3]]static void link:{java8-javadoc}/java/util/Collections.html#swap%2Djava.util.List%2Dint%2Dint%2D[swap](<>, int, int)++ (link:{java9-javadoc}/java/util/Collections.html#swap%2Djava.util.List%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Collections-unmodifiableCollection-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableCollection%2Djava.util.Collection%2D[unmodifiableCollection](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableCollection%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Collections-unmodifiableList-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableList%2Djava.util.List%2D[unmodifiableList](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableList%2Djava.util.List%2D[java 9]) +* ++[[painless-api-reference-Collections-unmodifiableMap-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableMap%2Djava.util.Map%2D[unmodifiableMap](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableMap%2Djava.util.Map%2D[java 9]) +* ++[[painless-api-reference-Collections-unmodifiableNavigableMap-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableNavigableMap%2Djava.util.NavigableMap%2D[unmodifiableNavigableMap](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableNavigableMap%2Djava.util.NavigableMap%2D[java 9]) +* ++[[painless-api-reference-Collections-unmodifiableNavigableSet-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableNavigableSet%2Djava.util.NavigableSet%2D[unmodifiableNavigableSet](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableNavigableSet%2Djava.util.NavigableSet%2D[java 9]) +* ++[[painless-api-reference-Collections-unmodifiableSet-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableSet%2Djava.util.Set%2D[unmodifiableSet](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableSet%2Djava.util.Set%2D[java 9]) +* ++[[painless-api-reference-Collections-unmodifiableSortedMap-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableSortedMap%2Djava.util.SortedMap%2D[unmodifiableSortedMap](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableSortedMap%2Djava.util.SortedMap%2D[java 9]) +* ++[[painless-api-reference-Collections-unmodifiableSortedSet-1]]static <> link:{java8-javadoc}/java/util/Collections.html#unmodifiableSortedSet%2Djava.util.SortedSet%2D[unmodifiableSortedSet](<>)++ (link:{java9-javadoc}/java/util/Collections.html#unmodifiableSortedSet%2Djava.util.SortedSet%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Collector.Characteristics.asciidoc b/docs/painless/painless-api-reference/Collector.Characteristics.asciidoc new file mode 100644 index 0000000000000..17c6bec94751c --- /dev/null +++ b/docs/painless/painless-api-reference/Collector.Characteristics.asciidoc @@ -0,0 +1,12 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Collector-Characteristics]]++Collector.Characteristics++:: +** [[painless-api-reference-Collector-Characteristics-CONCURRENT]]static <> link:{java8-javadoc}/java/util/stream/Collector$Characteristics.html#CONCURRENT[CONCURRENT] (link:{java9-javadoc}/java/util/stream/Collector$Characteristics.html#CONCURRENT[java 9]) +** [[painless-api-reference-Collector-Characteristics-IDENTITY_FINISH]]static <> link:{java8-javadoc}/java/util/stream/Collector$Characteristics.html#IDENTITY_FINISH[IDENTITY_FINISH] (link:{java9-javadoc}/java/util/stream/Collector$Characteristics.html#IDENTITY_FINISH[java 9]) +** [[painless-api-reference-Collector-Characteristics-UNORDERED]]static <> link:{java8-javadoc}/java/util/stream/Collector$Characteristics.html#UNORDERED[UNORDERED] (link:{java9-javadoc}/java/util/stream/Collector$Characteristics.html#UNORDERED[java 9]) +* ++[[painless-api-reference-Collector-Characteristics-valueOf-1]]static <> link:{java8-javadoc}/java/util/stream/Collector$Characteristics.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/util/stream/Collector$Characteristics.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Collector-Characteristics-values-0]]static <>[] link:{java8-javadoc}/java/util/stream/Collector$Characteristics.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/util/stream/Collector$Characteristics.html#values%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Collector.asciidoc b/docs/painless/painless-api-reference/Collector.asciidoc new file mode 100644 index 0000000000000..1d1b92f1e0911 --- /dev/null +++ b/docs/painless/painless-api-reference/Collector.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Collector]]++Collector++:: +* ++[[painless-api-reference-Collector-of-4]]static <> link:{java8-javadoc}/java/util/stream/Collector.html#of%2Djava.util.function.Supplier%2Djava.util.function.BiConsumer%2Djava.util.function.BinaryOperator%2Djava.util.stream.Collector$Characteristics:A%2D[of](<>, <>, <>, <>[])++ (link:{java9-javadoc}/java/util/stream/Collector.html#of%2Djava.util.function.Supplier%2Djava.util.function.BiConsumer%2Djava.util.function.BinaryOperator%2Djava.util.stream.Collector$Characteristics:A%2D[java 9]) +* ++[[painless-api-reference-Collector-of-5]]static <> link:{java8-javadoc}/java/util/stream/Collector.html#of%2Djava.util.function.Supplier%2Djava.util.function.BiConsumer%2Djava.util.function.BinaryOperator%2Djava.util.function.Function%2Djava.util.stream.Collector$Characteristics:A%2D[of](<>, <>, <>, <>, <>[])++ (link:{java9-javadoc}/java/util/stream/Collector.html#of%2Djava.util.function.Supplier%2Djava.util.function.BiConsumer%2Djava.util.function.BinaryOperator%2Djava.util.function.Function%2Djava.util.stream.Collector$Characteristics:A%2D[java 9]) +* ++[[painless-api-reference-Collector-accumulator-0]]<> link:{java8-javadoc}/java/util/stream/Collector.html#accumulator%2D%2D[accumulator]()++ (link:{java9-javadoc}/java/util/stream/Collector.html#accumulator%2D%2D[java 9]) +* ++[[painless-api-reference-Collector-characteristics-0]]<> link:{java8-javadoc}/java/util/stream/Collector.html#characteristics%2D%2D[characteristics]()++ (link:{java9-javadoc}/java/util/stream/Collector.html#characteristics%2D%2D[java 9]) +* ++[[painless-api-reference-Collector-combiner-0]]<> link:{java8-javadoc}/java/util/stream/Collector.html#combiner%2D%2D[combiner]()++ (link:{java9-javadoc}/java/util/stream/Collector.html#combiner%2D%2D[java 9]) +* ++[[painless-api-reference-Collector-finisher-0]]<> link:{java8-javadoc}/java/util/stream/Collector.html#finisher%2D%2D[finisher]()++ (link:{java9-javadoc}/java/util/stream/Collector.html#finisher%2D%2D[java 9]) +* ++[[painless-api-reference-Collector-supplier-0]]<> link:{java8-javadoc}/java/util/stream/Collector.html#supplier%2D%2D[supplier]()++ (link:{java9-javadoc}/java/util/stream/Collector.html#supplier%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Collectors.asciidoc b/docs/painless/painless-api-reference/Collectors.asciidoc new file mode 100644 index 0000000000000..ef4d0199386e1 --- /dev/null +++ b/docs/painless/painless-api-reference/Collectors.asciidoc @@ -0,0 +1,38 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Collectors]]++Collectors++:: +* ++[[painless-api-reference-Collectors-averagingDouble-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#averagingDouble%2Djava.util.function.ToDoubleFunction%2D[averagingDouble](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#averagingDouble%2Djava.util.function.ToDoubleFunction%2D[java 9]) +* ++[[painless-api-reference-Collectors-averagingInt-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#averagingInt%2Djava.util.function.ToIntFunction%2D[averagingInt](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#averagingInt%2Djava.util.function.ToIntFunction%2D[java 9]) +* ++[[painless-api-reference-Collectors-averagingLong-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#averagingLong%2Djava.util.function.ToLongFunction%2D[averagingLong](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#averagingLong%2Djava.util.function.ToLongFunction%2D[java 9]) +* ++[[painless-api-reference-Collectors-collectingAndThen-2]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#collectingAndThen%2Djava.util.stream.Collector%2Djava.util.function.Function%2D[collectingAndThen](<>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#collectingAndThen%2Djava.util.stream.Collector%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-Collectors-counting-0]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#counting%2D%2D[counting]()++ (link:{java9-javadoc}/java/util/stream/Collectors.html#counting%2D%2D[java 9]) +* ++[[painless-api-reference-Collectors-groupingBy-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#groupingBy%2Djava.util.function.Function%2D[groupingBy](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#groupingBy%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-Collectors-groupingBy-2]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#groupingBy%2Djava.util.function.Function%2Djava.util.stream.Collector%2D[groupingBy](<>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#groupingBy%2Djava.util.function.Function%2Djava.util.stream.Collector%2D[java 9]) +* ++[[painless-api-reference-Collectors-groupingBy-3]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#groupingBy%2Djava.util.function.Function%2Djava.util.function.Supplier%2Djava.util.stream.Collector%2D[groupingBy](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#groupingBy%2Djava.util.function.Function%2Djava.util.function.Supplier%2Djava.util.stream.Collector%2D[java 9]) +* ++[[painless-api-reference-Collectors-joining-0]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#joining%2D%2D[joining]()++ (link:{java9-javadoc}/java/util/stream/Collectors.html#joining%2D%2D[java 9]) +* ++[[painless-api-reference-Collectors-joining-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#joining%2Djava.lang.CharSequence%2D[joining](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#joining%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-Collectors-joining-3]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#joining%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2D[joining](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#joining%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-Collectors-mapping-2]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#mapping%2Djava.util.function.Function%2Djava.util.stream.Collector%2D[mapping](<>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#mapping%2Djava.util.function.Function%2Djava.util.stream.Collector%2D[java 9]) +* ++[[painless-api-reference-Collectors-maxBy-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#maxBy%2Djava.util.Comparator%2D[maxBy](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#maxBy%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Collectors-minBy-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#minBy%2Djava.util.Comparator%2D[minBy](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#minBy%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Collectors-partitioningBy-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#partitioningBy%2Djava.util.function.Predicate%2D[partitioningBy](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#partitioningBy%2Djava.util.function.Predicate%2D[java 9]) +* ++[[painless-api-reference-Collectors-partitioningBy-2]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#partitioningBy%2Djava.util.function.Predicate%2Djava.util.stream.Collector%2D[partitioningBy](<>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#partitioningBy%2Djava.util.function.Predicate%2Djava.util.stream.Collector%2D[java 9]) +* ++[[painless-api-reference-Collectors-reducing-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#reducing%2Djava.util.function.BinaryOperator%2D[reducing](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#reducing%2Djava.util.function.BinaryOperator%2D[java 9]) +* ++[[painless-api-reference-Collectors-reducing-2]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#reducing%2Djava.lang.Object%2Djava.util.function.BinaryOperator%2D[reducing](def, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#reducing%2Djava.lang.Object%2Djava.util.function.BinaryOperator%2D[java 9]) +* ++[[painless-api-reference-Collectors-reducing-3]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#reducing%2Djava.lang.Object%2Djava.util.function.Function%2Djava.util.function.BinaryOperator%2D[reducing](def, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#reducing%2Djava.lang.Object%2Djava.util.function.Function%2Djava.util.function.BinaryOperator%2D[java 9]) +* ++[[painless-api-reference-Collectors-summarizingDouble-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#summarizingDouble%2Djava.util.function.ToDoubleFunction%2D[summarizingDouble](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#summarizingDouble%2Djava.util.function.ToDoubleFunction%2D[java 9]) +* ++[[painless-api-reference-Collectors-summarizingInt-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#summarizingInt%2Djava.util.function.ToIntFunction%2D[summarizingInt](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#summarizingInt%2Djava.util.function.ToIntFunction%2D[java 9]) +* ++[[painless-api-reference-Collectors-summarizingLong-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#summarizingLong%2Djava.util.function.ToLongFunction%2D[summarizingLong](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#summarizingLong%2Djava.util.function.ToLongFunction%2D[java 9]) +* ++[[painless-api-reference-Collectors-summingDouble-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#summingDouble%2Djava.util.function.ToDoubleFunction%2D[summingDouble](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#summingDouble%2Djava.util.function.ToDoubleFunction%2D[java 9]) +* ++[[painless-api-reference-Collectors-summingInt-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#summingInt%2Djava.util.function.ToIntFunction%2D[summingInt](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#summingInt%2Djava.util.function.ToIntFunction%2D[java 9]) +* ++[[painless-api-reference-Collectors-summingLong-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#summingLong%2Djava.util.function.ToLongFunction%2D[summingLong](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#summingLong%2Djava.util.function.ToLongFunction%2D[java 9]) +* ++[[painless-api-reference-Collectors-toCollection-1]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#toCollection%2Djava.util.function.Supplier%2D[toCollection](<>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#toCollection%2Djava.util.function.Supplier%2D[java 9]) +* ++[[painless-api-reference-Collectors-toList-0]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#toList%2D%2D[toList]()++ (link:{java9-javadoc}/java/util/stream/Collectors.html#toList%2D%2D[java 9]) +* ++[[painless-api-reference-Collectors-toMap-2]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#toMap%2Djava.util.function.Function%2Djava.util.function.Function%2D[toMap](<>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#toMap%2Djava.util.function.Function%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-Collectors-toMap-3]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#toMap%2Djava.util.function.Function%2Djava.util.function.Function%2Djava.util.function.BinaryOperator%2D[toMap](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#toMap%2Djava.util.function.Function%2Djava.util.function.Function%2Djava.util.function.BinaryOperator%2D[java 9]) +* ++[[painless-api-reference-Collectors-toMap-4]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#toMap%2Djava.util.function.Function%2Djava.util.function.Function%2Djava.util.function.BinaryOperator%2Djava.util.function.Supplier%2D[toMap](<>, <>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Collectors.html#toMap%2Djava.util.function.Function%2Djava.util.function.Function%2Djava.util.function.BinaryOperator%2Djava.util.function.Supplier%2D[java 9]) +* ++[[painless-api-reference-Collectors-toSet-0]]static <> link:{java8-javadoc}/java/util/stream/Collectors.html#toSet%2D%2D[toSet]()++ (link:{java9-javadoc}/java/util/stream/Collectors.html#toSet%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Comparable.asciidoc b/docs/painless/painless-api-reference/Comparable.asciidoc new file mode 100644 index 0000000000000..1550d93b51144 --- /dev/null +++ b/docs/painless/painless-api-reference/Comparable.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Comparable]]++Comparable++:: +* ++[[painless-api-reference-Comparable-compareTo-1]]int link:{java8-javadoc}/java/lang/Comparable.html#compareTo%2Djava.lang.Object%2D[compareTo](def)++ (link:{java9-javadoc}/java/lang/Comparable.html#compareTo%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Comparator.asciidoc b/docs/painless/painless-api-reference/Comparator.asciidoc new file mode 100644 index 0000000000000..25545376942cc --- /dev/null +++ b/docs/painless/painless-api-reference/Comparator.asciidoc @@ -0,0 +1,24 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Comparator]]++Comparator++:: +* ++[[painless-api-reference-Comparator-comparing-1]]static <> link:{java8-javadoc}/java/util/Comparator.html#comparing%2Djava.util.function.Function%2D[comparing](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#comparing%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-Comparator-comparing-2]]static <> link:{java8-javadoc}/java/util/Comparator.html#comparing%2Djava.util.function.Function%2Djava.util.Comparator%2D[comparing](<>, <>)++ (link:{java9-javadoc}/java/util/Comparator.html#comparing%2Djava.util.function.Function%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Comparator-comparingDouble-1]]static <> link:{java8-javadoc}/java/util/Comparator.html#comparingDouble%2Djava.util.function.ToDoubleFunction%2D[comparingDouble](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#comparingDouble%2Djava.util.function.ToDoubleFunction%2D[java 9]) +* ++[[painless-api-reference-Comparator-comparingInt-1]]static <> link:{java8-javadoc}/java/util/Comparator.html#comparingInt%2Djava.util.function.ToIntFunction%2D[comparingInt](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#comparingInt%2Djava.util.function.ToIntFunction%2D[java 9]) +* ++[[painless-api-reference-Comparator-comparingLong-1]]static <> link:{java8-javadoc}/java/util/Comparator.html#comparingLong%2Djava.util.function.ToLongFunction%2D[comparingLong](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#comparingLong%2Djava.util.function.ToLongFunction%2D[java 9]) +* ++[[painless-api-reference-Comparator-naturalOrder-0]]static <> link:{java8-javadoc}/java/util/Comparator.html#naturalOrder%2D%2D[naturalOrder]()++ (link:{java9-javadoc}/java/util/Comparator.html#naturalOrder%2D%2D[java 9]) +* ++[[painless-api-reference-Comparator-nullsFirst-1]]static <> link:{java8-javadoc}/java/util/Comparator.html#nullsFirst%2Djava.util.Comparator%2D[nullsFirst](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#nullsFirst%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Comparator-nullsLast-1]]static <> link:{java8-javadoc}/java/util/Comparator.html#nullsLast%2Djava.util.Comparator%2D[nullsLast](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#nullsLast%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Comparator-reverseOrder-0]]static <> link:{java8-javadoc}/java/util/Comparator.html#reverseOrder%2D%2D[reverseOrder]()++ (link:{java9-javadoc}/java/util/Comparator.html#reverseOrder%2D%2D[java 9]) +* ++[[painless-api-reference-Comparator-compare-2]]int link:{java8-javadoc}/java/util/Comparator.html#compare%2Djava.lang.Object%2Djava.lang.Object%2D[compare](def, def)++ (link:{java9-javadoc}/java/util/Comparator.html#compare%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Comparator-equals-1]]boolean link:{java8-javadoc}/java/util/Comparator.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#equals%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Comparator-reversed-0]]<> link:{java8-javadoc}/java/util/Comparator.html#reversed%2D%2D[reversed]()++ (link:{java9-javadoc}/java/util/Comparator.html#reversed%2D%2D[java 9]) +* ++[[painless-api-reference-Comparator-thenComparing-1]]<> link:{java8-javadoc}/java/util/Comparator.html#thenComparing%2Djava.util.Comparator%2D[thenComparing](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#thenComparing%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Comparator-thenComparing-2]]<> link:{java8-javadoc}/java/util/Comparator.html#thenComparing%2Djava.util.function.Function%2Djava.util.Comparator%2D[thenComparing](<>, <>)++ (link:{java9-javadoc}/java/util/Comparator.html#thenComparing%2Djava.util.function.Function%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Comparator-thenComparingDouble-1]]<> link:{java8-javadoc}/java/util/Comparator.html#thenComparingDouble%2Djava.util.function.ToDoubleFunction%2D[thenComparingDouble](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#thenComparingDouble%2Djava.util.function.ToDoubleFunction%2D[java 9]) +* ++[[painless-api-reference-Comparator-thenComparingInt-1]]<> link:{java8-javadoc}/java/util/Comparator.html#thenComparingInt%2Djava.util.function.ToIntFunction%2D[thenComparingInt](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#thenComparingInt%2Djava.util.function.ToIntFunction%2D[java 9]) +* ++[[painless-api-reference-Comparator-thenComparingLong-1]]<> link:{java8-javadoc}/java/util/Comparator.html#thenComparingLong%2Djava.util.function.ToLongFunction%2D[thenComparingLong](<>)++ (link:{java9-javadoc}/java/util/Comparator.html#thenComparingLong%2Djava.util.function.ToLongFunction%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ConcurrentModificationException.asciidoc b/docs/painless/painless-api-reference/ConcurrentModificationException.asciidoc new file mode 100644 index 0000000000000..b33eefee1e5ad --- /dev/null +++ b/docs/painless/painless-api-reference/ConcurrentModificationException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ConcurrentModificationException]]++ConcurrentModificationException++:: +* ++[[painless-api-reference-ConcurrentModificationException-ConcurrentModificationException-0]]link:{java8-javadoc}/java/util/ConcurrentModificationException.html#ConcurrentModificationException%2D%2D[ConcurrentModificationException]()++ (link:{java9-javadoc}/java/util/ConcurrentModificationException.html#ConcurrentModificationException%2D%2D[java 9]) +* ++[[painless-api-reference-ConcurrentModificationException-ConcurrentModificationException-1]]link:{java8-javadoc}/java/util/ConcurrentModificationException.html#ConcurrentModificationException%2Djava.lang.String%2D[ConcurrentModificationException](<>)++ (link:{java9-javadoc}/java/util/ConcurrentModificationException.html#ConcurrentModificationException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Consumer.asciidoc b/docs/painless/painless-api-reference/Consumer.asciidoc new file mode 100644 index 0000000000000..a93f47e1a1a7c --- /dev/null +++ b/docs/painless/painless-api-reference/Consumer.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Consumer]]++Consumer++:: +* ++[[painless-api-reference-Consumer-accept-1]]void link:{java8-javadoc}/java/util/function/Consumer.html#accept%2Djava.lang.Object%2D[accept](def)++ (link:{java9-javadoc}/java/util/function/Consumer.html#accept%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Consumer-andThen-1]]<> link:{java8-javadoc}/java/util/function/Consumer.html#andThen%2Djava.util.function.Consumer%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/Consumer.html#andThen%2Djava.util.function.Consumer%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Currency.asciidoc b/docs/painless/painless-api-reference/Currency.asciidoc new file mode 100644 index 0000000000000..12e172e01eeea --- /dev/null +++ b/docs/painless/painless-api-reference/Currency.asciidoc @@ -0,0 +1,16 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Currency]]++Currency++:: +* ++[[painless-api-reference-Currency-getAvailableCurrencies-0]]static <> link:{java8-javadoc}/java/util/Currency.html#getAvailableCurrencies%2D%2D[getAvailableCurrencies]()++ (link:{java9-javadoc}/java/util/Currency.html#getAvailableCurrencies%2D%2D[java 9]) +* ++[[painless-api-reference-Currency-getInstance-1]]static <> link:{java8-javadoc}/java/util/Currency.html#getInstance%2Djava.lang.String%2D[getInstance](<>)++ (link:{java9-javadoc}/java/util/Currency.html#getInstance%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Currency-getCurrencyCode-0]]<> link:{java8-javadoc}/java/util/Currency.html#getCurrencyCode%2D%2D[getCurrencyCode]()++ (link:{java9-javadoc}/java/util/Currency.html#getCurrencyCode%2D%2D[java 9]) +* ++[[painless-api-reference-Currency-getDefaultFractionDigits-0]]int link:{java8-javadoc}/java/util/Currency.html#getDefaultFractionDigits%2D%2D[getDefaultFractionDigits]()++ (link:{java9-javadoc}/java/util/Currency.html#getDefaultFractionDigits%2D%2D[java 9]) +* ++[[painless-api-reference-Currency-getDisplayName-0]]<> link:{java8-javadoc}/java/util/Currency.html#getDisplayName%2D%2D[getDisplayName]()++ (link:{java9-javadoc}/java/util/Currency.html#getDisplayName%2D%2D[java 9]) +* ++[[painless-api-reference-Currency-getDisplayName-1]]<> link:{java8-javadoc}/java/util/Currency.html#getDisplayName%2Djava.util.Locale%2D[getDisplayName](<>)++ (link:{java9-javadoc}/java/util/Currency.html#getDisplayName%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Currency-getNumericCode-0]]int link:{java8-javadoc}/java/util/Currency.html#getNumericCode%2D%2D[getNumericCode]()++ (link:{java9-javadoc}/java/util/Currency.html#getNumericCode%2D%2D[java 9]) +* ++[[painless-api-reference-Currency-getSymbol-0]]<> link:{java8-javadoc}/java/util/Currency.html#getSymbol%2D%2D[getSymbol]()++ (link:{java9-javadoc}/java/util/Currency.html#getSymbol%2D%2D[java 9]) +* ++[[painless-api-reference-Currency-getSymbol-1]]<> link:{java8-javadoc}/java/util/Currency.html#getSymbol%2Djava.util.Locale%2D[getSymbol](<>)++ (link:{java9-javadoc}/java/util/Currency.html#getSymbol%2Djava.util.Locale%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Date.asciidoc b/docs/painless/painless-api-reference/Date.asciidoc new file mode 100644 index 0000000000000..5b83f70a0eaae --- /dev/null +++ b/docs/painless/painless-api-reference/Date.asciidoc @@ -0,0 +1,16 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Date]]++Date++:: +* ++[[painless-api-reference-Date-from-1]]static <> link:{java8-javadoc}/java/util/Date.html#from%2Djava.time.Instant%2D[from](<>)++ (link:{java9-javadoc}/java/util/Date.html#from%2Djava.time.Instant%2D[java 9]) +* ++[[painless-api-reference-Date-Date-0]]link:{java8-javadoc}/java/util/Date.html#Date%2D%2D[Date]()++ (link:{java9-javadoc}/java/util/Date.html#Date%2D%2D[java 9]) +* ++[[painless-api-reference-Date-Date-1]]link:{java8-javadoc}/java/util/Date.html#Date%2Dlong%2D[Date](long)++ (link:{java9-javadoc}/java/util/Date.html#Date%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Date-after-1]]boolean link:{java8-javadoc}/java/util/Date.html#after%2Djava.util.Date%2D[after](<>)++ (link:{java9-javadoc}/java/util/Date.html#after%2Djava.util.Date%2D[java 9]) +* ++[[painless-api-reference-Date-before-1]]boolean link:{java8-javadoc}/java/util/Date.html#before%2Djava.util.Date%2D[before](<>)++ (link:{java9-javadoc}/java/util/Date.html#before%2Djava.util.Date%2D[java 9]) +* ++[[painless-api-reference-Date-clone-0]]def link:{java8-javadoc}/java/util/Date.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/Date.html#clone%2D%2D[java 9]) +* ++[[painless-api-reference-Date-compareTo-1]]int link:{java8-javadoc}/java/util/Date.html#compareTo%2Djava.util.Date%2D[compareTo](<>)++ (link:{java9-javadoc}/java/util/Date.html#compareTo%2Djava.util.Date%2D[java 9]) +* ++[[painless-api-reference-Date-getTime-0]]long link:{java8-javadoc}/java/util/Date.html#getTime%2D%2D[getTime]()++ (link:{java9-javadoc}/java/util/Date.html#getTime%2D%2D[java 9]) +* ++[[painless-api-reference-Date-setTime-1]]void link:{java8-javadoc}/java/util/Date.html#setTime%2Dlong%2D[setTime](long)++ (link:{java9-javadoc}/java/util/Date.html#setTime%2Dlong%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DateFormat.Field.asciidoc b/docs/painless/painless-api-reference/DateFormat.Field.asciidoc new file mode 100644 index 0000000000000..b9ad2cda2926c --- /dev/null +++ b/docs/painless/painless-api-reference/DateFormat.Field.asciidoc @@ -0,0 +1,27 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DateFormat-Field]]++DateFormat.Field++:: +** [[painless-api-reference-DateFormat-Field-AM_PM]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#AM_PM[AM_PM] (link:{java9-javadoc}/java/text/DateFormat$Field.html#AM_PM[java 9]) +** [[painless-api-reference-DateFormat-Field-DAY_OF_MONTH]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#DAY_OF_MONTH[DAY_OF_MONTH] (link:{java9-javadoc}/java/text/DateFormat$Field.html#DAY_OF_MONTH[java 9]) +** [[painless-api-reference-DateFormat-Field-DAY_OF_WEEK]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#DAY_OF_WEEK[DAY_OF_WEEK] (link:{java9-javadoc}/java/text/DateFormat$Field.html#DAY_OF_WEEK[java 9]) +** [[painless-api-reference-DateFormat-Field-DAY_OF_WEEK_IN_MONTH]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#DAY_OF_WEEK_IN_MONTH[DAY_OF_WEEK_IN_MONTH] (link:{java9-javadoc}/java/text/DateFormat$Field.html#DAY_OF_WEEK_IN_MONTH[java 9]) +** [[painless-api-reference-DateFormat-Field-DAY_OF_YEAR]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#DAY_OF_YEAR[DAY_OF_YEAR] (link:{java9-javadoc}/java/text/DateFormat$Field.html#DAY_OF_YEAR[java 9]) +** [[painless-api-reference-DateFormat-Field-ERA]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#ERA[ERA] (link:{java9-javadoc}/java/text/DateFormat$Field.html#ERA[java 9]) +** [[painless-api-reference-DateFormat-Field-HOUR0]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#HOUR0[HOUR0] (link:{java9-javadoc}/java/text/DateFormat$Field.html#HOUR0[java 9]) +** [[painless-api-reference-DateFormat-Field-HOUR1]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#HOUR1[HOUR1] (link:{java9-javadoc}/java/text/DateFormat$Field.html#HOUR1[java 9]) +** [[painless-api-reference-DateFormat-Field-HOUR_OF_DAY0]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#HOUR_OF_DAY0[HOUR_OF_DAY0] (link:{java9-javadoc}/java/text/DateFormat$Field.html#HOUR_OF_DAY0[java 9]) +** [[painless-api-reference-DateFormat-Field-HOUR_OF_DAY1]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#HOUR_OF_DAY1[HOUR_OF_DAY1] (link:{java9-javadoc}/java/text/DateFormat$Field.html#HOUR_OF_DAY1[java 9]) +** [[painless-api-reference-DateFormat-Field-MILLISECOND]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#MILLISECOND[MILLISECOND] (link:{java9-javadoc}/java/text/DateFormat$Field.html#MILLISECOND[java 9]) +** [[painless-api-reference-DateFormat-Field-MINUTE]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#MINUTE[MINUTE] (link:{java9-javadoc}/java/text/DateFormat$Field.html#MINUTE[java 9]) +** [[painless-api-reference-DateFormat-Field-MONTH]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#MONTH[MONTH] (link:{java9-javadoc}/java/text/DateFormat$Field.html#MONTH[java 9]) +** [[painless-api-reference-DateFormat-Field-SECOND]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#SECOND[SECOND] (link:{java9-javadoc}/java/text/DateFormat$Field.html#SECOND[java 9]) +** [[painless-api-reference-DateFormat-Field-TIME_ZONE]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#TIME_ZONE[TIME_ZONE] (link:{java9-javadoc}/java/text/DateFormat$Field.html#TIME_ZONE[java 9]) +** [[painless-api-reference-DateFormat-Field-WEEK_OF_MONTH]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#WEEK_OF_MONTH[WEEK_OF_MONTH] (link:{java9-javadoc}/java/text/DateFormat$Field.html#WEEK_OF_MONTH[java 9]) +** [[painless-api-reference-DateFormat-Field-WEEK_OF_YEAR]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#WEEK_OF_YEAR[WEEK_OF_YEAR] (link:{java9-javadoc}/java/text/DateFormat$Field.html#WEEK_OF_YEAR[java 9]) +** [[painless-api-reference-DateFormat-Field-YEAR]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#YEAR[YEAR] (link:{java9-javadoc}/java/text/DateFormat$Field.html#YEAR[java 9]) +* ++[[painless-api-reference-DateFormat-Field-ofCalendarField-1]]static <> link:{java8-javadoc}/java/text/DateFormat$Field.html#ofCalendarField%2Dint%2D[ofCalendarField](int)++ (link:{java9-javadoc}/java/text/DateFormat$Field.html#ofCalendarField%2Dint%2D[java 9]) +* ++[[painless-api-reference-DateFormat-Field-getCalendarField-0]]int link:{java8-javadoc}/java/text/DateFormat$Field.html#getCalendarField%2D%2D[getCalendarField]()++ (link:{java9-javadoc}/java/text/DateFormat$Field.html#getCalendarField%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DateFormat.asciidoc b/docs/painless/painless-api-reference/DateFormat.asciidoc new file mode 100644 index 0000000000000..8bb4fdf2eff71 --- /dev/null +++ b/docs/painless/painless-api-reference/DateFormat.asciidoc @@ -0,0 +1,51 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DateFormat]]++DateFormat++:: +** [[painless-api-reference-DateFormat-AM_PM_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#AM_PM_FIELD[AM_PM_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#AM_PM_FIELD[java 9]) +** [[painless-api-reference-DateFormat-DATE_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#DATE_FIELD[DATE_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#DATE_FIELD[java 9]) +** [[painless-api-reference-DateFormat-DAY_OF_WEEK_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#DAY_OF_WEEK_FIELD[DAY_OF_WEEK_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#DAY_OF_WEEK_FIELD[java 9]) +** [[painless-api-reference-DateFormat-DAY_OF_WEEK_IN_MONTH_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#DAY_OF_WEEK_IN_MONTH_FIELD[DAY_OF_WEEK_IN_MONTH_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#DAY_OF_WEEK_IN_MONTH_FIELD[java 9]) +** [[painless-api-reference-DateFormat-DAY_OF_YEAR_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#DAY_OF_YEAR_FIELD[DAY_OF_YEAR_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#DAY_OF_YEAR_FIELD[java 9]) +** [[painless-api-reference-DateFormat-DEFAULT]]static int link:{java8-javadoc}/java/text/DateFormat.html#DEFAULT[DEFAULT] (link:{java9-javadoc}/java/text/DateFormat.html#DEFAULT[java 9]) +** [[painless-api-reference-DateFormat-ERA_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#ERA_FIELD[ERA_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#ERA_FIELD[java 9]) +** [[painless-api-reference-DateFormat-FULL]]static int link:{java8-javadoc}/java/text/DateFormat.html#FULL[FULL] (link:{java9-javadoc}/java/text/DateFormat.html#FULL[java 9]) +** [[painless-api-reference-DateFormat-HOUR0_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#HOUR0_FIELD[HOUR0_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#HOUR0_FIELD[java 9]) +** [[painless-api-reference-DateFormat-HOUR1_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#HOUR1_FIELD[HOUR1_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#HOUR1_FIELD[java 9]) +** [[painless-api-reference-DateFormat-HOUR_OF_DAY0_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#HOUR_OF_DAY0_FIELD[HOUR_OF_DAY0_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#HOUR_OF_DAY0_FIELD[java 9]) +** [[painless-api-reference-DateFormat-HOUR_OF_DAY1_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#HOUR_OF_DAY1_FIELD[HOUR_OF_DAY1_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#HOUR_OF_DAY1_FIELD[java 9]) +** [[painless-api-reference-DateFormat-LONG]]static int link:{java8-javadoc}/java/text/DateFormat.html#LONG[LONG] (link:{java9-javadoc}/java/text/DateFormat.html#LONG[java 9]) +** [[painless-api-reference-DateFormat-MEDIUM]]static int link:{java8-javadoc}/java/text/DateFormat.html#MEDIUM[MEDIUM] (link:{java9-javadoc}/java/text/DateFormat.html#MEDIUM[java 9]) +** [[painless-api-reference-DateFormat-MILLISECOND_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#MILLISECOND_FIELD[MILLISECOND_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#MILLISECOND_FIELD[java 9]) +** [[painless-api-reference-DateFormat-MINUTE_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#MINUTE_FIELD[MINUTE_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#MINUTE_FIELD[java 9]) +** [[painless-api-reference-DateFormat-MONTH_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#MONTH_FIELD[MONTH_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#MONTH_FIELD[java 9]) +** [[painless-api-reference-DateFormat-SECOND_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#SECOND_FIELD[SECOND_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#SECOND_FIELD[java 9]) +** [[painless-api-reference-DateFormat-SHORT]]static int link:{java8-javadoc}/java/text/DateFormat.html#SHORT[SHORT] (link:{java9-javadoc}/java/text/DateFormat.html#SHORT[java 9]) +** [[painless-api-reference-DateFormat-TIMEZONE_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#TIMEZONE_FIELD[TIMEZONE_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#TIMEZONE_FIELD[java 9]) +** [[painless-api-reference-DateFormat-WEEK_OF_MONTH_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#WEEK_OF_MONTH_FIELD[WEEK_OF_MONTH_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#WEEK_OF_MONTH_FIELD[java 9]) +** [[painless-api-reference-DateFormat-WEEK_OF_YEAR_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#WEEK_OF_YEAR_FIELD[WEEK_OF_YEAR_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#WEEK_OF_YEAR_FIELD[java 9]) +** [[painless-api-reference-DateFormat-YEAR_FIELD]]static int link:{java8-javadoc}/java/text/DateFormat.html#YEAR_FIELD[YEAR_FIELD] (link:{java9-javadoc}/java/text/DateFormat.html#YEAR_FIELD[java 9]) +* ++[[painless-api-reference-DateFormat-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/text/DateFormat.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getAvailableLocales%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormat-getDateInstance-0]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getDateInstance%2D%2D[getDateInstance]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getDateInstance%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormat-getDateInstance-1]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getDateInstance%2Dint%2D[getDateInstance](int)++ (link:{java9-javadoc}/java/text/DateFormat.html#getDateInstance%2Dint%2D[java 9]) +* ++[[painless-api-reference-DateFormat-getDateInstance-2]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getDateInstance%2Dint%2Djava.util.Locale%2D[getDateInstance](int, <>)++ (link:{java9-javadoc}/java/text/DateFormat.html#getDateInstance%2Dint%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-DateFormat-getDateTimeInstance-0]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getDateTimeInstance%2D%2D[getDateTimeInstance]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getDateTimeInstance%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormat-getDateTimeInstance-2]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getDateTimeInstance%2Dint%2Dint%2D[getDateTimeInstance](int, int)++ (link:{java9-javadoc}/java/text/DateFormat.html#getDateTimeInstance%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-DateFormat-getDateTimeInstance-3]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getDateTimeInstance%2Dint%2Dint%2Djava.util.Locale%2D[getDateTimeInstance](int, int, <>)++ (link:{java9-javadoc}/java/text/DateFormat.html#getDateTimeInstance%2Dint%2Dint%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-DateFormat-getInstance-0]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getInstance%2D%2D[getInstance]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getInstance%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormat-getTimeInstance-0]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getTimeInstance%2D%2D[getTimeInstance]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getTimeInstance%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormat-getTimeInstance-1]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getTimeInstance%2Dint%2D[getTimeInstance](int)++ (link:{java9-javadoc}/java/text/DateFormat.html#getTimeInstance%2Dint%2D[java 9]) +* ++[[painless-api-reference-DateFormat-getTimeInstance-2]]static <> link:{java8-javadoc}/java/text/DateFormat.html#getTimeInstance%2Dint%2Djava.util.Locale%2D[getTimeInstance](int, <>)++ (link:{java9-javadoc}/java/text/DateFormat.html#getTimeInstance%2Dint%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-DateFormat-getCalendar-0]]<> link:{java8-javadoc}/java/text/DateFormat.html#getCalendar%2D%2D[getCalendar]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getCalendar%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormat-getNumberFormat-0]]<> link:{java8-javadoc}/java/text/DateFormat.html#getNumberFormat%2D%2D[getNumberFormat]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getNumberFormat%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormat-getTimeZone-0]]<> link:{java8-javadoc}/java/text/DateFormat.html#getTimeZone%2D%2D[getTimeZone]()++ (link:{java9-javadoc}/java/text/DateFormat.html#getTimeZone%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormat-isLenient-0]]boolean link:{java8-javadoc}/java/text/DateFormat.html#isLenient%2D%2D[isLenient]()++ (link:{java9-javadoc}/java/text/DateFormat.html#isLenient%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormat-parse-1]]<> link:{java8-javadoc}/java/text/DateFormat.html#parse%2Djava.lang.String%2D[parse](<>)++ (link:{java9-javadoc}/java/text/DateFormat.html#parse%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DateFormat-parse-2]]<> link:{java8-javadoc}/java/text/DateFormat.html#parse%2Djava.lang.String%2Djava.text.ParsePosition%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/text/DateFormat.html#parse%2Djava.lang.String%2Djava.text.ParsePosition%2D[java 9]) +* ++[[painless-api-reference-DateFormat-setCalendar-1]]void link:{java8-javadoc}/java/text/DateFormat.html#setCalendar%2Djava.util.Calendar%2D[setCalendar](<>)++ (link:{java9-javadoc}/java/text/DateFormat.html#setCalendar%2Djava.util.Calendar%2D[java 9]) +* ++[[painless-api-reference-DateFormat-setLenient-1]]void link:{java8-javadoc}/java/text/DateFormat.html#setLenient%2Dboolean%2D[setLenient](boolean)++ (link:{java9-javadoc}/java/text/DateFormat.html#setLenient%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-DateFormat-setNumberFormat-1]]void link:{java8-javadoc}/java/text/DateFormat.html#setNumberFormat%2Djava.text.NumberFormat%2D[setNumberFormat](<>)++ (link:{java9-javadoc}/java/text/DateFormat.html#setNumberFormat%2Djava.text.NumberFormat%2D[java 9]) +* ++[[painless-api-reference-DateFormat-setTimeZone-1]]void link:{java8-javadoc}/java/text/DateFormat.html#setTimeZone%2Djava.util.TimeZone%2D[setTimeZone](<>)++ (link:{java9-javadoc}/java/text/DateFormat.html#setTimeZone%2Djava.util.TimeZone%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/DateFormatSymbols.asciidoc b/docs/painless/painless-api-reference/DateFormatSymbols.asciidoc new file mode 100644 index 0000000000000..31cf39bbc4586 --- /dev/null +++ b/docs/painless/painless-api-reference/DateFormatSymbols.asciidoc @@ -0,0 +1,30 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DateFormatSymbols]]++DateFormatSymbols++:: +* ++[[painless-api-reference-DateFormatSymbols-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getAvailableLocales%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-getInstance-0]]static <> link:{java8-javadoc}/java/text/DateFormatSymbols.html#getInstance%2D%2D[getInstance]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getInstance%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-getInstance-1]]static <> link:{java8-javadoc}/java/text/DateFormatSymbols.html#getInstance%2Djava.util.Locale%2D[getInstance](<>)++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getInstance%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-DateFormatSymbols-0]]link:{java8-javadoc}/java/text/DateFormatSymbols.html#DateFormatSymbols%2D%2D[DateFormatSymbols]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#DateFormatSymbols%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-DateFormatSymbols-1]]link:{java8-javadoc}/java/text/DateFormatSymbols.html#DateFormatSymbols%2Djava.util.Locale%2D[DateFormatSymbols](<>)++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#DateFormatSymbols%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-clone-0]]def link:{java8-javadoc}/java/text/DateFormatSymbols.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#clone%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-getAmPmStrings-0]]<>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getAmPmStrings%2D%2D[getAmPmStrings]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getAmPmStrings%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-getEras-0]]<>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getEras%2D%2D[getEras]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getEras%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-getLocalPatternChars-0]]<> link:{java8-javadoc}/java/text/DateFormatSymbols.html#getLocalPatternChars%2D%2D[getLocalPatternChars]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getLocalPatternChars%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-getMonths-0]]<>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getMonths%2D%2D[getMonths]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getMonths%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-getShortMonths-0]]<>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getShortMonths%2D%2D[getShortMonths]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getShortMonths%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-getShortWeekdays-0]]<>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getShortWeekdays%2D%2D[getShortWeekdays]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getShortWeekdays%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-getWeekdays-0]]<>[] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getWeekdays%2D%2D[getWeekdays]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getWeekdays%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-getZoneStrings-0]]<>[][] link:{java8-javadoc}/java/text/DateFormatSymbols.html#getZoneStrings%2D%2D[getZoneStrings]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#getZoneStrings%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-hashCode-0]]int link:{java8-javadoc}/java/text/DateFormatSymbols.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#hashCode%2D%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-setAmPmStrings-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setAmPmStrings%2Djava.lang.String:A%2D[setAmPmStrings](<>[])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setAmPmStrings%2Djava.lang.String:A%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-setEras-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setEras%2Djava.lang.String:A%2D[setEras](<>[])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setEras%2Djava.lang.String:A%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-setLocalPatternChars-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setLocalPatternChars%2Djava.lang.String%2D[setLocalPatternChars](<>)++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setLocalPatternChars%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-setMonths-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setMonths%2Djava.lang.String:A%2D[setMonths](<>[])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setMonths%2Djava.lang.String:A%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-setShortMonths-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setShortMonths%2Djava.lang.String:A%2D[setShortMonths](<>[])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setShortMonths%2Djava.lang.String:A%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-setShortWeekdays-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setShortWeekdays%2Djava.lang.String:A%2D[setShortWeekdays](<>[])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setShortWeekdays%2Djava.lang.String:A%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-setWeekdays-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setWeekdays%2Djava.lang.String:A%2D[setWeekdays](<>[])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setWeekdays%2Djava.lang.String:A%2D[java 9]) +* ++[[painless-api-reference-DateFormatSymbols-setZoneStrings-1]]void link:{java8-javadoc}/java/text/DateFormatSymbols.html#setZoneStrings%2Djava.lang.String:A%2D[setZoneStrings](<>[][])++ (link:{java9-javadoc}/java/text/DateFormatSymbols.html#setZoneStrings%2Djava.lang.String:A%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DateTimeException.asciidoc b/docs/painless/painless-api-reference/DateTimeException.asciidoc new file mode 100644 index 0000000000000..5a9953c5bd582 --- /dev/null +++ b/docs/painless/painless-api-reference/DateTimeException.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DateTimeException]]++DateTimeException++:: +* ++[[painless-api-reference-DateTimeException-DateTimeException-1]]link:{java8-javadoc}/java/time/DateTimeException.html#DateTimeException%2Djava.lang.String%2D[DateTimeException](<>)++ (link:{java9-javadoc}/java/time/DateTimeException.html#DateTimeException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/DateTimeFormatter.asciidoc b/docs/painless/painless-api-reference/DateTimeFormatter.asciidoc new file mode 100644 index 0000000000000..ad104dfe5d6d2 --- /dev/null +++ b/docs/painless/painless-api-reference/DateTimeFormatter.asciidoc @@ -0,0 +1,50 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DateTimeFormatter]]++DateTimeFormatter++:: +** [[painless-api-reference-DateTimeFormatter-BASIC_ISO_DATE]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#BASIC_ISO_DATE[BASIC_ISO_DATE] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#BASIC_ISO_DATE[java 9]) +** [[painless-api-reference-DateTimeFormatter-ISO_DATE]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_DATE[ISO_DATE] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_DATE[java 9]) +** [[painless-api-reference-DateTimeFormatter-ISO_DATE_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_DATE_TIME[ISO_DATE_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_DATE_TIME[java 9]) +** [[painless-api-reference-DateTimeFormatter-ISO_INSTANT]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_INSTANT[ISO_INSTANT] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_INSTANT[java 9]) +** [[painless-api-reference-DateTimeFormatter-ISO_LOCAL_DATE]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_LOCAL_DATE[ISO_LOCAL_DATE] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_LOCAL_DATE[java 9]) +** [[painless-api-reference-DateTimeFormatter-ISO_LOCAL_DATE_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_LOCAL_DATE_TIME[ISO_LOCAL_DATE_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_LOCAL_DATE_TIME[java 9]) +** [[painless-api-reference-DateTimeFormatter-ISO_LOCAL_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_LOCAL_TIME[ISO_LOCAL_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_LOCAL_TIME[java 9]) +** [[painless-api-reference-DateTimeFormatter-ISO_OFFSET_DATE]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE[ISO_OFFSET_DATE] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE[java 9]) +** [[painless-api-reference-DateTimeFormatter-ISO_OFFSET_DATE_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE_TIME[ISO_OFFSET_DATE_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE_TIME[java 9]) +** [[painless-api-reference-DateTimeFormatter-ISO_OFFSET_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_OFFSET_TIME[ISO_OFFSET_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_OFFSET_TIME[java 9]) +** [[painless-api-reference-DateTimeFormatter-ISO_ORDINAL_DATE]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_ORDINAL_DATE[ISO_ORDINAL_DATE] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_ORDINAL_DATE[java 9]) +** [[painless-api-reference-DateTimeFormatter-ISO_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_TIME[ISO_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_TIME[java 9]) +** [[painless-api-reference-DateTimeFormatter-ISO_WEEK_DATE]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_WEEK_DATE[ISO_WEEK_DATE] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_WEEK_DATE[java 9]) +** [[painless-api-reference-DateTimeFormatter-ISO_ZONED_DATE_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ISO_ZONED_DATE_TIME[ISO_ZONED_DATE_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ISO_ZONED_DATE_TIME[java 9]) +** [[painless-api-reference-DateTimeFormatter-RFC_1123_DATE_TIME]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#RFC_1123_DATE_TIME[RFC_1123_DATE_TIME] (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#RFC_1123_DATE_TIME[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-ofLocalizedDate-1]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedDate%2Djava.time.format.FormatStyle%2D[ofLocalizedDate](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedDate%2Djava.time.format.FormatStyle%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-ofLocalizedDateTime-1]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedDateTime%2Djava.time.format.FormatStyle%2D[ofLocalizedDateTime](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedDateTime%2Djava.time.format.FormatStyle%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-ofLocalizedDateTime-2]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedDateTime%2Djava.time.format.FormatStyle%2Djava.time.format.FormatStyle%2D[ofLocalizedDateTime](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedDateTime%2Djava.time.format.FormatStyle%2Djava.time.format.FormatStyle%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-ofLocalizedTime-1]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedTime%2Djava.time.format.FormatStyle%2D[ofLocalizedTime](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ofLocalizedTime%2Djava.time.format.FormatStyle%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-ofPattern-1]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ofPattern%2Djava.lang.String%2D[ofPattern](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ofPattern%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-ofPattern-2]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#ofPattern%2Djava.lang.String%2Djava.util.Locale%2D[ofPattern](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#ofPattern%2Djava.lang.String%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-parsedExcessDays-0]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#parsedExcessDays%2D%2D[parsedExcessDays]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#parsedExcessDays%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-parsedLeapSecond-0]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#parsedLeapSecond%2D%2D[parsedLeapSecond]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#parsedLeapSecond%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-format-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#format%2Djava.time.temporal.TemporalAccessor%2D[format](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#format%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-formatTo-2]]void link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#formatTo%2Djava.time.temporal.TemporalAccessor%2Djava.lang.Appendable%2D[formatTo](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#formatTo%2Djava.time.temporal.TemporalAccessor%2Djava.lang.Appendable%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-getChronology-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#getChronology%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-getDecimalStyle-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#getDecimalStyle%2D%2D[getDecimalStyle]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#getDecimalStyle%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-getLocale-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#getLocale%2D%2D[getLocale]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#getLocale%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-getResolverFields-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#getResolverFields%2D%2D[getResolverFields]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#getResolverFields%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-getResolverStyle-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#getResolverStyle%2D%2D[getResolverStyle]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#getResolverStyle%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-getZone-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#getZone%2D%2D[getZone]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#getZone%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-parse-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#parse%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-parse-2]]def link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#parse%2Djava.lang.CharSequence%2Djava.time.temporal.TemporalQuery%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#parse%2Djava.lang.CharSequence%2Djava.time.temporal.TemporalQuery%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-parseBest-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#parseBest%2Djava.lang.CharSequence%2Djava.time.temporal.TemporalQuery:A%2D[parseBest](<>, <>[])++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#parseBest%2Djava.lang.CharSequence%2Djava.time.temporal.TemporalQuery:A%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-parseUnresolved-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#parseUnresolved%2Djava.lang.CharSequence%2Djava.text.ParsePosition%2D[parseUnresolved](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#parseUnresolved%2Djava.lang.CharSequence%2Djava.text.ParsePosition%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-toFormat-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#toFormat%2D%2D[toFormat]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#toFormat%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-toFormat-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#toFormat%2Djava.time.temporal.TemporalQuery%2D[toFormat](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#toFormat%2Djava.time.temporal.TemporalQuery%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-withChronology-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#withChronology%2Djava.time.chrono.Chronology%2D[withChronology](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#withChronology%2Djava.time.chrono.Chronology%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-withDecimalStyle-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#withDecimalStyle%2Djava.time.format.DecimalStyle%2D[withDecimalStyle](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#withDecimalStyle%2Djava.time.format.DecimalStyle%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-withLocale-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#withLocale%2Djava.util.Locale%2D[withLocale](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#withLocale%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-withResolverFields-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#withResolverFields%2Djava.util.Set%2D[withResolverFields](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#withResolverFields%2Djava.util.Set%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-withResolverStyle-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#withResolverStyle%2Djava.time.format.ResolverStyle%2D[withResolverStyle](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#withResolverStyle%2Djava.time.format.ResolverStyle%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatter-withZone-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatter.html#withZone%2Djava.time.ZoneId%2D[withZone](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatter.html#withZone%2Djava.time.ZoneId%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DateTimeFormatterBuilder.asciidoc b/docs/painless/painless-api-reference/DateTimeFormatterBuilder.asciidoc new file mode 100644 index 0000000000000..cc352560f41a9 --- /dev/null +++ b/docs/painless/painless-api-reference/DateTimeFormatterBuilder.asciidoc @@ -0,0 +1,44 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DateTimeFormatterBuilder]]++DateTimeFormatterBuilder++:: +* ++[[painless-api-reference-DateTimeFormatterBuilder-getLocalizedDateTimePattern-4]]static <> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#getLocalizedDateTimePattern%2Djava.time.format.FormatStyle%2Djava.time.format.FormatStyle%2Djava.time.chrono.Chronology%2Djava.util.Locale%2D[getLocalizedDateTimePattern](<>, <>, <>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#getLocalizedDateTimePattern%2Djava.time.format.FormatStyle%2Djava.time.format.FormatStyle%2Djava.time.chrono.Chronology%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-DateTimeFormatterBuilder-0]]link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#DateTimeFormatterBuilder%2D%2D[DateTimeFormatterBuilder]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#DateTimeFormatterBuilder%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-append-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#append%2Djava.time.format.DateTimeFormatter%2D[append](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#append%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendChronologyId-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendChronologyId%2D%2D[appendChronologyId]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendChronologyId%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendChronologyText-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendChronologyText%2Djava.time.format.TextStyle%2D[appendChronologyText](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendChronologyText%2Djava.time.format.TextStyle%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendFraction-4]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendFraction%2Djava.time.temporal.TemporalField%2Dint%2Dint%2Dboolean%2D[appendFraction](<>, int, int, boolean)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendFraction%2Djava.time.temporal.TemporalField%2Dint%2Dint%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendInstant-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendInstant%2D%2D[appendInstant]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendInstant%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendInstant-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendInstant%2Dint%2D[appendInstant](int)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendInstant%2Dint%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendLiteral-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendLiteral%2Djava.lang.String%2D[appendLiteral](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendLiteral%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendLocalized-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendLocalized%2Djava.time.format.FormatStyle%2Djava.time.format.FormatStyle%2D[appendLocalized](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendLocalized%2Djava.time.format.FormatStyle%2Djava.time.format.FormatStyle%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendLocalizedOffset-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendLocalizedOffset%2Djava.time.format.TextStyle%2D[appendLocalizedOffset](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendLocalizedOffset%2Djava.time.format.TextStyle%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendOffset-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendOffset%2Djava.lang.String%2Djava.lang.String%2D[appendOffset](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendOffset%2Djava.lang.String%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendOffsetId-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendOffsetId%2D%2D[appendOffsetId]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendOffsetId%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendOptional-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendOptional%2Djava.time.format.DateTimeFormatter%2D[appendOptional](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendOptional%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendPattern-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendPattern%2Djava.lang.String%2D[appendPattern](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendPattern%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendText-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendText%2Djava.time.temporal.TemporalField%2D[appendText](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendText%2Djava.time.temporal.TemporalField%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendText-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendText%2Djava.time.temporal.TemporalField%2Djava.time.format.TextStyle%2D[appendText](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendText%2Djava.time.temporal.TemporalField%2Djava.time.format.TextStyle%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendValue-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValue%2Djava.time.temporal.TemporalField%2D[appendValue](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValue%2Djava.time.temporal.TemporalField%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendValue-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValue%2Djava.time.temporal.TemporalField%2Dint%2D[appendValue](<>, int)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValue%2Djava.time.temporal.TemporalField%2Dint%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendValue-4]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValue%2Djava.time.temporal.TemporalField%2Dint%2Dint%2Djava.time.format.SignStyle%2D[appendValue](<>, int, int, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValue%2Djava.time.temporal.TemporalField%2Dint%2Dint%2Djava.time.format.SignStyle%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendValueReduced-4]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValueReduced%2Djava.time.temporal.TemporalField%2Dint%2Dint%2Dint%2D[appendValueReduced](<>, int, int, int)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendValueReduced%2Djava.time.temporal.TemporalField%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendZoneId-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneId%2D%2D[appendZoneId]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneId%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendZoneOrOffsetId-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneOrOffsetId%2D%2D[appendZoneOrOffsetId]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneOrOffsetId%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendZoneRegionId-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneRegionId%2D%2D[appendZoneRegionId]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneRegionId%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendZoneText-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneText%2Djava.time.format.TextStyle%2D[appendZoneText](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneText%2Djava.time.format.TextStyle%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-appendZoneText-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneText%2Djava.time.format.TextStyle%2Djava.util.Set%2D[appendZoneText](<>, <>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#appendZoneText%2Djava.time.format.TextStyle%2Djava.util.Set%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-optionalEnd-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#optionalEnd%2D%2D[optionalEnd]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#optionalEnd%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-optionalStart-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#optionalStart%2D%2D[optionalStart]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#optionalStart%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-padNext-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#padNext%2Dint%2D[padNext](int)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#padNext%2Dint%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-padNext-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#padNext%2Dint%2Dchar%2D[padNext](int, char)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#padNext%2Dint%2Dchar%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-parseCaseInsensitive-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseCaseInsensitive%2D%2D[parseCaseInsensitive]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseCaseInsensitive%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-parseCaseSensitive-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseCaseSensitive%2D%2D[parseCaseSensitive]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseCaseSensitive%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-parseDefaulting-2]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseDefaulting%2Djava.time.temporal.TemporalField%2Dlong%2D[parseDefaulting](<>, long)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseDefaulting%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-parseLenient-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseLenient%2D%2D[parseLenient]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseLenient%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-parseStrict-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseStrict%2D%2D[parseStrict]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#parseStrict%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-toFormatter-0]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#toFormatter%2D%2D[toFormatter]()++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#toFormatter%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeFormatterBuilder-toFormatter-1]]<> link:{java8-javadoc}/java/time/format/DateTimeFormatterBuilder.html#toFormatter%2Djava.util.Locale%2D[toFormatter](<>)++ (link:{java9-javadoc}/java/time/format/DateTimeFormatterBuilder.html#toFormatter%2Djava.util.Locale%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DateTimeParseException.asciidoc b/docs/painless/painless-api-reference/DateTimeParseException.asciidoc new file mode 100644 index 0000000000000..8a9626b64052a --- /dev/null +++ b/docs/painless/painless-api-reference/DateTimeParseException.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DateTimeParseException]]++DateTimeParseException++:: +* ++[[painless-api-reference-DateTimeParseException-DateTimeParseException-3]]link:{java8-javadoc}/java/time/format/DateTimeParseException.html#DateTimeParseException%2Djava.lang.String%2Djava.lang.CharSequence%2Dint%2D[DateTimeParseException](<>, <>, int)++ (link:{java9-javadoc}/java/time/format/DateTimeParseException.html#DateTimeParseException%2Djava.lang.String%2Djava.lang.CharSequence%2Dint%2D[java 9]) +* ++[[painless-api-reference-DateTimeParseException-getErrorIndex-0]]int link:{java8-javadoc}/java/time/format/DateTimeParseException.html#getErrorIndex%2D%2D[getErrorIndex]()++ (link:{java9-javadoc}/java/time/format/DateTimeParseException.html#getErrorIndex%2D%2D[java 9]) +* ++[[painless-api-reference-DateTimeParseException-getParsedString-0]]<> link:{java8-javadoc}/java/time/format/DateTimeParseException.html#getParsedString%2D%2D[getParsedString]()++ (link:{java9-javadoc}/java/time/format/DateTimeParseException.html#getParsedString%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/DayOfWeek.asciidoc b/docs/painless/painless-api-reference/DayOfWeek.asciidoc new file mode 100644 index 0000000000000..fd847f3d019ab --- /dev/null +++ b/docs/painless/painless-api-reference/DayOfWeek.asciidoc @@ -0,0 +1,22 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DayOfWeek]]++DayOfWeek++:: +** [[painless-api-reference-DayOfWeek-FRIDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#FRIDAY[FRIDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#FRIDAY[java 9]) +** [[painless-api-reference-DayOfWeek-MONDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#MONDAY[MONDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#MONDAY[java 9]) +** [[painless-api-reference-DayOfWeek-SATURDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#SATURDAY[SATURDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#SATURDAY[java 9]) +** [[painless-api-reference-DayOfWeek-SUNDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#SUNDAY[SUNDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#SUNDAY[java 9]) +** [[painless-api-reference-DayOfWeek-THURSDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#THURSDAY[THURSDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#THURSDAY[java 9]) +** [[painless-api-reference-DayOfWeek-TUESDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#TUESDAY[TUESDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#TUESDAY[java 9]) +** [[painless-api-reference-DayOfWeek-WEDNESDAY]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#WEDNESDAY[WEDNESDAY] (link:{java9-javadoc}/java/time/DayOfWeek.html#WEDNESDAY[java 9]) +* ++[[painless-api-reference-DayOfWeek-from-1]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/DayOfWeek.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-DayOfWeek-of-1]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/DayOfWeek.html#of%2Dint%2D[java 9]) +* ++[[painless-api-reference-DayOfWeek-valueOf-1]]static <> link:{java8-javadoc}/java/time/DayOfWeek.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/DayOfWeek.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DayOfWeek-values-0]]static <>[] link:{java8-javadoc}/java/time/DayOfWeek.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/DayOfWeek.html#values%2D%2D[java 9]) +* ++[[painless-api-reference-DayOfWeek-getDisplayName-2]]<> link:{java8-javadoc}/java/time/DayOfWeek.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[getDisplayName](<>, <>)++ (link:{java9-javadoc}/java/time/DayOfWeek.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-DayOfWeek-getValue-0]]int link:{java8-javadoc}/java/time/DayOfWeek.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/DayOfWeek.html#getValue%2D%2D[java 9]) +* ++[[painless-api-reference-DayOfWeek-minus-1]]<> link:{java8-javadoc}/java/time/DayOfWeek.html#minus%2Dlong%2D[minus](long)++ (link:{java9-javadoc}/java/time/DayOfWeek.html#minus%2Dlong%2D[java 9]) +* ++[[painless-api-reference-DayOfWeek-plus-1]]<> link:{java8-javadoc}/java/time/DayOfWeek.html#plus%2Dlong%2D[plus](long)++ (link:{java9-javadoc}/java/time/DayOfWeek.html#plus%2Dlong%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Debug.asciidoc b/docs/painless/painless-api-reference/Debug.asciidoc new file mode 100644 index 0000000000000..8c64ed6bcc4ad --- /dev/null +++ b/docs/painless/painless-api-reference/Debug.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Debug]]++Debug++:: +* ++[[painless-api-reference-Debug-explain-1]]static void link:{painless-javadoc}/org/elasticsearch/painless/api/Debug.html#explain%2Djava.lang.Object%2D[explain](<>)++ +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DecimalFormat.asciidoc b/docs/painless/painless-api-reference/DecimalFormat.asciidoc new file mode 100644 index 0000000000000..96768b77273f8 --- /dev/null +++ b/docs/painless/painless-api-reference/DecimalFormat.asciidoc @@ -0,0 +1,32 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DecimalFormat]]++DecimalFormat++:: +* ++[[painless-api-reference-DecimalFormat-DecimalFormat-0]]link:{java8-javadoc}/java/text/DecimalFormat.html#DecimalFormat%2D%2D[DecimalFormat]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#DecimalFormat%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-DecimalFormat-1]]link:{java8-javadoc}/java/text/DecimalFormat.html#DecimalFormat%2Djava.lang.String%2D[DecimalFormat](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#DecimalFormat%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-DecimalFormat-2]]link:{java8-javadoc}/java/text/DecimalFormat.html#DecimalFormat%2Djava.lang.String%2Djava.text.DecimalFormatSymbols%2D[DecimalFormat](<>, <>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#DecimalFormat%2Djava.lang.String%2Djava.text.DecimalFormatSymbols%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-applyLocalizedPattern-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#applyLocalizedPattern%2Djava.lang.String%2D[applyLocalizedPattern](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#applyLocalizedPattern%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-applyPattern-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#applyPattern%2Djava.lang.String%2D[applyPattern](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#applyPattern%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-getDecimalFormatSymbols-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#getDecimalFormatSymbols%2D%2D[getDecimalFormatSymbols]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getDecimalFormatSymbols%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-getGroupingSize-0]]int link:{java8-javadoc}/java/text/DecimalFormat.html#getGroupingSize%2D%2D[getGroupingSize]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getGroupingSize%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-getMultiplier-0]]int link:{java8-javadoc}/java/text/DecimalFormat.html#getMultiplier%2D%2D[getMultiplier]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getMultiplier%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-getNegativePrefix-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#getNegativePrefix%2D%2D[getNegativePrefix]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getNegativePrefix%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-getNegativeSuffix-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#getNegativeSuffix%2D%2D[getNegativeSuffix]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getNegativeSuffix%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-getPositivePrefix-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#getPositivePrefix%2D%2D[getPositivePrefix]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getPositivePrefix%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-getPositiveSuffix-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#getPositiveSuffix%2D%2D[getPositiveSuffix]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#getPositiveSuffix%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-isDecimalSeparatorAlwaysShown-0]]boolean link:{java8-javadoc}/java/text/DecimalFormat.html#isDecimalSeparatorAlwaysShown%2D%2D[isDecimalSeparatorAlwaysShown]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#isDecimalSeparatorAlwaysShown%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-isParseBigDecimal-0]]boolean link:{java8-javadoc}/java/text/DecimalFormat.html#isParseBigDecimal%2D%2D[isParseBigDecimal]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#isParseBigDecimal%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-setDecimalFormatSymbols-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setDecimalFormatSymbols%2Djava.text.DecimalFormatSymbols%2D[setDecimalFormatSymbols](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setDecimalFormatSymbols%2Djava.text.DecimalFormatSymbols%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-setDecimalSeparatorAlwaysShown-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setDecimalSeparatorAlwaysShown%2Dboolean%2D[setDecimalSeparatorAlwaysShown](boolean)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setDecimalSeparatorAlwaysShown%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-setGroupingSize-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setGroupingSize%2Dint%2D[setGroupingSize](int)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setGroupingSize%2Dint%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-setMultiplier-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setMultiplier%2Dint%2D[setMultiplier](int)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setMultiplier%2Dint%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-setNegativePrefix-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setNegativePrefix%2Djava.lang.String%2D[setNegativePrefix](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setNegativePrefix%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-setNegativeSuffix-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setNegativeSuffix%2Djava.lang.String%2D[setNegativeSuffix](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setNegativeSuffix%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-setParseBigDecimal-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setParseBigDecimal%2Dboolean%2D[setParseBigDecimal](boolean)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setParseBigDecimal%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-setPositivePrefix-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setPositivePrefix%2Djava.lang.String%2D[setPositivePrefix](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setPositivePrefix%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-setPositiveSuffix-1]]void link:{java8-javadoc}/java/text/DecimalFormat.html#setPositiveSuffix%2Djava.lang.String%2D[setPositiveSuffix](<>)++ (link:{java9-javadoc}/java/text/DecimalFormat.html#setPositiveSuffix%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-toLocalizedPattern-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#toLocalizedPattern%2D%2D[toLocalizedPattern]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#toLocalizedPattern%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormat-toPattern-0]]<> link:{java8-javadoc}/java/text/DecimalFormat.html#toPattern%2D%2D[toPattern]()++ (link:{java9-javadoc}/java/text/DecimalFormat.html#toPattern%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/DecimalFormatSymbols.asciidoc b/docs/painless/painless-api-reference/DecimalFormatSymbols.asciidoc new file mode 100644 index 0000000000000..37f6fc090d105 --- /dev/null +++ b/docs/painless/painless-api-reference/DecimalFormatSymbols.asciidoc @@ -0,0 +1,43 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DecimalFormatSymbols]]++DecimalFormatSymbols++:: +* ++[[painless-api-reference-DecimalFormatSymbols-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getAvailableLocales%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getInstance-0]]static <> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getInstance%2D%2D[getInstance]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getInstance%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getInstance-1]]static <> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getInstance%2Djava.util.Locale%2D[getInstance](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getInstance%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-DecimalFormatSymbols-0]]link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#DecimalFormatSymbols%2D%2D[DecimalFormatSymbols]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#DecimalFormatSymbols%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-DecimalFormatSymbols-1]]link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#DecimalFormatSymbols%2Djava.util.Locale%2D[DecimalFormatSymbols](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#DecimalFormatSymbols%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-clone-0]]def link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#clone%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getCurrency-0]]<> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getCurrency%2D%2D[getCurrency]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getCurrency%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getCurrencySymbol-0]]<> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getCurrencySymbol%2D%2D[getCurrencySymbol]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getCurrencySymbol%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getDecimalSeparator-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getDecimalSeparator%2D%2D[getDecimalSeparator]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getDecimalSeparator%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getDigit-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getDigit%2D%2D[getDigit]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getDigit%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getExponentSeparator-0]]<> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getExponentSeparator%2D%2D[getExponentSeparator]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getExponentSeparator%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getGroupingSeparator-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getGroupingSeparator%2D%2D[getGroupingSeparator]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getGroupingSeparator%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getInfinity-0]]<> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getInfinity%2D%2D[getInfinity]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getInfinity%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getInternationalCurrencySymbol-0]]<> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getInternationalCurrencySymbol%2D%2D[getInternationalCurrencySymbol]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getInternationalCurrencySymbol%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getMinusSign-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getMinusSign%2D%2D[getMinusSign]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getMinusSign%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getMonetaryDecimalSeparator-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getMonetaryDecimalSeparator%2D%2D[getMonetaryDecimalSeparator]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getMonetaryDecimalSeparator%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getNaN-0]]<> link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getNaN%2D%2D[getNaN]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getNaN%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getPatternSeparator-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getPatternSeparator%2D%2D[getPatternSeparator]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getPatternSeparator%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getPerMill-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getPerMill%2D%2D[getPerMill]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getPerMill%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getPercent-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getPercent%2D%2D[getPercent]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getPercent%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-getZeroDigit-0]]char link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#getZeroDigit%2D%2D[getZeroDigit]()++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#getZeroDigit%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setCurrency-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setCurrency%2Djava.util.Currency%2D[setCurrency](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setCurrency%2Djava.util.Currency%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setCurrencySymbol-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setCurrencySymbol%2Djava.lang.String%2D[setCurrencySymbol](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setCurrencySymbol%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setDecimalSeparator-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setDecimalSeparator%2Dchar%2D[setDecimalSeparator](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setDecimalSeparator%2Dchar%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setDigit-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setDigit%2Dchar%2D[setDigit](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setDigit%2Dchar%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setExponentSeparator-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setExponentSeparator%2Djava.lang.String%2D[setExponentSeparator](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setExponentSeparator%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setGroupingSeparator-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setGroupingSeparator%2Dchar%2D[setGroupingSeparator](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setGroupingSeparator%2Dchar%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setInfinity-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setInfinity%2Djava.lang.String%2D[setInfinity](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setInfinity%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setInternationalCurrencySymbol-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setInternationalCurrencySymbol%2Djava.lang.String%2D[setInternationalCurrencySymbol](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setInternationalCurrencySymbol%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setMinusSign-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setMinusSign%2Dchar%2D[setMinusSign](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setMinusSign%2Dchar%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setMonetaryDecimalSeparator-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setMonetaryDecimalSeparator%2Dchar%2D[setMonetaryDecimalSeparator](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setMonetaryDecimalSeparator%2Dchar%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setNaN-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setNaN%2Djava.lang.String%2D[setNaN](<>)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setNaN%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setPatternSeparator-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setPatternSeparator%2Dchar%2D[setPatternSeparator](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setPatternSeparator%2Dchar%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setPerMill-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setPerMill%2Dchar%2D[setPerMill](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setPerMill%2Dchar%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setPercent-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setPercent%2Dchar%2D[setPercent](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setPercent%2Dchar%2D[java 9]) +* ++[[painless-api-reference-DecimalFormatSymbols-setZeroDigit-1]]void link:{java8-javadoc}/java/text/DecimalFormatSymbols.html#setZeroDigit%2Dchar%2D[setZeroDigit](char)++ (link:{java9-javadoc}/java/text/DecimalFormatSymbols.html#setZeroDigit%2Dchar%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DecimalStyle.asciidoc b/docs/painless/painless-api-reference/DecimalStyle.asciidoc new file mode 100644 index 0000000000000..8dac3a7ae2440 --- /dev/null +++ b/docs/painless/painless-api-reference/DecimalStyle.asciidoc @@ -0,0 +1,19 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DecimalStyle]]++DecimalStyle++:: +** [[painless-api-reference-DecimalStyle-STANDARD]]static <> link:{java8-javadoc}/java/time/format/DecimalStyle.html#STANDARD[STANDARD] (link:{java9-javadoc}/java/time/format/DecimalStyle.html#STANDARD[java 9]) +* ++[[painless-api-reference-DecimalStyle-getAvailableLocales-0]]static <> link:{java8-javadoc}/java/time/format/DecimalStyle.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#getAvailableLocales%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalStyle-of-1]]static <> link:{java8-javadoc}/java/time/format/DecimalStyle.html#of%2Djava.util.Locale%2D[of](<>)++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#of%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-DecimalStyle-ofDefaultLocale-0]]static <> link:{java8-javadoc}/java/time/format/DecimalStyle.html#ofDefaultLocale%2D%2D[ofDefaultLocale]()++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#ofDefaultLocale%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalStyle-getDecimalSeparator-0]]char link:{java8-javadoc}/java/time/format/DecimalStyle.html#getDecimalSeparator%2D%2D[getDecimalSeparator]()++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#getDecimalSeparator%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalStyle-getNegativeSign-0]]char link:{java8-javadoc}/java/time/format/DecimalStyle.html#getNegativeSign%2D%2D[getNegativeSign]()++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#getNegativeSign%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalStyle-getPositiveSign-0]]char link:{java8-javadoc}/java/time/format/DecimalStyle.html#getPositiveSign%2D%2D[getPositiveSign]()++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#getPositiveSign%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalStyle-getZeroDigit-0]]char link:{java8-javadoc}/java/time/format/DecimalStyle.html#getZeroDigit%2D%2D[getZeroDigit]()++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#getZeroDigit%2D%2D[java 9]) +* ++[[painless-api-reference-DecimalStyle-withDecimalSeparator-1]]<> link:{java8-javadoc}/java/time/format/DecimalStyle.html#withDecimalSeparator%2Dchar%2D[withDecimalSeparator](char)++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#withDecimalSeparator%2Dchar%2D[java 9]) +* ++[[painless-api-reference-DecimalStyle-withNegativeSign-1]]<> link:{java8-javadoc}/java/time/format/DecimalStyle.html#withNegativeSign%2Dchar%2D[withNegativeSign](char)++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#withNegativeSign%2Dchar%2D[java 9]) +* ++[[painless-api-reference-DecimalStyle-withPositiveSign-1]]<> link:{java8-javadoc}/java/time/format/DecimalStyle.html#withPositiveSign%2Dchar%2D[withPositiveSign](char)++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#withPositiveSign%2Dchar%2D[java 9]) +* ++[[painless-api-reference-DecimalStyle-withZeroDigit-1]]<> link:{java8-javadoc}/java/time/format/DecimalStyle.html#withZeroDigit%2Dchar%2D[withZeroDigit](char)++ (link:{java9-javadoc}/java/time/format/DecimalStyle.html#withZeroDigit%2Dchar%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Deque.asciidoc b/docs/painless/painless-api-reference/Deque.asciidoc new file mode 100644 index 0000000000000..4dc6b5c06fc84 --- /dev/null +++ b/docs/painless/painless-api-reference/Deque.asciidoc @@ -0,0 +1,25 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Deque]]++Deque++:: +* ++[[painless-api-reference-Deque-addFirst-1]]void link:{java8-javadoc}/java/util/Deque.html#addFirst%2Djava.lang.Object%2D[addFirst](def)++ (link:{java9-javadoc}/java/util/Deque.html#addFirst%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Deque-addLast-1]]void link:{java8-javadoc}/java/util/Deque.html#addLast%2Djava.lang.Object%2D[addLast](def)++ (link:{java9-javadoc}/java/util/Deque.html#addLast%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Deque-descendingIterator-0]]<> link:{java8-javadoc}/java/util/Deque.html#descendingIterator%2D%2D[descendingIterator]()++ (link:{java9-javadoc}/java/util/Deque.html#descendingIterator%2D%2D[java 9]) +* ++[[painless-api-reference-Deque-getFirst-0]]def link:{java8-javadoc}/java/util/Deque.html#getFirst%2D%2D[getFirst]()++ (link:{java9-javadoc}/java/util/Deque.html#getFirst%2D%2D[java 9]) +* ++[[painless-api-reference-Deque-getLast-0]]def link:{java8-javadoc}/java/util/Deque.html#getLast%2D%2D[getLast]()++ (link:{java9-javadoc}/java/util/Deque.html#getLast%2D%2D[java 9]) +* ++[[painless-api-reference-Deque-offerFirst-1]]boolean link:{java8-javadoc}/java/util/Deque.html#offerFirst%2Djava.lang.Object%2D[offerFirst](def)++ (link:{java9-javadoc}/java/util/Deque.html#offerFirst%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Deque-offerLast-1]]boolean link:{java8-javadoc}/java/util/Deque.html#offerLast%2Djava.lang.Object%2D[offerLast](def)++ (link:{java9-javadoc}/java/util/Deque.html#offerLast%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Deque-peekFirst-0]]def link:{java8-javadoc}/java/util/Deque.html#peekFirst%2D%2D[peekFirst]()++ (link:{java9-javadoc}/java/util/Deque.html#peekFirst%2D%2D[java 9]) +* ++[[painless-api-reference-Deque-peekLast-0]]def link:{java8-javadoc}/java/util/Deque.html#peekLast%2D%2D[peekLast]()++ (link:{java9-javadoc}/java/util/Deque.html#peekLast%2D%2D[java 9]) +* ++[[painless-api-reference-Deque-pollFirst-0]]def link:{java8-javadoc}/java/util/Deque.html#pollFirst%2D%2D[pollFirst]()++ (link:{java9-javadoc}/java/util/Deque.html#pollFirst%2D%2D[java 9]) +* ++[[painless-api-reference-Deque-pollLast-0]]def link:{java8-javadoc}/java/util/Deque.html#pollLast%2D%2D[pollLast]()++ (link:{java9-javadoc}/java/util/Deque.html#pollLast%2D%2D[java 9]) +* ++[[painless-api-reference-Deque-pop-0]]def link:{java8-javadoc}/java/util/Deque.html#pop%2D%2D[pop]()++ (link:{java9-javadoc}/java/util/Deque.html#pop%2D%2D[java 9]) +* ++[[painless-api-reference-Deque-push-1]]void link:{java8-javadoc}/java/util/Deque.html#push%2Djava.lang.Object%2D[push](def)++ (link:{java9-javadoc}/java/util/Deque.html#push%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Deque-remove-1]]boolean link:{java8-javadoc}/java/util/Deque.html#remove%2Djava.lang.Object%2D[remove](def)++ (link:{java9-javadoc}/java/util/Deque.html#remove%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Deque-removeFirst-0]]def link:{java8-javadoc}/java/util/Deque.html#removeFirst%2D%2D[removeFirst]()++ (link:{java9-javadoc}/java/util/Deque.html#removeFirst%2D%2D[java 9]) +* ++[[painless-api-reference-Deque-removeFirstOccurrence-1]]boolean link:{java8-javadoc}/java/util/Deque.html#removeFirstOccurrence%2Djava.lang.Object%2D[removeFirstOccurrence](def)++ (link:{java9-javadoc}/java/util/Deque.html#removeFirstOccurrence%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Deque-removeLast-0]]def link:{java8-javadoc}/java/util/Deque.html#removeLast%2D%2D[removeLast]()++ (link:{java9-javadoc}/java/util/Deque.html#removeLast%2D%2D[java 9]) +* ++[[painless-api-reference-Deque-removeLastOccurrence-1]]boolean link:{java8-javadoc}/java/util/Deque.html#removeLastOccurrence%2Djava.lang.Object%2D[removeLastOccurrence](def)++ (link:{java9-javadoc}/java/util/Deque.html#removeLastOccurrence%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Dictionary.asciidoc b/docs/painless/painless-api-reference/Dictionary.asciidoc new file mode 100644 index 0000000000000..bfa8f2c495e19 --- /dev/null +++ b/docs/painless/painless-api-reference/Dictionary.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Dictionary]]++Dictionary++:: +* ++[[painless-api-reference-Dictionary-elements-0]]<> link:{java8-javadoc}/java/util/Dictionary.html#elements%2D%2D[elements]()++ (link:{java9-javadoc}/java/util/Dictionary.html#elements%2D%2D[java 9]) +* ++[[painless-api-reference-Dictionary-get-1]]def link:{java8-javadoc}/java/util/Dictionary.html#get%2Djava.lang.Object%2D[get](def)++ (link:{java9-javadoc}/java/util/Dictionary.html#get%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Dictionary-isEmpty-0]]boolean link:{java8-javadoc}/java/util/Dictionary.html#isEmpty%2D%2D[isEmpty]()++ (link:{java9-javadoc}/java/util/Dictionary.html#isEmpty%2D%2D[java 9]) +* ++[[painless-api-reference-Dictionary-keys-0]]<> link:{java8-javadoc}/java/util/Dictionary.html#keys%2D%2D[keys]()++ (link:{java9-javadoc}/java/util/Dictionary.html#keys%2D%2D[java 9]) +* ++[[painless-api-reference-Dictionary-put-2]]def link:{java8-javadoc}/java/util/Dictionary.html#put%2Djava.lang.Object%2Djava.lang.Object%2D[put](def, def)++ (link:{java9-javadoc}/java/util/Dictionary.html#put%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Dictionary-remove-1]]def link:{java8-javadoc}/java/util/Dictionary.html#remove%2Djava.lang.Object%2D[remove](def)++ (link:{java9-javadoc}/java/util/Dictionary.html#remove%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Dictionary-size-0]]int link:{java8-javadoc}/java/util/Dictionary.html#size%2D%2D[size]()++ (link:{java9-javadoc}/java/util/Dictionary.html#size%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Double.asciidoc b/docs/painless/painless-api-reference/Double.asciidoc new file mode 100644 index 0000000000000..50d1d8c217cba --- /dev/null +++ b/docs/painless/painless-api-reference/Double.asciidoc @@ -0,0 +1,35 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Double]]++Double++:: +** [[painless-api-reference-Double-BYTES]]static int link:{java8-javadoc}/java/lang/Double.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Double.html#BYTES[java 9]) +** [[painless-api-reference-Double-MAX_EXPONENT]]static int link:{java8-javadoc}/java/lang/Double.html#MAX_EXPONENT[MAX_EXPONENT] (link:{java9-javadoc}/java/lang/Double.html#MAX_EXPONENT[java 9]) +** [[painless-api-reference-Double-MAX_VALUE]]static double link:{java8-javadoc}/java/lang/Double.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Double.html#MAX_VALUE[java 9]) +** [[painless-api-reference-Double-MIN_EXPONENT]]static int link:{java8-javadoc}/java/lang/Double.html#MIN_EXPONENT[MIN_EXPONENT] (link:{java9-javadoc}/java/lang/Double.html#MIN_EXPONENT[java 9]) +** [[painless-api-reference-Double-MIN_NORMAL]]static double link:{java8-javadoc}/java/lang/Double.html#MIN_NORMAL[MIN_NORMAL] (link:{java9-javadoc}/java/lang/Double.html#MIN_NORMAL[java 9]) +** [[painless-api-reference-Double-MIN_VALUE]]static double link:{java8-javadoc}/java/lang/Double.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Double.html#MIN_VALUE[java 9]) +** [[painless-api-reference-Double-NEGATIVE_INFINITY]]static double link:{java8-javadoc}/java/lang/Double.html#NEGATIVE_INFINITY[NEGATIVE_INFINITY] (link:{java9-javadoc}/java/lang/Double.html#NEGATIVE_INFINITY[java 9]) +** [[painless-api-reference-Double-NaN]]static double link:{java8-javadoc}/java/lang/Double.html#NaN[NaN] (link:{java9-javadoc}/java/lang/Double.html#NaN[java 9]) +** [[painless-api-reference-Double-POSITIVE_INFINITY]]static double link:{java8-javadoc}/java/lang/Double.html#POSITIVE_INFINITY[POSITIVE_INFINITY] (link:{java9-javadoc}/java/lang/Double.html#POSITIVE_INFINITY[java 9]) +** [[painless-api-reference-Double-SIZE]]static int link:{java8-javadoc}/java/lang/Double.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Double.html#SIZE[java 9]) +* ++[[painless-api-reference-Double-compare-2]]static int link:{java8-javadoc}/java/lang/Double.html#compare%2Ddouble%2Ddouble%2D[compare](double, double)++ (link:{java9-javadoc}/java/lang/Double.html#compare%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Double-doubleToLongBits-1]]static long link:{java8-javadoc}/java/lang/Double.html#doubleToLongBits%2Ddouble%2D[doubleToLongBits](double)++ (link:{java9-javadoc}/java/lang/Double.html#doubleToLongBits%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Double-doubleToRawLongBits-1]]static long link:{java8-javadoc}/java/lang/Double.html#doubleToRawLongBits%2Ddouble%2D[doubleToRawLongBits](double)++ (link:{java9-javadoc}/java/lang/Double.html#doubleToRawLongBits%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Double-hashCode-1]]static int link:{java8-javadoc}/java/lang/Double.html#hashCode%2Ddouble%2D[hashCode](double)++ (link:{java9-javadoc}/java/lang/Double.html#hashCode%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Double-isFinite-1]]static boolean link:{java8-javadoc}/java/lang/Double.html#isFinite%2Ddouble%2D[isFinite](double)++ (link:{java9-javadoc}/java/lang/Double.html#isFinite%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Double-isInfinite-1]]static boolean link:{java8-javadoc}/java/lang/Double.html#isInfinite%2Ddouble%2D[isInfinite](double)++ (link:{java9-javadoc}/java/lang/Double.html#isInfinite%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Double-isNaN-1]]static boolean link:{java8-javadoc}/java/lang/Double.html#isNaN%2Ddouble%2D[isNaN](double)++ (link:{java9-javadoc}/java/lang/Double.html#isNaN%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Double-longBitsToDouble-1]]static double link:{java8-javadoc}/java/lang/Double.html#longBitsToDouble%2Dlong%2D[longBitsToDouble](long)++ (link:{java9-javadoc}/java/lang/Double.html#longBitsToDouble%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Double-max-2]]static double link:{java8-javadoc}/java/lang/Double.html#max%2Ddouble%2Ddouble%2D[max](double, double)++ (link:{java9-javadoc}/java/lang/Double.html#max%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Double-min-2]]static double link:{java8-javadoc}/java/lang/Double.html#min%2Ddouble%2Ddouble%2D[min](double, double)++ (link:{java9-javadoc}/java/lang/Double.html#min%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Double-parseDouble-1]]static double link:{java8-javadoc}/java/lang/Double.html#parseDouble%2Djava.lang.String%2D[parseDouble](<>)++ (link:{java9-javadoc}/java/lang/Double.html#parseDouble%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Double-sum-2]]static double link:{java8-javadoc}/java/lang/Double.html#sum%2Ddouble%2Ddouble%2D[sum](double, double)++ (link:{java9-javadoc}/java/lang/Double.html#sum%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Double-toHexString-1]]static <> link:{java8-javadoc}/java/lang/Double.html#toHexString%2Ddouble%2D[toHexString](double)++ (link:{java9-javadoc}/java/lang/Double.html#toHexString%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Double-toString-1]]static <> link:{java8-javadoc}/java/lang/Double.html#toString%2Ddouble%2D[toString](double)++ (link:{java9-javadoc}/java/lang/Double.html#toString%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Double-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Double.html#valueOf%2Ddouble%2D[valueOf](double)++ (link:{java9-javadoc}/java/lang/Double.html#valueOf%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Double-compareTo-1]]int link:{java8-javadoc}/java/lang/Double.html#compareTo%2Djava.lang.Double%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Double.html#compareTo%2Djava.lang.Double%2D[java 9]) +* ++[[painless-api-reference-Double-isInfinite-0]]boolean link:{java8-javadoc}/java/lang/Double.html#isInfinite%2D%2D[isInfinite]()++ (link:{java9-javadoc}/java/lang/Double.html#isInfinite%2D%2D[java 9]) +* ++[[painless-api-reference-Double-isNaN-0]]boolean link:{java8-javadoc}/java/lang/Double.html#isNaN%2D%2D[isNaN]()++ (link:{java9-javadoc}/java/lang/Double.html#isNaN%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/DoubleBinaryOperator.asciidoc b/docs/painless/painless-api-reference/DoubleBinaryOperator.asciidoc new file mode 100644 index 0000000000000..deec8c5337c81 --- /dev/null +++ b/docs/painless/painless-api-reference/DoubleBinaryOperator.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DoubleBinaryOperator]]++DoubleBinaryOperator++:: +* ++[[painless-api-reference-DoubleBinaryOperator-applyAsDouble-2]]double link:{java8-javadoc}/java/util/function/DoubleBinaryOperator.html#applyAsDouble%2Ddouble%2Ddouble%2D[applyAsDouble](double, double)++ (link:{java9-javadoc}/java/util/function/DoubleBinaryOperator.html#applyAsDouble%2Ddouble%2Ddouble%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DoubleConsumer.asciidoc b/docs/painless/painless-api-reference/DoubleConsumer.asciidoc new file mode 100644 index 0000000000000..3c078a4d4fb1c --- /dev/null +++ b/docs/painless/painless-api-reference/DoubleConsumer.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DoubleConsumer]]++DoubleConsumer++:: +* ++[[painless-api-reference-DoubleConsumer-accept-1]]void link:{java8-javadoc}/java/util/function/DoubleConsumer.html#accept%2Ddouble%2D[accept](double)++ (link:{java9-javadoc}/java/util/function/DoubleConsumer.html#accept%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-DoubleConsumer-andThen-1]]<> link:{java8-javadoc}/java/util/function/DoubleConsumer.html#andThen%2Djava.util.function.DoubleConsumer%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/DoubleConsumer.html#andThen%2Djava.util.function.DoubleConsumer%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DoubleFunction.asciidoc b/docs/painless/painless-api-reference/DoubleFunction.asciidoc new file mode 100644 index 0000000000000..082a70001328e --- /dev/null +++ b/docs/painless/painless-api-reference/DoubleFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DoubleFunction]]++DoubleFunction++:: +* ++[[painless-api-reference-DoubleFunction-apply-1]]def link:{java8-javadoc}/java/util/function/DoubleFunction.html#apply%2Ddouble%2D[apply](double)++ (link:{java9-javadoc}/java/util/function/DoubleFunction.html#apply%2Ddouble%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DoublePredicate.asciidoc b/docs/painless/painless-api-reference/DoublePredicate.asciidoc new file mode 100644 index 0000000000000..48cfd3c59296c --- /dev/null +++ b/docs/painless/painless-api-reference/DoublePredicate.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DoublePredicate]]++DoublePredicate++:: +* ++[[painless-api-reference-DoublePredicate-and-1]]<> link:{java8-javadoc}/java/util/function/DoublePredicate.html#and%2Djava.util.function.DoublePredicate%2D[and](<>)++ (link:{java9-javadoc}/java/util/function/DoublePredicate.html#and%2Djava.util.function.DoublePredicate%2D[java 9]) +* ++[[painless-api-reference-DoublePredicate-negate-0]]<> link:{java8-javadoc}/java/util/function/DoublePredicate.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/util/function/DoublePredicate.html#negate%2D%2D[java 9]) +* ++[[painless-api-reference-DoublePredicate-or-1]]<> link:{java8-javadoc}/java/util/function/DoublePredicate.html#or%2Djava.util.function.DoublePredicate%2D[or](<>)++ (link:{java9-javadoc}/java/util/function/DoublePredicate.html#or%2Djava.util.function.DoublePredicate%2D[java 9]) +* ++[[painless-api-reference-DoublePredicate-test-1]]boolean link:{java8-javadoc}/java/util/function/DoublePredicate.html#test%2Ddouble%2D[test](double)++ (link:{java9-javadoc}/java/util/function/DoublePredicate.html#test%2Ddouble%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DoubleStream.Builder.asciidoc b/docs/painless/painless-api-reference/DoubleStream.Builder.asciidoc new file mode 100644 index 0000000000000..278a3f99a9ab9 --- /dev/null +++ b/docs/painless/painless-api-reference/DoubleStream.Builder.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DoubleStream-Builder]]++DoubleStream.Builder++:: +* ++[[painless-api-reference-DoubleStream-Builder-add-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream$Builder.html#add%2Ddouble%2D[add](double)++ (link:{java9-javadoc}/java/util/stream/DoubleStream$Builder.html#add%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-Builder-build-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream$Builder.html#build%2D%2D[build]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream$Builder.html#build%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/DoubleStream.asciidoc b/docs/painless/painless-api-reference/DoubleStream.asciidoc new file mode 100644 index 0000000000000..d3f517e29e40b --- /dev/null +++ b/docs/painless/painless-api-reference/DoubleStream.asciidoc @@ -0,0 +1,43 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DoubleStream]]++DoubleStream++:: +* ++[[painless-api-reference-DoubleStream-builder-0]]static <> link:{java8-javadoc}/java/util/stream/DoubleStream.html#builder%2D%2D[builder]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#builder%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-concat-2]]static <> link:{java8-javadoc}/java/util/stream/DoubleStream.html#concat%2Djava.util.stream.DoubleStream%2Djava.util.stream.DoubleStream%2D[concat](<>, <>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#concat%2Djava.util.stream.DoubleStream%2Djava.util.stream.DoubleStream%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-empty-0]]static <> link:{java8-javadoc}/java/util/stream/DoubleStream.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#empty%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-of-1]]static <> link:{java8-javadoc}/java/util/stream/DoubleStream.html#of%2Ddouble:A%2D[of](double[])++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#of%2Ddouble:A%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-allMatch-1]]boolean link:{java8-javadoc}/java/util/stream/DoubleStream.html#allMatch%2Djava.util.function.DoublePredicate%2D[allMatch](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#allMatch%2Djava.util.function.DoublePredicate%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-anyMatch-1]]boolean link:{java8-javadoc}/java/util/stream/DoubleStream.html#anyMatch%2Djava.util.function.DoublePredicate%2D[anyMatch](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#anyMatch%2Djava.util.function.DoublePredicate%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-average-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#average%2D%2D[average]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#average%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-boxed-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#boxed%2D%2D[boxed]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#boxed%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-collect-3]]def link:{java8-javadoc}/java/util/stream/DoubleStream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.ObjDoubleConsumer%2Djava.util.function.BiConsumer%2D[collect](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.ObjDoubleConsumer%2Djava.util.function.BiConsumer%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-count-0]]long link:{java8-javadoc}/java/util/stream/DoubleStream.html#count%2D%2D[count]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#count%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-distinct-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#distinct%2D%2D[distinct]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#distinct%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-filter-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#filter%2Djava.util.function.DoublePredicate%2D[filter](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#filter%2Djava.util.function.DoublePredicate%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-findAny-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#findAny%2D%2D[findAny]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#findAny%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-findFirst-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#findFirst%2D%2D[findFirst]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#findFirst%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-flatMap-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#flatMap%2Djava.util.function.DoubleFunction%2D[flatMap](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#flatMap%2Djava.util.function.DoubleFunction%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-forEach-1]]void link:{java8-javadoc}/java/util/stream/DoubleStream.html#forEach%2Djava.util.function.DoubleConsumer%2D[forEach](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#forEach%2Djava.util.function.DoubleConsumer%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-forEachOrdered-1]]void link:{java8-javadoc}/java/util/stream/DoubleStream.html#forEachOrdered%2Djava.util.function.DoubleConsumer%2D[forEachOrdered](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#forEachOrdered%2Djava.util.function.DoubleConsumer%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-iterator-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#iterator%2D%2D[iterator]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#iterator%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-limit-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#limit%2Dlong%2D[limit](long)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#limit%2Dlong%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-map-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#map%2Djava.util.function.DoubleUnaryOperator%2D[map](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#map%2Djava.util.function.DoubleUnaryOperator%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-mapToInt-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#mapToInt%2Djava.util.function.DoubleToIntFunction%2D[mapToInt](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#mapToInt%2Djava.util.function.DoubleToIntFunction%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-mapToLong-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#mapToLong%2Djava.util.function.DoubleToLongFunction%2D[mapToLong](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#mapToLong%2Djava.util.function.DoubleToLongFunction%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-mapToObj-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#mapToObj%2Djava.util.function.DoubleFunction%2D[mapToObj](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#mapToObj%2Djava.util.function.DoubleFunction%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-max-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#max%2D%2D[max]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#max%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-min-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#min%2D%2D[min]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#min%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-noneMatch-1]]boolean link:{java8-javadoc}/java/util/stream/DoubleStream.html#noneMatch%2Djava.util.function.DoublePredicate%2D[noneMatch](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#noneMatch%2Djava.util.function.DoublePredicate%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-peek-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#peek%2Djava.util.function.DoubleConsumer%2D[peek](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#peek%2Djava.util.function.DoubleConsumer%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-reduce-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#reduce%2Djava.util.function.DoubleBinaryOperator%2D[reduce](<>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#reduce%2Djava.util.function.DoubleBinaryOperator%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-reduce-2]]double link:{java8-javadoc}/java/util/stream/DoubleStream.html#reduce%2Ddouble%2Djava.util.function.DoubleBinaryOperator%2D[reduce](double, <>)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#reduce%2Ddouble%2Djava.util.function.DoubleBinaryOperator%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-sequential-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#sequential%2D%2D[sequential]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#sequential%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-skip-1]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#skip%2Dlong%2D[skip](long)++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#skip%2Dlong%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-sorted-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#sorted%2D%2D[sorted]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#sorted%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-spliterator-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#spliterator%2D%2D[spliterator]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#spliterator%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-sum-0]]double link:{java8-javadoc}/java/util/stream/DoubleStream.html#sum%2D%2D[sum]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#sum%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-summaryStatistics-0]]<> link:{java8-javadoc}/java/util/stream/DoubleStream.html#summaryStatistics%2D%2D[summaryStatistics]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#summaryStatistics%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleStream-toArray-0]]double[] link:{java8-javadoc}/java/util/stream/DoubleStream.html#toArray%2D%2D[toArray]()++ (link:{java9-javadoc}/java/util/stream/DoubleStream.html#toArray%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/DoubleSummaryStatistics.asciidoc b/docs/painless/painless-api-reference/DoubleSummaryStatistics.asciidoc new file mode 100644 index 0000000000000..976eb98440410 --- /dev/null +++ b/docs/painless/painless-api-reference/DoubleSummaryStatistics.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DoubleSummaryStatistics]]++DoubleSummaryStatistics++:: +* ++[[painless-api-reference-DoubleSummaryStatistics-DoubleSummaryStatistics-0]]link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#DoubleSummaryStatistics%2D%2D[DoubleSummaryStatistics]()++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#DoubleSummaryStatistics%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleSummaryStatistics-combine-1]]void link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#combine%2Djava.util.DoubleSummaryStatistics%2D[combine](<>)++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#combine%2Djava.util.DoubleSummaryStatistics%2D[java 9]) +* ++[[painless-api-reference-DoubleSummaryStatistics-getAverage-0]]double link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#getAverage%2D%2D[getAverage]()++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#getAverage%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleSummaryStatistics-getCount-0]]long link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#getCount%2D%2D[getCount]()++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#getCount%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleSummaryStatistics-getMax-0]]double link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#getMax%2D%2D[getMax]()++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#getMax%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleSummaryStatistics-getMin-0]]double link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#getMin%2D%2D[getMin]()++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#getMin%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleSummaryStatistics-getSum-0]]double link:{java8-javadoc}/java/util/DoubleSummaryStatistics.html#getSum%2D%2D[getSum]()++ (link:{java9-javadoc}/java/util/DoubleSummaryStatistics.html#getSum%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/DoubleSupplier.asciidoc b/docs/painless/painless-api-reference/DoubleSupplier.asciidoc new file mode 100644 index 0000000000000..1de478ab50f95 --- /dev/null +++ b/docs/painless/painless-api-reference/DoubleSupplier.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DoubleSupplier]]++DoubleSupplier++:: +* ++[[painless-api-reference-DoubleSupplier-getAsDouble-0]]double link:{java8-javadoc}/java/util/function/DoubleSupplier.html#getAsDouble%2D%2D[getAsDouble]()++ (link:{java9-javadoc}/java/util/function/DoubleSupplier.html#getAsDouble%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DoubleToIntFunction.asciidoc b/docs/painless/painless-api-reference/DoubleToIntFunction.asciidoc new file mode 100644 index 0000000000000..9448a88faf35a --- /dev/null +++ b/docs/painless/painless-api-reference/DoubleToIntFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DoubleToIntFunction]]++DoubleToIntFunction++:: +* ++[[painless-api-reference-DoubleToIntFunction-applyAsInt-1]]int link:{java8-javadoc}/java/util/function/DoubleToIntFunction.html#applyAsInt%2Ddouble%2D[applyAsInt](double)++ (link:{java9-javadoc}/java/util/function/DoubleToIntFunction.html#applyAsInt%2Ddouble%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DoubleToLongFunction.asciidoc b/docs/painless/painless-api-reference/DoubleToLongFunction.asciidoc new file mode 100644 index 0000000000000..62a3f841b3391 --- /dev/null +++ b/docs/painless/painless-api-reference/DoubleToLongFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DoubleToLongFunction]]++DoubleToLongFunction++:: +* ++[[painless-api-reference-DoubleToLongFunction-applyAsLong-1]]long link:{java8-javadoc}/java/util/function/DoubleToLongFunction.html#applyAsLong%2Ddouble%2D[applyAsLong](double)++ (link:{java9-javadoc}/java/util/function/DoubleToLongFunction.html#applyAsLong%2Ddouble%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DoubleUnaryOperator.asciidoc b/docs/painless/painless-api-reference/DoubleUnaryOperator.asciidoc new file mode 100644 index 0000000000000..a78b8ac5d0b1c --- /dev/null +++ b/docs/painless/painless-api-reference/DoubleUnaryOperator.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DoubleUnaryOperator]]++DoubleUnaryOperator++:: +* ++[[painless-api-reference-DoubleUnaryOperator-identity-0]]static <> link:{java8-javadoc}/java/util/function/DoubleUnaryOperator.html#identity%2D%2D[identity]()++ (link:{java9-javadoc}/java/util/function/DoubleUnaryOperator.html#identity%2D%2D[java 9]) +* ++[[painless-api-reference-DoubleUnaryOperator-andThen-1]]<> link:{java8-javadoc}/java/util/function/DoubleUnaryOperator.html#andThen%2Djava.util.function.DoubleUnaryOperator%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/DoubleUnaryOperator.html#andThen%2Djava.util.function.DoubleUnaryOperator%2D[java 9]) +* ++[[painless-api-reference-DoubleUnaryOperator-applyAsDouble-1]]double link:{java8-javadoc}/java/util/function/DoubleUnaryOperator.html#applyAsDouble%2Ddouble%2D[applyAsDouble](double)++ (link:{java9-javadoc}/java/util/function/DoubleUnaryOperator.html#applyAsDouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-DoubleUnaryOperator-compose-1]]<> link:{java8-javadoc}/java/util/function/DoubleUnaryOperator.html#compose%2Djava.util.function.DoubleUnaryOperator%2D[compose](<>)++ (link:{java9-javadoc}/java/util/function/DoubleUnaryOperator.html#compose%2Djava.util.function.DoubleUnaryOperator%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/DuplicateFormatFlagsException.asciidoc b/docs/painless/painless-api-reference/DuplicateFormatFlagsException.asciidoc new file mode 100644 index 0000000000000..f26103fdc198f --- /dev/null +++ b/docs/painless/painless-api-reference/DuplicateFormatFlagsException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-DuplicateFormatFlagsException]]++DuplicateFormatFlagsException++:: +* ++[[painless-api-reference-DuplicateFormatFlagsException-DuplicateFormatFlagsException-1]]link:{java8-javadoc}/java/util/DuplicateFormatFlagsException.html#DuplicateFormatFlagsException%2Djava.lang.String%2D[DuplicateFormatFlagsException](<>)++ (link:{java9-javadoc}/java/util/DuplicateFormatFlagsException.html#DuplicateFormatFlagsException%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-DuplicateFormatFlagsException-getFlags-0]]<> link:{java8-javadoc}/java/util/DuplicateFormatFlagsException.html#getFlags%2D%2D[getFlags]()++ (link:{java9-javadoc}/java/util/DuplicateFormatFlagsException.html#getFlags%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Duration.asciidoc b/docs/painless/painless-api-reference/Duration.asciidoc new file mode 100644 index 0000000000000..ef9ecf720a0f2 --- /dev/null +++ b/docs/painless/painless-api-reference/Duration.asciidoc @@ -0,0 +1,51 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Duration]]++Duration++:: +** [[painless-api-reference-Duration-ZERO]]static <> link:{java8-javadoc}/java/time/Duration.html#ZERO[ZERO] (link:{java9-javadoc}/java/time/Duration.html#ZERO[java 9]) +* ++[[painless-api-reference-Duration-between-2]]static <> link:{java8-javadoc}/java/time/Duration.html#between%2Djava.time.temporal.Temporal%2Djava.time.temporal.Temporal%2D[between](<>, <>)++ (link:{java9-javadoc}/java/time/Duration.html#between%2Djava.time.temporal.Temporal%2Djava.time.temporal.Temporal%2D[java 9]) +* ++[[painless-api-reference-Duration-from-1]]static <> link:{java8-javadoc}/java/time/Duration.html#from%2Djava.time.temporal.TemporalAmount%2D[from](<>)++ (link:{java9-javadoc}/java/time/Duration.html#from%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-Duration-of-2]]static <> link:{java8-javadoc}/java/time/Duration.html#of%2Dlong%2Djava.time.temporal.TemporalUnit%2D[of](long, <>)++ (link:{java9-javadoc}/java/time/Duration.html#of%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-Duration-ofDays-1]]static <> link:{java8-javadoc}/java/time/Duration.html#ofDays%2Dlong%2D[ofDays](long)++ (link:{java9-javadoc}/java/time/Duration.html#ofDays%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-ofHours-1]]static <> link:{java8-javadoc}/java/time/Duration.html#ofHours%2Dlong%2D[ofHours](long)++ (link:{java9-javadoc}/java/time/Duration.html#ofHours%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-ofMillis-1]]static <> link:{java8-javadoc}/java/time/Duration.html#ofMillis%2Dlong%2D[ofMillis](long)++ (link:{java9-javadoc}/java/time/Duration.html#ofMillis%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-ofMinutes-1]]static <> link:{java8-javadoc}/java/time/Duration.html#ofMinutes%2Dlong%2D[ofMinutes](long)++ (link:{java9-javadoc}/java/time/Duration.html#ofMinutes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-ofNanos-1]]static <> link:{java8-javadoc}/java/time/Duration.html#ofNanos%2Dlong%2D[ofNanos](long)++ (link:{java9-javadoc}/java/time/Duration.html#ofNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-ofSeconds-1]]static <> link:{java8-javadoc}/java/time/Duration.html#ofSeconds%2Dlong%2D[ofSeconds](long)++ (link:{java9-javadoc}/java/time/Duration.html#ofSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-ofSeconds-2]]static <> link:{java8-javadoc}/java/time/Duration.html#ofSeconds%2Dlong%2Dlong%2D[ofSeconds](long, long)++ (link:{java9-javadoc}/java/time/Duration.html#ofSeconds%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-parse-1]]static <> link:{java8-javadoc}/java/time/Duration.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/Duration.html#parse%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-Duration-abs-0]]<> link:{java8-javadoc}/java/time/Duration.html#abs%2D%2D[abs]()++ (link:{java9-javadoc}/java/time/Duration.html#abs%2D%2D[java 9]) +* ++[[painless-api-reference-Duration-compareTo-1]]int link:{java8-javadoc}/java/time/Duration.html#compareTo%2Djava.time.Duration%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/Duration.html#compareTo%2Djava.time.Duration%2D[java 9]) +* ++[[painless-api-reference-Duration-dividedBy-1]]<> link:{java8-javadoc}/java/time/Duration.html#dividedBy%2Dlong%2D[dividedBy](long)++ (link:{java9-javadoc}/java/time/Duration.html#dividedBy%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-getNano-0]]int link:{java8-javadoc}/java/time/Duration.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/Duration.html#getNano%2D%2D[java 9]) +* ++[[painless-api-reference-Duration-getSeconds-0]]long link:{java8-javadoc}/java/time/Duration.html#getSeconds%2D%2D[getSeconds]()++ (link:{java9-javadoc}/java/time/Duration.html#getSeconds%2D%2D[java 9]) +* ++[[painless-api-reference-Duration-isNegative-0]]boolean link:{java8-javadoc}/java/time/Duration.html#isNegative%2D%2D[isNegative]()++ (link:{java9-javadoc}/java/time/Duration.html#isNegative%2D%2D[java 9]) +* ++[[painless-api-reference-Duration-isZero-0]]boolean link:{java8-javadoc}/java/time/Duration.html#isZero%2D%2D[isZero]()++ (link:{java9-javadoc}/java/time/Duration.html#isZero%2D%2D[java 9]) +* ++[[painless-api-reference-Duration-minus-1]]<> link:{java8-javadoc}/java/time/Duration.html#minus%2Djava.time.Duration%2D[minus](<>)++ (link:{java9-javadoc}/java/time/Duration.html#minus%2Djava.time.Duration%2D[java 9]) +* ++[[painless-api-reference-Duration-minus-2]]<> link:{java8-javadoc}/java/time/Duration.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/Duration.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-Duration-minusDays-1]]<> link:{java8-javadoc}/java/time/Duration.html#minusDays%2Dlong%2D[minusDays](long)++ (link:{java9-javadoc}/java/time/Duration.html#minusDays%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-minusHours-1]]<> link:{java8-javadoc}/java/time/Duration.html#minusHours%2Dlong%2D[minusHours](long)++ (link:{java9-javadoc}/java/time/Duration.html#minusHours%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-minusMillis-1]]<> link:{java8-javadoc}/java/time/Duration.html#minusMillis%2Dlong%2D[minusMillis](long)++ (link:{java9-javadoc}/java/time/Duration.html#minusMillis%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-minusMinutes-1]]<> link:{java8-javadoc}/java/time/Duration.html#minusMinutes%2Dlong%2D[minusMinutes](long)++ (link:{java9-javadoc}/java/time/Duration.html#minusMinutes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-minusNanos-1]]<> link:{java8-javadoc}/java/time/Duration.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/Duration.html#minusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-minusSeconds-1]]<> link:{java8-javadoc}/java/time/Duration.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/Duration.html#minusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-multipliedBy-1]]<> link:{java8-javadoc}/java/time/Duration.html#multipliedBy%2Dlong%2D[multipliedBy](long)++ (link:{java9-javadoc}/java/time/Duration.html#multipliedBy%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-negated-0]]<> link:{java8-javadoc}/java/time/Duration.html#negated%2D%2D[negated]()++ (link:{java9-javadoc}/java/time/Duration.html#negated%2D%2D[java 9]) +* ++[[painless-api-reference-Duration-plus-1]]<> link:{java8-javadoc}/java/time/Duration.html#plus%2Djava.time.Duration%2D[plus](<>)++ (link:{java9-javadoc}/java/time/Duration.html#plus%2Djava.time.Duration%2D[java 9]) +* ++[[painless-api-reference-Duration-plus-2]]<> link:{java8-javadoc}/java/time/Duration.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/Duration.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-Duration-plusDays-1]]<> link:{java8-javadoc}/java/time/Duration.html#plusDays%2Dlong%2D[plusDays](long)++ (link:{java9-javadoc}/java/time/Duration.html#plusDays%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-plusHours-1]]<> link:{java8-javadoc}/java/time/Duration.html#plusHours%2Dlong%2D[plusHours](long)++ (link:{java9-javadoc}/java/time/Duration.html#plusHours%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-plusMillis-1]]<> link:{java8-javadoc}/java/time/Duration.html#plusMillis%2Dlong%2D[plusMillis](long)++ (link:{java9-javadoc}/java/time/Duration.html#plusMillis%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-plusMinutes-1]]<> link:{java8-javadoc}/java/time/Duration.html#plusMinutes%2Dlong%2D[plusMinutes](long)++ (link:{java9-javadoc}/java/time/Duration.html#plusMinutes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-plusNanos-1]]<> link:{java8-javadoc}/java/time/Duration.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/Duration.html#plusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-plusSeconds-1]]<> link:{java8-javadoc}/java/time/Duration.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/Duration.html#plusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Duration-toDays-0]]long link:{java8-javadoc}/java/time/Duration.html#toDays%2D%2D[toDays]()++ (link:{java9-javadoc}/java/time/Duration.html#toDays%2D%2D[java 9]) +* ++[[painless-api-reference-Duration-toHours-0]]long link:{java8-javadoc}/java/time/Duration.html#toHours%2D%2D[toHours]()++ (link:{java9-javadoc}/java/time/Duration.html#toHours%2D%2D[java 9]) +* ++[[painless-api-reference-Duration-toMillis-0]]long link:{java8-javadoc}/java/time/Duration.html#toMillis%2D%2D[toMillis]()++ (link:{java9-javadoc}/java/time/Duration.html#toMillis%2D%2D[java 9]) +* ++[[painless-api-reference-Duration-toMinutes-0]]long link:{java8-javadoc}/java/time/Duration.html#toMinutes%2D%2D[toMinutes]()++ (link:{java9-javadoc}/java/time/Duration.html#toMinutes%2D%2D[java 9]) +* ++[[painless-api-reference-Duration-toNanos-0]]long link:{java8-javadoc}/java/time/Duration.html#toNanos%2D%2D[toNanos]()++ (link:{java9-javadoc}/java/time/Duration.html#toNanos%2D%2D[java 9]) +* ++[[painless-api-reference-Duration-withNanos-1]]<> link:{java8-javadoc}/java/time/Duration.html#withNanos%2Dint%2D[withNanos](int)++ (link:{java9-javadoc}/java/time/Duration.html#withNanos%2Dint%2D[java 9]) +* ++[[painless-api-reference-Duration-withSeconds-1]]<> link:{java8-javadoc}/java/time/Duration.html#withSeconds%2Dlong%2D[withSeconds](long)++ (link:{java9-javadoc}/java/time/Duration.html#withSeconds%2Dlong%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/EmptyStackException.asciidoc b/docs/painless/painless-api-reference/EmptyStackException.asciidoc new file mode 100644 index 0000000000000..09ca4ca96be20 --- /dev/null +++ b/docs/painless/painless-api-reference/EmptyStackException.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-EmptyStackException]]++EmptyStackException++:: +* ++[[painless-api-reference-EmptyStackException-EmptyStackException-0]]link:{java8-javadoc}/java/util/EmptyStackException.html#EmptyStackException%2D%2D[EmptyStackException]()++ (link:{java9-javadoc}/java/util/EmptyStackException.html#EmptyStackException%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Enum.asciidoc b/docs/painless/painless-api-reference/Enum.asciidoc new file mode 100644 index 0000000000000..077044dbfb9cd --- /dev/null +++ b/docs/painless/painless-api-reference/Enum.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Enum]]++Enum++:: +* ++[[painless-api-reference-Enum-compareTo-1]]int link:{java8-javadoc}/java/lang/Enum.html#compareTo%2Djava.lang.Enum%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Enum.html#compareTo%2Djava.lang.Enum%2D[java 9]) +* ++[[painless-api-reference-Enum-name-0]]<> link:{java8-javadoc}/java/lang/Enum.html#name%2D%2D[name]()++ (link:{java9-javadoc}/java/lang/Enum.html#name%2D%2D[java 9]) +* ++[[painless-api-reference-Enum-ordinal-0]]int link:{java8-javadoc}/java/lang/Enum.html#ordinal%2D%2D[ordinal]()++ (link:{java9-javadoc}/java/lang/Enum.html#ordinal%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/EnumConstantNotPresentException.asciidoc b/docs/painless/painless-api-reference/EnumConstantNotPresentException.asciidoc new file mode 100644 index 0000000000000..66a709379d797 --- /dev/null +++ b/docs/painless/painless-api-reference/EnumConstantNotPresentException.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-EnumConstantNotPresentException]]++EnumConstantNotPresentException++:: +* ++[[painless-api-reference-EnumConstantNotPresentException-constantName-0]]<> link:{java8-javadoc}/java/lang/EnumConstantNotPresentException.html#constantName%2D%2D[constantName]()++ (link:{java9-javadoc}/java/lang/EnumConstantNotPresentException.html#constantName%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Enumeration.asciidoc b/docs/painless/painless-api-reference/Enumeration.asciidoc new file mode 100644 index 0000000000000..51849ec27de0f --- /dev/null +++ b/docs/painless/painless-api-reference/Enumeration.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Enumeration]]++Enumeration++:: +* ++[[painless-api-reference-Enumeration-hasMoreElements-0]]boolean link:{java8-javadoc}/java/util/Enumeration.html#hasMoreElements%2D%2D[hasMoreElements]()++ (link:{java9-javadoc}/java/util/Enumeration.html#hasMoreElements%2D%2D[java 9]) +* ++[[painless-api-reference-Enumeration-nextElement-0]]def link:{java8-javadoc}/java/util/Enumeration.html#nextElement%2D%2D[nextElement]()++ (link:{java9-javadoc}/java/util/Enumeration.html#nextElement%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Era.asciidoc b/docs/painless/painless-api-reference/Era.asciidoc new file mode 100644 index 0000000000000..db5ee6aa86b0d --- /dev/null +++ b/docs/painless/painless-api-reference/Era.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Era]]++Era++:: +* ++[[painless-api-reference-Era-getDisplayName-2]]<> link:{java8-javadoc}/java/time/chrono/Era.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[getDisplayName](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/Era.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Era-getValue-0]]int link:{java8-javadoc}/java/time/chrono/Era.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/chrono/Era.html#getValue%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/EventListener.asciidoc b/docs/painless/painless-api-reference/EventListener.asciidoc new file mode 100644 index 0000000000000..499095ae020a0 --- /dev/null +++ b/docs/painless/painless-api-reference/EventListener.asciidoc @@ -0,0 +1,7 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-EventListener]]++EventListener++:: +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/EventListenerProxy.asciidoc b/docs/painless/painless-api-reference/EventListenerProxy.asciidoc new file mode 100644 index 0000000000000..a3a4958ec7115 --- /dev/null +++ b/docs/painless/painless-api-reference/EventListenerProxy.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-EventListenerProxy]]++EventListenerProxy++:: +* ++[[painless-api-reference-EventListenerProxy-getListener-0]]<> link:{java8-javadoc}/java/util/EventListenerProxy.html#getListener%2D%2D[getListener]()++ (link:{java9-javadoc}/java/util/EventListenerProxy.html#getListener%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/EventObject.asciidoc b/docs/painless/painless-api-reference/EventObject.asciidoc new file mode 100644 index 0000000000000..4ac6b14150cb1 --- /dev/null +++ b/docs/painless/painless-api-reference/EventObject.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-EventObject]]++EventObject++:: +* ++[[painless-api-reference-EventObject-EventObject-1]]link:{java8-javadoc}/java/util/EventObject.html#EventObject%2Djava.lang.Object%2D[EventObject](<>)++ (link:{java9-javadoc}/java/util/EventObject.html#EventObject%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-EventObject-getSource-0]]<> link:{java8-javadoc}/java/util/EventObject.html#getSource%2D%2D[getSource]()++ (link:{java9-javadoc}/java/util/EventObject.html#getSource%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Exception.asciidoc b/docs/painless/painless-api-reference/Exception.asciidoc new file mode 100644 index 0000000000000..d6ff7b0a7e53e --- /dev/null +++ b/docs/painless/painless-api-reference/Exception.asciidoc @@ -0,0 +1,12 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Exception]]++Exception++:: +* ++[[painless-api-reference-Exception-Exception-0]]link:{java8-javadoc}/java/lang/Exception.html#Exception%2D%2D[Exception]()++ (link:{java9-javadoc}/java/lang/Exception.html#Exception%2D%2D[java 9]) +* ++[[painless-api-reference-Exception-Exception-1]]link:{java8-javadoc}/java/lang/Exception.html#Exception%2Djava.lang.String%2D[Exception](<>)++ (link:{java9-javadoc}/java/lang/Exception.html#Exception%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Exception-getLocalizedMessage-0]]<> link:{java8-javadoc}/java/lang/Exception.html#getLocalizedMessage%2D%2D[getLocalizedMessage]()++ (link:{java9-javadoc}/java/lang/Exception.html#getLocalizedMessage%2D%2D[java 9]) +* ++[[painless-api-reference-Exception-getMessage-0]]<> link:{java8-javadoc}/java/lang/Exception.html#getMessage%2D%2D[getMessage]()++ (link:{java9-javadoc}/java/lang/Exception.html#getMessage%2D%2D[java 9]) +* ++[[painless-api-reference-Exception-getStackTrace-0]]<>[] link:{java8-javadoc}/java/lang/Exception.html#getStackTrace%2D%2D[getStackTrace]()++ (link:{java9-javadoc}/java/lang/Exception.html#getStackTrace%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/FieldPosition.asciidoc b/docs/painless/painless-api-reference/FieldPosition.asciidoc new file mode 100644 index 0000000000000..7a4b5d78407ae --- /dev/null +++ b/docs/painless/painless-api-reference/FieldPosition.asciidoc @@ -0,0 +1,15 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-FieldPosition]]++FieldPosition++:: +* ++[[painless-api-reference-FieldPosition-FieldPosition-1]]link:{java8-javadoc}/java/text/FieldPosition.html#FieldPosition%2Dint%2D[FieldPosition](int)++ (link:{java9-javadoc}/java/text/FieldPosition.html#FieldPosition%2Dint%2D[java 9]) +* ++[[painless-api-reference-FieldPosition-FieldPosition-2]]link:{java8-javadoc}/java/text/FieldPosition.html#FieldPosition%2Djava.text.Format$Field%2Dint%2D[FieldPosition](<>, int)++ (link:{java9-javadoc}/java/text/FieldPosition.html#FieldPosition%2Djava.text.Format$Field%2Dint%2D[java 9]) +* ++[[painless-api-reference-FieldPosition-getBeginIndex-0]]int link:{java8-javadoc}/java/text/FieldPosition.html#getBeginIndex%2D%2D[getBeginIndex]()++ (link:{java9-javadoc}/java/text/FieldPosition.html#getBeginIndex%2D%2D[java 9]) +* ++[[painless-api-reference-FieldPosition-getEndIndex-0]]int link:{java8-javadoc}/java/text/FieldPosition.html#getEndIndex%2D%2D[getEndIndex]()++ (link:{java9-javadoc}/java/text/FieldPosition.html#getEndIndex%2D%2D[java 9]) +* ++[[painless-api-reference-FieldPosition-getField-0]]int link:{java8-javadoc}/java/text/FieldPosition.html#getField%2D%2D[getField]()++ (link:{java9-javadoc}/java/text/FieldPosition.html#getField%2D%2D[java 9]) +* ++[[painless-api-reference-FieldPosition-getFieldAttribute-0]]<> link:{java8-javadoc}/java/text/FieldPosition.html#getFieldAttribute%2D%2D[getFieldAttribute]()++ (link:{java9-javadoc}/java/text/FieldPosition.html#getFieldAttribute%2D%2D[java 9]) +* ++[[painless-api-reference-FieldPosition-setBeginIndex-1]]void link:{java8-javadoc}/java/text/FieldPosition.html#setBeginIndex%2Dint%2D[setBeginIndex](int)++ (link:{java9-javadoc}/java/text/FieldPosition.html#setBeginIndex%2Dint%2D[java 9]) +* ++[[painless-api-reference-FieldPosition-setEndIndex-1]]void link:{java8-javadoc}/java/text/FieldPosition.html#setEndIndex%2Dint%2D[setEndIndex](int)++ (link:{java9-javadoc}/java/text/FieldPosition.html#setEndIndex%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Float.asciidoc b/docs/painless/painless-api-reference/Float.asciidoc new file mode 100644 index 0000000000000..1d20357842cfc --- /dev/null +++ b/docs/painless/painless-api-reference/Float.asciidoc @@ -0,0 +1,35 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Float]]++Float++:: +** [[painless-api-reference-Float-BYTES]]static int link:{java8-javadoc}/java/lang/Float.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Float.html#BYTES[java 9]) +** [[painless-api-reference-Float-MAX_EXPONENT]]static int link:{java8-javadoc}/java/lang/Float.html#MAX_EXPONENT[MAX_EXPONENT] (link:{java9-javadoc}/java/lang/Float.html#MAX_EXPONENT[java 9]) +** [[painless-api-reference-Float-MAX_VALUE]]static float link:{java8-javadoc}/java/lang/Float.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Float.html#MAX_VALUE[java 9]) +** [[painless-api-reference-Float-MIN_EXPONENT]]static int link:{java8-javadoc}/java/lang/Float.html#MIN_EXPONENT[MIN_EXPONENT] (link:{java9-javadoc}/java/lang/Float.html#MIN_EXPONENT[java 9]) +** [[painless-api-reference-Float-MIN_NORMAL]]static float link:{java8-javadoc}/java/lang/Float.html#MIN_NORMAL[MIN_NORMAL] (link:{java9-javadoc}/java/lang/Float.html#MIN_NORMAL[java 9]) +** [[painless-api-reference-Float-MIN_VALUE]]static float link:{java8-javadoc}/java/lang/Float.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Float.html#MIN_VALUE[java 9]) +** [[painless-api-reference-Float-NEGATIVE_INFINITY]]static float link:{java8-javadoc}/java/lang/Float.html#NEGATIVE_INFINITY[NEGATIVE_INFINITY] (link:{java9-javadoc}/java/lang/Float.html#NEGATIVE_INFINITY[java 9]) +** [[painless-api-reference-Float-NaN]]static float link:{java8-javadoc}/java/lang/Float.html#NaN[NaN] (link:{java9-javadoc}/java/lang/Float.html#NaN[java 9]) +** [[painless-api-reference-Float-POSITIVE_INFINITY]]static float link:{java8-javadoc}/java/lang/Float.html#POSITIVE_INFINITY[POSITIVE_INFINITY] (link:{java9-javadoc}/java/lang/Float.html#POSITIVE_INFINITY[java 9]) +** [[painless-api-reference-Float-SIZE]]static int link:{java8-javadoc}/java/lang/Float.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Float.html#SIZE[java 9]) +* ++[[painless-api-reference-Float-compare-2]]static int link:{java8-javadoc}/java/lang/Float.html#compare%2Dfloat%2Dfloat%2D[compare](float, float)++ (link:{java9-javadoc}/java/lang/Float.html#compare%2Dfloat%2Dfloat%2D[java 9]) +* ++[[painless-api-reference-Float-floatToIntBits-1]]static int link:{java8-javadoc}/java/lang/Float.html#floatToIntBits%2Dfloat%2D[floatToIntBits](float)++ (link:{java9-javadoc}/java/lang/Float.html#floatToIntBits%2Dfloat%2D[java 9]) +* ++[[painless-api-reference-Float-floatToRawIntBits-1]]static int link:{java8-javadoc}/java/lang/Float.html#floatToRawIntBits%2Dfloat%2D[floatToRawIntBits](float)++ (link:{java9-javadoc}/java/lang/Float.html#floatToRawIntBits%2Dfloat%2D[java 9]) +* ++[[painless-api-reference-Float-hashCode-1]]static int link:{java8-javadoc}/java/lang/Float.html#hashCode%2Dfloat%2D[hashCode](float)++ (link:{java9-javadoc}/java/lang/Float.html#hashCode%2Dfloat%2D[java 9]) +* ++[[painless-api-reference-Float-intBitsToFloat-1]]static float link:{java8-javadoc}/java/lang/Float.html#intBitsToFloat%2Dint%2D[intBitsToFloat](int)++ (link:{java9-javadoc}/java/lang/Float.html#intBitsToFloat%2Dint%2D[java 9]) +* ++[[painless-api-reference-Float-isFinite-1]]static boolean link:{java8-javadoc}/java/lang/Float.html#isFinite%2Dfloat%2D[isFinite](float)++ (link:{java9-javadoc}/java/lang/Float.html#isFinite%2Dfloat%2D[java 9]) +* ++[[painless-api-reference-Float-isInfinite-1]]static boolean link:{java8-javadoc}/java/lang/Float.html#isInfinite%2Dfloat%2D[isInfinite](float)++ (link:{java9-javadoc}/java/lang/Float.html#isInfinite%2Dfloat%2D[java 9]) +* ++[[painless-api-reference-Float-isNaN-1]]static boolean link:{java8-javadoc}/java/lang/Float.html#isNaN%2Dfloat%2D[isNaN](float)++ (link:{java9-javadoc}/java/lang/Float.html#isNaN%2Dfloat%2D[java 9]) +* ++[[painless-api-reference-Float-max-2]]static float link:{java8-javadoc}/java/lang/Float.html#max%2Dfloat%2Dfloat%2D[max](float, float)++ (link:{java9-javadoc}/java/lang/Float.html#max%2Dfloat%2Dfloat%2D[java 9]) +* ++[[painless-api-reference-Float-min-2]]static float link:{java8-javadoc}/java/lang/Float.html#min%2Dfloat%2Dfloat%2D[min](float, float)++ (link:{java9-javadoc}/java/lang/Float.html#min%2Dfloat%2Dfloat%2D[java 9]) +* ++[[painless-api-reference-Float-parseFloat-1]]static float link:{java8-javadoc}/java/lang/Float.html#parseFloat%2Djava.lang.String%2D[parseFloat](<>)++ (link:{java9-javadoc}/java/lang/Float.html#parseFloat%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Float-sum-2]]static float link:{java8-javadoc}/java/lang/Float.html#sum%2Dfloat%2Dfloat%2D[sum](float, float)++ (link:{java9-javadoc}/java/lang/Float.html#sum%2Dfloat%2Dfloat%2D[java 9]) +* ++[[painless-api-reference-Float-toHexString-1]]static <> link:{java8-javadoc}/java/lang/Float.html#toHexString%2Dfloat%2D[toHexString](float)++ (link:{java9-javadoc}/java/lang/Float.html#toHexString%2Dfloat%2D[java 9]) +* ++[[painless-api-reference-Float-toString-1]]static <> link:{java8-javadoc}/java/lang/Float.html#toString%2Dfloat%2D[toString](float)++ (link:{java9-javadoc}/java/lang/Float.html#toString%2Dfloat%2D[java 9]) +* ++[[painless-api-reference-Float-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Float.html#valueOf%2Dfloat%2D[valueOf](float)++ (link:{java9-javadoc}/java/lang/Float.html#valueOf%2Dfloat%2D[java 9]) +* ++[[painless-api-reference-Float-compareTo-1]]int link:{java8-javadoc}/java/lang/Float.html#compareTo%2Djava.lang.Float%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Float.html#compareTo%2Djava.lang.Float%2D[java 9]) +* ++[[painless-api-reference-Float-isInfinite-0]]boolean link:{java8-javadoc}/java/lang/Float.html#isInfinite%2D%2D[isInfinite]()++ (link:{java9-javadoc}/java/lang/Float.html#isInfinite%2D%2D[java 9]) +* ++[[painless-api-reference-Float-isNaN-0]]boolean link:{java8-javadoc}/java/lang/Float.html#isNaN%2D%2D[isNaN]()++ (link:{java9-javadoc}/java/lang/Float.html#isNaN%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Format.Field.asciidoc b/docs/painless/painless-api-reference/Format.Field.asciidoc new file mode 100644 index 0000000000000..3ddf6ec6d2f15 --- /dev/null +++ b/docs/painless/painless-api-reference/Format.Field.asciidoc @@ -0,0 +1,7 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Format-Field]]++Format.Field++:: +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Format.asciidoc b/docs/painless/painless-api-reference/Format.asciidoc new file mode 100644 index 0000000000000..afa42a0598dce --- /dev/null +++ b/docs/painless/painless-api-reference/Format.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Format]]++Format++:: +* ++[[painless-api-reference-Format-clone-0]]def link:{java8-javadoc}/java/text/Format.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/text/Format.html#clone%2D%2D[java 9]) +* ++[[painless-api-reference-Format-format-1]]<> link:{java8-javadoc}/java/text/Format.html#format%2Djava.lang.Object%2D[format](<>)++ (link:{java9-javadoc}/java/text/Format.html#format%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Format-format-3]]<> link:{java8-javadoc}/java/text/Format.html#format%2Djava.lang.Object%2Djava.lang.StringBuffer%2Djava.text.FieldPosition%2D[format](<>, <>, <>)++ (link:{java9-javadoc}/java/text/Format.html#format%2Djava.lang.Object%2Djava.lang.StringBuffer%2Djava.text.FieldPosition%2D[java 9]) +* ++[[painless-api-reference-Format-formatToCharacterIterator-1]]<> link:{java8-javadoc}/java/text/Format.html#formatToCharacterIterator%2Djava.lang.Object%2D[formatToCharacterIterator](<>)++ (link:{java9-javadoc}/java/text/Format.html#formatToCharacterIterator%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Format-parseObject-1]]<> link:{java8-javadoc}/java/text/Format.html#parseObject%2Djava.lang.String%2D[parseObject](<>)++ (link:{java9-javadoc}/java/text/Format.html#parseObject%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Format-parseObject-2]]<> link:{java8-javadoc}/java/text/Format.html#parseObject%2Djava.lang.String%2Djava.text.ParsePosition%2D[parseObject](<>, <>)++ (link:{java9-javadoc}/java/text/Format.html#parseObject%2Djava.lang.String%2Djava.text.ParsePosition%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/FormatFlagsConversionMismatchException.asciidoc b/docs/painless/painless-api-reference/FormatFlagsConversionMismatchException.asciidoc new file mode 100644 index 0000000000000..b72d827b196e8 --- /dev/null +++ b/docs/painless/painless-api-reference/FormatFlagsConversionMismatchException.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-FormatFlagsConversionMismatchException]]++FormatFlagsConversionMismatchException++:: +* ++[[painless-api-reference-FormatFlagsConversionMismatchException-FormatFlagsConversionMismatchException-2]]link:{java8-javadoc}/java/util/FormatFlagsConversionMismatchException.html#FormatFlagsConversionMismatchException%2Djava.lang.String%2Dchar%2D[FormatFlagsConversionMismatchException](<>, char)++ (link:{java9-javadoc}/java/util/FormatFlagsConversionMismatchException.html#FormatFlagsConversionMismatchException%2Djava.lang.String%2Dchar%2D[java 9]) +* ++[[painless-api-reference-FormatFlagsConversionMismatchException-getConversion-0]]char link:{java8-javadoc}/java/util/FormatFlagsConversionMismatchException.html#getConversion%2D%2D[getConversion]()++ (link:{java9-javadoc}/java/util/FormatFlagsConversionMismatchException.html#getConversion%2D%2D[java 9]) +* ++[[painless-api-reference-FormatFlagsConversionMismatchException-getFlags-0]]<> link:{java8-javadoc}/java/util/FormatFlagsConversionMismatchException.html#getFlags%2D%2D[getFlags]()++ (link:{java9-javadoc}/java/util/FormatFlagsConversionMismatchException.html#getFlags%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/FormatStyle.asciidoc b/docs/painless/painless-api-reference/FormatStyle.asciidoc new file mode 100644 index 0000000000000..4db787c051ae6 --- /dev/null +++ b/docs/painless/painless-api-reference/FormatStyle.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-FormatStyle]]++FormatStyle++:: +** [[painless-api-reference-FormatStyle-FULL]]static <> link:{java8-javadoc}/java/time/format/FormatStyle.html#FULL[FULL] (link:{java9-javadoc}/java/time/format/FormatStyle.html#FULL[java 9]) +** [[painless-api-reference-FormatStyle-LONG]]static <> link:{java8-javadoc}/java/time/format/FormatStyle.html#LONG[LONG] (link:{java9-javadoc}/java/time/format/FormatStyle.html#LONG[java 9]) +** [[painless-api-reference-FormatStyle-MEDIUM]]static <> link:{java8-javadoc}/java/time/format/FormatStyle.html#MEDIUM[MEDIUM] (link:{java9-javadoc}/java/time/format/FormatStyle.html#MEDIUM[java 9]) +** [[painless-api-reference-FormatStyle-SHORT]]static <> link:{java8-javadoc}/java/time/format/FormatStyle.html#SHORT[SHORT] (link:{java9-javadoc}/java/time/format/FormatStyle.html#SHORT[java 9]) +* ++[[painless-api-reference-FormatStyle-valueOf-1]]static <> link:{java8-javadoc}/java/time/format/FormatStyle.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/format/FormatStyle.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-FormatStyle-values-0]]static <>[] link:{java8-javadoc}/java/time/format/FormatStyle.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/format/FormatStyle.html#values%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Formattable.asciidoc b/docs/painless/painless-api-reference/Formattable.asciidoc new file mode 100644 index 0000000000000..ab70d1319e939 --- /dev/null +++ b/docs/painless/painless-api-reference/Formattable.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Formattable]]++Formattable++:: +* ++[[painless-api-reference-Formattable-formatTo-4]]void link:{java8-javadoc}/java/util/Formattable.html#formatTo%2Djava.util.Formatter%2Dint%2Dint%2Dint%2D[formatTo](<>, int, int, int)++ (link:{java9-javadoc}/java/util/Formattable.html#formatTo%2Djava.util.Formatter%2Dint%2Dint%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/FormattableFlags.asciidoc b/docs/painless/painless-api-reference/FormattableFlags.asciidoc new file mode 100644 index 0000000000000..dec639fe70b3e --- /dev/null +++ b/docs/painless/painless-api-reference/FormattableFlags.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-FormattableFlags]]++FormattableFlags++:: +** [[painless-api-reference-FormattableFlags-ALTERNATE]]static int link:{java8-javadoc}/java/util/FormattableFlags.html#ALTERNATE[ALTERNATE] (link:{java9-javadoc}/java/util/FormattableFlags.html#ALTERNATE[java 9]) +** [[painless-api-reference-FormattableFlags-LEFT_JUSTIFY]]static int link:{java8-javadoc}/java/util/FormattableFlags.html#LEFT_JUSTIFY[LEFT_JUSTIFY] (link:{java9-javadoc}/java/util/FormattableFlags.html#LEFT_JUSTIFY[java 9]) +** [[painless-api-reference-FormattableFlags-UPPERCASE]]static int link:{java8-javadoc}/java/util/FormattableFlags.html#UPPERCASE[UPPERCASE] (link:{java9-javadoc}/java/util/FormattableFlags.html#UPPERCASE[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Formatter.BigDecimalLayoutForm.asciidoc b/docs/painless/painless-api-reference/Formatter.BigDecimalLayoutForm.asciidoc new file mode 100644 index 0000000000000..92d8852e13c09 --- /dev/null +++ b/docs/painless/painless-api-reference/Formatter.BigDecimalLayoutForm.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Formatter-BigDecimalLayoutForm]]++Formatter.BigDecimalLayoutForm++:: +** [[painless-api-reference-Formatter-BigDecimalLayoutForm-DECIMAL_FLOAT]]static <> link:{java8-javadoc}/java/util/Formatter$BigDecimalLayoutForm.html#DECIMAL_FLOAT[DECIMAL_FLOAT] (link:{java9-javadoc}/java/util/Formatter$BigDecimalLayoutForm.html#DECIMAL_FLOAT[java 9]) +** [[painless-api-reference-Formatter-BigDecimalLayoutForm-SCIENTIFIC]]static <> link:{java8-javadoc}/java/util/Formatter$BigDecimalLayoutForm.html#SCIENTIFIC[SCIENTIFIC] (link:{java9-javadoc}/java/util/Formatter$BigDecimalLayoutForm.html#SCIENTIFIC[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Formatter.asciidoc b/docs/painless/painless-api-reference/Formatter.asciidoc new file mode 100644 index 0000000000000..4cad434f1308f --- /dev/null +++ b/docs/painless/painless-api-reference/Formatter.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Formatter]]++Formatter++:: +* ++[[painless-api-reference-Formatter-Formatter-0]]link:{java8-javadoc}/java/util/Formatter.html#Formatter%2D%2D[Formatter]()++ (link:{java9-javadoc}/java/util/Formatter.html#Formatter%2D%2D[java 9]) +* ++[[painless-api-reference-Formatter-Formatter-1]]link:{java8-javadoc}/java/util/Formatter.html#Formatter%2Djava.lang.Appendable%2D[Formatter](<>)++ (link:{java9-javadoc}/java/util/Formatter.html#Formatter%2Djava.lang.Appendable%2D[java 9]) +* ++[[painless-api-reference-Formatter-Formatter-2]]link:{java8-javadoc}/java/util/Formatter.html#Formatter%2Djava.lang.Appendable%2Djava.util.Locale%2D[Formatter](<>, <>)++ (link:{java9-javadoc}/java/util/Formatter.html#Formatter%2Djava.lang.Appendable%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Formatter-format-2]]<> link:{java8-javadoc}/java/util/Formatter.html#format%2Djava.lang.String%2Djava.lang.Object:A%2D[format](<>, def[])++ (link:{java9-javadoc}/java/util/Formatter.html#format%2Djava.lang.String%2Djava.lang.Object:A%2D[java 9]) +* ++[[painless-api-reference-Formatter-format-3]]<> link:{java8-javadoc}/java/util/Formatter.html#format%2Djava.util.Locale%2Djava.lang.String%2Djava.lang.Object:A%2D[format](<>, <>, def[])++ (link:{java9-javadoc}/java/util/Formatter.html#format%2Djava.util.Locale%2Djava.lang.String%2Djava.lang.Object:A%2D[java 9]) +* ++[[painless-api-reference-Formatter-locale-0]]<> link:{java8-javadoc}/java/util/Formatter.html#locale%2D%2D[locale]()++ (link:{java9-javadoc}/java/util/Formatter.html#locale%2D%2D[java 9]) +* ++[[painless-api-reference-Formatter-out-0]]<> link:{java8-javadoc}/java/util/Formatter.html#out%2D%2D[out]()++ (link:{java9-javadoc}/java/util/Formatter.html#out%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/FormatterClosedException.asciidoc b/docs/painless/painless-api-reference/FormatterClosedException.asciidoc new file mode 100644 index 0000000000000..00c4f5971d335 --- /dev/null +++ b/docs/painless/painless-api-reference/FormatterClosedException.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-FormatterClosedException]]++FormatterClosedException++:: +* ++[[painless-api-reference-FormatterClosedException-FormatterClosedException-0]]link:{java8-javadoc}/java/util/FormatterClosedException.html#FormatterClosedException%2D%2D[FormatterClosedException]()++ (link:{java9-javadoc}/java/util/FormatterClosedException.html#FormatterClosedException%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Function.asciidoc b/docs/painless/painless-api-reference/Function.asciidoc new file mode 100644 index 0000000000000..61fe3b6b5da70 --- /dev/null +++ b/docs/painless/painless-api-reference/Function.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Function]]++Function++:: +* ++[[painless-api-reference-Function-identity-0]]static <> link:{java8-javadoc}/java/util/function/Function.html#identity%2D%2D[identity]()++ (link:{java9-javadoc}/java/util/function/Function.html#identity%2D%2D[java 9]) +* ++[[painless-api-reference-Function-andThen-1]]<> link:{java8-javadoc}/java/util/function/Function.html#andThen%2Djava.util.function.Function%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/Function.html#andThen%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-Function-apply-1]]def link:{java8-javadoc}/java/util/function/Function.html#apply%2Djava.lang.Object%2D[apply](def)++ (link:{java9-javadoc}/java/util/function/Function.html#apply%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Function-compose-1]]<> link:{java8-javadoc}/java/util/function/Function.html#compose%2Djava.util.function.Function%2D[compose](<>)++ (link:{java9-javadoc}/java/util/function/Function.html#compose%2Djava.util.function.Function%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/GregorianCalendar.asciidoc b/docs/painless/painless-api-reference/GregorianCalendar.asciidoc new file mode 100644 index 0000000000000..8f7b9ccecc7e6 --- /dev/null +++ b/docs/painless/painless-api-reference/GregorianCalendar.asciidoc @@ -0,0 +1,20 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-GregorianCalendar]]++GregorianCalendar++:: +** [[painless-api-reference-GregorianCalendar-AD]]static int link:{java8-javadoc}/java/util/GregorianCalendar.html#AD[AD] (link:{java9-javadoc}/java/util/GregorianCalendar.html#AD[java 9]) +** [[painless-api-reference-GregorianCalendar-BC]]static int link:{java8-javadoc}/java/util/GregorianCalendar.html#BC[BC] (link:{java9-javadoc}/java/util/GregorianCalendar.html#BC[java 9]) +* ++[[painless-api-reference-GregorianCalendar-from-1]]static <> link:{java8-javadoc}/java/util/GregorianCalendar.html#from%2Djava.time.ZonedDateTime%2D[from](<>)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#from%2Djava.time.ZonedDateTime%2D[java 9]) +* ++[[painless-api-reference-GregorianCalendar-GregorianCalendar-0]]link:{java8-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2D%2D[GregorianCalendar]()++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2D%2D[java 9]) +* ++[[painless-api-reference-GregorianCalendar-GregorianCalendar-1]]link:{java8-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Djava.util.TimeZone%2D[GregorianCalendar](<>)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Djava.util.TimeZone%2D[java 9]) +* ++[[painless-api-reference-GregorianCalendar-GregorianCalendar-2]]link:{java8-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Djava.util.TimeZone%2Djava.util.Locale%2D[GregorianCalendar](<>, <>)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Djava.util.TimeZone%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-GregorianCalendar-GregorianCalendar-3]]link:{java8-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Dint%2Dint%2Dint%2D[GregorianCalendar](int, int, int)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-GregorianCalendar-GregorianCalendar-5]]link:{java8-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Dint%2Dint%2Dint%2Dint%2Dint%2D[GregorianCalendar](int, int, int, int, int)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-GregorianCalendar-GregorianCalendar-6]]link:{java8-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[GregorianCalendar](int, int, int, int, int, int)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#GregorianCalendar%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-GregorianCalendar-getGregorianChange-0]]<> link:{java8-javadoc}/java/util/GregorianCalendar.html#getGregorianChange%2D%2D[getGregorianChange]()++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#getGregorianChange%2D%2D[java 9]) +* ++[[painless-api-reference-GregorianCalendar-isLeapYear-1]]boolean link:{java8-javadoc}/java/util/GregorianCalendar.html#isLeapYear%2Dint%2D[isLeapYear](int)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#isLeapYear%2Dint%2D[java 9]) +* ++[[painless-api-reference-GregorianCalendar-setGregorianChange-1]]void link:{java8-javadoc}/java/util/GregorianCalendar.html#setGregorianChange%2Djava.util.Date%2D[setGregorianChange](<>)++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#setGregorianChange%2Djava.util.Date%2D[java 9]) +* ++[[painless-api-reference-GregorianCalendar-toZonedDateTime-0]]<> link:{java8-javadoc}/java/util/GregorianCalendar.html#toZonedDateTime%2D%2D[toZonedDateTime]()++ (link:{java9-javadoc}/java/util/GregorianCalendar.html#toZonedDateTime%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/HashMap.asciidoc b/docs/painless/painless-api-reference/HashMap.asciidoc new file mode 100644 index 0000000000000..f67e75d702f0c --- /dev/null +++ b/docs/painless/painless-api-reference/HashMap.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-HashMap]]++HashMap++:: +* ++[[painless-api-reference-HashMap-HashMap-0]]link:{java8-javadoc}/java/util/HashMap.html#HashMap%2D%2D[HashMap]()++ (link:{java9-javadoc}/java/util/HashMap.html#HashMap%2D%2D[java 9]) +* ++[[painless-api-reference-HashMap-HashMap-1]]link:{java8-javadoc}/java/util/HashMap.html#HashMap%2Djava.util.Map%2D[HashMap](<>)++ (link:{java9-javadoc}/java/util/HashMap.html#HashMap%2Djava.util.Map%2D[java 9]) +* ++[[painless-api-reference-HashMap-clone-0]]def link:{java8-javadoc}/java/util/HashMap.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/HashMap.html#clone%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/HashSet.asciidoc b/docs/painless/painless-api-reference/HashSet.asciidoc new file mode 100644 index 0000000000000..af4171def2262 --- /dev/null +++ b/docs/painless/painless-api-reference/HashSet.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-HashSet]]++HashSet++:: +* ++[[painless-api-reference-HashSet-HashSet-0]]link:{java8-javadoc}/java/util/HashSet.html#HashSet%2D%2D[HashSet]()++ (link:{java9-javadoc}/java/util/HashSet.html#HashSet%2D%2D[java 9]) +* ++[[painless-api-reference-HashSet-HashSet-1]]link:{java8-javadoc}/java/util/HashSet.html#HashSet%2Djava.util.Collection%2D[HashSet](<>)++ (link:{java9-javadoc}/java/util/HashSet.html#HashSet%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-HashSet-clone-0]]def link:{java8-javadoc}/java/util/HashSet.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/HashSet.html#clone%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Hashtable.asciidoc b/docs/painless/painless-api-reference/Hashtable.asciidoc new file mode 100644 index 0000000000000..34702e8466065 --- /dev/null +++ b/docs/painless/painless-api-reference/Hashtable.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Hashtable]]++Hashtable++:: +* ++[[painless-api-reference-Hashtable-Hashtable-0]]link:{java8-javadoc}/java/util/Hashtable.html#Hashtable%2D%2D[Hashtable]()++ (link:{java9-javadoc}/java/util/Hashtable.html#Hashtable%2D%2D[java 9]) +* ++[[painless-api-reference-Hashtable-Hashtable-1]]link:{java8-javadoc}/java/util/Hashtable.html#Hashtable%2Djava.util.Map%2D[Hashtable](<>)++ (link:{java9-javadoc}/java/util/Hashtable.html#Hashtable%2Djava.util.Map%2D[java 9]) +* ++[[painless-api-reference-Hashtable-clone-0]]def link:{java8-javadoc}/java/util/Hashtable.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/Hashtable.html#clone%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/HijrahChronology.asciidoc b/docs/painless/painless-api-reference/HijrahChronology.asciidoc new file mode 100644 index 0000000000000..b7577fbdfd720 --- /dev/null +++ b/docs/painless/painless-api-reference/HijrahChronology.asciidoc @@ -0,0 +1,16 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-HijrahChronology]]++HijrahChronology++:: +** [[painless-api-reference-HijrahChronology-INSTANCE]]static <> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#INSTANCE[INSTANCE] (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#INSTANCE[java 9]) +* ++[[painless-api-reference-HijrahChronology-date-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[date](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-HijrahChronology-date-3]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#date%2Dint%2Dint%2Dint%2D[date](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#date%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-HijrahChronology-date-4]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[date](<>, int, int, int)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-HijrahChronology-dateEpochDay-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#dateEpochDay%2Dlong%2D[dateEpochDay](long)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#dateEpochDay%2Dlong%2D[java 9]) +* ++[[painless-api-reference-HijrahChronology-dateYearDay-2]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#dateYearDay%2Dint%2Dint%2D[dateYearDay](int, int)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#dateYearDay%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-HijrahChronology-dateYearDay-3]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[dateYearDay](<>, int, int)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-HijrahChronology-eraOf-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#eraOf%2Dint%2D[eraOf](int)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#eraOf%2Dint%2D[java 9]) +* ++[[painless-api-reference-HijrahChronology-resolveDate-2]]<> link:{java8-javadoc}/java/time/chrono/HijrahChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[resolveDate](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/HijrahChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/HijrahDate.asciidoc b/docs/painless/painless-api-reference/HijrahDate.asciidoc new file mode 100644 index 0000000000000..09ebf31682a13 --- /dev/null +++ b/docs/painless/painless-api-reference/HijrahDate.asciidoc @@ -0,0 +1,18 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-HijrahDate]]++HijrahDate++:: +* ++[[painless-api-reference-HijrahDate-from-1]]static <> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-HijrahDate-of-3]]static <> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#of%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-HijrahDate-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#getChronology%2D%2D[java 9]) +* ++[[painless-api-reference-HijrahDate-getEra-0]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#getEra%2D%2D[getEra]()++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#getEra%2D%2D[java 9]) +* ++[[painless-api-reference-HijrahDate-minus-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-HijrahDate-minus-2]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-HijrahDate-plus-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-HijrahDate-plus-2]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-HijrahDate-with-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-HijrahDate-with-2]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* ++[[painless-api-reference-HijrahDate-withVariant-1]]<> link:{java8-javadoc}/java/time/chrono/HijrahDate.html#withVariant%2Djava.time.chrono.HijrahChronology%2D[withVariant](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahDate.html#withVariant%2Djava.time.chrono.HijrahChronology%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/HijrahEra.asciidoc b/docs/painless/painless-api-reference/HijrahEra.asciidoc new file mode 100644 index 0000000000000..9bfe561d2fa58 --- /dev/null +++ b/docs/painless/painless-api-reference/HijrahEra.asciidoc @@ -0,0 +1,12 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-HijrahEra]]++HijrahEra++:: +** [[painless-api-reference-HijrahEra-AH]]static <> link:{java8-javadoc}/java/time/chrono/HijrahEra.html#AH[AH] (link:{java9-javadoc}/java/time/chrono/HijrahEra.html#AH[java 9]) +* ++[[painless-api-reference-HijrahEra-of-1]]static <> link:{java8-javadoc}/java/time/chrono/HijrahEra.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/chrono/HijrahEra.html#of%2Dint%2D[java 9]) +* ++[[painless-api-reference-HijrahEra-valueOf-1]]static <> link:{java8-javadoc}/java/time/chrono/HijrahEra.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/chrono/HijrahEra.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-HijrahEra-values-0]]static <>[] link:{java8-javadoc}/java/time/chrono/HijrahEra.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/chrono/HijrahEra.html#values%2D%2D[java 9]) +* ++[[painless-api-reference-HijrahEra-getValue-0]]int link:{java8-javadoc}/java/time/chrono/HijrahEra.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/chrono/HijrahEra.html#getValue%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IdentityHashMap.asciidoc b/docs/painless/painless-api-reference/IdentityHashMap.asciidoc new file mode 100644 index 0000000000000..050d0eaeb1c8e --- /dev/null +++ b/docs/painless/painless-api-reference/IdentityHashMap.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IdentityHashMap]]++IdentityHashMap++:: +* ++[[painless-api-reference-IdentityHashMap-IdentityHashMap-0]]link:{java8-javadoc}/java/util/IdentityHashMap.html#IdentityHashMap%2D%2D[IdentityHashMap]()++ (link:{java9-javadoc}/java/util/IdentityHashMap.html#IdentityHashMap%2D%2D[java 9]) +* ++[[painless-api-reference-IdentityHashMap-IdentityHashMap-1]]link:{java8-javadoc}/java/util/IdentityHashMap.html#IdentityHashMap%2Djava.util.Map%2D[IdentityHashMap](<>)++ (link:{java9-javadoc}/java/util/IdentityHashMap.html#IdentityHashMap%2Djava.util.Map%2D[java 9]) +* ++[[painless-api-reference-IdentityHashMap-clone-0]]def link:{java8-javadoc}/java/util/IdentityHashMap.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/IdentityHashMap.html#clone%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IllegalAccessException.asciidoc b/docs/painless/painless-api-reference/IllegalAccessException.asciidoc new file mode 100644 index 0000000000000..30c8ba4536d02 --- /dev/null +++ b/docs/painless/painless-api-reference/IllegalAccessException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IllegalAccessException]]++IllegalAccessException++:: +* ++[[painless-api-reference-IllegalAccessException-IllegalAccessException-0]]link:{java8-javadoc}/java/lang/IllegalAccessException.html#IllegalAccessException%2D%2D[IllegalAccessException]()++ (link:{java9-javadoc}/java/lang/IllegalAccessException.html#IllegalAccessException%2D%2D[java 9]) +* ++[[painless-api-reference-IllegalAccessException-IllegalAccessException-1]]link:{java8-javadoc}/java/lang/IllegalAccessException.html#IllegalAccessException%2Djava.lang.String%2D[IllegalAccessException](<>)++ (link:{java9-javadoc}/java/lang/IllegalAccessException.html#IllegalAccessException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IllegalArgumentException.asciidoc b/docs/painless/painless-api-reference/IllegalArgumentException.asciidoc new file mode 100644 index 0000000000000..538fa46a656e2 --- /dev/null +++ b/docs/painless/painless-api-reference/IllegalArgumentException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IllegalArgumentException]]++IllegalArgumentException++:: +* ++[[painless-api-reference-IllegalArgumentException-IllegalArgumentException-0]]link:{java8-javadoc}/java/lang/IllegalArgumentException.html#IllegalArgumentException%2D%2D[IllegalArgumentException]()++ (link:{java9-javadoc}/java/lang/IllegalArgumentException.html#IllegalArgumentException%2D%2D[java 9]) +* ++[[painless-api-reference-IllegalArgumentException-IllegalArgumentException-1]]link:{java8-javadoc}/java/lang/IllegalArgumentException.html#IllegalArgumentException%2Djava.lang.String%2D[IllegalArgumentException](<>)++ (link:{java9-javadoc}/java/lang/IllegalArgumentException.html#IllegalArgumentException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IllegalFormatCodePointException.asciidoc b/docs/painless/painless-api-reference/IllegalFormatCodePointException.asciidoc new file mode 100644 index 0000000000000..2a533384198d1 --- /dev/null +++ b/docs/painless/painless-api-reference/IllegalFormatCodePointException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IllegalFormatCodePointException]]++IllegalFormatCodePointException++:: +* ++[[painless-api-reference-IllegalFormatCodePointException-IllegalFormatCodePointException-1]]link:{java8-javadoc}/java/util/IllegalFormatCodePointException.html#IllegalFormatCodePointException%2Dint%2D[IllegalFormatCodePointException](int)++ (link:{java9-javadoc}/java/util/IllegalFormatCodePointException.html#IllegalFormatCodePointException%2Dint%2D[java 9]) +* ++[[painless-api-reference-IllegalFormatCodePointException-getCodePoint-0]]int link:{java8-javadoc}/java/util/IllegalFormatCodePointException.html#getCodePoint%2D%2D[getCodePoint]()++ (link:{java9-javadoc}/java/util/IllegalFormatCodePointException.html#getCodePoint%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IllegalFormatConversionException.asciidoc b/docs/painless/painless-api-reference/IllegalFormatConversionException.asciidoc new file mode 100644 index 0000000000000..2a36b5367a339 --- /dev/null +++ b/docs/painless/painless-api-reference/IllegalFormatConversionException.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IllegalFormatConversionException]]++IllegalFormatConversionException++:: +* ++[[painless-api-reference-IllegalFormatConversionException-getConversion-0]]char link:{java8-javadoc}/java/util/IllegalFormatConversionException.html#getConversion%2D%2D[getConversion]()++ (link:{java9-javadoc}/java/util/IllegalFormatConversionException.html#getConversion%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IllegalFormatException.asciidoc b/docs/painless/painless-api-reference/IllegalFormatException.asciidoc new file mode 100644 index 0000000000000..98a58ceddd265 --- /dev/null +++ b/docs/painless/painless-api-reference/IllegalFormatException.asciidoc @@ -0,0 +1,7 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IllegalFormatException]]++IllegalFormatException++:: +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IllegalFormatFlagsException.asciidoc b/docs/painless/painless-api-reference/IllegalFormatFlagsException.asciidoc new file mode 100644 index 0000000000000..9ed7810f0e60f --- /dev/null +++ b/docs/painless/painless-api-reference/IllegalFormatFlagsException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IllegalFormatFlagsException]]++IllegalFormatFlagsException++:: +* ++[[painless-api-reference-IllegalFormatFlagsException-IllegalFormatFlagsException-1]]link:{java8-javadoc}/java/util/IllegalFormatFlagsException.html#IllegalFormatFlagsException%2Djava.lang.String%2D[IllegalFormatFlagsException](<>)++ (link:{java9-javadoc}/java/util/IllegalFormatFlagsException.html#IllegalFormatFlagsException%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-IllegalFormatFlagsException-getFlags-0]]<> link:{java8-javadoc}/java/util/IllegalFormatFlagsException.html#getFlags%2D%2D[getFlags]()++ (link:{java9-javadoc}/java/util/IllegalFormatFlagsException.html#getFlags%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IllegalFormatPrecisionException.asciidoc b/docs/painless/painless-api-reference/IllegalFormatPrecisionException.asciidoc new file mode 100644 index 0000000000000..318b80dcfaa6c --- /dev/null +++ b/docs/painless/painless-api-reference/IllegalFormatPrecisionException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IllegalFormatPrecisionException]]++IllegalFormatPrecisionException++:: +* ++[[painless-api-reference-IllegalFormatPrecisionException-IllegalFormatPrecisionException-1]]link:{java8-javadoc}/java/util/IllegalFormatPrecisionException.html#IllegalFormatPrecisionException%2Dint%2D[IllegalFormatPrecisionException](int)++ (link:{java9-javadoc}/java/util/IllegalFormatPrecisionException.html#IllegalFormatPrecisionException%2Dint%2D[java 9]) +* ++[[painless-api-reference-IllegalFormatPrecisionException-getPrecision-0]]int link:{java8-javadoc}/java/util/IllegalFormatPrecisionException.html#getPrecision%2D%2D[getPrecision]()++ (link:{java9-javadoc}/java/util/IllegalFormatPrecisionException.html#getPrecision%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IllegalFormatWidthException.asciidoc b/docs/painless/painless-api-reference/IllegalFormatWidthException.asciidoc new file mode 100644 index 0000000000000..96f008b4a5334 --- /dev/null +++ b/docs/painless/painless-api-reference/IllegalFormatWidthException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IllegalFormatWidthException]]++IllegalFormatWidthException++:: +* ++[[painless-api-reference-IllegalFormatWidthException-IllegalFormatWidthException-1]]link:{java8-javadoc}/java/util/IllegalFormatWidthException.html#IllegalFormatWidthException%2Dint%2D[IllegalFormatWidthException](int)++ (link:{java9-javadoc}/java/util/IllegalFormatWidthException.html#IllegalFormatWidthException%2Dint%2D[java 9]) +* ++[[painless-api-reference-IllegalFormatWidthException-getWidth-0]]int link:{java8-javadoc}/java/util/IllegalFormatWidthException.html#getWidth%2D%2D[getWidth]()++ (link:{java9-javadoc}/java/util/IllegalFormatWidthException.html#getWidth%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IllegalMonitorStateException.asciidoc b/docs/painless/painless-api-reference/IllegalMonitorStateException.asciidoc new file mode 100644 index 0000000000000..057c58f948b73 --- /dev/null +++ b/docs/painless/painless-api-reference/IllegalMonitorStateException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IllegalMonitorStateException]]++IllegalMonitorStateException++:: +* ++[[painless-api-reference-IllegalMonitorStateException-IllegalMonitorStateException-0]]link:{java8-javadoc}/java/lang/IllegalMonitorStateException.html#IllegalMonitorStateException%2D%2D[IllegalMonitorStateException]()++ (link:{java9-javadoc}/java/lang/IllegalMonitorStateException.html#IllegalMonitorStateException%2D%2D[java 9]) +* ++[[painless-api-reference-IllegalMonitorStateException-IllegalMonitorStateException-1]]link:{java8-javadoc}/java/lang/IllegalMonitorStateException.html#IllegalMonitorStateException%2Djava.lang.String%2D[IllegalMonitorStateException](<>)++ (link:{java9-javadoc}/java/lang/IllegalMonitorStateException.html#IllegalMonitorStateException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IllegalStateException.asciidoc b/docs/painless/painless-api-reference/IllegalStateException.asciidoc new file mode 100644 index 0000000000000..fa8ddf72fa5bd --- /dev/null +++ b/docs/painless/painless-api-reference/IllegalStateException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IllegalStateException]]++IllegalStateException++:: +* ++[[painless-api-reference-IllegalStateException-IllegalStateException-0]]link:{java8-javadoc}/java/lang/IllegalStateException.html#IllegalStateException%2D%2D[IllegalStateException]()++ (link:{java9-javadoc}/java/lang/IllegalStateException.html#IllegalStateException%2D%2D[java 9]) +* ++[[painless-api-reference-IllegalStateException-IllegalStateException-1]]link:{java8-javadoc}/java/lang/IllegalStateException.html#IllegalStateException%2Djava.lang.String%2D[IllegalStateException](<>)++ (link:{java9-javadoc}/java/lang/IllegalStateException.html#IllegalStateException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IllegalThreadStateException.asciidoc b/docs/painless/painless-api-reference/IllegalThreadStateException.asciidoc new file mode 100644 index 0000000000000..5fc8293fe0933 --- /dev/null +++ b/docs/painless/painless-api-reference/IllegalThreadStateException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IllegalThreadStateException]]++IllegalThreadStateException++:: +* ++[[painless-api-reference-IllegalThreadStateException-IllegalThreadStateException-0]]link:{java8-javadoc}/java/lang/IllegalThreadStateException.html#IllegalThreadStateException%2D%2D[IllegalThreadStateException]()++ (link:{java9-javadoc}/java/lang/IllegalThreadStateException.html#IllegalThreadStateException%2D%2D[java 9]) +* ++[[painless-api-reference-IllegalThreadStateException-IllegalThreadStateException-1]]link:{java8-javadoc}/java/lang/IllegalThreadStateException.html#IllegalThreadStateException%2Djava.lang.String%2D[IllegalThreadStateException](<>)++ (link:{java9-javadoc}/java/lang/IllegalThreadStateException.html#IllegalThreadStateException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IllformedLocaleException.asciidoc b/docs/painless/painless-api-reference/IllformedLocaleException.asciidoc new file mode 100644 index 0000000000000..59a814d9520dd --- /dev/null +++ b/docs/painless/painless-api-reference/IllformedLocaleException.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IllformedLocaleException]]++IllformedLocaleException++:: +* ++[[painless-api-reference-IllformedLocaleException-IllformedLocaleException-0]]link:{java8-javadoc}/java/util/IllformedLocaleException.html#IllformedLocaleException%2D%2D[IllformedLocaleException]()++ (link:{java9-javadoc}/java/util/IllformedLocaleException.html#IllformedLocaleException%2D%2D[java 9]) +* ++[[painless-api-reference-IllformedLocaleException-IllformedLocaleException-1]]link:{java8-javadoc}/java/util/IllformedLocaleException.html#IllformedLocaleException%2Djava.lang.String%2D[IllformedLocaleException](<>)++ (link:{java9-javadoc}/java/util/IllformedLocaleException.html#IllformedLocaleException%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-IllformedLocaleException-IllformedLocaleException-2]]link:{java8-javadoc}/java/util/IllformedLocaleException.html#IllformedLocaleException%2Djava.lang.String%2Dint%2D[IllformedLocaleException](<>, int)++ (link:{java9-javadoc}/java/util/IllformedLocaleException.html#IllformedLocaleException%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-IllformedLocaleException-getErrorIndex-0]]int link:{java8-javadoc}/java/util/IllformedLocaleException.html#getErrorIndex%2D%2D[getErrorIndex]()++ (link:{java9-javadoc}/java/util/IllformedLocaleException.html#getErrorIndex%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IndexOutOfBoundsException.asciidoc b/docs/painless/painless-api-reference/IndexOutOfBoundsException.asciidoc new file mode 100644 index 0000000000000..a141ec6b04152 --- /dev/null +++ b/docs/painless/painless-api-reference/IndexOutOfBoundsException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IndexOutOfBoundsException]]++IndexOutOfBoundsException++:: +* ++[[painless-api-reference-IndexOutOfBoundsException-IndexOutOfBoundsException-0]]link:{java8-javadoc}/java/lang/IndexOutOfBoundsException.html#IndexOutOfBoundsException%2D%2D[IndexOutOfBoundsException]()++ (link:{java9-javadoc}/java/lang/IndexOutOfBoundsException.html#IndexOutOfBoundsException%2D%2D[java 9]) +* ++[[painless-api-reference-IndexOutOfBoundsException-IndexOutOfBoundsException-1]]link:{java8-javadoc}/java/lang/IndexOutOfBoundsException.html#IndexOutOfBoundsException%2Djava.lang.String%2D[IndexOutOfBoundsException](<>)++ (link:{java9-javadoc}/java/lang/IndexOutOfBoundsException.html#IndexOutOfBoundsException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/InputMismatchException.asciidoc b/docs/painless/painless-api-reference/InputMismatchException.asciidoc new file mode 100644 index 0000000000000..9497cf9486e03 --- /dev/null +++ b/docs/painless/painless-api-reference/InputMismatchException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-InputMismatchException]]++InputMismatchException++:: +* ++[[painless-api-reference-InputMismatchException-InputMismatchException-0]]link:{java8-javadoc}/java/util/InputMismatchException.html#InputMismatchException%2D%2D[InputMismatchException]()++ (link:{java9-javadoc}/java/util/InputMismatchException.html#InputMismatchException%2D%2D[java 9]) +* ++[[painless-api-reference-InputMismatchException-InputMismatchException-1]]link:{java8-javadoc}/java/util/InputMismatchException.html#InputMismatchException%2Djava.lang.String%2D[InputMismatchException](<>)++ (link:{java9-javadoc}/java/util/InputMismatchException.html#InputMismatchException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Instant.asciidoc b/docs/painless/painless-api-reference/Instant.asciidoc new file mode 100644 index 0000000000000..4a7ffe45d991c --- /dev/null +++ b/docs/painless/painless-api-reference/Instant.asciidoc @@ -0,0 +1,36 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Instant]]++Instant++:: +** [[painless-api-reference-Instant-EPOCH]]static <> link:{java8-javadoc}/java/time/Instant.html#EPOCH[EPOCH] (link:{java9-javadoc}/java/time/Instant.html#EPOCH[java 9]) +** [[painless-api-reference-Instant-MAX]]static <> link:{java8-javadoc}/java/time/Instant.html#MAX[MAX] (link:{java9-javadoc}/java/time/Instant.html#MAX[java 9]) +** [[painless-api-reference-Instant-MIN]]static <> link:{java8-javadoc}/java/time/Instant.html#MIN[MIN] (link:{java9-javadoc}/java/time/Instant.html#MIN[java 9]) +* ++[[painless-api-reference-Instant-from-1]]static <> link:{java8-javadoc}/java/time/Instant.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/Instant.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-Instant-ofEpochMilli-1]]static <> link:{java8-javadoc}/java/time/Instant.html#ofEpochMilli%2Dlong%2D[ofEpochMilli](long)++ (link:{java9-javadoc}/java/time/Instant.html#ofEpochMilli%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Instant-ofEpochSecond-1]]static <> link:{java8-javadoc}/java/time/Instant.html#ofEpochSecond%2Dlong%2D[ofEpochSecond](long)++ (link:{java9-javadoc}/java/time/Instant.html#ofEpochSecond%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Instant-ofEpochSecond-2]]static <> link:{java8-javadoc}/java/time/Instant.html#ofEpochSecond%2Dlong%2Dlong%2D[ofEpochSecond](long, long)++ (link:{java9-javadoc}/java/time/Instant.html#ofEpochSecond%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Instant-parse-1]]static <> link:{java8-javadoc}/java/time/Instant.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/Instant.html#parse%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-Instant-atOffset-1]]<> link:{java8-javadoc}/java/time/Instant.html#atOffset%2Djava.time.ZoneOffset%2D[atOffset](<>)++ (link:{java9-javadoc}/java/time/Instant.html#atOffset%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-Instant-atZone-1]]<> link:{java8-javadoc}/java/time/Instant.html#atZone%2Djava.time.ZoneId%2D[atZone](<>)++ (link:{java9-javadoc}/java/time/Instant.html#atZone%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-Instant-compareTo-1]]int link:{java8-javadoc}/java/time/Instant.html#compareTo%2Djava.time.Instant%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/Instant.html#compareTo%2Djava.time.Instant%2D[java 9]) +* ++[[painless-api-reference-Instant-getEpochSecond-0]]long link:{java8-javadoc}/java/time/Instant.html#getEpochSecond%2D%2D[getEpochSecond]()++ (link:{java9-javadoc}/java/time/Instant.html#getEpochSecond%2D%2D[java 9]) +* ++[[painless-api-reference-Instant-getNano-0]]int link:{java8-javadoc}/java/time/Instant.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/Instant.html#getNano%2D%2D[java 9]) +* ++[[painless-api-reference-Instant-isAfter-1]]boolean link:{java8-javadoc}/java/time/Instant.html#isAfter%2Djava.time.Instant%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/Instant.html#isAfter%2Djava.time.Instant%2D[java 9]) +* ++[[painless-api-reference-Instant-isBefore-1]]boolean link:{java8-javadoc}/java/time/Instant.html#isBefore%2Djava.time.Instant%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/Instant.html#isBefore%2Djava.time.Instant%2D[java 9]) +* ++[[painless-api-reference-Instant-minus-1]]<> link:{java8-javadoc}/java/time/Instant.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/Instant.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-Instant-minus-2]]<> link:{java8-javadoc}/java/time/Instant.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/Instant.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-Instant-minusMillis-1]]<> link:{java8-javadoc}/java/time/Instant.html#minusMillis%2Dlong%2D[minusMillis](long)++ (link:{java9-javadoc}/java/time/Instant.html#minusMillis%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Instant-minusNanos-1]]<> link:{java8-javadoc}/java/time/Instant.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/Instant.html#minusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Instant-minusSeconds-1]]<> link:{java8-javadoc}/java/time/Instant.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/Instant.html#minusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Instant-plus-1]]<> link:{java8-javadoc}/java/time/Instant.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/Instant.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-Instant-plus-2]]<> link:{java8-javadoc}/java/time/Instant.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/Instant.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-Instant-plusMillis-1]]<> link:{java8-javadoc}/java/time/Instant.html#plusMillis%2Dlong%2D[plusMillis](long)++ (link:{java9-javadoc}/java/time/Instant.html#plusMillis%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Instant-plusNanos-1]]<> link:{java8-javadoc}/java/time/Instant.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/Instant.html#plusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Instant-plusSeconds-1]]<> link:{java8-javadoc}/java/time/Instant.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/Instant.html#plusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Instant-toEpochMilli-0]]long link:{java8-javadoc}/java/time/Instant.html#toEpochMilli%2D%2D[toEpochMilli]()++ (link:{java9-javadoc}/java/time/Instant.html#toEpochMilli%2D%2D[java 9]) +* ++[[painless-api-reference-Instant-truncatedTo-1]]<> link:{java8-javadoc}/java/time/Instant.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[truncatedTo](<>)++ (link:{java9-javadoc}/java/time/Instant.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-Instant-with-1]]<> link:{java8-javadoc}/java/time/Instant.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/Instant.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-Instant-with-2]]<> link:{java8-javadoc}/java/time/Instant.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/Instant.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/InstantiationException.asciidoc b/docs/painless/painless-api-reference/InstantiationException.asciidoc new file mode 100644 index 0000000000000..06d88eb654f00 --- /dev/null +++ b/docs/painless/painless-api-reference/InstantiationException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-InstantiationException]]++InstantiationException++:: +* ++[[painless-api-reference-InstantiationException-InstantiationException-0]]link:{java8-javadoc}/java/lang/InstantiationException.html#InstantiationException%2D%2D[InstantiationException]()++ (link:{java9-javadoc}/java/lang/InstantiationException.html#InstantiationException%2D%2D[java 9]) +* ++[[painless-api-reference-InstantiationException-InstantiationException-1]]link:{java8-javadoc}/java/lang/InstantiationException.html#InstantiationException%2Djava.lang.String%2D[InstantiationException](<>)++ (link:{java9-javadoc}/java/lang/InstantiationException.html#InstantiationException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IntBinaryOperator.asciidoc b/docs/painless/painless-api-reference/IntBinaryOperator.asciidoc new file mode 100644 index 0000000000000..ca3c64ab3d898 --- /dev/null +++ b/docs/painless/painless-api-reference/IntBinaryOperator.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IntBinaryOperator]]++IntBinaryOperator++:: +* ++[[painless-api-reference-IntBinaryOperator-applyAsInt-2]]int link:{java8-javadoc}/java/util/function/IntBinaryOperator.html#applyAsInt%2Dint%2Dint%2D[applyAsInt](int, int)++ (link:{java9-javadoc}/java/util/function/IntBinaryOperator.html#applyAsInt%2Dint%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/IntConsumer.asciidoc b/docs/painless/painless-api-reference/IntConsumer.asciidoc new file mode 100644 index 0000000000000..e8325838dac51 --- /dev/null +++ b/docs/painless/painless-api-reference/IntConsumer.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IntConsumer]]++IntConsumer++:: +* ++[[painless-api-reference-IntConsumer-accept-1]]void link:{java8-javadoc}/java/util/function/IntConsumer.html#accept%2Dint%2D[accept](int)++ (link:{java9-javadoc}/java/util/function/IntConsumer.html#accept%2Dint%2D[java 9]) +* ++[[painless-api-reference-IntConsumer-andThen-1]]<> link:{java8-javadoc}/java/util/function/IntConsumer.html#andThen%2Djava.util.function.IntConsumer%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/IntConsumer.html#andThen%2Djava.util.function.IntConsumer%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/IntFunction.asciidoc b/docs/painless/painless-api-reference/IntFunction.asciidoc new file mode 100644 index 0000000000000..336c1f7c3cb70 --- /dev/null +++ b/docs/painless/painless-api-reference/IntFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IntFunction]]++IntFunction++:: +* ++[[painless-api-reference-IntFunction-apply-1]]def link:{java8-javadoc}/java/util/function/IntFunction.html#apply%2Dint%2D[apply](int)++ (link:{java9-javadoc}/java/util/function/IntFunction.html#apply%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/IntPredicate.asciidoc b/docs/painless/painless-api-reference/IntPredicate.asciidoc new file mode 100644 index 0000000000000..a9c45c95af855 --- /dev/null +++ b/docs/painless/painless-api-reference/IntPredicate.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IntPredicate]]++IntPredicate++:: +* ++[[painless-api-reference-IntPredicate-and-1]]<> link:{java8-javadoc}/java/util/function/IntPredicate.html#and%2Djava.util.function.IntPredicate%2D[and](<>)++ (link:{java9-javadoc}/java/util/function/IntPredicate.html#and%2Djava.util.function.IntPredicate%2D[java 9]) +* ++[[painless-api-reference-IntPredicate-negate-0]]<> link:{java8-javadoc}/java/util/function/IntPredicate.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/util/function/IntPredicate.html#negate%2D%2D[java 9]) +* ++[[painless-api-reference-IntPredicate-or-1]]<> link:{java8-javadoc}/java/util/function/IntPredicate.html#or%2Djava.util.function.IntPredicate%2D[or](<>)++ (link:{java9-javadoc}/java/util/function/IntPredicate.html#or%2Djava.util.function.IntPredicate%2D[java 9]) +* ++[[painless-api-reference-IntPredicate-test-1]]boolean link:{java8-javadoc}/java/util/function/IntPredicate.html#test%2Dint%2D[test](int)++ (link:{java9-javadoc}/java/util/function/IntPredicate.html#test%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/IntStream.Builder.asciidoc b/docs/painless/painless-api-reference/IntStream.Builder.asciidoc new file mode 100644 index 0000000000000..d99913881733a --- /dev/null +++ b/docs/painless/painless-api-reference/IntStream.Builder.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IntStream-Builder]]++IntStream.Builder++:: +* ++[[painless-api-reference-IntStream-Builder-add-1]]<> link:{java8-javadoc}/java/util/stream/IntStream$Builder.html#add%2Dint%2D[add](int)++ (link:{java9-javadoc}/java/util/stream/IntStream$Builder.html#add%2Dint%2D[java 9]) +* ++[[painless-api-reference-IntStream-Builder-build-0]]<> link:{java8-javadoc}/java/util/stream/IntStream$Builder.html#build%2D%2D[build]()++ (link:{java9-javadoc}/java/util/stream/IntStream$Builder.html#build%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IntStream.asciidoc b/docs/painless/painless-api-reference/IntStream.asciidoc new file mode 100644 index 0000000000000..10e1149268986 --- /dev/null +++ b/docs/painless/painless-api-reference/IntStream.asciidoc @@ -0,0 +1,47 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IntStream]]++IntStream++:: +* ++[[painless-api-reference-IntStream-builder-0]]static <> link:{java8-javadoc}/java/util/stream/IntStream.html#builder%2D%2D[builder]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#builder%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-concat-2]]static <> link:{java8-javadoc}/java/util/stream/IntStream.html#concat%2Djava.util.stream.IntStream%2Djava.util.stream.IntStream%2D[concat](<>, <>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#concat%2Djava.util.stream.IntStream%2Djava.util.stream.IntStream%2D[java 9]) +* ++[[painless-api-reference-IntStream-empty-0]]static <> link:{java8-javadoc}/java/util/stream/IntStream.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#empty%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-of-1]]static <> link:{java8-javadoc}/java/util/stream/IntStream.html#of%2Dint:A%2D[of](int[])++ (link:{java9-javadoc}/java/util/stream/IntStream.html#of%2Dint:A%2D[java 9]) +* ++[[painless-api-reference-IntStream-range-2]]static <> link:{java8-javadoc}/java/util/stream/IntStream.html#range%2Dint%2Dint%2D[range](int, int)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#range%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-IntStream-rangeClosed-2]]static <> link:{java8-javadoc}/java/util/stream/IntStream.html#rangeClosed%2Dint%2Dint%2D[rangeClosed](int, int)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#rangeClosed%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-IntStream-allMatch-1]]boolean link:{java8-javadoc}/java/util/stream/IntStream.html#allMatch%2Djava.util.function.IntPredicate%2D[allMatch](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#allMatch%2Djava.util.function.IntPredicate%2D[java 9]) +* ++[[painless-api-reference-IntStream-anyMatch-1]]boolean link:{java8-javadoc}/java/util/stream/IntStream.html#anyMatch%2Djava.util.function.IntPredicate%2D[anyMatch](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#anyMatch%2Djava.util.function.IntPredicate%2D[java 9]) +* ++[[painless-api-reference-IntStream-asDoubleStream-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#asDoubleStream%2D%2D[asDoubleStream]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#asDoubleStream%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-asLongStream-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#asLongStream%2D%2D[asLongStream]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#asLongStream%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-average-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#average%2D%2D[average]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#average%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-boxed-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#boxed%2D%2D[boxed]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#boxed%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-collect-3]]def link:{java8-javadoc}/java/util/stream/IntStream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.ObjIntConsumer%2Djava.util.function.BiConsumer%2D[collect](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.ObjIntConsumer%2Djava.util.function.BiConsumer%2D[java 9]) +* ++[[painless-api-reference-IntStream-count-0]]long link:{java8-javadoc}/java/util/stream/IntStream.html#count%2D%2D[count]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#count%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-distinct-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#distinct%2D%2D[distinct]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#distinct%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-filter-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#filter%2Djava.util.function.IntPredicate%2D[filter](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#filter%2Djava.util.function.IntPredicate%2D[java 9]) +* ++[[painless-api-reference-IntStream-findAny-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#findAny%2D%2D[findAny]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#findAny%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-findFirst-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#findFirst%2D%2D[findFirst]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#findFirst%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-flatMap-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#flatMap%2Djava.util.function.IntFunction%2D[flatMap](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#flatMap%2Djava.util.function.IntFunction%2D[java 9]) +* ++[[painless-api-reference-IntStream-forEach-1]]void link:{java8-javadoc}/java/util/stream/IntStream.html#forEach%2Djava.util.function.IntConsumer%2D[forEach](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#forEach%2Djava.util.function.IntConsumer%2D[java 9]) +* ++[[painless-api-reference-IntStream-forEachOrdered-1]]void link:{java8-javadoc}/java/util/stream/IntStream.html#forEachOrdered%2Djava.util.function.IntConsumer%2D[forEachOrdered](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#forEachOrdered%2Djava.util.function.IntConsumer%2D[java 9]) +* ++[[painless-api-reference-IntStream-iterator-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#iterator%2D%2D[iterator]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#iterator%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-limit-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#limit%2Dlong%2D[limit](long)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#limit%2Dlong%2D[java 9]) +* ++[[painless-api-reference-IntStream-map-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#map%2Djava.util.function.IntUnaryOperator%2D[map](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#map%2Djava.util.function.IntUnaryOperator%2D[java 9]) +* ++[[painless-api-reference-IntStream-mapToDouble-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#mapToDouble%2Djava.util.function.IntToDoubleFunction%2D[mapToDouble](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#mapToDouble%2Djava.util.function.IntToDoubleFunction%2D[java 9]) +* ++[[painless-api-reference-IntStream-mapToLong-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#mapToLong%2Djava.util.function.IntToLongFunction%2D[mapToLong](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#mapToLong%2Djava.util.function.IntToLongFunction%2D[java 9]) +* ++[[painless-api-reference-IntStream-mapToObj-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#mapToObj%2Djava.util.function.IntFunction%2D[mapToObj](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#mapToObj%2Djava.util.function.IntFunction%2D[java 9]) +* ++[[painless-api-reference-IntStream-max-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#max%2D%2D[max]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#max%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-min-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#min%2D%2D[min]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#min%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-noneMatch-1]]boolean link:{java8-javadoc}/java/util/stream/IntStream.html#noneMatch%2Djava.util.function.IntPredicate%2D[noneMatch](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#noneMatch%2Djava.util.function.IntPredicate%2D[java 9]) +* ++[[painless-api-reference-IntStream-peek-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#peek%2Djava.util.function.IntConsumer%2D[peek](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#peek%2Djava.util.function.IntConsumer%2D[java 9]) +* ++[[painless-api-reference-IntStream-reduce-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#reduce%2Djava.util.function.IntBinaryOperator%2D[reduce](<>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#reduce%2Djava.util.function.IntBinaryOperator%2D[java 9]) +* ++[[painless-api-reference-IntStream-reduce-2]]int link:{java8-javadoc}/java/util/stream/IntStream.html#reduce%2Dint%2Djava.util.function.IntBinaryOperator%2D[reduce](int, <>)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#reduce%2Dint%2Djava.util.function.IntBinaryOperator%2D[java 9]) +* ++[[painless-api-reference-IntStream-sequential-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#sequential%2D%2D[sequential]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#sequential%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-skip-1]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#skip%2Dlong%2D[skip](long)++ (link:{java9-javadoc}/java/util/stream/IntStream.html#skip%2Dlong%2D[java 9]) +* ++[[painless-api-reference-IntStream-sorted-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#sorted%2D%2D[sorted]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#sorted%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-spliterator-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#spliterator%2D%2D[spliterator]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#spliterator%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-sum-0]]int link:{java8-javadoc}/java/util/stream/IntStream.html#sum%2D%2D[sum]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#sum%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-summaryStatistics-0]]<> link:{java8-javadoc}/java/util/stream/IntStream.html#summaryStatistics%2D%2D[summaryStatistics]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#summaryStatistics%2D%2D[java 9]) +* ++[[painless-api-reference-IntStream-toArray-0]]int[] link:{java8-javadoc}/java/util/stream/IntStream.html#toArray%2D%2D[toArray]()++ (link:{java9-javadoc}/java/util/stream/IntStream.html#toArray%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IntSummaryStatistics.asciidoc b/docs/painless/painless-api-reference/IntSummaryStatistics.asciidoc new file mode 100644 index 0000000000000..e13d47ad7c4aa --- /dev/null +++ b/docs/painless/painless-api-reference/IntSummaryStatistics.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IntSummaryStatistics]]++IntSummaryStatistics++:: +* ++[[painless-api-reference-IntSummaryStatistics-IntSummaryStatistics-0]]link:{java8-javadoc}/java/util/IntSummaryStatistics.html#IntSummaryStatistics%2D%2D[IntSummaryStatistics]()++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#IntSummaryStatistics%2D%2D[java 9]) +* ++[[painless-api-reference-IntSummaryStatistics-combine-1]]void link:{java8-javadoc}/java/util/IntSummaryStatistics.html#combine%2Djava.util.IntSummaryStatistics%2D[combine](<>)++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#combine%2Djava.util.IntSummaryStatistics%2D[java 9]) +* ++[[painless-api-reference-IntSummaryStatistics-getAverage-0]]double link:{java8-javadoc}/java/util/IntSummaryStatistics.html#getAverage%2D%2D[getAverage]()++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#getAverage%2D%2D[java 9]) +* ++[[painless-api-reference-IntSummaryStatistics-getCount-0]]long link:{java8-javadoc}/java/util/IntSummaryStatistics.html#getCount%2D%2D[getCount]()++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#getCount%2D%2D[java 9]) +* ++[[painless-api-reference-IntSummaryStatistics-getMax-0]]int link:{java8-javadoc}/java/util/IntSummaryStatistics.html#getMax%2D%2D[getMax]()++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#getMax%2D%2D[java 9]) +* ++[[painless-api-reference-IntSummaryStatistics-getMin-0]]int link:{java8-javadoc}/java/util/IntSummaryStatistics.html#getMin%2D%2D[getMin]()++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#getMin%2D%2D[java 9]) +* ++[[painless-api-reference-IntSummaryStatistics-getSum-0]]long link:{java8-javadoc}/java/util/IntSummaryStatistics.html#getSum%2D%2D[getSum]()++ (link:{java9-javadoc}/java/util/IntSummaryStatistics.html#getSum%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IntSupplier.asciidoc b/docs/painless/painless-api-reference/IntSupplier.asciidoc new file mode 100644 index 0000000000000..bca332f7e6cf4 --- /dev/null +++ b/docs/painless/painless-api-reference/IntSupplier.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IntSupplier]]++IntSupplier++:: +* ++[[painless-api-reference-IntSupplier-getAsInt-0]]int link:{java8-javadoc}/java/util/function/IntSupplier.html#getAsInt%2D%2D[getAsInt]()++ (link:{java9-javadoc}/java/util/function/IntSupplier.html#getAsInt%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/IntToDoubleFunction.asciidoc b/docs/painless/painless-api-reference/IntToDoubleFunction.asciidoc new file mode 100644 index 0000000000000..1cfadec16b2d8 --- /dev/null +++ b/docs/painless/painless-api-reference/IntToDoubleFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IntToDoubleFunction]]++IntToDoubleFunction++:: +* ++[[painless-api-reference-IntToDoubleFunction-applyAsDouble-1]]double link:{java8-javadoc}/java/util/function/IntToDoubleFunction.html#applyAsDouble%2Dint%2D[applyAsDouble](int)++ (link:{java9-javadoc}/java/util/function/IntToDoubleFunction.html#applyAsDouble%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/IntToLongFunction.asciidoc b/docs/painless/painless-api-reference/IntToLongFunction.asciidoc new file mode 100644 index 0000000000000..3cffe6cddea16 --- /dev/null +++ b/docs/painless/painless-api-reference/IntToLongFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IntToLongFunction]]++IntToLongFunction++:: +* ++[[painless-api-reference-IntToLongFunction-applyAsLong-1]]long link:{java8-javadoc}/java/util/function/IntToLongFunction.html#applyAsLong%2Dint%2D[applyAsLong](int)++ (link:{java9-javadoc}/java/util/function/IntToLongFunction.html#applyAsLong%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/IntUnaryOperator.asciidoc b/docs/painless/painless-api-reference/IntUnaryOperator.asciidoc new file mode 100644 index 0000000000000..a1bb1651fee56 --- /dev/null +++ b/docs/painless/painless-api-reference/IntUnaryOperator.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IntUnaryOperator]]++IntUnaryOperator++:: +* ++[[painless-api-reference-IntUnaryOperator-identity-0]]static <> link:{java8-javadoc}/java/util/function/IntUnaryOperator.html#identity%2D%2D[identity]()++ (link:{java9-javadoc}/java/util/function/IntUnaryOperator.html#identity%2D%2D[java 9]) +* ++[[painless-api-reference-IntUnaryOperator-andThen-1]]<> link:{java8-javadoc}/java/util/function/IntUnaryOperator.html#andThen%2Djava.util.function.IntUnaryOperator%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/IntUnaryOperator.html#andThen%2Djava.util.function.IntUnaryOperator%2D[java 9]) +* ++[[painless-api-reference-IntUnaryOperator-applyAsInt-1]]int link:{java8-javadoc}/java/util/function/IntUnaryOperator.html#applyAsInt%2Dint%2D[applyAsInt](int)++ (link:{java9-javadoc}/java/util/function/IntUnaryOperator.html#applyAsInt%2Dint%2D[java 9]) +* ++[[painless-api-reference-IntUnaryOperator-compose-1]]<> link:{java8-javadoc}/java/util/function/IntUnaryOperator.html#compose%2Djava.util.function.IntUnaryOperator%2D[compose](<>)++ (link:{java9-javadoc}/java/util/function/IntUnaryOperator.html#compose%2Djava.util.function.IntUnaryOperator%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Integer.asciidoc b/docs/painless/painless-api-reference/Integer.asciidoc new file mode 100644 index 0000000000000..2f608bcc662f7 --- /dev/null +++ b/docs/painless/painless-api-reference/Integer.asciidoc @@ -0,0 +1,44 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Integer]]++Integer++:: +** [[painless-api-reference-Integer-BYTES]]static int link:{java8-javadoc}/java/lang/Integer.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Integer.html#BYTES[java 9]) +** [[painless-api-reference-Integer-MAX_VALUE]]static int link:{java8-javadoc}/java/lang/Integer.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Integer.html#MAX_VALUE[java 9]) +** [[painless-api-reference-Integer-MIN_VALUE]]static int link:{java8-javadoc}/java/lang/Integer.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Integer.html#MIN_VALUE[java 9]) +** [[painless-api-reference-Integer-SIZE]]static int link:{java8-javadoc}/java/lang/Integer.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Integer.html#SIZE[java 9]) +* ++[[painless-api-reference-Integer-bitCount-1]]static int link:{java8-javadoc}/java/lang/Integer.html#bitCount%2Dint%2D[bitCount](int)++ (link:{java9-javadoc}/java/lang/Integer.html#bitCount%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-compare-2]]static int link:{java8-javadoc}/java/lang/Integer.html#compare%2Dint%2Dint%2D[compare](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#compare%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-compareUnsigned-2]]static int link:{java8-javadoc}/java/lang/Integer.html#compareUnsigned%2Dint%2Dint%2D[compareUnsigned](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#compareUnsigned%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-decode-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#decode%2Djava.lang.String%2D[decode](<>)++ (link:{java9-javadoc}/java/lang/Integer.html#decode%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Integer-divideUnsigned-2]]static int link:{java8-javadoc}/java/lang/Integer.html#divideUnsigned%2Dint%2Dint%2D[divideUnsigned](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#divideUnsigned%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-hashCode-1]]static int link:{java8-javadoc}/java/lang/Integer.html#hashCode%2Dint%2D[hashCode](int)++ (link:{java9-javadoc}/java/lang/Integer.html#hashCode%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-highestOneBit-1]]static int link:{java8-javadoc}/java/lang/Integer.html#highestOneBit%2Dint%2D[highestOneBit](int)++ (link:{java9-javadoc}/java/lang/Integer.html#highestOneBit%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-lowestOneBit-1]]static int link:{java8-javadoc}/java/lang/Integer.html#lowestOneBit%2Dint%2D[lowestOneBit](int)++ (link:{java9-javadoc}/java/lang/Integer.html#lowestOneBit%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-max-2]]static int link:{java8-javadoc}/java/lang/Integer.html#max%2Dint%2Dint%2D[max](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#max%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-min-2]]static int link:{java8-javadoc}/java/lang/Integer.html#min%2Dint%2Dint%2D[min](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#min%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-numberOfLeadingZeros-1]]static int link:{java8-javadoc}/java/lang/Integer.html#numberOfLeadingZeros%2Dint%2D[numberOfLeadingZeros](int)++ (link:{java9-javadoc}/java/lang/Integer.html#numberOfLeadingZeros%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-numberOfTrailingZeros-1]]static int link:{java8-javadoc}/java/lang/Integer.html#numberOfTrailingZeros%2Dint%2D[numberOfTrailingZeros](int)++ (link:{java9-javadoc}/java/lang/Integer.html#numberOfTrailingZeros%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-parseInt-1]]static int link:{java8-javadoc}/java/lang/Integer.html#parseInt%2Djava.lang.String%2D[parseInt](<>)++ (link:{java9-javadoc}/java/lang/Integer.html#parseInt%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Integer-parseInt-2]]static int link:{java8-javadoc}/java/lang/Integer.html#parseInt%2Djava.lang.String%2Dint%2D[parseInt](<>, int)++ (link:{java9-javadoc}/java/lang/Integer.html#parseInt%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-parseUnsignedInt-1]]static int link:{java8-javadoc}/java/lang/Integer.html#parseUnsignedInt%2Djava.lang.String%2D[parseUnsignedInt](<>)++ (link:{java9-javadoc}/java/lang/Integer.html#parseUnsignedInt%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Integer-parseUnsignedInt-2]]static int link:{java8-javadoc}/java/lang/Integer.html#parseUnsignedInt%2Djava.lang.String%2Dint%2D[parseUnsignedInt](<>, int)++ (link:{java9-javadoc}/java/lang/Integer.html#parseUnsignedInt%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-remainderUnsigned-2]]static int link:{java8-javadoc}/java/lang/Integer.html#remainderUnsigned%2Dint%2Dint%2D[remainderUnsigned](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#remainderUnsigned%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-reverse-1]]static int link:{java8-javadoc}/java/lang/Integer.html#reverse%2Dint%2D[reverse](int)++ (link:{java9-javadoc}/java/lang/Integer.html#reverse%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-reverseBytes-1]]static int link:{java8-javadoc}/java/lang/Integer.html#reverseBytes%2Dint%2D[reverseBytes](int)++ (link:{java9-javadoc}/java/lang/Integer.html#reverseBytes%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-rotateLeft-2]]static int link:{java8-javadoc}/java/lang/Integer.html#rotateLeft%2Dint%2Dint%2D[rotateLeft](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#rotateLeft%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-rotateRight-2]]static int link:{java8-javadoc}/java/lang/Integer.html#rotateRight%2Dint%2Dint%2D[rotateRight](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#rotateRight%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-signum-1]]static int link:{java8-javadoc}/java/lang/Integer.html#signum%2Dint%2D[signum](int)++ (link:{java9-javadoc}/java/lang/Integer.html#signum%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-toBinaryString-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#toBinaryString%2Dint%2D[toBinaryString](int)++ (link:{java9-javadoc}/java/lang/Integer.html#toBinaryString%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-toHexString-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#toHexString%2Dint%2D[toHexString](int)++ (link:{java9-javadoc}/java/lang/Integer.html#toHexString%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-toOctalString-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#toOctalString%2Dint%2D[toOctalString](int)++ (link:{java9-javadoc}/java/lang/Integer.html#toOctalString%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-toString-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#toString%2Dint%2D[toString](int)++ (link:{java9-javadoc}/java/lang/Integer.html#toString%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-toString-2]]static <> link:{java8-javadoc}/java/lang/Integer.html#toString%2Dint%2Dint%2D[toString](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#toString%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-toUnsignedLong-1]]static long link:{java8-javadoc}/java/lang/Integer.html#toUnsignedLong%2Dint%2D[toUnsignedLong](int)++ (link:{java9-javadoc}/java/lang/Integer.html#toUnsignedLong%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-toUnsignedString-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#toUnsignedString%2Dint%2D[toUnsignedString](int)++ (link:{java9-javadoc}/java/lang/Integer.html#toUnsignedString%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-toUnsignedString-2]]static <> link:{java8-javadoc}/java/lang/Integer.html#toUnsignedString%2Dint%2Dint%2D[toUnsignedString](int, int)++ (link:{java9-javadoc}/java/lang/Integer.html#toUnsignedString%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Integer.html#valueOf%2Dint%2D[valueOf](int)++ (link:{java9-javadoc}/java/lang/Integer.html#valueOf%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-valueOf-2]]static <> link:{java8-javadoc}/java/lang/Integer.html#valueOf%2Djava.lang.String%2Dint%2D[valueOf](<>, int)++ (link:{java9-javadoc}/java/lang/Integer.html#valueOf%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-Integer-compareTo-1]]int link:{java8-javadoc}/java/lang/Integer.html#compareTo%2Djava.lang.Integer%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Integer.html#compareTo%2Djava.lang.Integer%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/InterruptedException.asciidoc b/docs/painless/painless-api-reference/InterruptedException.asciidoc new file mode 100644 index 0000000000000..cd0ef457f76ab --- /dev/null +++ b/docs/painless/painless-api-reference/InterruptedException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-InterruptedException]]++InterruptedException++:: +* ++[[painless-api-reference-InterruptedException-InterruptedException-0]]link:{java8-javadoc}/java/lang/InterruptedException.html#InterruptedException%2D%2D[InterruptedException]()++ (link:{java9-javadoc}/java/lang/InterruptedException.html#InterruptedException%2D%2D[java 9]) +* ++[[painless-api-reference-InterruptedException-InterruptedException-1]]link:{java8-javadoc}/java/lang/InterruptedException.html#InterruptedException%2Djava.lang.String%2D[InterruptedException](<>)++ (link:{java9-javadoc}/java/lang/InterruptedException.html#InterruptedException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IsoChronology.asciidoc b/docs/painless/painless-api-reference/IsoChronology.asciidoc new file mode 100644 index 0000000000000..3990839b041fc --- /dev/null +++ b/docs/painless/painless-api-reference/IsoChronology.asciidoc @@ -0,0 +1,20 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IsoChronology]]++IsoChronology++:: +** [[painless-api-reference-IsoChronology-INSTANCE]]static <> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#INSTANCE[INSTANCE] (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#INSTANCE[java 9]) +* ++[[painless-api-reference-IsoChronology-date-1]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[date](<>)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-IsoChronology-date-3]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#date%2Dint%2Dint%2Dint%2D[date](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#date%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-IsoChronology-date-4]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[date](<>, int, int, int)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-IsoChronology-dateEpochDay-1]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#dateEpochDay%2Dlong%2D[dateEpochDay](long)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#dateEpochDay%2Dlong%2D[java 9]) +* ++[[painless-api-reference-IsoChronology-dateYearDay-2]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#dateYearDay%2Dint%2Dint%2D[dateYearDay](int, int)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#dateYearDay%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-IsoChronology-dateYearDay-3]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[dateYearDay](<>, int, int)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-IsoChronology-eraOf-1]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#eraOf%2Dint%2D[eraOf](int)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#eraOf%2Dint%2D[java 9]) +* ++[[painless-api-reference-IsoChronology-localDateTime-1]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#localDateTime%2Djava.time.temporal.TemporalAccessor%2D[localDateTime](<>)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#localDateTime%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-IsoChronology-period-3]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#period%2Dint%2Dint%2Dint%2D[period](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#period%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-IsoChronology-resolveDate-2]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[resolveDate](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[java 9]) +* ++[[painless-api-reference-IsoChronology-zonedDateTime-1]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#zonedDateTime%2Djava.time.temporal.TemporalAccessor%2D[zonedDateTime](<>)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#zonedDateTime%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-IsoChronology-zonedDateTime-2]]<> link:{java8-javadoc}/java/time/chrono/IsoChronology.html#zonedDateTime%2Djava.time.Instant%2Djava.time.ZoneId%2D[zonedDateTime](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/IsoChronology.html#zonedDateTime%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/IsoEra.asciidoc b/docs/painless/painless-api-reference/IsoEra.asciidoc new file mode 100644 index 0000000000000..975d7d1153aef --- /dev/null +++ b/docs/painless/painless-api-reference/IsoEra.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IsoEra]]++IsoEra++:: +** [[painless-api-reference-IsoEra-BCE]]static <> link:{java8-javadoc}/java/time/chrono/IsoEra.html#BCE[BCE] (link:{java9-javadoc}/java/time/chrono/IsoEra.html#BCE[java 9]) +** [[painless-api-reference-IsoEra-CE]]static <> link:{java8-javadoc}/java/time/chrono/IsoEra.html#CE[CE] (link:{java9-javadoc}/java/time/chrono/IsoEra.html#CE[java 9]) +* ++[[painless-api-reference-IsoEra-of-1]]static <> link:{java8-javadoc}/java/time/chrono/IsoEra.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/chrono/IsoEra.html#of%2Dint%2D[java 9]) +* ++[[painless-api-reference-IsoEra-valueOf-1]]static <> link:{java8-javadoc}/java/time/chrono/IsoEra.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/chrono/IsoEra.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-IsoEra-values-0]]static <>[] link:{java8-javadoc}/java/time/chrono/IsoEra.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/chrono/IsoEra.html#values%2D%2D[java 9]) +* ++[[painless-api-reference-IsoEra-getValue-0]]int link:{java8-javadoc}/java/time/chrono/IsoEra.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/chrono/IsoEra.html#getValue%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/IsoFields.asciidoc b/docs/painless/painless-api-reference/IsoFields.asciidoc new file mode 100644 index 0000000000000..db3f5bbefbb5e --- /dev/null +++ b/docs/painless/painless-api-reference/IsoFields.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-IsoFields]]++IsoFields++:: +** [[painless-api-reference-IsoFields-DAY_OF_QUARTER]]static <> link:{java8-javadoc}/java/time/temporal/IsoFields.html#DAY_OF_QUARTER[DAY_OF_QUARTER] (link:{java9-javadoc}/java/time/temporal/IsoFields.html#DAY_OF_QUARTER[java 9]) +** [[painless-api-reference-IsoFields-QUARTER_OF_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/IsoFields.html#QUARTER_OF_YEAR[QUARTER_OF_YEAR] (link:{java9-javadoc}/java/time/temporal/IsoFields.html#QUARTER_OF_YEAR[java 9]) +** [[painless-api-reference-IsoFields-QUARTER_YEARS]]static <> link:{java8-javadoc}/java/time/temporal/IsoFields.html#QUARTER_YEARS[QUARTER_YEARS] (link:{java9-javadoc}/java/time/temporal/IsoFields.html#QUARTER_YEARS[java 9]) +** [[painless-api-reference-IsoFields-WEEK_BASED_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/IsoFields.html#WEEK_BASED_YEAR[WEEK_BASED_YEAR] (link:{java9-javadoc}/java/time/temporal/IsoFields.html#WEEK_BASED_YEAR[java 9]) +** [[painless-api-reference-IsoFields-WEEK_BASED_YEARS]]static <> link:{java8-javadoc}/java/time/temporal/IsoFields.html#WEEK_BASED_YEARS[WEEK_BASED_YEARS] (link:{java9-javadoc}/java/time/temporal/IsoFields.html#WEEK_BASED_YEARS[java 9]) +** [[painless-api-reference-IsoFields-WEEK_OF_WEEK_BASED_YEAR]]static <> link:{java8-javadoc}/java/time/temporal/IsoFields.html#WEEK_OF_WEEK_BASED_YEAR[WEEK_OF_WEEK_BASED_YEAR] (link:{java9-javadoc}/java/time/temporal/IsoFields.html#WEEK_OF_WEEK_BASED_YEAR[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Iterable.asciidoc b/docs/painless/painless-api-reference/Iterable.asciidoc new file mode 100644 index 0000000000000..b7d626767c710 --- /dev/null +++ b/docs/painless/painless-api-reference/Iterable.asciidoc @@ -0,0 +1,21 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Iterable]]++Iterable++:: +* ++[[painless-api-reference-Iterable-any-1]]boolean link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#any%2Djava.lang.Iterable%2Djava.util.function.Predicate%2D[any](<>)++ +* ++[[painless-api-reference-Iterable-asCollection-0]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#asCollection%2Djava.lang.Iterable%2D[asCollection]()++ +* ++[[painless-api-reference-Iterable-asList-0]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#asList%2Djava.lang.Iterable%2D[asList]()++ +* ++[[painless-api-reference-Iterable-each-1]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#each%2Djava.lang.Iterable%2Djava.util.function.Consumer%2D[each](<>)++ +* ++[[painless-api-reference-Iterable-eachWithIndex-1]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#eachWithIndex%2Djava.lang.Iterable%2Djava.util.function.ObjIntConsumer%2D[eachWithIndex](<>)++ +* ++[[painless-api-reference-Iterable-every-1]]boolean link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#every%2Djava.lang.Iterable%2Djava.util.function.Predicate%2D[every](<>)++ +* ++[[painless-api-reference-Iterable-findResults-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findResults%2Djava.lang.Iterable%2Djava.util.function.Function%2D[findResults](<>)++ +* ++[[painless-api-reference-Iterable-forEach-1]]void link:{java8-javadoc}/java/lang/Iterable.html#forEach%2Djava.util.function.Consumer%2D[forEach](<>)++ (link:{java9-javadoc}/java/lang/Iterable.html#forEach%2Djava.util.function.Consumer%2D[java 9]) +* ++[[painless-api-reference-Iterable-groupBy-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#groupBy%2Djava.lang.Iterable%2Djava.util.function.Function%2D[groupBy](<>)++ +* ++[[painless-api-reference-Iterable-iterator-0]]<> link:{java8-javadoc}/java/lang/Iterable.html#iterator%2D%2D[iterator]()++ (link:{java9-javadoc}/java/lang/Iterable.html#iterator%2D%2D[java 9]) +* ++[[painless-api-reference-Iterable-join-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#join%2Djava.lang.Iterable%2Djava.lang.String%2D[join](<>)++ +* ++[[painless-api-reference-Iterable-spliterator-0]]<> link:{java8-javadoc}/java/lang/Iterable.html#spliterator%2D%2D[spliterator]()++ (link:{java9-javadoc}/java/lang/Iterable.html#spliterator%2D%2D[java 9]) +* ++[[painless-api-reference-Iterable-sum-0]]double link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#sum%2Djava.lang.Iterable%2D[sum]()++ +* ++[[painless-api-reference-Iterable-sum-1]]double link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#sum%2Djava.lang.Iterable%2Djava.util.function.ToDoubleFunction%2D[sum](<>)++ +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Iterator.asciidoc b/docs/painless/painless-api-reference/Iterator.asciidoc new file mode 100644 index 0000000000000..9dd3f5601d54b --- /dev/null +++ b/docs/painless/painless-api-reference/Iterator.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Iterator]]++Iterator++:: +* ++[[painless-api-reference-Iterator-forEachRemaining-1]]void link:{java8-javadoc}/java/util/Iterator.html#forEachRemaining%2Djava.util.function.Consumer%2D[forEachRemaining](<>)++ (link:{java9-javadoc}/java/util/Iterator.html#forEachRemaining%2Djava.util.function.Consumer%2D[java 9]) +* ++[[painless-api-reference-Iterator-hasNext-0]]boolean link:{java8-javadoc}/java/util/Iterator.html#hasNext%2D%2D[hasNext]()++ (link:{java9-javadoc}/java/util/Iterator.html#hasNext%2D%2D[java 9]) +* ++[[painless-api-reference-Iterator-next-0]]def link:{java8-javadoc}/java/util/Iterator.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/util/Iterator.html#next%2D%2D[java 9]) +* ++[[painless-api-reference-Iterator-remove-0]]void link:{java8-javadoc}/java/util/Iterator.html#remove%2D%2D[remove]()++ (link:{java9-javadoc}/java/util/Iterator.html#remove%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/JapaneseChronology.asciidoc b/docs/painless/painless-api-reference/JapaneseChronology.asciidoc new file mode 100644 index 0000000000000..166f3ca176d09 --- /dev/null +++ b/docs/painless/painless-api-reference/JapaneseChronology.asciidoc @@ -0,0 +1,16 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-JapaneseChronology]]++JapaneseChronology++:: +** [[painless-api-reference-JapaneseChronology-INSTANCE]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#INSTANCE[INSTANCE] (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#INSTANCE[java 9]) +* ++[[painless-api-reference-JapaneseChronology-date-1]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[date](<>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-JapaneseChronology-date-3]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#date%2Dint%2Dint%2Dint%2D[date](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#date%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-JapaneseChronology-date-4]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[date](<>, int, int, int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-JapaneseChronology-dateEpochDay-1]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#dateEpochDay%2Dlong%2D[dateEpochDay](long)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#dateEpochDay%2Dlong%2D[java 9]) +* ++[[painless-api-reference-JapaneseChronology-dateYearDay-2]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#dateYearDay%2Dint%2Dint%2D[dateYearDay](int, int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#dateYearDay%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-JapaneseChronology-dateYearDay-3]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[dateYearDay](<>, int, int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-JapaneseChronology-eraOf-1]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#eraOf%2Dint%2D[eraOf](int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#eraOf%2Dint%2D[java 9]) +* ++[[painless-api-reference-JapaneseChronology-resolveDate-2]]<> link:{java8-javadoc}/java/time/chrono/JapaneseChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[resolveDate](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/JapaneseDate.asciidoc b/docs/painless/painless-api-reference/JapaneseDate.asciidoc new file mode 100644 index 0000000000000..560ebc7a664bc --- /dev/null +++ b/docs/painless/painless-api-reference/JapaneseDate.asciidoc @@ -0,0 +1,17 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-JapaneseDate]]++JapaneseDate++:: +* ++[[painless-api-reference-JapaneseDate-from-1]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-JapaneseDate-of-3]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#of%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-JapaneseDate-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#getChronology%2D%2D[java 9]) +* ++[[painless-api-reference-JapaneseDate-getEra-0]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#getEra%2D%2D[getEra]()++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#getEra%2D%2D[java 9]) +* ++[[painless-api-reference-JapaneseDate-minus-1]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-JapaneseDate-minus-2]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-JapaneseDate-plus-1]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-JapaneseDate-plus-2]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-JapaneseDate-with-1]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-JapaneseDate-with-2]]<> link:{java8-javadoc}/java/time/chrono/JapaneseDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/JapaneseDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/JapaneseEra.asciidoc b/docs/painless/painless-api-reference/JapaneseEra.asciidoc new file mode 100644 index 0000000000000..b5a9c7f63bc04 --- /dev/null +++ b/docs/painless/painless-api-reference/JapaneseEra.asciidoc @@ -0,0 +1,15 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-JapaneseEra]]++JapaneseEra++:: +** [[painless-api-reference-JapaneseEra-HEISEI]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#HEISEI[HEISEI] (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#HEISEI[java 9]) +** [[painless-api-reference-JapaneseEra-MEIJI]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#MEIJI[MEIJI] (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#MEIJI[java 9]) +** [[painless-api-reference-JapaneseEra-SHOWA]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#SHOWA[SHOWA] (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#SHOWA[java 9]) +** [[painless-api-reference-JapaneseEra-TAISHO]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#TAISHO[TAISHO] (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#TAISHO[java 9]) +* ++[[painless-api-reference-JapaneseEra-of-1]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#of%2Dint%2D[java 9]) +* ++[[painless-api-reference-JapaneseEra-valueOf-1]]static <> link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-JapaneseEra-values-0]]static <>[] link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#values%2D%2D[java 9]) +* ++[[painless-api-reference-JapaneseEra-getValue-0]]int link:{java8-javadoc}/java/time/chrono/JapaneseEra.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/chrono/JapaneseEra.html#getValue%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/JulianFields.asciidoc b/docs/painless/painless-api-reference/JulianFields.asciidoc new file mode 100644 index 0000000000000..19ee6c5e2ebd6 --- /dev/null +++ b/docs/painless/painless-api-reference/JulianFields.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-JulianFields]]++JulianFields++:: +** [[painless-api-reference-JulianFields-JULIAN_DAY]]static <> link:{java8-javadoc}/java/time/temporal/JulianFields.html#JULIAN_DAY[JULIAN_DAY] (link:{java9-javadoc}/java/time/temporal/JulianFields.html#JULIAN_DAY[java 9]) +** [[painless-api-reference-JulianFields-MODIFIED_JULIAN_DAY]]static <> link:{java8-javadoc}/java/time/temporal/JulianFields.html#MODIFIED_JULIAN_DAY[MODIFIED_JULIAN_DAY] (link:{java9-javadoc}/java/time/temporal/JulianFields.html#MODIFIED_JULIAN_DAY[java 9]) +** [[painless-api-reference-JulianFields-RATA_DIE]]static <> link:{java8-javadoc}/java/time/temporal/JulianFields.html#RATA_DIE[RATA_DIE] (link:{java9-javadoc}/java/time/temporal/JulianFields.html#RATA_DIE[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/LinkedHashMap.asciidoc b/docs/painless/painless-api-reference/LinkedHashMap.asciidoc new file mode 100644 index 0000000000000..bda2d45f22cb6 --- /dev/null +++ b/docs/painless/painless-api-reference/LinkedHashMap.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LinkedHashMap]]++LinkedHashMap++:: +* ++[[painless-api-reference-LinkedHashMap-LinkedHashMap-0]]link:{java8-javadoc}/java/util/LinkedHashMap.html#LinkedHashMap%2D%2D[LinkedHashMap]()++ (link:{java9-javadoc}/java/util/LinkedHashMap.html#LinkedHashMap%2D%2D[java 9]) +* ++[[painless-api-reference-LinkedHashMap-LinkedHashMap-1]]link:{java8-javadoc}/java/util/LinkedHashMap.html#LinkedHashMap%2Djava.util.Map%2D[LinkedHashMap](<>)++ (link:{java9-javadoc}/java/util/LinkedHashMap.html#LinkedHashMap%2Djava.util.Map%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/LinkedHashSet.asciidoc b/docs/painless/painless-api-reference/LinkedHashSet.asciidoc new file mode 100644 index 0000000000000..3a7710771e8ba --- /dev/null +++ b/docs/painless/painless-api-reference/LinkedHashSet.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LinkedHashSet]]++LinkedHashSet++:: +* ++[[painless-api-reference-LinkedHashSet-LinkedHashSet-0]]link:{java8-javadoc}/java/util/LinkedHashSet.html#LinkedHashSet%2D%2D[LinkedHashSet]()++ (link:{java9-javadoc}/java/util/LinkedHashSet.html#LinkedHashSet%2D%2D[java 9]) +* ++[[painless-api-reference-LinkedHashSet-LinkedHashSet-1]]link:{java8-javadoc}/java/util/LinkedHashSet.html#LinkedHashSet%2Djava.util.Collection%2D[LinkedHashSet](<>)++ (link:{java9-javadoc}/java/util/LinkedHashSet.html#LinkedHashSet%2Djava.util.Collection%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/LinkedList.asciidoc b/docs/painless/painless-api-reference/LinkedList.asciidoc new file mode 100644 index 0000000000000..5d18b373ba360 --- /dev/null +++ b/docs/painless/painless-api-reference/LinkedList.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LinkedList]]++LinkedList++:: +* ++[[painless-api-reference-LinkedList-LinkedList-0]]link:{java8-javadoc}/java/util/LinkedList.html#LinkedList%2D%2D[LinkedList]()++ (link:{java9-javadoc}/java/util/LinkedList.html#LinkedList%2D%2D[java 9]) +* ++[[painless-api-reference-LinkedList-LinkedList-1]]link:{java8-javadoc}/java/util/LinkedList.html#LinkedList%2Djava.util.Collection%2D[LinkedList](<>)++ (link:{java9-javadoc}/java/util/LinkedList.html#LinkedList%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-LinkedList-clone-0]]def link:{java8-javadoc}/java/util/LinkedList.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/LinkedList.html#clone%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/List.asciidoc b/docs/painless/painless-api-reference/List.asciidoc new file mode 100644 index 0000000000000..abe438451b86b --- /dev/null +++ b/docs/painless/painless-api-reference/List.asciidoc @@ -0,0 +1,22 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-List]]++List++:: +* ++[[painless-api-reference-List-add-2]]void link:{java8-javadoc}/java/util/List.html#add%2Dint%2Djava.lang.Object%2D[add](int, def)++ (link:{java9-javadoc}/java/util/List.html#add%2Dint%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-List-addAll-2]]boolean link:{java8-javadoc}/java/util/List.html#addAll%2Dint%2Djava.util.Collection%2D[addAll](int, <>)++ (link:{java9-javadoc}/java/util/List.html#addAll%2Dint%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-List-equals-1]]boolean link:{java8-javadoc}/java/util/List.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/util/List.html#equals%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-List-get-1]]def link:{java8-javadoc}/java/util/List.html#get%2Dint%2D[get](int)++ (link:{java9-javadoc}/java/util/List.html#get%2Dint%2D[java 9]) +* ++[[painless-api-reference-List-getLength-0]]int link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#getLength%2Djava.util.List%2D[getLength]()++ +* ++[[painless-api-reference-List-hashCode-0]]int link:{java8-javadoc}/java/util/List.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/util/List.html#hashCode%2D%2D[java 9]) +* ++[[painless-api-reference-List-indexOf-1]]int link:{java8-javadoc}/java/util/List.html#indexOf%2Djava.lang.Object%2D[indexOf](def)++ (link:{java9-javadoc}/java/util/List.html#indexOf%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-List-lastIndexOf-1]]int link:{java8-javadoc}/java/util/List.html#lastIndexOf%2Djava.lang.Object%2D[lastIndexOf](def)++ (link:{java9-javadoc}/java/util/List.html#lastIndexOf%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-List-listIterator-0]]<> link:{java8-javadoc}/java/util/List.html#listIterator%2D%2D[listIterator]()++ (link:{java9-javadoc}/java/util/List.html#listIterator%2D%2D[java 9]) +* ++[[painless-api-reference-List-listIterator-1]]<> link:{java8-javadoc}/java/util/List.html#listIterator%2Dint%2D[listIterator](int)++ (link:{java9-javadoc}/java/util/List.html#listIterator%2Dint%2D[java 9]) +* ++[[painless-api-reference-List-remove-1]]def link:{java8-javadoc}/java/util/List.html#remove%2Dint%2D[remove](int)++ (link:{java9-javadoc}/java/util/List.html#remove%2Dint%2D[java 9]) +* ++[[painless-api-reference-List-replaceAll-1]]void link:{java8-javadoc}/java/util/List.html#replaceAll%2Djava.util.function.UnaryOperator%2D[replaceAll](<>)++ (link:{java9-javadoc}/java/util/List.html#replaceAll%2Djava.util.function.UnaryOperator%2D[java 9]) +* ++[[painless-api-reference-List-set-2]]def link:{java8-javadoc}/java/util/List.html#set%2Dint%2Djava.lang.Object%2D[set](int, def)++ (link:{java9-javadoc}/java/util/List.html#set%2Dint%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-List-sort-1]]void link:{java8-javadoc}/java/util/List.html#sort%2Djava.util.Comparator%2D[sort](<>)++ (link:{java9-javadoc}/java/util/List.html#sort%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-List-subList-2]]<> link:{java8-javadoc}/java/util/List.html#subList%2Dint%2Dint%2D[subList](int, int)++ (link:{java9-javadoc}/java/util/List.html#subList%2Dint%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ListIterator.asciidoc b/docs/painless/painless-api-reference/ListIterator.asciidoc new file mode 100644 index 0000000000000..37d04b2a755cf --- /dev/null +++ b/docs/painless/painless-api-reference/ListIterator.asciidoc @@ -0,0 +1,12 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ListIterator]]++ListIterator++:: +* ++[[painless-api-reference-ListIterator-add-1]]void link:{java8-javadoc}/java/util/ListIterator.html#add%2Djava.lang.Object%2D[add](def)++ (link:{java9-javadoc}/java/util/ListIterator.html#add%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-ListIterator-hasPrevious-0]]boolean link:{java8-javadoc}/java/util/ListIterator.html#hasPrevious%2D%2D[hasPrevious]()++ (link:{java9-javadoc}/java/util/ListIterator.html#hasPrevious%2D%2D[java 9]) +* ++[[painless-api-reference-ListIterator-nextIndex-0]]int link:{java8-javadoc}/java/util/ListIterator.html#nextIndex%2D%2D[nextIndex]()++ (link:{java9-javadoc}/java/util/ListIterator.html#nextIndex%2D%2D[java 9]) +* ++[[painless-api-reference-ListIterator-previousIndex-0]]int link:{java8-javadoc}/java/util/ListIterator.html#previousIndex%2D%2D[previousIndex]()++ (link:{java9-javadoc}/java/util/ListIterator.html#previousIndex%2D%2D[java 9]) +* ++[[painless-api-reference-ListIterator-set-1]]void link:{java8-javadoc}/java/util/ListIterator.html#set%2Djava.lang.Object%2D[set](def)++ (link:{java9-javadoc}/java/util/ListIterator.html#set%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/LocalDate.asciidoc b/docs/painless/painless-api-reference/LocalDate.asciidoc new file mode 100644 index 0000000000000..8498474d02a0f --- /dev/null +++ b/docs/painless/painless-api-reference/LocalDate.asciidoc @@ -0,0 +1,47 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LocalDate]]++LocalDate++:: +** [[painless-api-reference-LocalDate-MAX]]static <> link:{java8-javadoc}/java/time/LocalDate.html#MAX[MAX] (link:{java9-javadoc}/java/time/LocalDate.html#MAX[java 9]) +** [[painless-api-reference-LocalDate-MIN]]static <> link:{java8-javadoc}/java/time/LocalDate.html#MIN[MIN] (link:{java9-javadoc}/java/time/LocalDate.html#MIN[java 9]) +* ++[[painless-api-reference-LocalDate-from-1]]static <> link:{java8-javadoc}/java/time/LocalDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-LocalDate-of-3]]static <> link:{java8-javadoc}/java/time/LocalDate.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/LocalDate.html#of%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDate-ofEpochDay-1]]static <> link:{java8-javadoc}/java/time/LocalDate.html#ofEpochDay%2Dlong%2D[ofEpochDay](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#ofEpochDay%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDate-ofYearDay-2]]static <> link:{java8-javadoc}/java/time/LocalDate.html#ofYearDay%2Dint%2Dint%2D[ofYearDay](int, int)++ (link:{java9-javadoc}/java/time/LocalDate.html#ofYearDay%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDate-parse-1]]static <> link:{java8-javadoc}/java/time/LocalDate.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#parse%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-LocalDate-parse-2]]static <> link:{java8-javadoc}/java/time/LocalDate.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/LocalDate.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-LocalDate-atStartOfDay-0]]<> link:{java8-javadoc}/java/time/LocalDate.html#atStartOfDay%2D%2D[atStartOfDay]()++ (link:{java9-javadoc}/java/time/LocalDate.html#atStartOfDay%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDate-atStartOfDay-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#atStartOfDay%2Djava.time.ZoneId%2D[atStartOfDay](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#atStartOfDay%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-LocalDate-atTime-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#atTime%2Djava.time.LocalTime%2D[atTime](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#atTime%2Djava.time.LocalTime%2D[java 9]) +* ++[[painless-api-reference-LocalDate-atTime-2]]<> link:{java8-javadoc}/java/time/LocalDate.html#atTime%2Dint%2Dint%2D[atTime](int, int)++ (link:{java9-javadoc}/java/time/LocalDate.html#atTime%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDate-atTime-3]]<> link:{java8-javadoc}/java/time/LocalDate.html#atTime%2Dint%2Dint%2Dint%2D[atTime](int, int, int)++ (link:{java9-javadoc}/java/time/LocalDate.html#atTime%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDate-atTime-4]]<> link:{java8-javadoc}/java/time/LocalDate.html#atTime%2Dint%2Dint%2Dint%2Dint%2D[atTime](int, int, int, int)++ (link:{java9-javadoc}/java/time/LocalDate.html#atTime%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDate-getChronology-0]]<> link:{java8-javadoc}/java/time/LocalDate.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getChronology%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDate-getDayOfMonth-0]]int link:{java8-javadoc}/java/time/LocalDate.html#getDayOfMonth%2D%2D[getDayOfMonth]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getDayOfMonth%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDate-getDayOfWeek-0]]<> link:{java8-javadoc}/java/time/LocalDate.html#getDayOfWeek%2D%2D[getDayOfWeek]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getDayOfWeek%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDate-getDayOfYear-0]]int link:{java8-javadoc}/java/time/LocalDate.html#getDayOfYear%2D%2D[getDayOfYear]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getDayOfYear%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDate-getMonth-0]]<> link:{java8-javadoc}/java/time/LocalDate.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getMonth%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDate-getMonthValue-0]]int link:{java8-javadoc}/java/time/LocalDate.html#getMonthValue%2D%2D[getMonthValue]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getMonthValue%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDate-getYear-0]]int link:{java8-javadoc}/java/time/LocalDate.html#getYear%2D%2D[getYear]()++ (link:{java9-javadoc}/java/time/LocalDate.html#getYear%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDate-minus-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-LocalDate-minus-2]]<> link:{java8-javadoc}/java/time/LocalDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/LocalDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-LocalDate-minusDays-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#minusDays%2Dlong%2D[minusDays](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#minusDays%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDate-minusMonths-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#minusMonths%2Dlong%2D[minusMonths](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#minusMonths%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDate-minusWeeks-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#minusWeeks%2Dlong%2D[minusWeeks](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#minusWeeks%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDate-minusYears-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#minusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDate-plus-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-LocalDate-plus-2]]<> link:{java8-javadoc}/java/time/LocalDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/LocalDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-LocalDate-plusDays-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#plusDays%2Dlong%2D[plusDays](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#plusDays%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDate-plusMonths-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#plusMonths%2Dlong%2D[plusMonths](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#plusMonths%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDate-plusWeeks-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#plusWeeks%2Dlong%2D[plusWeeks](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#plusWeeks%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDate-plusYears-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/LocalDate.html#plusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDate-until-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#until%2Djava.time.chrono.ChronoLocalDate%2D[until](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#until%2Djava.time.chrono.ChronoLocalDate%2D[java 9]) +* ++[[painless-api-reference-LocalDate-with-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/LocalDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-LocalDate-with-2]]<> link:{java8-javadoc}/java/time/LocalDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/LocalDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDate-withDayOfMonth-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#withDayOfMonth%2Dint%2D[withDayOfMonth](int)++ (link:{java9-javadoc}/java/time/LocalDate.html#withDayOfMonth%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDate-withDayOfYear-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#withDayOfYear%2Dint%2D[withDayOfYear](int)++ (link:{java9-javadoc}/java/time/LocalDate.html#withDayOfYear%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDate-withMonth-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#withMonth%2Dint%2D[withMonth](int)++ (link:{java9-javadoc}/java/time/LocalDate.html#withMonth%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDate-withYear-1]]<> link:{java8-javadoc}/java/time/LocalDate.html#withYear%2Dint%2D[withYear](int)++ (link:{java9-javadoc}/java/time/LocalDate.html#withYear%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/LocalDateTime.asciidoc b/docs/painless/painless-api-reference/LocalDateTime.asciidoc new file mode 100644 index 0000000000000..9507ff1dd5231 --- /dev/null +++ b/docs/painless/painless-api-reference/LocalDateTime.asciidoc @@ -0,0 +1,61 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LocalDateTime]]++LocalDateTime++:: +** [[painless-api-reference-LocalDateTime-MAX]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#MAX[MAX] (link:{java9-javadoc}/java/time/LocalDateTime.html#MAX[java 9]) +** [[painless-api-reference-LocalDateTime-MIN]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#MIN[MIN] (link:{java9-javadoc}/java/time/LocalDateTime.html#MIN[java 9]) +* ++[[painless-api-reference-LocalDateTime-from-1]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-of-2]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#of%2Djava.time.LocalDate%2Djava.time.LocalTime%2D[of](<>, <>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#of%2Djava.time.LocalDate%2Djava.time.LocalTime%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-of-5]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2D[of](int, int, int, int, int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-of-6]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[of](int, int, int, int, int, int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-of-7]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[of](int, int, int, int, int, int, int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-ofEpochSecond-3]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#ofEpochSecond%2Dlong%2Dint%2Djava.time.ZoneOffset%2D[ofEpochSecond](long, int, <>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#ofEpochSecond%2Dlong%2Dint%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-ofInstant-2]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[ofInstant](<>, <>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-parse-1]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#parse%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-parse-2]]static <> link:{java8-javadoc}/java/time/LocalDateTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-atOffset-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#atOffset%2Djava.time.ZoneOffset%2D[atOffset](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#atOffset%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-atZone-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#atZone%2Djava.time.ZoneId%2D[atZone](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#atZone%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-getDayOfMonth-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getDayOfMonth%2D%2D[getDayOfMonth]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getDayOfMonth%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-getDayOfWeek-0]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#getDayOfWeek%2D%2D[getDayOfWeek]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getDayOfWeek%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-getDayOfYear-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getDayOfYear%2D%2D[getDayOfYear]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getDayOfYear%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-getHour-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getHour%2D%2D[getHour]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getHour%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-getMinute-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getMinute%2D%2D[getMinute]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getMinute%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-getMonth-0]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getMonth%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-getMonthValue-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getMonthValue%2D%2D[getMonthValue]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getMonthValue%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-getNano-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getNano%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-getSecond-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getSecond%2D%2D[getSecond]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getSecond%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-getYear-0]]int link:{java8-javadoc}/java/time/LocalDateTime.html#getYear%2D%2D[getYear]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#getYear%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-minus-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-minus-2]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-minusDays-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusDays%2Dlong%2D[minusDays](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusDays%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-minusHours-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusHours%2Dlong%2D[minusHours](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusHours%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-minusMinutes-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusMinutes%2Dlong%2D[minusMinutes](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusMinutes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-minusMonths-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusMonths%2Dlong%2D[minusMonths](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusMonths%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-minusNanos-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-minusSeconds-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-minusWeeks-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusWeeks%2Dlong%2D[minusWeeks](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusWeeks%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-minusYears-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#minusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-plus-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-plus-2]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-plusDays-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusDays%2Dlong%2D[plusDays](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusDays%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-plusHours-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusHours%2Dlong%2D[plusHours](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusHours%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-plusMinutes-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusMinutes%2Dlong%2D[plusMinutes](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusMinutes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-plusMonths-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusMonths%2Dlong%2D[plusMonths](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusMonths%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-plusNanos-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-plusSeconds-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-plusWeeks-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusWeeks%2Dlong%2D[plusWeeks](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusWeeks%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-plusYears-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#plusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-toLocalDate-0]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#toLocalDate%2D%2D[toLocalDate]()++ (link:{java9-javadoc}/java/time/LocalDateTime.html#toLocalDate%2D%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-truncatedTo-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[truncatedTo](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-with-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-with-2]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-withDayOfMonth-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withDayOfMonth%2Dint%2D[withDayOfMonth](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withDayOfMonth%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-withDayOfYear-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withDayOfYear%2Dint%2D[withDayOfYear](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withDayOfYear%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-withHour-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withHour%2Dint%2D[withHour](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withHour%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-withMinute-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withMinute%2Dint%2D[withMinute](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withMinute%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-withMonth-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withMonth%2Dint%2D[withMonth](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withMonth%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-withSecond-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withSecond%2Dint%2D[withSecond](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withSecond%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalDateTime-withYear-1]]<> link:{java8-javadoc}/java/time/LocalDateTime.html#withYear%2Dint%2D[withYear](int)++ (link:{java9-javadoc}/java/time/LocalDateTime.html#withYear%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/LocalTime.asciidoc b/docs/painless/painless-api-reference/LocalTime.asciidoc new file mode 100644 index 0000000000000..f12e13f4e7854 --- /dev/null +++ b/docs/painless/painless-api-reference/LocalTime.asciidoc @@ -0,0 +1,50 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LocalTime]]++LocalTime++:: +** [[painless-api-reference-LocalTime-MAX]]static <> link:{java8-javadoc}/java/time/LocalTime.html#MAX[MAX] (link:{java9-javadoc}/java/time/LocalTime.html#MAX[java 9]) +** [[painless-api-reference-LocalTime-MIDNIGHT]]static <> link:{java8-javadoc}/java/time/LocalTime.html#MIDNIGHT[MIDNIGHT] (link:{java9-javadoc}/java/time/LocalTime.html#MIDNIGHT[java 9]) +** [[painless-api-reference-LocalTime-MIN]]static <> link:{java8-javadoc}/java/time/LocalTime.html#MIN[MIN] (link:{java9-javadoc}/java/time/LocalTime.html#MIN[java 9]) +** [[painless-api-reference-LocalTime-NOON]]static <> link:{java8-javadoc}/java/time/LocalTime.html#NOON[NOON] (link:{java9-javadoc}/java/time/LocalTime.html#NOON[java 9]) +* ++[[painless-api-reference-LocalTime-from-1]]static <> link:{java8-javadoc}/java/time/LocalTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-LocalTime-of-2]]static <> link:{java8-javadoc}/java/time/LocalTime.html#of%2Dint%2Dint%2D[of](int, int)++ (link:{java9-javadoc}/java/time/LocalTime.html#of%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalTime-of-3]]static <> link:{java8-javadoc}/java/time/LocalTime.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/LocalTime.html#of%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalTime-of-4]]static <> link:{java8-javadoc}/java/time/LocalTime.html#of%2Dint%2Dint%2Dint%2Dint%2D[of](int, int, int, int)++ (link:{java9-javadoc}/java/time/LocalTime.html#of%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalTime-ofNanoOfDay-1]]static <> link:{java8-javadoc}/java/time/LocalTime.html#ofNanoOfDay%2Dlong%2D[ofNanoOfDay](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#ofNanoOfDay%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalTime-ofSecondOfDay-1]]static <> link:{java8-javadoc}/java/time/LocalTime.html#ofSecondOfDay%2Dlong%2D[ofSecondOfDay](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#ofSecondOfDay%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalTime-parse-1]]static <> link:{java8-javadoc}/java/time/LocalTime.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#parse%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-LocalTime-parse-2]]static <> link:{java8-javadoc}/java/time/LocalTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/LocalTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-LocalTime-atDate-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#atDate%2Djava.time.LocalDate%2D[atDate](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#atDate%2Djava.time.LocalDate%2D[java 9]) +* ++[[painless-api-reference-LocalTime-atOffset-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#atOffset%2Djava.time.ZoneOffset%2D[atOffset](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#atOffset%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-LocalTime-compareTo-1]]int link:{java8-javadoc}/java/time/LocalTime.html#compareTo%2Djava.time.LocalTime%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#compareTo%2Djava.time.LocalTime%2D[java 9]) +* ++[[painless-api-reference-LocalTime-format-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-LocalTime-getHour-0]]int link:{java8-javadoc}/java/time/LocalTime.html#getHour%2D%2D[getHour]()++ (link:{java9-javadoc}/java/time/LocalTime.html#getHour%2D%2D[java 9]) +* ++[[painless-api-reference-LocalTime-getMinute-0]]int link:{java8-javadoc}/java/time/LocalTime.html#getMinute%2D%2D[getMinute]()++ (link:{java9-javadoc}/java/time/LocalTime.html#getMinute%2D%2D[java 9]) +* ++[[painless-api-reference-LocalTime-getNano-0]]int link:{java8-javadoc}/java/time/LocalTime.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/LocalTime.html#getNano%2D%2D[java 9]) +* ++[[painless-api-reference-LocalTime-getSecond-0]]int link:{java8-javadoc}/java/time/LocalTime.html#getSecond%2D%2D[getSecond]()++ (link:{java9-javadoc}/java/time/LocalTime.html#getSecond%2D%2D[java 9]) +* ++[[painless-api-reference-LocalTime-isAfter-1]]boolean link:{java8-javadoc}/java/time/LocalTime.html#isAfter%2Djava.time.LocalTime%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#isAfter%2Djava.time.LocalTime%2D[java 9]) +* ++[[painless-api-reference-LocalTime-isBefore-1]]boolean link:{java8-javadoc}/java/time/LocalTime.html#isBefore%2Djava.time.LocalTime%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#isBefore%2Djava.time.LocalTime%2D[java 9]) +* ++[[painless-api-reference-LocalTime-minus-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-LocalTime-minus-2]]<> link:{java8-javadoc}/java/time/LocalTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/LocalTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-LocalTime-minusHours-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#minusHours%2Dlong%2D[minusHours](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#minusHours%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalTime-minusMinutes-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#minusMinutes%2Dlong%2D[minusMinutes](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#minusMinutes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalTime-minusNanos-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#minusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalTime-minusSeconds-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#minusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalTime-plus-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-LocalTime-plus-2]]<> link:{java8-javadoc}/java/time/LocalTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/LocalTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-LocalTime-plusHours-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#plusHours%2Dlong%2D[plusHours](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#plusHours%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalTime-plusMinutes-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#plusMinutes%2Dlong%2D[plusMinutes](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#plusMinutes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalTime-plusNanos-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#plusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalTime-plusSeconds-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/LocalTime.html#plusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalTime-toNanoOfDay-0]]long link:{java8-javadoc}/java/time/LocalTime.html#toNanoOfDay%2D%2D[toNanoOfDay]()++ (link:{java9-javadoc}/java/time/LocalTime.html#toNanoOfDay%2D%2D[java 9]) +* ++[[painless-api-reference-LocalTime-toSecondOfDay-0]]int link:{java8-javadoc}/java/time/LocalTime.html#toSecondOfDay%2D%2D[toSecondOfDay]()++ (link:{java9-javadoc}/java/time/LocalTime.html#toSecondOfDay%2D%2D[java 9]) +* ++[[painless-api-reference-LocalTime-truncatedTo-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[truncatedTo](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-LocalTime-with-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/LocalTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-LocalTime-with-2]]<> link:{java8-javadoc}/java/time/LocalTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/LocalTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LocalTime-withHour-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#withHour%2Dint%2D[withHour](int)++ (link:{java9-javadoc}/java/time/LocalTime.html#withHour%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalTime-withMinute-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#withMinute%2Dint%2D[withMinute](int)++ (link:{java9-javadoc}/java/time/LocalTime.html#withMinute%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalTime-withNano-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#withNano%2Dint%2D[withNano](int)++ (link:{java9-javadoc}/java/time/LocalTime.html#withNano%2Dint%2D[java 9]) +* ++[[painless-api-reference-LocalTime-withSecond-1]]<> link:{java8-javadoc}/java/time/LocalTime.html#withSecond%2Dint%2D[withSecond](int)++ (link:{java9-javadoc}/java/time/LocalTime.html#withSecond%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Locale.Builder.asciidoc b/docs/painless/painless-api-reference/Locale.Builder.asciidoc new file mode 100644 index 0000000000000..8fad8099de9d9 --- /dev/null +++ b/docs/painless/painless-api-reference/Locale.Builder.asciidoc @@ -0,0 +1,21 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Locale-Builder]]++Locale.Builder++:: +* ++[[painless-api-reference-Locale-Builder-Locale.Builder-0]]link:{java8-javadoc}/java/util/Locale$Builder.html#Locale.Builder%2D%2D[Locale.Builder]()++ (link:{java9-javadoc}/java/util/Locale$Builder.html#Locale.Builder%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-Builder-addUnicodeLocaleAttribute-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#addUnicodeLocaleAttribute%2Djava.lang.String%2D[addUnicodeLocaleAttribute](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#addUnicodeLocaleAttribute%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-Builder-build-0]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#build%2D%2D[build]()++ (link:{java9-javadoc}/java/util/Locale$Builder.html#build%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-Builder-clear-0]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#clear%2D%2D[clear]()++ (link:{java9-javadoc}/java/util/Locale$Builder.html#clear%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-Builder-clearExtensions-0]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#clearExtensions%2D%2D[clearExtensions]()++ (link:{java9-javadoc}/java/util/Locale$Builder.html#clearExtensions%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-Builder-removeUnicodeLocaleAttribute-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#removeUnicodeLocaleAttribute%2Djava.lang.String%2D[removeUnicodeLocaleAttribute](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#removeUnicodeLocaleAttribute%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-Builder-setExtension-2]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setExtension%2Dchar%2Djava.lang.String%2D[setExtension](char, <>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setExtension%2Dchar%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-Builder-setLanguage-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setLanguage%2Djava.lang.String%2D[setLanguage](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setLanguage%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-Builder-setLanguageTag-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setLanguageTag%2Djava.lang.String%2D[setLanguageTag](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setLanguageTag%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-Builder-setLocale-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setLocale%2Djava.util.Locale%2D[setLocale](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setLocale%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Locale-Builder-setRegion-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setRegion%2Djava.lang.String%2D[setRegion](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setRegion%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-Builder-setScript-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setScript%2Djava.lang.String%2D[setScript](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setScript%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-Builder-setUnicodeLocaleKeyword-2]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setUnicodeLocaleKeyword%2Djava.lang.String%2Djava.lang.String%2D[setUnicodeLocaleKeyword](<>, <>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setUnicodeLocaleKeyword%2Djava.lang.String%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-Builder-setVariant-1]]<> link:{java8-javadoc}/java/util/Locale$Builder.html#setVariant%2Djava.lang.String%2D[setVariant](<>)++ (link:{java9-javadoc}/java/util/Locale$Builder.html#setVariant%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Locale.Category.asciidoc b/docs/painless/painless-api-reference/Locale.Category.asciidoc new file mode 100644 index 0000000000000..37a57018963dc --- /dev/null +++ b/docs/painless/painless-api-reference/Locale.Category.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Locale-Category]]++Locale.Category++:: +** [[painless-api-reference-Locale-Category-DISPLAY]]static <> link:{java8-javadoc}/java/util/Locale$Category.html#DISPLAY[DISPLAY] (link:{java9-javadoc}/java/util/Locale$Category.html#DISPLAY[java 9]) +** [[painless-api-reference-Locale-Category-FORMAT]]static <> link:{java8-javadoc}/java/util/Locale$Category.html#FORMAT[FORMAT] (link:{java9-javadoc}/java/util/Locale$Category.html#FORMAT[java 9]) +* ++[[painless-api-reference-Locale-Category-valueOf-1]]static <> link:{java8-javadoc}/java/util/Locale$Category.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/util/Locale$Category.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-Category-values-0]]static <>[] link:{java8-javadoc}/java/util/Locale$Category.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/util/Locale$Category.html#values%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Locale.FilteringMode.asciidoc b/docs/painless/painless-api-reference/Locale.FilteringMode.asciidoc new file mode 100644 index 0000000000000..e4399e146fcc0 --- /dev/null +++ b/docs/painless/painless-api-reference/Locale.FilteringMode.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Locale-FilteringMode]]++Locale.FilteringMode++:: +** [[painless-api-reference-Locale-FilteringMode-AUTOSELECT_FILTERING]]static <> link:{java8-javadoc}/java/util/Locale$FilteringMode.html#AUTOSELECT_FILTERING[AUTOSELECT_FILTERING] (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#AUTOSELECT_FILTERING[java 9]) +** [[painless-api-reference-Locale-FilteringMode-EXTENDED_FILTERING]]static <> link:{java8-javadoc}/java/util/Locale$FilteringMode.html#EXTENDED_FILTERING[EXTENDED_FILTERING] (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#EXTENDED_FILTERING[java 9]) +** [[painless-api-reference-Locale-FilteringMode-IGNORE_EXTENDED_RANGES]]static <> link:{java8-javadoc}/java/util/Locale$FilteringMode.html#IGNORE_EXTENDED_RANGES[IGNORE_EXTENDED_RANGES] (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#IGNORE_EXTENDED_RANGES[java 9]) +** [[painless-api-reference-Locale-FilteringMode-MAP_EXTENDED_RANGES]]static <> link:{java8-javadoc}/java/util/Locale$FilteringMode.html#MAP_EXTENDED_RANGES[MAP_EXTENDED_RANGES] (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#MAP_EXTENDED_RANGES[java 9]) +** [[painless-api-reference-Locale-FilteringMode-REJECT_EXTENDED_RANGES]]static <> link:{java8-javadoc}/java/util/Locale$FilteringMode.html#REJECT_EXTENDED_RANGES[REJECT_EXTENDED_RANGES] (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#REJECT_EXTENDED_RANGES[java 9]) +* ++[[painless-api-reference-Locale-FilteringMode-valueOf-1]]static <> link:{java8-javadoc}/java/util/Locale$FilteringMode.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-FilteringMode-values-0]]static <>[] link:{java8-javadoc}/java/util/Locale$FilteringMode.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/util/Locale$FilteringMode.html#values%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Locale.LanguageRange.asciidoc b/docs/painless/painless-api-reference/Locale.LanguageRange.asciidoc new file mode 100644 index 0000000000000..0e0038d3dd68d --- /dev/null +++ b/docs/painless/painless-api-reference/Locale.LanguageRange.asciidoc @@ -0,0 +1,16 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Locale-LanguageRange]]++Locale.LanguageRange++:: +** [[painless-api-reference-Locale-LanguageRange-MAX_WEIGHT]]static double link:{java8-javadoc}/java/util/Locale$LanguageRange.html#MAX_WEIGHT[MAX_WEIGHT] (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#MAX_WEIGHT[java 9]) +** [[painless-api-reference-Locale-LanguageRange-MIN_WEIGHT]]static double link:{java8-javadoc}/java/util/Locale$LanguageRange.html#MIN_WEIGHT[MIN_WEIGHT] (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#MIN_WEIGHT[java 9]) +* ++[[painless-api-reference-Locale-LanguageRange-mapEquivalents-2]]static <> link:{java8-javadoc}/java/util/Locale$LanguageRange.html#mapEquivalents%2Djava.util.List%2Djava.util.Map%2D[mapEquivalents](<>, <>)++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#mapEquivalents%2Djava.util.List%2Djava.util.Map%2D[java 9]) +* ++[[painless-api-reference-Locale-LanguageRange-parse-1]]static <> link:{java8-javadoc}/java/util/Locale$LanguageRange.html#parse%2Djava.lang.String%2D[parse](<>)++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#parse%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-LanguageRange-parse-2]]static <> link:{java8-javadoc}/java/util/Locale$LanguageRange.html#parse%2Djava.lang.String%2Djava.util.Map%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#parse%2Djava.lang.String%2Djava.util.Map%2D[java 9]) +* ++[[painless-api-reference-Locale-LanguageRange-Locale.LanguageRange-1]]link:{java8-javadoc}/java/util/Locale$LanguageRange.html#Locale.LanguageRange%2Djava.lang.String%2D[Locale.LanguageRange](<>)++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#Locale.LanguageRange%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-LanguageRange-Locale.LanguageRange-2]]link:{java8-javadoc}/java/util/Locale$LanguageRange.html#Locale.LanguageRange%2Djava.lang.String%2Ddouble%2D[Locale.LanguageRange](<>, double)++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#Locale.LanguageRange%2Djava.lang.String%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Locale-LanguageRange-getRange-0]]<> link:{java8-javadoc}/java/util/Locale$LanguageRange.html#getRange%2D%2D[getRange]()++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#getRange%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-LanguageRange-getWeight-0]]double link:{java8-javadoc}/java/util/Locale$LanguageRange.html#getWeight%2D%2D[getWeight]()++ (link:{java9-javadoc}/java/util/Locale$LanguageRange.html#getWeight%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Locale.asciidoc b/docs/painless/painless-api-reference/Locale.asciidoc new file mode 100644 index 0000000000000..d041c6c1f7fb3 --- /dev/null +++ b/docs/painless/painless-api-reference/Locale.asciidoc @@ -0,0 +1,69 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Locale]]++Locale++:: +** [[painless-api-reference-Locale-CANADA]]static <> link:{java8-javadoc}/java/util/Locale.html#CANADA[CANADA] (link:{java9-javadoc}/java/util/Locale.html#CANADA[java 9]) +** [[painless-api-reference-Locale-CANADA_FRENCH]]static <> link:{java8-javadoc}/java/util/Locale.html#CANADA_FRENCH[CANADA_FRENCH] (link:{java9-javadoc}/java/util/Locale.html#CANADA_FRENCH[java 9]) +** [[painless-api-reference-Locale-CHINA]]static <> link:{java8-javadoc}/java/util/Locale.html#CHINA[CHINA] (link:{java9-javadoc}/java/util/Locale.html#CHINA[java 9]) +** [[painless-api-reference-Locale-CHINESE]]static <> link:{java8-javadoc}/java/util/Locale.html#CHINESE[CHINESE] (link:{java9-javadoc}/java/util/Locale.html#CHINESE[java 9]) +** [[painless-api-reference-Locale-ENGLISH]]static <> link:{java8-javadoc}/java/util/Locale.html#ENGLISH[ENGLISH] (link:{java9-javadoc}/java/util/Locale.html#ENGLISH[java 9]) +** [[painless-api-reference-Locale-FRANCE]]static <> link:{java8-javadoc}/java/util/Locale.html#FRANCE[FRANCE] (link:{java9-javadoc}/java/util/Locale.html#FRANCE[java 9]) +** [[painless-api-reference-Locale-FRENCH]]static <> link:{java8-javadoc}/java/util/Locale.html#FRENCH[FRENCH] (link:{java9-javadoc}/java/util/Locale.html#FRENCH[java 9]) +** [[painless-api-reference-Locale-GERMAN]]static <> link:{java8-javadoc}/java/util/Locale.html#GERMAN[GERMAN] (link:{java9-javadoc}/java/util/Locale.html#GERMAN[java 9]) +** [[painless-api-reference-Locale-GERMANY]]static <> link:{java8-javadoc}/java/util/Locale.html#GERMANY[GERMANY] (link:{java9-javadoc}/java/util/Locale.html#GERMANY[java 9]) +** [[painless-api-reference-Locale-ITALIAN]]static <> link:{java8-javadoc}/java/util/Locale.html#ITALIAN[ITALIAN] (link:{java9-javadoc}/java/util/Locale.html#ITALIAN[java 9]) +** [[painless-api-reference-Locale-ITALY]]static <> link:{java8-javadoc}/java/util/Locale.html#ITALY[ITALY] (link:{java9-javadoc}/java/util/Locale.html#ITALY[java 9]) +** [[painless-api-reference-Locale-JAPAN]]static <> link:{java8-javadoc}/java/util/Locale.html#JAPAN[JAPAN] (link:{java9-javadoc}/java/util/Locale.html#JAPAN[java 9]) +** [[painless-api-reference-Locale-JAPANESE]]static <> link:{java8-javadoc}/java/util/Locale.html#JAPANESE[JAPANESE] (link:{java9-javadoc}/java/util/Locale.html#JAPANESE[java 9]) +** [[painless-api-reference-Locale-KOREA]]static <> link:{java8-javadoc}/java/util/Locale.html#KOREA[KOREA] (link:{java9-javadoc}/java/util/Locale.html#KOREA[java 9]) +** [[painless-api-reference-Locale-KOREAN]]static <> link:{java8-javadoc}/java/util/Locale.html#KOREAN[KOREAN] (link:{java9-javadoc}/java/util/Locale.html#KOREAN[java 9]) +** [[painless-api-reference-Locale-PRC]]static <> link:{java8-javadoc}/java/util/Locale.html#PRC[PRC] (link:{java9-javadoc}/java/util/Locale.html#PRC[java 9]) +** [[painless-api-reference-Locale-PRIVATE_USE_EXTENSION]]static char link:{java8-javadoc}/java/util/Locale.html#PRIVATE_USE_EXTENSION[PRIVATE_USE_EXTENSION] (link:{java9-javadoc}/java/util/Locale.html#PRIVATE_USE_EXTENSION[java 9]) +** [[painless-api-reference-Locale-ROOT]]static <> link:{java8-javadoc}/java/util/Locale.html#ROOT[ROOT] (link:{java9-javadoc}/java/util/Locale.html#ROOT[java 9]) +** [[painless-api-reference-Locale-SIMPLIFIED_CHINESE]]static <> link:{java8-javadoc}/java/util/Locale.html#SIMPLIFIED_CHINESE[SIMPLIFIED_CHINESE] (link:{java9-javadoc}/java/util/Locale.html#SIMPLIFIED_CHINESE[java 9]) +** [[painless-api-reference-Locale-TAIWAN]]static <> link:{java8-javadoc}/java/util/Locale.html#TAIWAN[TAIWAN] (link:{java9-javadoc}/java/util/Locale.html#TAIWAN[java 9]) +** [[painless-api-reference-Locale-TRADITIONAL_CHINESE]]static <> link:{java8-javadoc}/java/util/Locale.html#TRADITIONAL_CHINESE[TRADITIONAL_CHINESE] (link:{java9-javadoc}/java/util/Locale.html#TRADITIONAL_CHINESE[java 9]) +** [[painless-api-reference-Locale-UK]]static <> link:{java8-javadoc}/java/util/Locale.html#UK[UK] (link:{java9-javadoc}/java/util/Locale.html#UK[java 9]) +** [[painless-api-reference-Locale-UNICODE_LOCALE_EXTENSION]]static char link:{java8-javadoc}/java/util/Locale.html#UNICODE_LOCALE_EXTENSION[UNICODE_LOCALE_EXTENSION] (link:{java9-javadoc}/java/util/Locale.html#UNICODE_LOCALE_EXTENSION[java 9]) +** [[painless-api-reference-Locale-US]]static <> link:{java8-javadoc}/java/util/Locale.html#US[US] (link:{java9-javadoc}/java/util/Locale.html#US[java 9]) +* ++[[painless-api-reference-Locale-filter-2]]static <> link:{java8-javadoc}/java/util/Locale.html#filter%2Djava.util.List%2Djava.util.Collection%2D[filter](<>, <>)++ (link:{java9-javadoc}/java/util/Locale.html#filter%2Djava.util.List%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Locale-filterTags-2]]static <> link:{java8-javadoc}/java/util/Locale.html#filterTags%2Djava.util.List%2Djava.util.Collection%2D[filterTags](<>, <>)++ (link:{java9-javadoc}/java/util/Locale.html#filterTags%2Djava.util.List%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Locale-forLanguageTag-1]]static <> link:{java8-javadoc}/java/util/Locale.html#forLanguageTag%2Djava.lang.String%2D[forLanguageTag](<>)++ (link:{java9-javadoc}/java/util/Locale.html#forLanguageTag%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/util/Locale.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/util/Locale.html#getAvailableLocales%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getDefault-0]]static <> link:{java8-javadoc}/java/util/Locale.html#getDefault%2D%2D[getDefault]()++ (link:{java9-javadoc}/java/util/Locale.html#getDefault%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getDefault-1]]static <> link:{java8-javadoc}/java/util/Locale.html#getDefault%2Djava.util.Locale$Category%2D[getDefault](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getDefault%2Djava.util.Locale$Category%2D[java 9]) +* ++[[painless-api-reference-Locale-getISOCountries-0]]static <>[] link:{java8-javadoc}/java/util/Locale.html#getISOCountries%2D%2D[getISOCountries]()++ (link:{java9-javadoc}/java/util/Locale.html#getISOCountries%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getISOLanguages-0]]static <>[] link:{java8-javadoc}/java/util/Locale.html#getISOLanguages%2D%2D[getISOLanguages]()++ (link:{java9-javadoc}/java/util/Locale.html#getISOLanguages%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-lookup-2]]static <> link:{java8-javadoc}/java/util/Locale.html#lookup%2Djava.util.List%2Djava.util.Collection%2D[lookup](<>, <>)++ (link:{java9-javadoc}/java/util/Locale.html#lookup%2Djava.util.List%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Locale-lookupTag-2]]static <> link:{java8-javadoc}/java/util/Locale.html#lookupTag%2Djava.util.List%2Djava.util.Collection%2D[lookupTag](<>, <>)++ (link:{java9-javadoc}/java/util/Locale.html#lookupTag%2Djava.util.List%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Locale-Locale-1]]link:{java8-javadoc}/java/util/Locale.html#Locale%2Djava.lang.String%2D[Locale](<>)++ (link:{java9-javadoc}/java/util/Locale.html#Locale%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-Locale-2]]link:{java8-javadoc}/java/util/Locale.html#Locale%2Djava.lang.String%2Djava.lang.String%2D[Locale](<>, <>)++ (link:{java9-javadoc}/java/util/Locale.html#Locale%2Djava.lang.String%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-Locale-3]]link:{java8-javadoc}/java/util/Locale.html#Locale%2Djava.lang.String%2Djava.lang.String%2Djava.lang.String%2D[Locale](<>, <>, <>)++ (link:{java9-javadoc}/java/util/Locale.html#Locale%2Djava.lang.String%2Djava.lang.String%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-clone-0]]def link:{java8-javadoc}/java/util/Locale.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/Locale.html#clone%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getCountry-0]]<> link:{java8-javadoc}/java/util/Locale.html#getCountry%2D%2D[getCountry]()++ (link:{java9-javadoc}/java/util/Locale.html#getCountry%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getDisplayCountry-0]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayCountry%2D%2D[getDisplayCountry]()++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayCountry%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getDisplayCountry-1]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayCountry%2Djava.util.Locale%2D[getDisplayCountry](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayCountry%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Locale-getDisplayLanguage-0]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayLanguage%2D%2D[getDisplayLanguage]()++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayLanguage%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getDisplayLanguage-1]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayLanguage%2Djava.util.Locale%2D[getDisplayLanguage](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayLanguage%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Locale-getDisplayName-0]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayName%2D%2D[getDisplayName]()++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayName%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getDisplayName-1]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayName%2Djava.util.Locale%2D[getDisplayName](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayName%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Locale-getDisplayScript-0]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayScript%2D%2D[getDisplayScript]()++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayScript%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getDisplayScript-1]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayScript%2Djava.util.Locale%2D[getDisplayScript](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayScript%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Locale-getDisplayVariant-0]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayVariant%2D%2D[getDisplayVariant]()++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayVariant%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getDisplayVariant-1]]<> link:{java8-javadoc}/java/util/Locale.html#getDisplayVariant%2Djava.util.Locale%2D[getDisplayVariant](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getDisplayVariant%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Locale-getExtension-1]]<> link:{java8-javadoc}/java/util/Locale.html#getExtension%2Dchar%2D[getExtension](char)++ (link:{java9-javadoc}/java/util/Locale.html#getExtension%2Dchar%2D[java 9]) +* ++[[painless-api-reference-Locale-getExtensionKeys-0]]<> link:{java8-javadoc}/java/util/Locale.html#getExtensionKeys%2D%2D[getExtensionKeys]()++ (link:{java9-javadoc}/java/util/Locale.html#getExtensionKeys%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getISO3Country-0]]<> link:{java8-javadoc}/java/util/Locale.html#getISO3Country%2D%2D[getISO3Country]()++ (link:{java9-javadoc}/java/util/Locale.html#getISO3Country%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getISO3Language-0]]<> link:{java8-javadoc}/java/util/Locale.html#getISO3Language%2D%2D[getISO3Language]()++ (link:{java9-javadoc}/java/util/Locale.html#getISO3Language%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getLanguage-0]]<> link:{java8-javadoc}/java/util/Locale.html#getLanguage%2D%2D[getLanguage]()++ (link:{java9-javadoc}/java/util/Locale.html#getLanguage%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getScript-0]]<> link:{java8-javadoc}/java/util/Locale.html#getScript%2D%2D[getScript]()++ (link:{java9-javadoc}/java/util/Locale.html#getScript%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getUnicodeLocaleAttributes-0]]<> link:{java8-javadoc}/java/util/Locale.html#getUnicodeLocaleAttributes%2D%2D[getUnicodeLocaleAttributes]()++ (link:{java9-javadoc}/java/util/Locale.html#getUnicodeLocaleAttributes%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getUnicodeLocaleKeys-0]]<> link:{java8-javadoc}/java/util/Locale.html#getUnicodeLocaleKeys%2D%2D[getUnicodeLocaleKeys]()++ (link:{java9-javadoc}/java/util/Locale.html#getUnicodeLocaleKeys%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-getUnicodeLocaleType-1]]<> link:{java8-javadoc}/java/util/Locale.html#getUnicodeLocaleType%2Djava.lang.String%2D[getUnicodeLocaleType](<>)++ (link:{java9-javadoc}/java/util/Locale.html#getUnicodeLocaleType%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Locale-getVariant-0]]<> link:{java8-javadoc}/java/util/Locale.html#getVariant%2D%2D[getVariant]()++ (link:{java9-javadoc}/java/util/Locale.html#getVariant%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-hasExtensions-0]]boolean link:{java8-javadoc}/java/util/Locale.html#hasExtensions%2D%2D[hasExtensions]()++ (link:{java9-javadoc}/java/util/Locale.html#hasExtensions%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-stripExtensions-0]]<> link:{java8-javadoc}/java/util/Locale.html#stripExtensions%2D%2D[stripExtensions]()++ (link:{java9-javadoc}/java/util/Locale.html#stripExtensions%2D%2D[java 9]) +* ++[[painless-api-reference-Locale-toLanguageTag-0]]<> link:{java8-javadoc}/java/util/Locale.html#toLanguageTag%2D%2D[toLanguageTag]()++ (link:{java9-javadoc}/java/util/Locale.html#toLanguageTag%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Long.asciidoc b/docs/painless/painless-api-reference/Long.asciidoc new file mode 100644 index 0000000000000..e35c80f63b96d --- /dev/null +++ b/docs/painless/painless-api-reference/Long.asciidoc @@ -0,0 +1,44 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Long]]++Long++:: +** [[painless-api-reference-Long-BYTES]]static int link:{java8-javadoc}/java/lang/Long.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Long.html#BYTES[java 9]) +** [[painless-api-reference-Long-MAX_VALUE]]static long link:{java8-javadoc}/java/lang/Long.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Long.html#MAX_VALUE[java 9]) +** [[painless-api-reference-Long-MIN_VALUE]]static long link:{java8-javadoc}/java/lang/Long.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Long.html#MIN_VALUE[java 9]) +** [[painless-api-reference-Long-SIZE]]static int link:{java8-javadoc}/java/lang/Long.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Long.html#SIZE[java 9]) +* ++[[painless-api-reference-Long-bitCount-1]]static int link:{java8-javadoc}/java/lang/Long.html#bitCount%2Dlong%2D[bitCount](long)++ (link:{java9-javadoc}/java/lang/Long.html#bitCount%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-compare-2]]static int link:{java8-javadoc}/java/lang/Long.html#compare%2Dlong%2Dlong%2D[compare](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#compare%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-compareUnsigned-2]]static int link:{java8-javadoc}/java/lang/Long.html#compareUnsigned%2Dlong%2Dlong%2D[compareUnsigned](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#compareUnsigned%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-decode-1]]static <> link:{java8-javadoc}/java/lang/Long.html#decode%2Djava.lang.String%2D[decode](<>)++ (link:{java9-javadoc}/java/lang/Long.html#decode%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Long-divideUnsigned-2]]static long link:{java8-javadoc}/java/lang/Long.html#divideUnsigned%2Dlong%2Dlong%2D[divideUnsigned](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#divideUnsigned%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-hashCode-1]]static int link:{java8-javadoc}/java/lang/Long.html#hashCode%2Dlong%2D[hashCode](long)++ (link:{java9-javadoc}/java/lang/Long.html#hashCode%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-highestOneBit-1]]static long link:{java8-javadoc}/java/lang/Long.html#highestOneBit%2Dlong%2D[highestOneBit](long)++ (link:{java9-javadoc}/java/lang/Long.html#highestOneBit%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-lowestOneBit-1]]static long link:{java8-javadoc}/java/lang/Long.html#lowestOneBit%2Dlong%2D[lowestOneBit](long)++ (link:{java9-javadoc}/java/lang/Long.html#lowestOneBit%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-max-2]]static long link:{java8-javadoc}/java/lang/Long.html#max%2Dlong%2Dlong%2D[max](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#max%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-min-2]]static long link:{java8-javadoc}/java/lang/Long.html#min%2Dlong%2Dlong%2D[min](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#min%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-numberOfLeadingZeros-1]]static int link:{java8-javadoc}/java/lang/Long.html#numberOfLeadingZeros%2Dlong%2D[numberOfLeadingZeros](long)++ (link:{java9-javadoc}/java/lang/Long.html#numberOfLeadingZeros%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-numberOfTrailingZeros-1]]static int link:{java8-javadoc}/java/lang/Long.html#numberOfTrailingZeros%2Dlong%2D[numberOfTrailingZeros](long)++ (link:{java9-javadoc}/java/lang/Long.html#numberOfTrailingZeros%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-parseLong-1]]static long link:{java8-javadoc}/java/lang/Long.html#parseLong%2Djava.lang.String%2D[parseLong](<>)++ (link:{java9-javadoc}/java/lang/Long.html#parseLong%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Long-parseLong-2]]static long link:{java8-javadoc}/java/lang/Long.html#parseLong%2Djava.lang.String%2Dint%2D[parseLong](<>, int)++ (link:{java9-javadoc}/java/lang/Long.html#parseLong%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-Long-parseUnsignedLong-1]]static long link:{java8-javadoc}/java/lang/Long.html#parseUnsignedLong%2Djava.lang.String%2D[parseUnsignedLong](<>)++ (link:{java9-javadoc}/java/lang/Long.html#parseUnsignedLong%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Long-parseUnsignedLong-2]]static long link:{java8-javadoc}/java/lang/Long.html#parseUnsignedLong%2Djava.lang.String%2Dint%2D[parseUnsignedLong](<>, int)++ (link:{java9-javadoc}/java/lang/Long.html#parseUnsignedLong%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-Long-remainderUnsigned-2]]static long link:{java8-javadoc}/java/lang/Long.html#remainderUnsigned%2Dlong%2Dlong%2D[remainderUnsigned](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#remainderUnsigned%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-reverse-1]]static long link:{java8-javadoc}/java/lang/Long.html#reverse%2Dlong%2D[reverse](long)++ (link:{java9-javadoc}/java/lang/Long.html#reverse%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-reverseBytes-1]]static long link:{java8-javadoc}/java/lang/Long.html#reverseBytes%2Dlong%2D[reverseBytes](long)++ (link:{java9-javadoc}/java/lang/Long.html#reverseBytes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-rotateLeft-2]]static long link:{java8-javadoc}/java/lang/Long.html#rotateLeft%2Dlong%2Dint%2D[rotateLeft](long, int)++ (link:{java9-javadoc}/java/lang/Long.html#rotateLeft%2Dlong%2Dint%2D[java 9]) +* ++[[painless-api-reference-Long-rotateRight-2]]static long link:{java8-javadoc}/java/lang/Long.html#rotateRight%2Dlong%2Dint%2D[rotateRight](long, int)++ (link:{java9-javadoc}/java/lang/Long.html#rotateRight%2Dlong%2Dint%2D[java 9]) +* ++[[painless-api-reference-Long-signum-1]]static int link:{java8-javadoc}/java/lang/Long.html#signum%2Dlong%2D[signum](long)++ (link:{java9-javadoc}/java/lang/Long.html#signum%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-sum-2]]static long link:{java8-javadoc}/java/lang/Long.html#sum%2Dlong%2Dlong%2D[sum](long, long)++ (link:{java9-javadoc}/java/lang/Long.html#sum%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-toBinaryString-1]]static <> link:{java8-javadoc}/java/lang/Long.html#toBinaryString%2Dlong%2D[toBinaryString](long)++ (link:{java9-javadoc}/java/lang/Long.html#toBinaryString%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-toHexString-1]]static <> link:{java8-javadoc}/java/lang/Long.html#toHexString%2Dlong%2D[toHexString](long)++ (link:{java9-javadoc}/java/lang/Long.html#toHexString%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-toOctalString-1]]static <> link:{java8-javadoc}/java/lang/Long.html#toOctalString%2Dlong%2D[toOctalString](long)++ (link:{java9-javadoc}/java/lang/Long.html#toOctalString%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-toString-1]]static <> link:{java8-javadoc}/java/lang/Long.html#toString%2Dlong%2D[toString](long)++ (link:{java9-javadoc}/java/lang/Long.html#toString%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-toString-2]]static <> link:{java8-javadoc}/java/lang/Long.html#toString%2Dlong%2Dint%2D[toString](long, int)++ (link:{java9-javadoc}/java/lang/Long.html#toString%2Dlong%2Dint%2D[java 9]) +* ++[[painless-api-reference-Long-toUnsignedString-1]]static <> link:{java8-javadoc}/java/lang/Long.html#toUnsignedString%2Dlong%2D[toUnsignedString](long)++ (link:{java9-javadoc}/java/lang/Long.html#toUnsignedString%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-toUnsignedString-2]]static <> link:{java8-javadoc}/java/lang/Long.html#toUnsignedString%2Dlong%2Dint%2D[toUnsignedString](long, int)++ (link:{java9-javadoc}/java/lang/Long.html#toUnsignedString%2Dlong%2Dint%2D[java 9]) +* ++[[painless-api-reference-Long-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Long.html#valueOf%2Dlong%2D[valueOf](long)++ (link:{java9-javadoc}/java/lang/Long.html#valueOf%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Long-valueOf-2]]static <> link:{java8-javadoc}/java/lang/Long.html#valueOf%2Djava.lang.String%2Dint%2D[valueOf](<>, int)++ (link:{java9-javadoc}/java/lang/Long.html#valueOf%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-Long-compareTo-1]]int link:{java8-javadoc}/java/lang/Long.html#compareTo%2Djava.lang.Long%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Long.html#compareTo%2Djava.lang.Long%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/LongBinaryOperator.asciidoc b/docs/painless/painless-api-reference/LongBinaryOperator.asciidoc new file mode 100644 index 0000000000000..45a777a69715b --- /dev/null +++ b/docs/painless/painless-api-reference/LongBinaryOperator.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LongBinaryOperator]]++LongBinaryOperator++:: +* ++[[painless-api-reference-LongBinaryOperator-applyAsLong-2]]long link:{java8-javadoc}/java/util/function/LongBinaryOperator.html#applyAsLong%2Dlong%2Dlong%2D[applyAsLong](long, long)++ (link:{java9-javadoc}/java/util/function/LongBinaryOperator.html#applyAsLong%2Dlong%2Dlong%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/LongConsumer.asciidoc b/docs/painless/painless-api-reference/LongConsumer.asciidoc new file mode 100644 index 0000000000000..28bbc92c03121 --- /dev/null +++ b/docs/painless/painless-api-reference/LongConsumer.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LongConsumer]]++LongConsumer++:: +* ++[[painless-api-reference-LongConsumer-accept-1]]void link:{java8-javadoc}/java/util/function/LongConsumer.html#accept%2Dlong%2D[accept](long)++ (link:{java9-javadoc}/java/util/function/LongConsumer.html#accept%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LongConsumer-andThen-1]]<> link:{java8-javadoc}/java/util/function/LongConsumer.html#andThen%2Djava.util.function.LongConsumer%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/LongConsumer.html#andThen%2Djava.util.function.LongConsumer%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/LongFunction.asciidoc b/docs/painless/painless-api-reference/LongFunction.asciidoc new file mode 100644 index 0000000000000..033392914a5e3 --- /dev/null +++ b/docs/painless/painless-api-reference/LongFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LongFunction]]++LongFunction++:: +* ++[[painless-api-reference-LongFunction-apply-1]]def link:{java8-javadoc}/java/util/function/LongFunction.html#apply%2Dlong%2D[apply](long)++ (link:{java9-javadoc}/java/util/function/LongFunction.html#apply%2Dlong%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/LongPredicate.asciidoc b/docs/painless/painless-api-reference/LongPredicate.asciidoc new file mode 100644 index 0000000000000..59b719572e3d1 --- /dev/null +++ b/docs/painless/painless-api-reference/LongPredicate.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LongPredicate]]++LongPredicate++:: +* ++[[painless-api-reference-LongPredicate-and-1]]<> link:{java8-javadoc}/java/util/function/LongPredicate.html#and%2Djava.util.function.LongPredicate%2D[and](<>)++ (link:{java9-javadoc}/java/util/function/LongPredicate.html#and%2Djava.util.function.LongPredicate%2D[java 9]) +* ++[[painless-api-reference-LongPredicate-negate-0]]<> link:{java8-javadoc}/java/util/function/LongPredicate.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/util/function/LongPredicate.html#negate%2D%2D[java 9]) +* ++[[painless-api-reference-LongPredicate-or-1]]<> link:{java8-javadoc}/java/util/function/LongPredicate.html#or%2Djava.util.function.LongPredicate%2D[or](<>)++ (link:{java9-javadoc}/java/util/function/LongPredicate.html#or%2Djava.util.function.LongPredicate%2D[java 9]) +* ++[[painless-api-reference-LongPredicate-test-1]]boolean link:{java8-javadoc}/java/util/function/LongPredicate.html#test%2Dlong%2D[test](long)++ (link:{java9-javadoc}/java/util/function/LongPredicate.html#test%2Dlong%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/LongStream.Builder.asciidoc b/docs/painless/painless-api-reference/LongStream.Builder.asciidoc new file mode 100644 index 0000000000000..98c55b149ae68 --- /dev/null +++ b/docs/painless/painless-api-reference/LongStream.Builder.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LongStream-Builder]]++LongStream.Builder++:: +* ++[[painless-api-reference-LongStream-Builder-add-1]]<> link:{java8-javadoc}/java/util/stream/LongStream$Builder.html#add%2Dlong%2D[add](long)++ (link:{java9-javadoc}/java/util/stream/LongStream$Builder.html#add%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LongStream-Builder-build-0]]<> link:{java8-javadoc}/java/util/stream/LongStream$Builder.html#build%2D%2D[build]()++ (link:{java9-javadoc}/java/util/stream/LongStream$Builder.html#build%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/LongStream.asciidoc b/docs/painless/painless-api-reference/LongStream.asciidoc new file mode 100644 index 0000000000000..08ea178451a0a --- /dev/null +++ b/docs/painless/painless-api-reference/LongStream.asciidoc @@ -0,0 +1,46 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LongStream]]++LongStream++:: +* ++[[painless-api-reference-LongStream-builder-0]]static <> link:{java8-javadoc}/java/util/stream/LongStream.html#builder%2D%2D[builder]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#builder%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-concat-2]]static <> link:{java8-javadoc}/java/util/stream/LongStream.html#concat%2Djava.util.stream.LongStream%2Djava.util.stream.LongStream%2D[concat](<>, <>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#concat%2Djava.util.stream.LongStream%2Djava.util.stream.LongStream%2D[java 9]) +* ++[[painless-api-reference-LongStream-empty-0]]static <> link:{java8-javadoc}/java/util/stream/LongStream.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#empty%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-of-1]]static <> link:{java8-javadoc}/java/util/stream/LongStream.html#of%2Dlong:A%2D[of](long[])++ (link:{java9-javadoc}/java/util/stream/LongStream.html#of%2Dlong:A%2D[java 9]) +* ++[[painless-api-reference-LongStream-range-2]]static <> link:{java8-javadoc}/java/util/stream/LongStream.html#range%2Dlong%2Dlong%2D[range](long, long)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#range%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LongStream-rangeClosed-2]]static <> link:{java8-javadoc}/java/util/stream/LongStream.html#rangeClosed%2Dlong%2Dlong%2D[rangeClosed](long, long)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#rangeClosed%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LongStream-allMatch-1]]boolean link:{java8-javadoc}/java/util/stream/LongStream.html#allMatch%2Djava.util.function.LongPredicate%2D[allMatch](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#allMatch%2Djava.util.function.LongPredicate%2D[java 9]) +* ++[[painless-api-reference-LongStream-anyMatch-1]]boolean link:{java8-javadoc}/java/util/stream/LongStream.html#anyMatch%2Djava.util.function.LongPredicate%2D[anyMatch](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#anyMatch%2Djava.util.function.LongPredicate%2D[java 9]) +* ++[[painless-api-reference-LongStream-asDoubleStream-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#asDoubleStream%2D%2D[asDoubleStream]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#asDoubleStream%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-average-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#average%2D%2D[average]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#average%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-boxed-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#boxed%2D%2D[boxed]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#boxed%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-collect-3]]def link:{java8-javadoc}/java/util/stream/LongStream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.ObjLongConsumer%2Djava.util.function.BiConsumer%2D[collect](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.ObjLongConsumer%2Djava.util.function.BiConsumer%2D[java 9]) +* ++[[painless-api-reference-LongStream-count-0]]long link:{java8-javadoc}/java/util/stream/LongStream.html#count%2D%2D[count]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#count%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-distinct-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#distinct%2D%2D[distinct]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#distinct%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-filter-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#filter%2Djava.util.function.LongPredicate%2D[filter](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#filter%2Djava.util.function.LongPredicate%2D[java 9]) +* ++[[painless-api-reference-LongStream-findAny-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#findAny%2D%2D[findAny]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#findAny%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-findFirst-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#findFirst%2D%2D[findFirst]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#findFirst%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-flatMap-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#flatMap%2Djava.util.function.LongFunction%2D[flatMap](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#flatMap%2Djava.util.function.LongFunction%2D[java 9]) +* ++[[painless-api-reference-LongStream-forEach-1]]void link:{java8-javadoc}/java/util/stream/LongStream.html#forEach%2Djava.util.function.LongConsumer%2D[forEach](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#forEach%2Djava.util.function.LongConsumer%2D[java 9]) +* ++[[painless-api-reference-LongStream-forEachOrdered-1]]void link:{java8-javadoc}/java/util/stream/LongStream.html#forEachOrdered%2Djava.util.function.LongConsumer%2D[forEachOrdered](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#forEachOrdered%2Djava.util.function.LongConsumer%2D[java 9]) +* ++[[painless-api-reference-LongStream-iterator-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#iterator%2D%2D[iterator]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#iterator%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-limit-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#limit%2Dlong%2D[limit](long)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#limit%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LongStream-map-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#map%2Djava.util.function.LongUnaryOperator%2D[map](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#map%2Djava.util.function.LongUnaryOperator%2D[java 9]) +* ++[[painless-api-reference-LongStream-mapToDouble-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#mapToDouble%2Djava.util.function.LongToDoubleFunction%2D[mapToDouble](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#mapToDouble%2Djava.util.function.LongToDoubleFunction%2D[java 9]) +* ++[[painless-api-reference-LongStream-mapToInt-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#mapToInt%2Djava.util.function.LongToIntFunction%2D[mapToInt](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#mapToInt%2Djava.util.function.LongToIntFunction%2D[java 9]) +* ++[[painless-api-reference-LongStream-mapToObj-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#mapToObj%2Djava.util.function.LongFunction%2D[mapToObj](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#mapToObj%2Djava.util.function.LongFunction%2D[java 9]) +* ++[[painless-api-reference-LongStream-max-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#max%2D%2D[max]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#max%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-min-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#min%2D%2D[min]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#min%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-noneMatch-1]]boolean link:{java8-javadoc}/java/util/stream/LongStream.html#noneMatch%2Djava.util.function.LongPredicate%2D[noneMatch](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#noneMatch%2Djava.util.function.LongPredicate%2D[java 9]) +* ++[[painless-api-reference-LongStream-peek-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#peek%2Djava.util.function.LongConsumer%2D[peek](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#peek%2Djava.util.function.LongConsumer%2D[java 9]) +* ++[[painless-api-reference-LongStream-reduce-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#reduce%2Djava.util.function.LongBinaryOperator%2D[reduce](<>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#reduce%2Djava.util.function.LongBinaryOperator%2D[java 9]) +* ++[[painless-api-reference-LongStream-reduce-2]]long link:{java8-javadoc}/java/util/stream/LongStream.html#reduce%2Dlong%2Djava.util.function.LongBinaryOperator%2D[reduce](long, <>)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#reduce%2Dlong%2Djava.util.function.LongBinaryOperator%2D[java 9]) +* ++[[painless-api-reference-LongStream-sequential-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#sequential%2D%2D[sequential]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#sequential%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-skip-1]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#skip%2Dlong%2D[skip](long)++ (link:{java9-javadoc}/java/util/stream/LongStream.html#skip%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LongStream-sorted-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#sorted%2D%2D[sorted]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#sorted%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-spliterator-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#spliterator%2D%2D[spliterator]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#spliterator%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-sum-0]]long link:{java8-javadoc}/java/util/stream/LongStream.html#sum%2D%2D[sum]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#sum%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-summaryStatistics-0]]<> link:{java8-javadoc}/java/util/stream/LongStream.html#summaryStatistics%2D%2D[summaryStatistics]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#summaryStatistics%2D%2D[java 9]) +* ++[[painless-api-reference-LongStream-toArray-0]]long[] link:{java8-javadoc}/java/util/stream/LongStream.html#toArray%2D%2D[toArray]()++ (link:{java9-javadoc}/java/util/stream/LongStream.html#toArray%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/LongSummaryStatistics.asciidoc b/docs/painless/painless-api-reference/LongSummaryStatistics.asciidoc new file mode 100644 index 0000000000000..b678d51efa4ac --- /dev/null +++ b/docs/painless/painless-api-reference/LongSummaryStatistics.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LongSummaryStatistics]]++LongSummaryStatistics++:: +* ++[[painless-api-reference-LongSummaryStatistics-LongSummaryStatistics-0]]link:{java8-javadoc}/java/util/LongSummaryStatistics.html#LongSummaryStatistics%2D%2D[LongSummaryStatistics]()++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#LongSummaryStatistics%2D%2D[java 9]) +* ++[[painless-api-reference-LongSummaryStatistics-combine-1]]void link:{java8-javadoc}/java/util/LongSummaryStatistics.html#combine%2Djava.util.LongSummaryStatistics%2D[combine](<>)++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#combine%2Djava.util.LongSummaryStatistics%2D[java 9]) +* ++[[painless-api-reference-LongSummaryStatistics-getAverage-0]]double link:{java8-javadoc}/java/util/LongSummaryStatistics.html#getAverage%2D%2D[getAverage]()++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#getAverage%2D%2D[java 9]) +* ++[[painless-api-reference-LongSummaryStatistics-getCount-0]]long link:{java8-javadoc}/java/util/LongSummaryStatistics.html#getCount%2D%2D[getCount]()++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#getCount%2D%2D[java 9]) +* ++[[painless-api-reference-LongSummaryStatistics-getMax-0]]long link:{java8-javadoc}/java/util/LongSummaryStatistics.html#getMax%2D%2D[getMax]()++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#getMax%2D%2D[java 9]) +* ++[[painless-api-reference-LongSummaryStatistics-getMin-0]]long link:{java8-javadoc}/java/util/LongSummaryStatistics.html#getMin%2D%2D[getMin]()++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#getMin%2D%2D[java 9]) +* ++[[painless-api-reference-LongSummaryStatistics-getSum-0]]long link:{java8-javadoc}/java/util/LongSummaryStatistics.html#getSum%2D%2D[getSum]()++ (link:{java9-javadoc}/java/util/LongSummaryStatistics.html#getSum%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/LongSupplier.asciidoc b/docs/painless/painless-api-reference/LongSupplier.asciidoc new file mode 100644 index 0000000000000..6693e51f17a47 --- /dev/null +++ b/docs/painless/painless-api-reference/LongSupplier.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LongSupplier]]++LongSupplier++:: +* ++[[painless-api-reference-LongSupplier-getAsLong-0]]long link:{java8-javadoc}/java/util/function/LongSupplier.html#getAsLong%2D%2D[getAsLong]()++ (link:{java9-javadoc}/java/util/function/LongSupplier.html#getAsLong%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/LongToDoubleFunction.asciidoc b/docs/painless/painless-api-reference/LongToDoubleFunction.asciidoc new file mode 100644 index 0000000000000..0bed3833371c7 --- /dev/null +++ b/docs/painless/painless-api-reference/LongToDoubleFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LongToDoubleFunction]]++LongToDoubleFunction++:: +* ++[[painless-api-reference-LongToDoubleFunction-applyAsDouble-1]]double link:{java8-javadoc}/java/util/function/LongToDoubleFunction.html#applyAsDouble%2Dlong%2D[applyAsDouble](long)++ (link:{java9-javadoc}/java/util/function/LongToDoubleFunction.html#applyAsDouble%2Dlong%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/LongToIntFunction.asciidoc b/docs/painless/painless-api-reference/LongToIntFunction.asciidoc new file mode 100644 index 0000000000000..2586ba85511b0 --- /dev/null +++ b/docs/painless/painless-api-reference/LongToIntFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LongToIntFunction]]++LongToIntFunction++:: +* ++[[painless-api-reference-LongToIntFunction-applyAsInt-1]]int link:{java8-javadoc}/java/util/function/LongToIntFunction.html#applyAsInt%2Dlong%2D[applyAsInt](long)++ (link:{java9-javadoc}/java/util/function/LongToIntFunction.html#applyAsInt%2Dlong%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/LongUnaryOperator.asciidoc b/docs/painless/painless-api-reference/LongUnaryOperator.asciidoc new file mode 100644 index 0000000000000..eb84a3688bdeb --- /dev/null +++ b/docs/painless/painless-api-reference/LongUnaryOperator.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-LongUnaryOperator]]++LongUnaryOperator++:: +* ++[[painless-api-reference-LongUnaryOperator-identity-0]]static <> link:{java8-javadoc}/java/util/function/LongUnaryOperator.html#identity%2D%2D[identity]()++ (link:{java9-javadoc}/java/util/function/LongUnaryOperator.html#identity%2D%2D[java 9]) +* ++[[painless-api-reference-LongUnaryOperator-andThen-1]]<> link:{java8-javadoc}/java/util/function/LongUnaryOperator.html#andThen%2Djava.util.function.LongUnaryOperator%2D[andThen](<>)++ (link:{java9-javadoc}/java/util/function/LongUnaryOperator.html#andThen%2Djava.util.function.LongUnaryOperator%2D[java 9]) +* ++[[painless-api-reference-LongUnaryOperator-applyAsLong-1]]long link:{java8-javadoc}/java/util/function/LongUnaryOperator.html#applyAsLong%2Dlong%2D[applyAsLong](long)++ (link:{java9-javadoc}/java/util/function/LongUnaryOperator.html#applyAsLong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-LongUnaryOperator-compose-1]]<> link:{java8-javadoc}/java/util/function/LongUnaryOperator.html#compose%2Djava.util.function.LongUnaryOperator%2D[compose](<>)++ (link:{java9-javadoc}/java/util/function/LongUnaryOperator.html#compose%2Djava.util.function.LongUnaryOperator%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Map.Entry.asciidoc b/docs/painless/painless-api-reference/Map.Entry.asciidoc new file mode 100644 index 0000000000000..f40e61a70b060 --- /dev/null +++ b/docs/painless/painless-api-reference/Map.Entry.asciidoc @@ -0,0 +1,16 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Map-Entry]]++Map.Entry++:: +* ++[[painless-api-reference-Map-Entry-comparingByKey-0]]static <> link:{java8-javadoc}/java/util/Map$Entry.html#comparingByKey%2D%2D[comparingByKey]()++ (link:{java9-javadoc}/java/util/Map$Entry.html#comparingByKey%2D%2D[java 9]) +* ++[[painless-api-reference-Map-Entry-comparingByKey-1]]static <> link:{java8-javadoc}/java/util/Map$Entry.html#comparingByKey%2Djava.util.Comparator%2D[comparingByKey](<>)++ (link:{java9-javadoc}/java/util/Map$Entry.html#comparingByKey%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Map-Entry-comparingByValue-0]]static <> link:{java8-javadoc}/java/util/Map$Entry.html#comparingByValue%2D%2D[comparingByValue]()++ (link:{java9-javadoc}/java/util/Map$Entry.html#comparingByValue%2D%2D[java 9]) +* ++[[painless-api-reference-Map-Entry-comparingByValue-1]]static <> link:{java8-javadoc}/java/util/Map$Entry.html#comparingByValue%2Djava.util.Comparator%2D[comparingByValue](<>)++ (link:{java9-javadoc}/java/util/Map$Entry.html#comparingByValue%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Map-Entry-equals-1]]boolean link:{java8-javadoc}/java/util/Map$Entry.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/util/Map$Entry.html#equals%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Map-Entry-getKey-0]]def link:{java8-javadoc}/java/util/Map$Entry.html#getKey%2D%2D[getKey]()++ (link:{java9-javadoc}/java/util/Map$Entry.html#getKey%2D%2D[java 9]) +* ++[[painless-api-reference-Map-Entry-getValue-0]]def link:{java8-javadoc}/java/util/Map$Entry.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/util/Map$Entry.html#getValue%2D%2D[java 9]) +* ++[[painless-api-reference-Map-Entry-hashCode-0]]int link:{java8-javadoc}/java/util/Map$Entry.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/util/Map$Entry.html#hashCode%2D%2D[java 9]) +* ++[[painless-api-reference-Map-Entry-setValue-1]]def link:{java8-javadoc}/java/util/Map$Entry.html#setValue%2Djava.lang.Object%2D[setValue](def)++ (link:{java9-javadoc}/java/util/Map$Entry.html#setValue%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Map.asciidoc b/docs/painless/painless-api-reference/Map.asciidoc new file mode 100644 index 0000000000000..a32c9c137d8cd --- /dev/null +++ b/docs/painless/painless-api-reference/Map.asciidoc @@ -0,0 +1,42 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Map]]++Map++:: +* ++[[painless-api-reference-Map-clear-0]]void link:{java8-javadoc}/java/util/Map.html#clear%2D%2D[clear]()++ (link:{java9-javadoc}/java/util/Map.html#clear%2D%2D[java 9]) +* ++[[painless-api-reference-Map-collect-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#collect%2Djava.util.Map%2Djava.util.function.BiFunction%2D[collect](<>)++ +* ++[[painless-api-reference-Map-collect-2]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#collect%2Djava.util.Map%2Djava.util.Collection%2Djava.util.function.BiFunction%2D[collect](<>, <>)++ +* ++[[painless-api-reference-Map-compute-2]]def link:{java8-javadoc}/java/util/Map.html#compute%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[compute](def, <>)++ (link:{java9-javadoc}/java/util/Map.html#compute%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[java 9]) +* ++[[painless-api-reference-Map-computeIfAbsent-2]]def link:{java8-javadoc}/java/util/Map.html#computeIfAbsent%2Djava.lang.Object%2Djava.util.function.Function%2D[computeIfAbsent](def, <>)++ (link:{java9-javadoc}/java/util/Map.html#computeIfAbsent%2Djava.lang.Object%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-Map-computeIfPresent-2]]def link:{java8-javadoc}/java/util/Map.html#computeIfPresent%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[computeIfPresent](def, <>)++ (link:{java9-javadoc}/java/util/Map.html#computeIfPresent%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[java 9]) +* ++[[painless-api-reference-Map-containsKey-1]]boolean link:{java8-javadoc}/java/util/Map.html#containsKey%2Djava.lang.Object%2D[containsKey](def)++ (link:{java9-javadoc}/java/util/Map.html#containsKey%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Map-containsValue-1]]boolean link:{java8-javadoc}/java/util/Map.html#containsValue%2Djava.lang.Object%2D[containsValue](def)++ (link:{java9-javadoc}/java/util/Map.html#containsValue%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Map-count-1]]int link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#count%2Djava.util.Map%2Djava.util.function.BiPredicate%2D[count](<>)++ +* ++[[painless-api-reference-Map-each-1]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#each%2Djava.util.Map%2Djava.util.function.BiConsumer%2D[each](<>)++ +* ++[[painless-api-reference-Map-entrySet-0]]<> link:{java8-javadoc}/java/util/Map.html#entrySet%2D%2D[entrySet]()++ (link:{java9-javadoc}/java/util/Map.html#entrySet%2D%2D[java 9]) +* ++[[painless-api-reference-Map-equals-1]]boolean link:{java8-javadoc}/java/util/Map.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/util/Map.html#equals%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Map-every-1]]boolean link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#every%2Djava.util.Map%2Djava.util.function.BiPredicate%2D[every](<>)++ +* ++[[painless-api-reference-Map-find-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#find%2Djava.util.Map%2Djava.util.function.BiPredicate%2D[find](<>)++ +* ++[[painless-api-reference-Map-findAll-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findAll%2Djava.util.Map%2Djava.util.function.BiPredicate%2D[findAll](<>)++ +* ++[[painless-api-reference-Map-findResult-1]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findResult%2Djava.util.Map%2Djava.util.function.BiFunction%2D[findResult](<>)++ +* ++[[painless-api-reference-Map-findResult-2]]def link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findResult%2Djava.util.Map%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[findResult](def, <>)++ +* ++[[painless-api-reference-Map-findResults-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#findResults%2Djava.util.Map%2Djava.util.function.BiFunction%2D[findResults](<>)++ +* ++[[painless-api-reference-Map-forEach-1]]void link:{java8-javadoc}/java/util/Map.html#forEach%2Djava.util.function.BiConsumer%2D[forEach](<>)++ (link:{java9-javadoc}/java/util/Map.html#forEach%2Djava.util.function.BiConsumer%2D[java 9]) +* ++[[painless-api-reference-Map-get-1]]def link:{java8-javadoc}/java/util/Map.html#get%2Djava.lang.Object%2D[get](def)++ (link:{java9-javadoc}/java/util/Map.html#get%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Map-getOrDefault-2]]def link:{java8-javadoc}/java/util/Map.html#getOrDefault%2Djava.lang.Object%2Djava.lang.Object%2D[getOrDefault](def, def)++ (link:{java9-javadoc}/java/util/Map.html#getOrDefault%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Map-groupBy-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#groupBy%2Djava.util.Map%2Djava.util.function.BiFunction%2D[groupBy](<>)++ +* ++[[painless-api-reference-Map-isEmpty-0]]boolean link:{java8-javadoc}/java/util/Map.html#isEmpty%2D%2D[isEmpty]()++ (link:{java9-javadoc}/java/util/Map.html#isEmpty%2D%2D[java 9]) +* ++[[painless-api-reference-Map-keySet-0]]<> link:{java8-javadoc}/java/util/Map.html#keySet%2D%2D[keySet]()++ (link:{java9-javadoc}/java/util/Map.html#keySet%2D%2D[java 9]) +* ++[[painless-api-reference-Map-merge-3]]def link:{java8-javadoc}/java/util/Map.html#merge%2Djava.lang.Object%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[merge](def, def, <>)++ (link:{java9-javadoc}/java/util/Map.html#merge%2Djava.lang.Object%2Djava.lang.Object%2Djava.util.function.BiFunction%2D[java 9]) +* ++[[painless-api-reference-Map-put-2]]def link:{java8-javadoc}/java/util/Map.html#put%2Djava.lang.Object%2Djava.lang.Object%2D[put](def, def)++ (link:{java9-javadoc}/java/util/Map.html#put%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Map-putAll-1]]void link:{java8-javadoc}/java/util/Map.html#putAll%2Djava.util.Map%2D[putAll](<>)++ (link:{java9-javadoc}/java/util/Map.html#putAll%2Djava.util.Map%2D[java 9]) +* ++[[painless-api-reference-Map-putIfAbsent-2]]def link:{java8-javadoc}/java/util/Map.html#putIfAbsent%2Djava.lang.Object%2Djava.lang.Object%2D[putIfAbsent](def, def)++ (link:{java9-javadoc}/java/util/Map.html#putIfAbsent%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Map-remove-1]]def link:{java8-javadoc}/java/util/Map.html#remove%2Djava.lang.Object%2D[remove](def)++ (link:{java9-javadoc}/java/util/Map.html#remove%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Map-remove-2]]boolean link:{java8-javadoc}/java/util/Map.html#remove%2Djava.lang.Object%2Djava.lang.Object%2D[remove](def, def)++ (link:{java9-javadoc}/java/util/Map.html#remove%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Map-replace-2]]def link:{java8-javadoc}/java/util/Map.html#replace%2Djava.lang.Object%2Djava.lang.Object%2D[replace](def, def)++ (link:{java9-javadoc}/java/util/Map.html#replace%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Map-replace-3]]boolean link:{java8-javadoc}/java/util/Map.html#replace%2Djava.lang.Object%2Djava.lang.Object%2Djava.lang.Object%2D[replace](def, def, def)++ (link:{java9-javadoc}/java/util/Map.html#replace%2Djava.lang.Object%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Map-replaceAll-1]]void link:{java8-javadoc}/java/util/Map.html#replaceAll%2Djava.util.function.BiFunction%2D[replaceAll](<>)++ (link:{java9-javadoc}/java/util/Map.html#replaceAll%2Djava.util.function.BiFunction%2D[java 9]) +* ++[[painless-api-reference-Map-size-0]]int link:{java8-javadoc}/java/util/Map.html#size%2D%2D[size]()++ (link:{java9-javadoc}/java/util/Map.html#size%2D%2D[java 9]) +* ++[[painless-api-reference-Map-values-0]]<> link:{java8-javadoc}/java/util/Map.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/util/Map.html#values%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Matcher.asciidoc b/docs/painless/painless-api-reference/Matcher.asciidoc new file mode 100644 index 0000000000000..85198e0bc8db1 --- /dev/null +++ b/docs/painless/painless-api-reference/Matcher.asciidoc @@ -0,0 +1,34 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Matcher]]++Matcher++:: +* ++[[painless-api-reference-Matcher-quoteReplacement-1]]static <> link:{java8-javadoc}/java/util/regex/Matcher.html#quoteReplacement%2Djava.lang.String%2D[quoteReplacement](<>)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#quoteReplacement%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Matcher-end-0]]int link:{java8-javadoc}/java/util/regex/Matcher.html#end%2D%2D[end]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#end%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-end-1]]int link:{java8-javadoc}/java/util/regex/Matcher.html#end%2Dint%2D[end](int)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#end%2Dint%2D[java 9]) +* ++[[painless-api-reference-Matcher-find-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#find%2D%2D[find]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#find%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-find-1]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#find%2Dint%2D[find](int)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#find%2Dint%2D[java 9]) +* ++[[painless-api-reference-Matcher-group-0]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#group%2D%2D[group]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#group%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-group-1]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#group%2Dint%2D[group](int)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#group%2Dint%2D[java 9]) +* ++[[painless-api-reference-Matcher-groupCount-0]]int link:{java8-javadoc}/java/util/regex/Matcher.html#groupCount%2D%2D[groupCount]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#groupCount%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-hasAnchoringBounds-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#hasAnchoringBounds%2D%2D[hasAnchoringBounds]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#hasAnchoringBounds%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-hasTransparentBounds-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#hasTransparentBounds%2D%2D[hasTransparentBounds]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#hasTransparentBounds%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-hitEnd-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#hitEnd%2D%2D[hitEnd]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#hitEnd%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-lookingAt-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#lookingAt%2D%2D[lookingAt]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#lookingAt%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-matches-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#matches%2D%2D[matches]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#matches%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-namedGroup-1]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#namedGroup%2Djava.util.regex.Matcher%2Djava.lang.String%2D[namedGroup](<>)++ +* ++[[painless-api-reference-Matcher-pattern-0]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#pattern%2D%2D[pattern]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#pattern%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-region-2]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#region%2Dint%2Dint%2D[region](int, int)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#region%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Matcher-regionEnd-0]]int link:{java8-javadoc}/java/util/regex/Matcher.html#regionEnd%2D%2D[regionEnd]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#regionEnd%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-regionStart-0]]int link:{java8-javadoc}/java/util/regex/Matcher.html#regionStart%2D%2D[regionStart]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#regionStart%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-replaceAll-1]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#replaceAll%2Djava.lang.String%2D[replaceAll](<>)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#replaceAll%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Matcher-replaceFirst-1]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#replaceFirst%2Djava.lang.String%2D[replaceFirst](<>)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#replaceFirst%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Matcher-requireEnd-0]]boolean link:{java8-javadoc}/java/util/regex/Matcher.html#requireEnd%2D%2D[requireEnd]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#requireEnd%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-reset-0]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#reset%2D%2D[reset]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#reset%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-start-0]]int link:{java8-javadoc}/java/util/regex/Matcher.html#start%2D%2D[start]()++ (link:{java9-javadoc}/java/util/regex/Matcher.html#start%2D%2D[java 9]) +* ++[[painless-api-reference-Matcher-start-1]]int link:{java8-javadoc}/java/util/regex/Matcher.html#start%2Dint%2D[start](int)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#start%2Dint%2D[java 9]) +* ++[[painless-api-reference-Matcher-useAnchoringBounds-1]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#useAnchoringBounds%2Dboolean%2D[useAnchoringBounds](boolean)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#useAnchoringBounds%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-Matcher-usePattern-1]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#usePattern%2Djava.util.regex.Pattern%2D[usePattern](<>)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#usePattern%2Djava.util.regex.Pattern%2D[java 9]) +* ++[[painless-api-reference-Matcher-useTransparentBounds-1]]<> link:{java8-javadoc}/java/util/regex/Matcher.html#useTransparentBounds%2Dboolean%2D[useTransparentBounds](boolean)++ (link:{java9-javadoc}/java/util/regex/Matcher.html#useTransparentBounds%2Dboolean%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Math.asciidoc b/docs/painless/painless-api-reference/Math.asciidoc new file mode 100644 index 0000000000000..4b8322c15fc50 --- /dev/null +++ b/docs/painless/painless-api-reference/Math.asciidoc @@ -0,0 +1,46 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Math]]++Math++:: +** [[painless-api-reference-Math-E]]static double link:{java8-javadoc}/java/lang/Math.html#E[E] (link:{java9-javadoc}/java/lang/Math.html#E[java 9]) +** [[painless-api-reference-Math-PI]]static double link:{java8-javadoc}/java/lang/Math.html#PI[PI] (link:{java9-javadoc}/java/lang/Math.html#PI[java 9]) +* ++[[painless-api-reference-Math-IEEEremainder-2]]static double link:{java8-javadoc}/java/lang/Math.html#IEEEremainder%2Ddouble%2Ddouble%2D[IEEEremainder](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#IEEEremainder%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-abs-1]]static double link:{java8-javadoc}/java/lang/Math.html#abs%2Ddouble%2D[abs](double)++ (link:{java9-javadoc}/java/lang/Math.html#abs%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-acos-1]]static double link:{java8-javadoc}/java/lang/Math.html#acos%2Ddouble%2D[acos](double)++ (link:{java9-javadoc}/java/lang/Math.html#acos%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-asin-1]]static double link:{java8-javadoc}/java/lang/Math.html#asin%2Ddouble%2D[asin](double)++ (link:{java9-javadoc}/java/lang/Math.html#asin%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-atan-1]]static double link:{java8-javadoc}/java/lang/Math.html#atan%2Ddouble%2D[atan](double)++ (link:{java9-javadoc}/java/lang/Math.html#atan%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-atan2-2]]static double link:{java8-javadoc}/java/lang/Math.html#atan2%2Ddouble%2Ddouble%2D[atan2](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#atan2%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-cbrt-1]]static double link:{java8-javadoc}/java/lang/Math.html#cbrt%2Ddouble%2D[cbrt](double)++ (link:{java9-javadoc}/java/lang/Math.html#cbrt%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-ceil-1]]static double link:{java8-javadoc}/java/lang/Math.html#ceil%2Ddouble%2D[ceil](double)++ (link:{java9-javadoc}/java/lang/Math.html#ceil%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-copySign-2]]static double link:{java8-javadoc}/java/lang/Math.html#copySign%2Ddouble%2Ddouble%2D[copySign](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#copySign%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-cos-1]]static double link:{java8-javadoc}/java/lang/Math.html#cos%2Ddouble%2D[cos](double)++ (link:{java9-javadoc}/java/lang/Math.html#cos%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-cosh-1]]static double link:{java8-javadoc}/java/lang/Math.html#cosh%2Ddouble%2D[cosh](double)++ (link:{java9-javadoc}/java/lang/Math.html#cosh%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-exp-1]]static double link:{java8-javadoc}/java/lang/Math.html#exp%2Ddouble%2D[exp](double)++ (link:{java9-javadoc}/java/lang/Math.html#exp%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-expm1-1]]static double link:{java8-javadoc}/java/lang/Math.html#expm1%2Ddouble%2D[expm1](double)++ (link:{java9-javadoc}/java/lang/Math.html#expm1%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-floor-1]]static double link:{java8-javadoc}/java/lang/Math.html#floor%2Ddouble%2D[floor](double)++ (link:{java9-javadoc}/java/lang/Math.html#floor%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-hypot-2]]static double link:{java8-javadoc}/java/lang/Math.html#hypot%2Ddouble%2Ddouble%2D[hypot](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#hypot%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-log-1]]static double link:{java8-javadoc}/java/lang/Math.html#log%2Ddouble%2D[log](double)++ (link:{java9-javadoc}/java/lang/Math.html#log%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-log10-1]]static double link:{java8-javadoc}/java/lang/Math.html#log10%2Ddouble%2D[log10](double)++ (link:{java9-javadoc}/java/lang/Math.html#log10%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-log1p-1]]static double link:{java8-javadoc}/java/lang/Math.html#log1p%2Ddouble%2D[log1p](double)++ (link:{java9-javadoc}/java/lang/Math.html#log1p%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-max-2]]static double link:{java8-javadoc}/java/lang/Math.html#max%2Ddouble%2Ddouble%2D[max](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#max%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-min-2]]static double link:{java8-javadoc}/java/lang/Math.html#min%2Ddouble%2Ddouble%2D[min](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#min%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-nextAfter-2]]static double link:{java8-javadoc}/java/lang/Math.html#nextAfter%2Ddouble%2Ddouble%2D[nextAfter](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#nextAfter%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-nextDown-1]]static double link:{java8-javadoc}/java/lang/Math.html#nextDown%2Ddouble%2D[nextDown](double)++ (link:{java9-javadoc}/java/lang/Math.html#nextDown%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-nextUp-1]]static double link:{java8-javadoc}/java/lang/Math.html#nextUp%2Ddouble%2D[nextUp](double)++ (link:{java9-javadoc}/java/lang/Math.html#nextUp%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-pow-2]]static double link:{java8-javadoc}/java/lang/Math.html#pow%2Ddouble%2Ddouble%2D[pow](double, double)++ (link:{java9-javadoc}/java/lang/Math.html#pow%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-random-0]]static double link:{java8-javadoc}/java/lang/Math.html#random%2D%2D[random]()++ (link:{java9-javadoc}/java/lang/Math.html#random%2D%2D[java 9]) +* ++[[painless-api-reference-Math-rint-1]]static double link:{java8-javadoc}/java/lang/Math.html#rint%2Ddouble%2D[rint](double)++ (link:{java9-javadoc}/java/lang/Math.html#rint%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-round-1]]static long link:{java8-javadoc}/java/lang/Math.html#round%2Ddouble%2D[round](double)++ (link:{java9-javadoc}/java/lang/Math.html#round%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-scalb-2]]static double link:{java8-javadoc}/java/lang/Math.html#scalb%2Ddouble%2Dint%2D[scalb](double, int)++ (link:{java9-javadoc}/java/lang/Math.html#scalb%2Ddouble%2Dint%2D[java 9]) +* ++[[painless-api-reference-Math-signum-1]]static double link:{java8-javadoc}/java/lang/Math.html#signum%2Ddouble%2D[signum](double)++ (link:{java9-javadoc}/java/lang/Math.html#signum%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-sin-1]]static double link:{java8-javadoc}/java/lang/Math.html#sin%2Ddouble%2D[sin](double)++ (link:{java9-javadoc}/java/lang/Math.html#sin%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-sinh-1]]static double link:{java8-javadoc}/java/lang/Math.html#sinh%2Ddouble%2D[sinh](double)++ (link:{java9-javadoc}/java/lang/Math.html#sinh%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-sqrt-1]]static double link:{java8-javadoc}/java/lang/Math.html#sqrt%2Ddouble%2D[sqrt](double)++ (link:{java9-javadoc}/java/lang/Math.html#sqrt%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-tan-1]]static double link:{java8-javadoc}/java/lang/Math.html#tan%2Ddouble%2D[tan](double)++ (link:{java9-javadoc}/java/lang/Math.html#tan%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-tanh-1]]static double link:{java8-javadoc}/java/lang/Math.html#tanh%2Ddouble%2D[tanh](double)++ (link:{java9-javadoc}/java/lang/Math.html#tanh%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-toDegrees-1]]static double link:{java8-javadoc}/java/lang/Math.html#toDegrees%2Ddouble%2D[toDegrees](double)++ (link:{java9-javadoc}/java/lang/Math.html#toDegrees%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-toRadians-1]]static double link:{java8-javadoc}/java/lang/Math.html#toRadians%2Ddouble%2D[toRadians](double)++ (link:{java9-javadoc}/java/lang/Math.html#toRadians%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Math-ulp-1]]static double link:{java8-javadoc}/java/lang/Math.html#ulp%2Ddouble%2D[ulp](double)++ (link:{java9-javadoc}/java/lang/Math.html#ulp%2Ddouble%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/MathContext.asciidoc b/docs/painless/painless-api-reference/MathContext.asciidoc new file mode 100644 index 0000000000000..c8167367fd59b --- /dev/null +++ b/docs/painless/painless-api-reference/MathContext.asciidoc @@ -0,0 +1,15 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-MathContext]]++MathContext++:: +** [[painless-api-reference-MathContext-DECIMAL128]]static <> link:{java8-javadoc}/java/math/MathContext.html#DECIMAL128[DECIMAL128] (link:{java9-javadoc}/java/math/MathContext.html#DECIMAL128[java 9]) +** [[painless-api-reference-MathContext-DECIMAL32]]static <> link:{java8-javadoc}/java/math/MathContext.html#DECIMAL32[DECIMAL32] (link:{java9-javadoc}/java/math/MathContext.html#DECIMAL32[java 9]) +** [[painless-api-reference-MathContext-DECIMAL64]]static <> link:{java8-javadoc}/java/math/MathContext.html#DECIMAL64[DECIMAL64] (link:{java9-javadoc}/java/math/MathContext.html#DECIMAL64[java 9]) +** [[painless-api-reference-MathContext-UNLIMITED]]static <> link:{java8-javadoc}/java/math/MathContext.html#UNLIMITED[UNLIMITED] (link:{java9-javadoc}/java/math/MathContext.html#UNLIMITED[java 9]) +* ++[[painless-api-reference-MathContext-MathContext-1]]link:{java8-javadoc}/java/math/MathContext.html#MathContext%2Dint%2D[MathContext](int)++ (link:{java9-javadoc}/java/math/MathContext.html#MathContext%2Dint%2D[java 9]) +* ++[[painless-api-reference-MathContext-MathContext-2]]link:{java8-javadoc}/java/math/MathContext.html#MathContext%2Dint%2Djava.math.RoundingMode%2D[MathContext](int, <>)++ (link:{java9-javadoc}/java/math/MathContext.html#MathContext%2Dint%2Djava.math.RoundingMode%2D[java 9]) +* ++[[painless-api-reference-MathContext-getPrecision-0]]int link:{java8-javadoc}/java/math/MathContext.html#getPrecision%2D%2D[getPrecision]()++ (link:{java9-javadoc}/java/math/MathContext.html#getPrecision%2D%2D[java 9]) +* ++[[painless-api-reference-MathContext-getRoundingMode-0]]<> link:{java8-javadoc}/java/math/MathContext.html#getRoundingMode%2D%2D[getRoundingMode]()++ (link:{java9-javadoc}/java/math/MathContext.html#getRoundingMode%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/MessageFormat.Field.asciidoc b/docs/painless/painless-api-reference/MessageFormat.Field.asciidoc new file mode 100644 index 0000000000000..6b0e71e0a5f43 --- /dev/null +++ b/docs/painless/painless-api-reference/MessageFormat.Field.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-MessageFormat-Field]]++MessageFormat.Field++:: +** [[painless-api-reference-MessageFormat-Field-ARGUMENT]]static <> link:{java8-javadoc}/java/text/MessageFormat$Field.html#ARGUMENT[ARGUMENT] (link:{java9-javadoc}/java/text/MessageFormat$Field.html#ARGUMENT[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/MessageFormat.asciidoc b/docs/painless/painless-api-reference/MessageFormat.asciidoc new file mode 100644 index 0000000000000..eaed777d9f0f7 --- /dev/null +++ b/docs/painless/painless-api-reference/MessageFormat.asciidoc @@ -0,0 +1,20 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-MessageFormat]]++MessageFormat++:: +* ++[[painless-api-reference-MessageFormat-format-2]]static <> link:{java8-javadoc}/java/text/MessageFormat.html#format%2Djava.lang.String%2Djava.lang.Object:A%2D[format](<>, <>[])++ (link:{java9-javadoc}/java/text/MessageFormat.html#format%2Djava.lang.String%2Djava.lang.Object:A%2D[java 9]) +* ++[[painless-api-reference-MessageFormat-applyPattern-1]]void link:{java8-javadoc}/java/text/MessageFormat.html#applyPattern%2Djava.lang.String%2D[applyPattern](<>)++ (link:{java9-javadoc}/java/text/MessageFormat.html#applyPattern%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-MessageFormat-getFormats-0]]<>[] link:{java8-javadoc}/java/text/MessageFormat.html#getFormats%2D%2D[getFormats]()++ (link:{java9-javadoc}/java/text/MessageFormat.html#getFormats%2D%2D[java 9]) +* ++[[painless-api-reference-MessageFormat-getFormatsByArgumentIndex-0]]<>[] link:{java8-javadoc}/java/text/MessageFormat.html#getFormatsByArgumentIndex%2D%2D[getFormatsByArgumentIndex]()++ (link:{java9-javadoc}/java/text/MessageFormat.html#getFormatsByArgumentIndex%2D%2D[java 9]) +* ++[[painless-api-reference-MessageFormat-getLocale-0]]<> link:{java8-javadoc}/java/text/MessageFormat.html#getLocale%2D%2D[getLocale]()++ (link:{java9-javadoc}/java/text/MessageFormat.html#getLocale%2D%2D[java 9]) +* ++[[painless-api-reference-MessageFormat-parse-1]]<>[] link:{java8-javadoc}/java/text/MessageFormat.html#parse%2Djava.lang.String%2D[parse](<>)++ (link:{java9-javadoc}/java/text/MessageFormat.html#parse%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-MessageFormat-parse-2]]<>[] link:{java8-javadoc}/java/text/MessageFormat.html#parse%2Djava.lang.String%2Djava.text.ParsePosition%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/text/MessageFormat.html#parse%2Djava.lang.String%2Djava.text.ParsePosition%2D[java 9]) +* ++[[painless-api-reference-MessageFormat-setFormat-2]]void link:{java8-javadoc}/java/text/MessageFormat.html#setFormat%2Dint%2Djava.text.Format%2D[setFormat](int, <>)++ (link:{java9-javadoc}/java/text/MessageFormat.html#setFormat%2Dint%2Djava.text.Format%2D[java 9]) +* ++[[painless-api-reference-MessageFormat-setFormatByArgumentIndex-2]]void link:{java8-javadoc}/java/text/MessageFormat.html#setFormatByArgumentIndex%2Dint%2Djava.text.Format%2D[setFormatByArgumentIndex](int, <>)++ (link:{java9-javadoc}/java/text/MessageFormat.html#setFormatByArgumentIndex%2Dint%2Djava.text.Format%2D[java 9]) +* ++[[painless-api-reference-MessageFormat-setFormats-1]]void link:{java8-javadoc}/java/text/MessageFormat.html#setFormats%2Djava.text.Format:A%2D[setFormats](<>[])++ (link:{java9-javadoc}/java/text/MessageFormat.html#setFormats%2Djava.text.Format:A%2D[java 9]) +* ++[[painless-api-reference-MessageFormat-setFormatsByArgumentIndex-1]]void link:{java8-javadoc}/java/text/MessageFormat.html#setFormatsByArgumentIndex%2Djava.text.Format:A%2D[setFormatsByArgumentIndex](<>[])++ (link:{java9-javadoc}/java/text/MessageFormat.html#setFormatsByArgumentIndex%2Djava.text.Format:A%2D[java 9]) +* ++[[painless-api-reference-MessageFormat-setLocale-1]]void link:{java8-javadoc}/java/text/MessageFormat.html#setLocale%2Djava.util.Locale%2D[setLocale](<>)++ (link:{java9-javadoc}/java/text/MessageFormat.html#setLocale%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-MessageFormat-toPattern-0]]<> link:{java8-javadoc}/java/text/MessageFormat.html#toPattern%2D%2D[toPattern]()++ (link:{java9-javadoc}/java/text/MessageFormat.html#toPattern%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/MinguoChronology.asciidoc b/docs/painless/painless-api-reference/MinguoChronology.asciidoc new file mode 100644 index 0000000000000..9e9afb2cedfa2 --- /dev/null +++ b/docs/painless/painless-api-reference/MinguoChronology.asciidoc @@ -0,0 +1,16 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-MinguoChronology]]++MinguoChronology++:: +** [[painless-api-reference-MinguoChronology-INSTANCE]]static <> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#INSTANCE[INSTANCE] (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#INSTANCE[java 9]) +* ++[[painless-api-reference-MinguoChronology-date-1]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[date](<>)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-MinguoChronology-date-3]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#date%2Dint%2Dint%2Dint%2D[date](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#date%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-MinguoChronology-date-4]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[date](<>, int, int, int)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-MinguoChronology-dateEpochDay-1]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#dateEpochDay%2Dlong%2D[dateEpochDay](long)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#dateEpochDay%2Dlong%2D[java 9]) +* ++[[painless-api-reference-MinguoChronology-dateYearDay-2]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#dateYearDay%2Dint%2Dint%2D[dateYearDay](int, int)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#dateYearDay%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-MinguoChronology-dateYearDay-3]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[dateYearDay](<>, int, int)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-MinguoChronology-eraOf-1]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#eraOf%2Dint%2D[eraOf](int)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#eraOf%2Dint%2D[java 9]) +* ++[[painless-api-reference-MinguoChronology-resolveDate-2]]<> link:{java8-javadoc}/java/time/chrono/MinguoChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[resolveDate](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/MinguoChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/MinguoDate.asciidoc b/docs/painless/painless-api-reference/MinguoDate.asciidoc new file mode 100644 index 0000000000000..38d0b87da762d --- /dev/null +++ b/docs/painless/painless-api-reference/MinguoDate.asciidoc @@ -0,0 +1,17 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-MinguoDate]]++MinguoDate++:: +* ++[[painless-api-reference-MinguoDate-from-1]]static <> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-MinguoDate-of-3]]static <> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#of%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-MinguoDate-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#getChronology%2D%2D[java 9]) +* ++[[painless-api-reference-MinguoDate-getEra-0]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#getEra%2D%2D[getEra]()++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#getEra%2D%2D[java 9]) +* ++[[painless-api-reference-MinguoDate-minus-1]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-MinguoDate-minus-2]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-MinguoDate-plus-1]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-MinguoDate-plus-2]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-MinguoDate-with-1]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-MinguoDate-with-2]]<> link:{java8-javadoc}/java/time/chrono/MinguoDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/MinguoDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/MinguoEra.asciidoc b/docs/painless/painless-api-reference/MinguoEra.asciidoc new file mode 100644 index 0000000000000..0b32509913d91 --- /dev/null +++ b/docs/painless/painless-api-reference/MinguoEra.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-MinguoEra]]++MinguoEra++:: +** [[painless-api-reference-MinguoEra-BEFORE_ROC]]static <> link:{java8-javadoc}/java/time/chrono/MinguoEra.html#BEFORE_ROC[BEFORE_ROC] (link:{java9-javadoc}/java/time/chrono/MinguoEra.html#BEFORE_ROC[java 9]) +** [[painless-api-reference-MinguoEra-ROC]]static <> link:{java8-javadoc}/java/time/chrono/MinguoEra.html#ROC[ROC] (link:{java9-javadoc}/java/time/chrono/MinguoEra.html#ROC[java 9]) +* ++[[painless-api-reference-MinguoEra-of-1]]static <> link:{java8-javadoc}/java/time/chrono/MinguoEra.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/chrono/MinguoEra.html#of%2Dint%2D[java 9]) +* ++[[painless-api-reference-MinguoEra-valueOf-1]]static <> link:{java8-javadoc}/java/time/chrono/MinguoEra.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/chrono/MinguoEra.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-MinguoEra-values-0]]static <>[] link:{java8-javadoc}/java/time/chrono/MinguoEra.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/chrono/MinguoEra.html#values%2D%2D[java 9]) +* ++[[painless-api-reference-MinguoEra-getValue-0]]int link:{java8-javadoc}/java/time/chrono/MinguoEra.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/chrono/MinguoEra.html#getValue%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/MissingFormatArgumentException.asciidoc b/docs/painless/painless-api-reference/MissingFormatArgumentException.asciidoc new file mode 100644 index 0000000000000..7c09ad6ba5cb7 --- /dev/null +++ b/docs/painless/painless-api-reference/MissingFormatArgumentException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-MissingFormatArgumentException]]++MissingFormatArgumentException++:: +* ++[[painless-api-reference-MissingFormatArgumentException-MissingFormatArgumentException-1]]link:{java8-javadoc}/java/util/MissingFormatArgumentException.html#MissingFormatArgumentException%2Djava.lang.String%2D[MissingFormatArgumentException](<>)++ (link:{java9-javadoc}/java/util/MissingFormatArgumentException.html#MissingFormatArgumentException%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-MissingFormatArgumentException-getFormatSpecifier-0]]<> link:{java8-javadoc}/java/util/MissingFormatArgumentException.html#getFormatSpecifier%2D%2D[getFormatSpecifier]()++ (link:{java9-javadoc}/java/util/MissingFormatArgumentException.html#getFormatSpecifier%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/MissingFormatWidthException.asciidoc b/docs/painless/painless-api-reference/MissingFormatWidthException.asciidoc new file mode 100644 index 0000000000000..155637b48dd2e --- /dev/null +++ b/docs/painless/painless-api-reference/MissingFormatWidthException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-MissingFormatWidthException]]++MissingFormatWidthException++:: +* ++[[painless-api-reference-MissingFormatWidthException-MissingFormatWidthException-1]]link:{java8-javadoc}/java/util/MissingFormatWidthException.html#MissingFormatWidthException%2Djava.lang.String%2D[MissingFormatWidthException](<>)++ (link:{java9-javadoc}/java/util/MissingFormatWidthException.html#MissingFormatWidthException%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-MissingFormatWidthException-getFormatSpecifier-0]]<> link:{java8-javadoc}/java/util/MissingFormatWidthException.html#getFormatSpecifier%2D%2D[getFormatSpecifier]()++ (link:{java9-javadoc}/java/util/MissingFormatWidthException.html#getFormatSpecifier%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/MissingResourceException.asciidoc b/docs/painless/painless-api-reference/MissingResourceException.asciidoc new file mode 100644 index 0000000000000..a415708a728a6 --- /dev/null +++ b/docs/painless/painless-api-reference/MissingResourceException.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-MissingResourceException]]++MissingResourceException++:: +* ++[[painless-api-reference-MissingResourceException-MissingResourceException-3]]link:{java8-javadoc}/java/util/MissingResourceException.html#MissingResourceException%2Djava.lang.String%2Djava.lang.String%2Djava.lang.String%2D[MissingResourceException](<>, <>, <>)++ (link:{java9-javadoc}/java/util/MissingResourceException.html#MissingResourceException%2Djava.lang.String%2Djava.lang.String%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-MissingResourceException-getClassName-0]]<> link:{java8-javadoc}/java/util/MissingResourceException.html#getClassName%2D%2D[getClassName]()++ (link:{java9-javadoc}/java/util/MissingResourceException.html#getClassName%2D%2D[java 9]) +* ++[[painless-api-reference-MissingResourceException-getKey-0]]<> link:{java8-javadoc}/java/util/MissingResourceException.html#getKey%2D%2D[getKey]()++ (link:{java9-javadoc}/java/util/MissingResourceException.html#getKey%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Month.asciidoc b/docs/painless/painless-api-reference/Month.asciidoc new file mode 100644 index 0000000000000..f4eaa9cc12944 --- /dev/null +++ b/docs/painless/painless-api-reference/Month.asciidoc @@ -0,0 +1,32 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Month]]++Month++:: +** [[painless-api-reference-Month-APRIL]]static <> link:{java8-javadoc}/java/time/Month.html#APRIL[APRIL] (link:{java9-javadoc}/java/time/Month.html#APRIL[java 9]) +** [[painless-api-reference-Month-AUGUST]]static <> link:{java8-javadoc}/java/time/Month.html#AUGUST[AUGUST] (link:{java9-javadoc}/java/time/Month.html#AUGUST[java 9]) +** [[painless-api-reference-Month-DECEMBER]]static <> link:{java8-javadoc}/java/time/Month.html#DECEMBER[DECEMBER] (link:{java9-javadoc}/java/time/Month.html#DECEMBER[java 9]) +** [[painless-api-reference-Month-FEBRUARY]]static <> link:{java8-javadoc}/java/time/Month.html#FEBRUARY[FEBRUARY] (link:{java9-javadoc}/java/time/Month.html#FEBRUARY[java 9]) +** [[painless-api-reference-Month-JANUARY]]static <> link:{java8-javadoc}/java/time/Month.html#JANUARY[JANUARY] (link:{java9-javadoc}/java/time/Month.html#JANUARY[java 9]) +** [[painless-api-reference-Month-JULY]]static <> link:{java8-javadoc}/java/time/Month.html#JULY[JULY] (link:{java9-javadoc}/java/time/Month.html#JULY[java 9]) +** [[painless-api-reference-Month-JUNE]]static <> link:{java8-javadoc}/java/time/Month.html#JUNE[JUNE] (link:{java9-javadoc}/java/time/Month.html#JUNE[java 9]) +** [[painless-api-reference-Month-MARCH]]static <> link:{java8-javadoc}/java/time/Month.html#MARCH[MARCH] (link:{java9-javadoc}/java/time/Month.html#MARCH[java 9]) +** [[painless-api-reference-Month-MAY]]static <> link:{java8-javadoc}/java/time/Month.html#MAY[MAY] (link:{java9-javadoc}/java/time/Month.html#MAY[java 9]) +** [[painless-api-reference-Month-NOVEMBER]]static <> link:{java8-javadoc}/java/time/Month.html#NOVEMBER[NOVEMBER] (link:{java9-javadoc}/java/time/Month.html#NOVEMBER[java 9]) +** [[painless-api-reference-Month-OCTOBER]]static <> link:{java8-javadoc}/java/time/Month.html#OCTOBER[OCTOBER] (link:{java9-javadoc}/java/time/Month.html#OCTOBER[java 9]) +** [[painless-api-reference-Month-SEPTEMBER]]static <> link:{java8-javadoc}/java/time/Month.html#SEPTEMBER[SEPTEMBER] (link:{java9-javadoc}/java/time/Month.html#SEPTEMBER[java 9]) +* ++[[painless-api-reference-Month-from-1]]static <> link:{java8-javadoc}/java/time/Month.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/Month.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-Month-of-1]]static <> link:{java8-javadoc}/java/time/Month.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/Month.html#of%2Dint%2D[java 9]) +* ++[[painless-api-reference-Month-valueOf-1]]static <> link:{java8-javadoc}/java/time/Month.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/Month.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Month-values-0]]static <>[] link:{java8-javadoc}/java/time/Month.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/Month.html#values%2D%2D[java 9]) +* ++[[painless-api-reference-Month-firstDayOfYear-1]]int link:{java8-javadoc}/java/time/Month.html#firstDayOfYear%2Dboolean%2D[firstDayOfYear](boolean)++ (link:{java9-javadoc}/java/time/Month.html#firstDayOfYear%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-Month-firstMonthOfQuarter-0]]<> link:{java8-javadoc}/java/time/Month.html#firstMonthOfQuarter%2D%2D[firstMonthOfQuarter]()++ (link:{java9-javadoc}/java/time/Month.html#firstMonthOfQuarter%2D%2D[java 9]) +* ++[[painless-api-reference-Month-getDisplayName-2]]<> link:{java8-javadoc}/java/time/Month.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[getDisplayName](<>, <>)++ (link:{java9-javadoc}/java/time/Month.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-Month-getValue-0]]int link:{java8-javadoc}/java/time/Month.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/Month.html#getValue%2D%2D[java 9]) +* ++[[painless-api-reference-Month-length-1]]int link:{java8-javadoc}/java/time/Month.html#length%2Dboolean%2D[length](boolean)++ (link:{java9-javadoc}/java/time/Month.html#length%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-Month-maxLength-0]]int link:{java8-javadoc}/java/time/Month.html#maxLength%2D%2D[maxLength]()++ (link:{java9-javadoc}/java/time/Month.html#maxLength%2D%2D[java 9]) +* ++[[painless-api-reference-Month-minLength-0]]int link:{java8-javadoc}/java/time/Month.html#minLength%2D%2D[minLength]()++ (link:{java9-javadoc}/java/time/Month.html#minLength%2D%2D[java 9]) +* ++[[painless-api-reference-Month-minus-1]]<> link:{java8-javadoc}/java/time/Month.html#minus%2Dlong%2D[minus](long)++ (link:{java9-javadoc}/java/time/Month.html#minus%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Month-plus-1]]<> link:{java8-javadoc}/java/time/Month.html#plus%2Dlong%2D[plus](long)++ (link:{java9-javadoc}/java/time/Month.html#plus%2Dlong%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/MonthDay.asciidoc b/docs/painless/painless-api-reference/MonthDay.asciidoc new file mode 100644 index 0000000000000..4bb7c675d70da --- /dev/null +++ b/docs/painless/painless-api-reference/MonthDay.asciidoc @@ -0,0 +1,23 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-MonthDay]]++MonthDay++:: +* ++[[painless-api-reference-MonthDay-from-1]]static <> link:{java8-javadoc}/java/time/MonthDay.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-MonthDay-of-2]]static <> link:{java8-javadoc}/java/time/MonthDay.html#of%2Dint%2Dint%2D[of](int, int)++ (link:{java9-javadoc}/java/time/MonthDay.html#of%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-MonthDay-parse-1]]static <> link:{java8-javadoc}/java/time/MonthDay.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#parse%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-MonthDay-parse-2]]static <> link:{java8-javadoc}/java/time/MonthDay.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/MonthDay.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-MonthDay-atYear-1]]<> link:{java8-javadoc}/java/time/MonthDay.html#atYear%2Dint%2D[atYear](int)++ (link:{java9-javadoc}/java/time/MonthDay.html#atYear%2Dint%2D[java 9]) +* ++[[painless-api-reference-MonthDay-compareTo-1]]int link:{java8-javadoc}/java/time/MonthDay.html#compareTo%2Djava.time.MonthDay%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#compareTo%2Djava.time.MonthDay%2D[java 9]) +* ++[[painless-api-reference-MonthDay-format-1]]<> link:{java8-javadoc}/java/time/MonthDay.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-MonthDay-getDayOfMonth-0]]int link:{java8-javadoc}/java/time/MonthDay.html#getDayOfMonth%2D%2D[getDayOfMonth]()++ (link:{java9-javadoc}/java/time/MonthDay.html#getDayOfMonth%2D%2D[java 9]) +* ++[[painless-api-reference-MonthDay-getMonth-0]]<> link:{java8-javadoc}/java/time/MonthDay.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/MonthDay.html#getMonth%2D%2D[java 9]) +* ++[[painless-api-reference-MonthDay-getMonthValue-0]]int link:{java8-javadoc}/java/time/MonthDay.html#getMonthValue%2D%2D[getMonthValue]()++ (link:{java9-javadoc}/java/time/MonthDay.html#getMonthValue%2D%2D[java 9]) +* ++[[painless-api-reference-MonthDay-isAfter-1]]boolean link:{java8-javadoc}/java/time/MonthDay.html#isAfter%2Djava.time.MonthDay%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#isAfter%2Djava.time.MonthDay%2D[java 9]) +* ++[[painless-api-reference-MonthDay-isBefore-1]]boolean link:{java8-javadoc}/java/time/MonthDay.html#isBefore%2Djava.time.MonthDay%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#isBefore%2Djava.time.MonthDay%2D[java 9]) +* ++[[painless-api-reference-MonthDay-isValidYear-1]]boolean link:{java8-javadoc}/java/time/MonthDay.html#isValidYear%2Dint%2D[isValidYear](int)++ (link:{java9-javadoc}/java/time/MonthDay.html#isValidYear%2Dint%2D[java 9]) +* ++[[painless-api-reference-MonthDay-with-1]]<> link:{java8-javadoc}/java/time/MonthDay.html#with%2Djava.time.Month%2D[with](<>)++ (link:{java9-javadoc}/java/time/MonthDay.html#with%2Djava.time.Month%2D[java 9]) +* ++[[painless-api-reference-MonthDay-withDayOfMonth-1]]<> link:{java8-javadoc}/java/time/MonthDay.html#withDayOfMonth%2Dint%2D[withDayOfMonth](int)++ (link:{java9-javadoc}/java/time/MonthDay.html#withDayOfMonth%2Dint%2D[java 9]) +* ++[[painless-api-reference-MonthDay-withMonth-1]]<> link:{java8-javadoc}/java/time/MonthDay.html#withMonth%2Dint%2D[withMonth](int)++ (link:{java9-javadoc}/java/time/MonthDay.html#withMonth%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/NavigableMap.asciidoc b/docs/painless/painless-api-reference/NavigableMap.asciidoc new file mode 100644 index 0000000000000..507ab8acd61f3 --- /dev/null +++ b/docs/painless/painless-api-reference/NavigableMap.asciidoc @@ -0,0 +1,24 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-NavigableMap]]++NavigableMap++:: +* ++[[painless-api-reference-NavigableMap-ceilingEntry-1]]<> link:{java8-javadoc}/java/util/NavigableMap.html#ceilingEntry%2Djava.lang.Object%2D[ceilingEntry](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#ceilingEntry%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-ceilingKey-1]]def link:{java8-javadoc}/java/util/NavigableMap.html#ceilingKey%2Djava.lang.Object%2D[ceilingKey](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#ceilingKey%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-descendingKeySet-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#descendingKeySet%2D%2D[descendingKeySet]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#descendingKeySet%2D%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-descendingMap-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#descendingMap%2D%2D[descendingMap]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#descendingMap%2D%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-firstEntry-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#firstEntry%2D%2D[firstEntry]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#firstEntry%2D%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-floorEntry-1]]<> link:{java8-javadoc}/java/util/NavigableMap.html#floorEntry%2Djava.lang.Object%2D[floorEntry](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#floorEntry%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-floorKey-1]]def link:{java8-javadoc}/java/util/NavigableMap.html#floorKey%2Djava.lang.Object%2D[floorKey](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#floorKey%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-headMap-2]]<> link:{java8-javadoc}/java/util/NavigableMap.html#headMap%2Djava.lang.Object%2Dboolean%2D[headMap](def, boolean)++ (link:{java9-javadoc}/java/util/NavigableMap.html#headMap%2Djava.lang.Object%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-higherEntry-1]]<> link:{java8-javadoc}/java/util/NavigableMap.html#higherEntry%2Djava.lang.Object%2D[higherEntry](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#higherEntry%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-higherKey-1]]def link:{java8-javadoc}/java/util/NavigableMap.html#higherKey%2Djava.lang.Object%2D[higherKey](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#higherKey%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-lastEntry-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#lastEntry%2D%2D[lastEntry]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#lastEntry%2D%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-lowerEntry-1]]<> link:{java8-javadoc}/java/util/NavigableMap.html#lowerEntry%2Djava.lang.Object%2D[lowerEntry](def)++ (link:{java9-javadoc}/java/util/NavigableMap.html#lowerEntry%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-navigableKeySet-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#navigableKeySet%2D%2D[navigableKeySet]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#navigableKeySet%2D%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-pollFirstEntry-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#pollFirstEntry%2D%2D[pollFirstEntry]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#pollFirstEntry%2D%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-pollLastEntry-0]]<> link:{java8-javadoc}/java/util/NavigableMap.html#pollLastEntry%2D%2D[pollLastEntry]()++ (link:{java9-javadoc}/java/util/NavigableMap.html#pollLastEntry%2D%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-subMap-4]]<> link:{java8-javadoc}/java/util/NavigableMap.html#subMap%2Djava.lang.Object%2Dboolean%2Djava.lang.Object%2Dboolean%2D[subMap](def, boolean, def, boolean)++ (link:{java9-javadoc}/java/util/NavigableMap.html#subMap%2Djava.lang.Object%2Dboolean%2Djava.lang.Object%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-NavigableMap-tailMap-2]]<> link:{java8-javadoc}/java/util/NavigableMap.html#tailMap%2Djava.lang.Object%2Dboolean%2D[tailMap](def, boolean)++ (link:{java9-javadoc}/java/util/NavigableMap.html#tailMap%2Djava.lang.Object%2Dboolean%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/NavigableSet.asciidoc b/docs/painless/painless-api-reference/NavigableSet.asciidoc new file mode 100644 index 0000000000000..15fff25f44d5b --- /dev/null +++ b/docs/painless/painless-api-reference/NavigableSet.asciidoc @@ -0,0 +1,18 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-NavigableSet]]++NavigableSet++:: +* ++[[painless-api-reference-NavigableSet-ceiling-1]]def link:{java8-javadoc}/java/util/NavigableSet.html#ceiling%2Djava.lang.Object%2D[ceiling](def)++ (link:{java9-javadoc}/java/util/NavigableSet.html#ceiling%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-NavigableSet-descendingIterator-0]]<> link:{java8-javadoc}/java/util/NavigableSet.html#descendingIterator%2D%2D[descendingIterator]()++ (link:{java9-javadoc}/java/util/NavigableSet.html#descendingIterator%2D%2D[java 9]) +* ++[[painless-api-reference-NavigableSet-descendingSet-0]]<> link:{java8-javadoc}/java/util/NavigableSet.html#descendingSet%2D%2D[descendingSet]()++ (link:{java9-javadoc}/java/util/NavigableSet.html#descendingSet%2D%2D[java 9]) +* ++[[painless-api-reference-NavigableSet-floor-1]]def link:{java8-javadoc}/java/util/NavigableSet.html#floor%2Djava.lang.Object%2D[floor](def)++ (link:{java9-javadoc}/java/util/NavigableSet.html#floor%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-NavigableSet-headSet-2]]<> link:{java8-javadoc}/java/util/NavigableSet.html#headSet%2Djava.lang.Object%2Dboolean%2D[headSet](def, boolean)++ (link:{java9-javadoc}/java/util/NavigableSet.html#headSet%2Djava.lang.Object%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-NavigableSet-higher-1]]def link:{java8-javadoc}/java/util/NavigableSet.html#higher%2Djava.lang.Object%2D[higher](def)++ (link:{java9-javadoc}/java/util/NavigableSet.html#higher%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-NavigableSet-lower-1]]def link:{java8-javadoc}/java/util/NavigableSet.html#lower%2Djava.lang.Object%2D[lower](def)++ (link:{java9-javadoc}/java/util/NavigableSet.html#lower%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-NavigableSet-pollFirst-0]]def link:{java8-javadoc}/java/util/NavigableSet.html#pollFirst%2D%2D[pollFirst]()++ (link:{java9-javadoc}/java/util/NavigableSet.html#pollFirst%2D%2D[java 9]) +* ++[[painless-api-reference-NavigableSet-pollLast-0]]def link:{java8-javadoc}/java/util/NavigableSet.html#pollLast%2D%2D[pollLast]()++ (link:{java9-javadoc}/java/util/NavigableSet.html#pollLast%2D%2D[java 9]) +* ++[[painless-api-reference-NavigableSet-subSet-4]]<> link:{java8-javadoc}/java/util/NavigableSet.html#subSet%2Djava.lang.Object%2Dboolean%2Djava.lang.Object%2Dboolean%2D[subSet](def, boolean, def, boolean)++ (link:{java9-javadoc}/java/util/NavigableSet.html#subSet%2Djava.lang.Object%2Dboolean%2Djava.lang.Object%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-NavigableSet-tailSet-2]]<> link:{java8-javadoc}/java/util/NavigableSet.html#tailSet%2Djava.lang.Object%2Dboolean%2D[tailSet](def, boolean)++ (link:{java9-javadoc}/java/util/NavigableSet.html#tailSet%2Djava.lang.Object%2Dboolean%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/NegativeArraySizeException.asciidoc b/docs/painless/painless-api-reference/NegativeArraySizeException.asciidoc new file mode 100644 index 0000000000000..78ee0006eca71 --- /dev/null +++ b/docs/painless/painless-api-reference/NegativeArraySizeException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-NegativeArraySizeException]]++NegativeArraySizeException++:: +* ++[[painless-api-reference-NegativeArraySizeException-NegativeArraySizeException-0]]link:{java8-javadoc}/java/lang/NegativeArraySizeException.html#NegativeArraySizeException%2D%2D[NegativeArraySizeException]()++ (link:{java9-javadoc}/java/lang/NegativeArraySizeException.html#NegativeArraySizeException%2D%2D[java 9]) +* ++[[painless-api-reference-NegativeArraySizeException-NegativeArraySizeException-1]]link:{java8-javadoc}/java/lang/NegativeArraySizeException.html#NegativeArraySizeException%2Djava.lang.String%2D[NegativeArraySizeException](<>)++ (link:{java9-javadoc}/java/lang/NegativeArraySizeException.html#NegativeArraySizeException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/NoSuchElementException.asciidoc b/docs/painless/painless-api-reference/NoSuchElementException.asciidoc new file mode 100644 index 0000000000000..7a28bdfb5dd24 --- /dev/null +++ b/docs/painless/painless-api-reference/NoSuchElementException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-NoSuchElementException]]++NoSuchElementException++:: +* ++[[painless-api-reference-NoSuchElementException-NoSuchElementException-0]]link:{java8-javadoc}/java/util/NoSuchElementException.html#NoSuchElementException%2D%2D[NoSuchElementException]()++ (link:{java9-javadoc}/java/util/NoSuchElementException.html#NoSuchElementException%2D%2D[java 9]) +* ++[[painless-api-reference-NoSuchElementException-NoSuchElementException-1]]link:{java8-javadoc}/java/util/NoSuchElementException.html#NoSuchElementException%2Djava.lang.String%2D[NoSuchElementException](<>)++ (link:{java9-javadoc}/java/util/NoSuchElementException.html#NoSuchElementException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/NoSuchFieldException.asciidoc b/docs/painless/painless-api-reference/NoSuchFieldException.asciidoc new file mode 100644 index 0000000000000..6f3e12d16d7d0 --- /dev/null +++ b/docs/painless/painless-api-reference/NoSuchFieldException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-NoSuchFieldException]]++NoSuchFieldException++:: +* ++[[painless-api-reference-NoSuchFieldException-NoSuchFieldException-0]]link:{java8-javadoc}/java/lang/NoSuchFieldException.html#NoSuchFieldException%2D%2D[NoSuchFieldException]()++ (link:{java9-javadoc}/java/lang/NoSuchFieldException.html#NoSuchFieldException%2D%2D[java 9]) +* ++[[painless-api-reference-NoSuchFieldException-NoSuchFieldException-1]]link:{java8-javadoc}/java/lang/NoSuchFieldException.html#NoSuchFieldException%2Djava.lang.String%2D[NoSuchFieldException](<>)++ (link:{java9-javadoc}/java/lang/NoSuchFieldException.html#NoSuchFieldException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/NoSuchMethodException.asciidoc b/docs/painless/painless-api-reference/NoSuchMethodException.asciidoc new file mode 100644 index 0000000000000..0fec067e9d281 --- /dev/null +++ b/docs/painless/painless-api-reference/NoSuchMethodException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-NoSuchMethodException]]++NoSuchMethodException++:: +* ++[[painless-api-reference-NoSuchMethodException-NoSuchMethodException-0]]link:{java8-javadoc}/java/lang/NoSuchMethodException.html#NoSuchMethodException%2D%2D[NoSuchMethodException]()++ (link:{java9-javadoc}/java/lang/NoSuchMethodException.html#NoSuchMethodException%2D%2D[java 9]) +* ++[[painless-api-reference-NoSuchMethodException-NoSuchMethodException-1]]link:{java8-javadoc}/java/lang/NoSuchMethodException.html#NoSuchMethodException%2Djava.lang.String%2D[NoSuchMethodException](<>)++ (link:{java9-javadoc}/java/lang/NoSuchMethodException.html#NoSuchMethodException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Normalizer.Form.asciidoc b/docs/painless/painless-api-reference/Normalizer.Form.asciidoc new file mode 100644 index 0000000000000..f74d4d243db95 --- /dev/null +++ b/docs/painless/painless-api-reference/Normalizer.Form.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Normalizer-Form]]++Normalizer.Form++:: +** [[painless-api-reference-Normalizer-Form-NFC]]static <> link:{java8-javadoc}/java/text/Normalizer$Form.html#NFC[NFC] (link:{java9-javadoc}/java/text/Normalizer$Form.html#NFC[java 9]) +** [[painless-api-reference-Normalizer-Form-NFD]]static <> link:{java8-javadoc}/java/text/Normalizer$Form.html#NFD[NFD] (link:{java9-javadoc}/java/text/Normalizer$Form.html#NFD[java 9]) +** [[painless-api-reference-Normalizer-Form-NFKC]]static <> link:{java8-javadoc}/java/text/Normalizer$Form.html#NFKC[NFKC] (link:{java9-javadoc}/java/text/Normalizer$Form.html#NFKC[java 9]) +** [[painless-api-reference-Normalizer-Form-NFKD]]static <> link:{java8-javadoc}/java/text/Normalizer$Form.html#NFKD[NFKD] (link:{java9-javadoc}/java/text/Normalizer$Form.html#NFKD[java 9]) +* ++[[painless-api-reference-Normalizer-Form-valueOf-1]]static <> link:{java8-javadoc}/java/text/Normalizer$Form.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/text/Normalizer$Form.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Normalizer-Form-values-0]]static <>[] link:{java8-javadoc}/java/text/Normalizer$Form.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/text/Normalizer$Form.html#values%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Normalizer.asciidoc b/docs/painless/painless-api-reference/Normalizer.asciidoc new file mode 100644 index 0000000000000..ecfe03c7fb4bd --- /dev/null +++ b/docs/painless/painless-api-reference/Normalizer.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Normalizer]]++Normalizer++:: +* ++[[painless-api-reference-Normalizer-isNormalized-2]]static boolean link:{java8-javadoc}/java/text/Normalizer.html#isNormalized%2Djava.lang.CharSequence%2Djava.text.Normalizer$Form%2D[isNormalized](<>, <>)++ (link:{java9-javadoc}/java/text/Normalizer.html#isNormalized%2Djava.lang.CharSequence%2Djava.text.Normalizer$Form%2D[java 9]) +* ++[[painless-api-reference-Normalizer-normalize-2]]static <> link:{java8-javadoc}/java/text/Normalizer.html#normalize%2Djava.lang.CharSequence%2Djava.text.Normalizer$Form%2D[normalize](<>, <>)++ (link:{java9-javadoc}/java/text/Normalizer.html#normalize%2Djava.lang.CharSequence%2Djava.text.Normalizer$Form%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/NullPointerException.asciidoc b/docs/painless/painless-api-reference/NullPointerException.asciidoc new file mode 100644 index 0000000000000..72795c7399060 --- /dev/null +++ b/docs/painless/painless-api-reference/NullPointerException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-NullPointerException]]++NullPointerException++:: +* ++[[painless-api-reference-NullPointerException-NullPointerException-0]]link:{java8-javadoc}/java/lang/NullPointerException.html#NullPointerException%2D%2D[NullPointerException]()++ (link:{java9-javadoc}/java/lang/NullPointerException.html#NullPointerException%2D%2D[java 9]) +* ++[[painless-api-reference-NullPointerException-NullPointerException-1]]link:{java8-javadoc}/java/lang/NullPointerException.html#NullPointerException%2Djava.lang.String%2D[NullPointerException](<>)++ (link:{java9-javadoc}/java/lang/NullPointerException.html#NullPointerException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Number.asciidoc b/docs/painless/painless-api-reference/Number.asciidoc new file mode 100644 index 0000000000000..a8c588f33704e --- /dev/null +++ b/docs/painless/painless-api-reference/Number.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Number]]++Number++:: +* ++[[painless-api-reference-Number-byteValue-0]]byte link:{java8-javadoc}/java/lang/Number.html#byteValue%2D%2D[byteValue]()++ (link:{java9-javadoc}/java/lang/Number.html#byteValue%2D%2D[java 9]) +* ++[[painless-api-reference-Number-doubleValue-0]]double link:{java8-javadoc}/java/lang/Number.html#doubleValue%2D%2D[doubleValue]()++ (link:{java9-javadoc}/java/lang/Number.html#doubleValue%2D%2D[java 9]) +* ++[[painless-api-reference-Number-floatValue-0]]float link:{java8-javadoc}/java/lang/Number.html#floatValue%2D%2D[floatValue]()++ (link:{java9-javadoc}/java/lang/Number.html#floatValue%2D%2D[java 9]) +* ++[[painless-api-reference-Number-intValue-0]]int link:{java8-javadoc}/java/lang/Number.html#intValue%2D%2D[intValue]()++ (link:{java9-javadoc}/java/lang/Number.html#intValue%2D%2D[java 9]) +* ++[[painless-api-reference-Number-longValue-0]]long link:{java8-javadoc}/java/lang/Number.html#longValue%2D%2D[longValue]()++ (link:{java9-javadoc}/java/lang/Number.html#longValue%2D%2D[java 9]) +* ++[[painless-api-reference-Number-shortValue-0]]short link:{java8-javadoc}/java/lang/Number.html#shortValue%2D%2D[shortValue]()++ (link:{java9-javadoc}/java/lang/Number.html#shortValue%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/NumberFormat.Field.asciidoc b/docs/painless/painless-api-reference/NumberFormat.Field.asciidoc new file mode 100644 index 0000000000000..aec4fc0635790 --- /dev/null +++ b/docs/painless/painless-api-reference/NumberFormat.Field.asciidoc @@ -0,0 +1,18 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-NumberFormat-Field]]++NumberFormat.Field++:: +** [[painless-api-reference-NumberFormat-Field-CURRENCY]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#CURRENCY[CURRENCY] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#CURRENCY[java 9]) +** [[painless-api-reference-NumberFormat-Field-DECIMAL_SEPARATOR]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#DECIMAL_SEPARATOR[DECIMAL_SEPARATOR] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#DECIMAL_SEPARATOR[java 9]) +** [[painless-api-reference-NumberFormat-Field-EXPONENT]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#EXPONENT[EXPONENT] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#EXPONENT[java 9]) +** [[painless-api-reference-NumberFormat-Field-EXPONENT_SIGN]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#EXPONENT_SIGN[EXPONENT_SIGN] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#EXPONENT_SIGN[java 9]) +** [[painless-api-reference-NumberFormat-Field-EXPONENT_SYMBOL]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#EXPONENT_SYMBOL[EXPONENT_SYMBOL] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#EXPONENT_SYMBOL[java 9]) +** [[painless-api-reference-NumberFormat-Field-FRACTION]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#FRACTION[FRACTION] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#FRACTION[java 9]) +** [[painless-api-reference-NumberFormat-Field-GROUPING_SEPARATOR]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#GROUPING_SEPARATOR[GROUPING_SEPARATOR] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#GROUPING_SEPARATOR[java 9]) +** [[painless-api-reference-NumberFormat-Field-INTEGER]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#INTEGER[INTEGER] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#INTEGER[java 9]) +** [[painless-api-reference-NumberFormat-Field-PERCENT]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#PERCENT[PERCENT] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#PERCENT[java 9]) +** [[painless-api-reference-NumberFormat-Field-PERMILLE]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#PERMILLE[PERMILLE] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#PERMILLE[java 9]) +** [[painless-api-reference-NumberFormat-Field-SIGN]]static <> link:{java8-javadoc}/java/text/NumberFormat$Field.html#SIGN[SIGN] (link:{java9-javadoc}/java/text/NumberFormat$Field.html#SIGN[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/NumberFormat.asciidoc b/docs/painless/painless-api-reference/NumberFormat.asciidoc new file mode 100644 index 0000000000000..ddca8ccd6826f --- /dev/null +++ b/docs/painless/painless-api-reference/NumberFormat.asciidoc @@ -0,0 +1,38 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-NumberFormat]]++NumberFormat++:: +** [[painless-api-reference-NumberFormat-FRACTION_FIELD]]static int link:{java8-javadoc}/java/text/NumberFormat.html#FRACTION_FIELD[FRACTION_FIELD] (link:{java9-javadoc}/java/text/NumberFormat.html#FRACTION_FIELD[java 9]) +** [[painless-api-reference-NumberFormat-INTEGER_FIELD]]static int link:{java8-javadoc}/java/text/NumberFormat.html#INTEGER_FIELD[INTEGER_FIELD] (link:{java9-javadoc}/java/text/NumberFormat.html#INTEGER_FIELD[java 9]) +* ++[[painless-api-reference-NumberFormat-getAvailableLocales-0]]static <>[] link:{java8-javadoc}/java/text/NumberFormat.html#getAvailableLocales%2D%2D[getAvailableLocales]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getAvailableLocales%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getCurrencyInstance-0]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getCurrencyInstance%2D%2D[getCurrencyInstance]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getCurrencyInstance%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getCurrencyInstance-1]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getCurrencyInstance%2Djava.util.Locale%2D[getCurrencyInstance](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#getCurrencyInstance%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getInstance-0]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getInstance%2D%2D[getInstance]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getInstance%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getInstance-1]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getInstance%2Djava.util.Locale%2D[getInstance](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#getInstance%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getIntegerInstance-0]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getIntegerInstance%2D%2D[getIntegerInstance]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getIntegerInstance%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getIntegerInstance-1]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getIntegerInstance%2Djava.util.Locale%2D[getIntegerInstance](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#getIntegerInstance%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getNumberInstance-0]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getNumberInstance%2D%2D[getNumberInstance]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getNumberInstance%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getNumberInstance-1]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getNumberInstance%2Djava.util.Locale%2D[getNumberInstance](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#getNumberInstance%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getPercentInstance-0]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getPercentInstance%2D%2D[getPercentInstance]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getPercentInstance%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getPercentInstance-1]]static <> link:{java8-javadoc}/java/text/NumberFormat.html#getPercentInstance%2Djava.util.Locale%2D[getPercentInstance](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#getPercentInstance%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getCurrency-0]]<> link:{java8-javadoc}/java/text/NumberFormat.html#getCurrency%2D%2D[getCurrency]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getCurrency%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getMaximumFractionDigits-0]]int link:{java8-javadoc}/java/text/NumberFormat.html#getMaximumFractionDigits%2D%2D[getMaximumFractionDigits]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getMaximumFractionDigits%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getMaximumIntegerDigits-0]]int link:{java8-javadoc}/java/text/NumberFormat.html#getMaximumIntegerDigits%2D%2D[getMaximumIntegerDigits]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getMaximumIntegerDigits%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getMinimumFractionDigits-0]]int link:{java8-javadoc}/java/text/NumberFormat.html#getMinimumFractionDigits%2D%2D[getMinimumFractionDigits]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getMinimumFractionDigits%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getMinimumIntegerDigits-0]]int link:{java8-javadoc}/java/text/NumberFormat.html#getMinimumIntegerDigits%2D%2D[getMinimumIntegerDigits]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getMinimumIntegerDigits%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-getRoundingMode-0]]<> link:{java8-javadoc}/java/text/NumberFormat.html#getRoundingMode%2D%2D[getRoundingMode]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#getRoundingMode%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-isGroupingUsed-0]]boolean link:{java8-javadoc}/java/text/NumberFormat.html#isGroupingUsed%2D%2D[isGroupingUsed]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#isGroupingUsed%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-isParseIntegerOnly-0]]boolean link:{java8-javadoc}/java/text/NumberFormat.html#isParseIntegerOnly%2D%2D[isParseIntegerOnly]()++ (link:{java9-javadoc}/java/text/NumberFormat.html#isParseIntegerOnly%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-parse-1]]<> link:{java8-javadoc}/java/text/NumberFormat.html#parse%2Djava.lang.String%2D[parse](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#parse%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-parse-2]]<> link:{java8-javadoc}/java/text/NumberFormat.html#parse%2Djava.lang.String%2Djava.text.ParsePosition%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#parse%2Djava.lang.String%2Djava.text.ParsePosition%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-setCurrency-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setCurrency%2Djava.util.Currency%2D[setCurrency](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setCurrency%2Djava.util.Currency%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-setGroupingUsed-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setGroupingUsed%2Dboolean%2D[setGroupingUsed](boolean)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setGroupingUsed%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-setMaximumFractionDigits-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setMaximumFractionDigits%2Dint%2D[setMaximumFractionDigits](int)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setMaximumFractionDigits%2Dint%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-setMaximumIntegerDigits-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setMaximumIntegerDigits%2Dint%2D[setMaximumIntegerDigits](int)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setMaximumIntegerDigits%2Dint%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-setMinimumFractionDigits-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setMinimumFractionDigits%2Dint%2D[setMinimumFractionDigits](int)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setMinimumFractionDigits%2Dint%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-setMinimumIntegerDigits-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setMinimumIntegerDigits%2Dint%2D[setMinimumIntegerDigits](int)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setMinimumIntegerDigits%2Dint%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-setParseIntegerOnly-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setParseIntegerOnly%2Dboolean%2D[setParseIntegerOnly](boolean)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setParseIntegerOnly%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-NumberFormat-setRoundingMode-1]]void link:{java8-javadoc}/java/text/NumberFormat.html#setRoundingMode%2Djava.math.RoundingMode%2D[setRoundingMode](<>)++ (link:{java9-javadoc}/java/text/NumberFormat.html#setRoundingMode%2Djava.math.RoundingMode%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/NumberFormatException.asciidoc b/docs/painless/painless-api-reference/NumberFormatException.asciidoc new file mode 100644 index 0000000000000..4066147650c33 --- /dev/null +++ b/docs/painless/painless-api-reference/NumberFormatException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-NumberFormatException]]++NumberFormatException++:: +* ++[[painless-api-reference-NumberFormatException-NumberFormatException-0]]link:{java8-javadoc}/java/lang/NumberFormatException.html#NumberFormatException%2D%2D[NumberFormatException]()++ (link:{java9-javadoc}/java/lang/NumberFormatException.html#NumberFormatException%2D%2D[java 9]) +* ++[[painless-api-reference-NumberFormatException-NumberFormatException-1]]link:{java8-javadoc}/java/lang/NumberFormatException.html#NumberFormatException%2Djava.lang.String%2D[NumberFormatException](<>)++ (link:{java9-javadoc}/java/lang/NumberFormatException.html#NumberFormatException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ObjDoubleConsumer.asciidoc b/docs/painless/painless-api-reference/ObjDoubleConsumer.asciidoc new file mode 100644 index 0000000000000..2fc41f9ce0fb5 --- /dev/null +++ b/docs/painless/painless-api-reference/ObjDoubleConsumer.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ObjDoubleConsumer]]++ObjDoubleConsumer++:: +* ++[[painless-api-reference-ObjDoubleConsumer-accept-2]]void link:{java8-javadoc}/java/util/function/ObjDoubleConsumer.html#accept%2Djava.lang.Object%2Ddouble%2D[accept](def, double)++ (link:{java9-javadoc}/java/util/function/ObjDoubleConsumer.html#accept%2Djava.lang.Object%2Ddouble%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ObjIntConsumer.asciidoc b/docs/painless/painless-api-reference/ObjIntConsumer.asciidoc new file mode 100644 index 0000000000000..8779b333df92c --- /dev/null +++ b/docs/painless/painless-api-reference/ObjIntConsumer.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ObjIntConsumer]]++ObjIntConsumer++:: +* ++[[painless-api-reference-ObjIntConsumer-accept-2]]void link:{java8-javadoc}/java/util/function/ObjIntConsumer.html#accept%2Djava.lang.Object%2Dint%2D[accept](def, int)++ (link:{java9-javadoc}/java/util/function/ObjIntConsumer.html#accept%2Djava.lang.Object%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ObjLongConsumer.asciidoc b/docs/painless/painless-api-reference/ObjLongConsumer.asciidoc new file mode 100644 index 0000000000000..cdb0ed6546c4f --- /dev/null +++ b/docs/painless/painless-api-reference/ObjLongConsumer.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ObjLongConsumer]]++ObjLongConsumer++:: +* ++[[painless-api-reference-ObjLongConsumer-accept-2]]void link:{java8-javadoc}/java/util/function/ObjLongConsumer.html#accept%2Djava.lang.Object%2Dlong%2D[accept](def, long)++ (link:{java9-javadoc}/java/util/function/ObjLongConsumer.html#accept%2Djava.lang.Object%2Dlong%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Object.asciidoc b/docs/painless/painless-api-reference/Object.asciidoc new file mode 100644 index 0000000000000..99c4ae5cffdb6 --- /dev/null +++ b/docs/painless/painless-api-reference/Object.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Object]]++Object++:: +* ++[[painless-api-reference-Object-equals-1]]boolean link:{java8-javadoc}/java/lang/Object.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/lang/Object.html#equals%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Object-hashCode-0]]int link:{java8-javadoc}/java/lang/Object.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/lang/Object.html#hashCode%2D%2D[java 9]) +* ++[[painless-api-reference-Object-toString-0]]<> link:{java8-javadoc}/java/lang/Object.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/lang/Object.html#toString%2D%2D[java 9]) diff --git a/docs/painless/painless-api-reference/Objects.asciidoc b/docs/painless/painless-api-reference/Objects.asciidoc new file mode 100644 index 0000000000000..9fdbad2e5b71d --- /dev/null +++ b/docs/painless/painless-api-reference/Objects.asciidoc @@ -0,0 +1,18 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Objects]]++Objects++:: +* ++[[painless-api-reference-Objects-compare-3]]static int link:{java8-javadoc}/java/util/Objects.html#compare%2Djava.lang.Object%2Djava.lang.Object%2Djava.util.Comparator%2D[compare](def, def, <>)++ (link:{java9-javadoc}/java/util/Objects.html#compare%2Djava.lang.Object%2Djava.lang.Object%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Objects-deepEquals-2]]static boolean link:{java8-javadoc}/java/util/Objects.html#deepEquals%2Djava.lang.Object%2Djava.lang.Object%2D[deepEquals](<>, <>)++ (link:{java9-javadoc}/java/util/Objects.html#deepEquals%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Objects-equals-2]]static boolean link:{java8-javadoc}/java/util/Objects.html#equals%2Djava.lang.Object%2Djava.lang.Object%2D[equals](<>, <>)++ (link:{java9-javadoc}/java/util/Objects.html#equals%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Objects-hash-1]]static int link:{java8-javadoc}/java/util/Objects.html#hash%2Djava.lang.Object:A%2D[hash](<>[])++ (link:{java9-javadoc}/java/util/Objects.html#hash%2Djava.lang.Object:A%2D[java 9]) +* ++[[painless-api-reference-Objects-hashCode-1]]static int link:{java8-javadoc}/java/util/Objects.html#hashCode%2Djava.lang.Object%2D[hashCode](<>)++ (link:{java9-javadoc}/java/util/Objects.html#hashCode%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Objects-isNull-1]]static boolean link:{java8-javadoc}/java/util/Objects.html#isNull%2Djava.lang.Object%2D[isNull](<>)++ (link:{java9-javadoc}/java/util/Objects.html#isNull%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Objects-nonNull-1]]static boolean link:{java8-javadoc}/java/util/Objects.html#nonNull%2Djava.lang.Object%2D[nonNull](<>)++ (link:{java9-javadoc}/java/util/Objects.html#nonNull%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Objects-requireNonNull-1]]static def link:{java8-javadoc}/java/util/Objects.html#requireNonNull%2Djava.lang.Object%2D[requireNonNull](def)++ (link:{java9-javadoc}/java/util/Objects.html#requireNonNull%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Objects-requireNonNull-2]]static def link:{java8-javadoc}/java/util/Objects.html#requireNonNull%2Djava.lang.Object%2Djava.lang.String%2D[requireNonNull](def, <>)++ (link:{java9-javadoc}/java/util/Objects.html#requireNonNull%2Djava.lang.Object%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Objects-toString-1]]static <> link:{java8-javadoc}/java/util/Objects.html#toString%2Djava.lang.Object%2D[toString](<>)++ (link:{java9-javadoc}/java/util/Objects.html#toString%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Objects-toString-2]]static <> link:{java8-javadoc}/java/util/Objects.html#toString%2Djava.lang.Object%2Djava.lang.String%2D[toString](<>, <>)++ (link:{java9-javadoc}/java/util/Objects.html#toString%2Djava.lang.Object%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Observable.asciidoc b/docs/painless/painless-api-reference/Observable.asciidoc new file mode 100644 index 0000000000000..65d8fae45ae36 --- /dev/null +++ b/docs/painless/painless-api-reference/Observable.asciidoc @@ -0,0 +1,15 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Observable]]++Observable++:: +* ++[[painless-api-reference-Observable-Observable-0]]link:{java8-javadoc}/java/util/Observable.html#Observable%2D%2D[Observable]()++ (link:{java9-javadoc}/java/util/Observable.html#Observable%2D%2D[java 9]) +* ++[[painless-api-reference-Observable-addObserver-1]]void link:{java8-javadoc}/java/util/Observable.html#addObserver%2Djava.util.Observer%2D[addObserver](<>)++ (link:{java9-javadoc}/java/util/Observable.html#addObserver%2Djava.util.Observer%2D[java 9]) +* ++[[painless-api-reference-Observable-countObservers-0]]int link:{java8-javadoc}/java/util/Observable.html#countObservers%2D%2D[countObservers]()++ (link:{java9-javadoc}/java/util/Observable.html#countObservers%2D%2D[java 9]) +* ++[[painless-api-reference-Observable-deleteObserver-1]]void link:{java8-javadoc}/java/util/Observable.html#deleteObserver%2Djava.util.Observer%2D[deleteObserver](<>)++ (link:{java9-javadoc}/java/util/Observable.html#deleteObserver%2Djava.util.Observer%2D[java 9]) +* ++[[painless-api-reference-Observable-deleteObservers-0]]void link:{java8-javadoc}/java/util/Observable.html#deleteObservers%2D%2D[deleteObservers]()++ (link:{java9-javadoc}/java/util/Observable.html#deleteObservers%2D%2D[java 9]) +* ++[[painless-api-reference-Observable-hasChanged-0]]boolean link:{java8-javadoc}/java/util/Observable.html#hasChanged%2D%2D[hasChanged]()++ (link:{java9-javadoc}/java/util/Observable.html#hasChanged%2D%2D[java 9]) +* ++[[painless-api-reference-Observable-notifyObservers-0]]void link:{java8-javadoc}/java/util/Observable.html#notifyObservers%2D%2D[notifyObservers]()++ (link:{java9-javadoc}/java/util/Observable.html#notifyObservers%2D%2D[java 9]) +* ++[[painless-api-reference-Observable-notifyObservers-1]]void link:{java8-javadoc}/java/util/Observable.html#notifyObservers%2Djava.lang.Object%2D[notifyObservers](<>)++ (link:{java9-javadoc}/java/util/Observable.html#notifyObservers%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Observer.asciidoc b/docs/painless/painless-api-reference/Observer.asciidoc new file mode 100644 index 0000000000000..9277663dd6232 --- /dev/null +++ b/docs/painless/painless-api-reference/Observer.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Observer]]++Observer++:: +* ++[[painless-api-reference-Observer-update-2]]void link:{java8-javadoc}/java/util/Observer.html#update%2Djava.util.Observable%2Djava.lang.Object%2D[update](<>, <>)++ (link:{java9-javadoc}/java/util/Observer.html#update%2Djava.util.Observable%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/OffsetDateTime.asciidoc b/docs/painless/painless-api-reference/OffsetDateTime.asciidoc new file mode 100644 index 0000000000000..d476c181c55f0 --- /dev/null +++ b/docs/painless/painless-api-reference/OffsetDateTime.asciidoc @@ -0,0 +1,75 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-OffsetDateTime]]++OffsetDateTime++:: +** [[painless-api-reference-OffsetDateTime-MAX]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#MAX[MAX] (link:{java9-javadoc}/java/time/OffsetDateTime.html#MAX[java 9]) +** [[painless-api-reference-OffsetDateTime-MIN]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#MIN[MIN] (link:{java9-javadoc}/java/time/OffsetDateTime.html#MIN[java 9]) +* ++[[painless-api-reference-OffsetDateTime-from-1]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-of-2]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#of%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2D[of](<>, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#of%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-of-3]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#of%2Djava.time.LocalDate%2Djava.time.LocalTime%2Djava.time.ZoneOffset%2D[of](<>, <>, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#of%2Djava.time.LocalDate%2Djava.time.LocalTime%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-of-8]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Djava.time.ZoneOffset%2D[of](int, int, int, int, int, int, int, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-ofInstant-2]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[ofInstant](<>, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-parse-1]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#parse%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-parse-2]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-timeLineOrder-0]]static <> link:{java8-javadoc}/java/time/OffsetDateTime.html#timeLineOrder%2D%2D[timeLineOrder]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#timeLineOrder%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-atZoneSameInstant-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#atZoneSameInstant%2Djava.time.ZoneId%2D[atZoneSameInstant](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#atZoneSameInstant%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-atZoneSimilarLocal-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#atZoneSimilarLocal%2Djava.time.ZoneId%2D[atZoneSimilarLocal](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#atZoneSimilarLocal%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-compareTo-1]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#compareTo%2Djava.time.OffsetDateTime%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#compareTo%2Djava.time.OffsetDateTime%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-format-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-getDayOfMonth-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getDayOfMonth%2D%2D[getDayOfMonth]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getDayOfMonth%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-getDayOfWeek-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#getDayOfWeek%2D%2D[getDayOfWeek]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getDayOfWeek%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-getDayOfYear-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getDayOfYear%2D%2D[getDayOfYear]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getDayOfYear%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-getHour-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getHour%2D%2D[getHour]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getHour%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-getMinute-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getMinute%2D%2D[getMinute]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getMinute%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-getMonth-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getMonth%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-getMonthValue-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getMonthValue%2D%2D[getMonthValue]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getMonthValue%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-getNano-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getNano%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-getOffset-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#getOffset%2D%2D[getOffset]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getOffset%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-getSecond-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getSecond%2D%2D[getSecond]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getSecond%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-getYear-0]]int link:{java8-javadoc}/java/time/OffsetDateTime.html#getYear%2D%2D[getYear]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#getYear%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-isAfter-1]]boolean link:{java8-javadoc}/java/time/OffsetDateTime.html#isAfter%2Djava.time.OffsetDateTime%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#isAfter%2Djava.time.OffsetDateTime%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-isBefore-1]]boolean link:{java8-javadoc}/java/time/OffsetDateTime.html#isBefore%2Djava.time.OffsetDateTime%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#isBefore%2Djava.time.OffsetDateTime%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-isEqual-1]]boolean link:{java8-javadoc}/java/time/OffsetDateTime.html#isEqual%2Djava.time.OffsetDateTime%2D[isEqual](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#isEqual%2Djava.time.OffsetDateTime%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-minus-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-minus-2]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-minusDays-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusDays%2Dlong%2D[minusDays](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusDays%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-minusHours-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusHours%2Dlong%2D[minusHours](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusHours%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-minusMinutes-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusMinutes%2Dlong%2D[minusMinutes](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusMinutes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-minusMonths-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusMonths%2Dlong%2D[minusMonths](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusMonths%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-minusNanos-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-minusSeconds-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-minusWeeks-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusWeeks%2Dlong%2D[minusWeeks](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusWeeks%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-minusYears-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#minusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-plus-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-plus-2]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-plusDays-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusDays%2Dlong%2D[plusDays](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusDays%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-plusHours-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusHours%2Dlong%2D[plusHours](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusHours%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-plusMinutes-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusMinutes%2Dlong%2D[plusMinutes](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusMinutes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-plusMonths-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusMonths%2Dlong%2D[plusMonths](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusMonths%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-plusNanos-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-plusSeconds-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-plusWeeks-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusWeeks%2Dlong%2D[plusWeeks](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusWeeks%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-plusYears-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#plusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-toEpochSecond-0]]long link:{java8-javadoc}/java/time/OffsetDateTime.html#toEpochSecond%2D%2D[toEpochSecond]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toEpochSecond%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-toInstant-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#toInstant%2D%2D[toInstant]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toInstant%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-toLocalDate-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#toLocalDate%2D%2D[toLocalDate]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toLocalDate%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-toLocalDateTime-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#toLocalDateTime%2D%2D[toLocalDateTime]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toLocalDateTime%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-toLocalTime-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#toLocalTime%2D%2D[toLocalTime]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toLocalTime%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-toOffsetTime-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#toOffsetTime%2D%2D[toOffsetTime]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toOffsetTime%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-toZonedDateTime-0]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#toZonedDateTime%2D%2D[toZonedDateTime]()++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#toZonedDateTime%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-truncatedTo-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[truncatedTo](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-with-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-with-2]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-withDayOfMonth-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withDayOfMonth%2Dint%2D[withDayOfMonth](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withDayOfMonth%2Dint%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-withDayOfYear-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withDayOfYear%2Dint%2D[withDayOfYear](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withDayOfYear%2Dint%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-withHour-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withHour%2Dint%2D[withHour](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withHour%2Dint%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-withMinute-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withMinute%2Dint%2D[withMinute](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withMinute%2Dint%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-withMonth-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withMonth%2Dint%2D[withMonth](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withMonth%2Dint%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-withNano-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withNano%2Dint%2D[withNano](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withNano%2Dint%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-withOffsetSameInstant-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withOffsetSameInstant%2Djava.time.ZoneOffset%2D[withOffsetSameInstant](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withOffsetSameInstant%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-withOffsetSameLocal-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withOffsetSameLocal%2Djava.time.ZoneOffset%2D[withOffsetSameLocal](<>)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withOffsetSameLocal%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-withSecond-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withSecond%2Dint%2D[withSecond](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withSecond%2Dint%2D[java 9]) +* ++[[painless-api-reference-OffsetDateTime-withYear-1]]<> link:{java8-javadoc}/java/time/OffsetDateTime.html#withYear%2Dint%2D[withYear](int)++ (link:{java9-javadoc}/java/time/OffsetDateTime.html#withYear%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/OffsetTime.asciidoc b/docs/painless/painless-api-reference/OffsetTime.asciidoc new file mode 100644 index 0000000000000..b23e9bb9577d0 --- /dev/null +++ b/docs/painless/painless-api-reference/OffsetTime.asciidoc @@ -0,0 +1,47 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-OffsetTime]]++OffsetTime++:: +** [[painless-api-reference-OffsetTime-MAX]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#MAX[MAX] (link:{java9-javadoc}/java/time/OffsetTime.html#MAX[java 9]) +** [[painless-api-reference-OffsetTime-MIN]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#MIN[MIN] (link:{java9-javadoc}/java/time/OffsetTime.html#MIN[java 9]) +* ++[[painless-api-reference-OffsetTime-from-1]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-of-2]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#of%2Djava.time.LocalTime%2Djava.time.ZoneOffset%2D[of](<>, <>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#of%2Djava.time.LocalTime%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-of-5]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#of%2Dint%2Dint%2Dint%2Dint%2Djava.time.ZoneOffset%2D[of](int, int, int, int, <>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#of%2Dint%2Dint%2Dint%2Dint%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-ofInstant-2]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[ofInstant](<>, <>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-parse-1]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#parse%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-parse-2]]static <> link:{java8-javadoc}/java/time/OffsetTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-compareTo-1]]int link:{java8-javadoc}/java/time/OffsetTime.html#compareTo%2Djava.time.OffsetTime%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#compareTo%2Djava.time.OffsetTime%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-format-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-getHour-0]]int link:{java8-javadoc}/java/time/OffsetTime.html#getHour%2D%2D[getHour]()++ (link:{java9-javadoc}/java/time/OffsetTime.html#getHour%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-getMinute-0]]int link:{java8-javadoc}/java/time/OffsetTime.html#getMinute%2D%2D[getMinute]()++ (link:{java9-javadoc}/java/time/OffsetTime.html#getMinute%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-getNano-0]]int link:{java8-javadoc}/java/time/OffsetTime.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/OffsetTime.html#getNano%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-getOffset-0]]<> link:{java8-javadoc}/java/time/OffsetTime.html#getOffset%2D%2D[getOffset]()++ (link:{java9-javadoc}/java/time/OffsetTime.html#getOffset%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-getSecond-0]]int link:{java8-javadoc}/java/time/OffsetTime.html#getSecond%2D%2D[getSecond]()++ (link:{java9-javadoc}/java/time/OffsetTime.html#getSecond%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-isAfter-1]]boolean link:{java8-javadoc}/java/time/OffsetTime.html#isAfter%2Djava.time.OffsetTime%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#isAfter%2Djava.time.OffsetTime%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-isBefore-1]]boolean link:{java8-javadoc}/java/time/OffsetTime.html#isBefore%2Djava.time.OffsetTime%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#isBefore%2Djava.time.OffsetTime%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-isEqual-1]]boolean link:{java8-javadoc}/java/time/OffsetTime.html#isEqual%2Djava.time.OffsetTime%2D[isEqual](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#isEqual%2Djava.time.OffsetTime%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-minus-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-minus-2]]<> link:{java8-javadoc}/java/time/OffsetTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-minusHours-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#minusHours%2Dlong%2D[minusHours](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#minusHours%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-minusMinutes-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#minusMinutes%2Dlong%2D[minusMinutes](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#minusMinutes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-minusNanos-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#minusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-minusSeconds-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#minusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-plus-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-plus-2]]<> link:{java8-javadoc}/java/time/OffsetTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-plusHours-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#plusHours%2Dlong%2D[plusHours](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#plusHours%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-plusMinutes-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#plusMinutes%2Dlong%2D[plusMinutes](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#plusMinutes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-plusNanos-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#plusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-plusSeconds-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#plusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-toLocalTime-0]]<> link:{java8-javadoc}/java/time/OffsetTime.html#toLocalTime%2D%2D[toLocalTime]()++ (link:{java9-javadoc}/java/time/OffsetTime.html#toLocalTime%2D%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-truncatedTo-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[truncatedTo](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-with-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-with-2]]<> link:{java8-javadoc}/java/time/OffsetTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/OffsetTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-withHour-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#withHour%2Dint%2D[withHour](int)++ (link:{java9-javadoc}/java/time/OffsetTime.html#withHour%2Dint%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-withMinute-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#withMinute%2Dint%2D[withMinute](int)++ (link:{java9-javadoc}/java/time/OffsetTime.html#withMinute%2Dint%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-withNano-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#withNano%2Dint%2D[withNano](int)++ (link:{java9-javadoc}/java/time/OffsetTime.html#withNano%2Dint%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-withOffsetSameInstant-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#withOffsetSameInstant%2Djava.time.ZoneOffset%2D[withOffsetSameInstant](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#withOffsetSameInstant%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-withOffsetSameLocal-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#withOffsetSameLocal%2Djava.time.ZoneOffset%2D[withOffsetSameLocal](<>)++ (link:{java9-javadoc}/java/time/OffsetTime.html#withOffsetSameLocal%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-OffsetTime-withSecond-1]]<> link:{java8-javadoc}/java/time/OffsetTime.html#withSecond%2Dint%2D[withSecond](int)++ (link:{java9-javadoc}/java/time/OffsetTime.html#withSecond%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Optional.asciidoc b/docs/painless/painless-api-reference/Optional.asciidoc new file mode 100644 index 0000000000000..a67616f006fea --- /dev/null +++ b/docs/painless/painless-api-reference/Optional.asciidoc @@ -0,0 +1,19 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Optional]]++Optional++:: +* ++[[painless-api-reference-Optional-empty-0]]static <> link:{java8-javadoc}/java/util/Optional.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/Optional.html#empty%2D%2D[java 9]) +* ++[[painless-api-reference-Optional-of-1]]static <> link:{java8-javadoc}/java/util/Optional.html#of%2Djava.lang.Object%2D[of](def)++ (link:{java9-javadoc}/java/util/Optional.html#of%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Optional-ofNullable-1]]static <> link:{java8-javadoc}/java/util/Optional.html#ofNullable%2Djava.lang.Object%2D[ofNullable](def)++ (link:{java9-javadoc}/java/util/Optional.html#ofNullable%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Optional-filter-1]]<> link:{java8-javadoc}/java/util/Optional.html#filter%2Djava.util.function.Predicate%2D[filter](<>)++ (link:{java9-javadoc}/java/util/Optional.html#filter%2Djava.util.function.Predicate%2D[java 9]) +* ++[[painless-api-reference-Optional-flatMap-1]]<> link:{java8-javadoc}/java/util/Optional.html#flatMap%2Djava.util.function.Function%2D[flatMap](<>)++ (link:{java9-javadoc}/java/util/Optional.html#flatMap%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-Optional-get-0]]def link:{java8-javadoc}/java/util/Optional.html#get%2D%2D[get]()++ (link:{java9-javadoc}/java/util/Optional.html#get%2D%2D[java 9]) +* ++[[painless-api-reference-Optional-ifPresent-1]]void link:{java8-javadoc}/java/util/Optional.html#ifPresent%2Djava.util.function.Consumer%2D[ifPresent](<>)++ (link:{java9-javadoc}/java/util/Optional.html#ifPresent%2Djava.util.function.Consumer%2D[java 9]) +* ++[[painless-api-reference-Optional-isPresent-0]]boolean link:{java8-javadoc}/java/util/Optional.html#isPresent%2D%2D[isPresent]()++ (link:{java9-javadoc}/java/util/Optional.html#isPresent%2D%2D[java 9]) +* ++[[painless-api-reference-Optional-map-1]]<> link:{java8-javadoc}/java/util/Optional.html#map%2Djava.util.function.Function%2D[map](<>)++ (link:{java9-javadoc}/java/util/Optional.html#map%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-Optional-orElse-1]]def link:{java8-javadoc}/java/util/Optional.html#orElse%2Djava.lang.Object%2D[orElse](def)++ (link:{java9-javadoc}/java/util/Optional.html#orElse%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Optional-orElseGet-1]]def link:{java8-javadoc}/java/util/Optional.html#orElseGet%2Djava.util.function.Supplier%2D[orElseGet](<>)++ (link:{java9-javadoc}/java/util/Optional.html#orElseGet%2Djava.util.function.Supplier%2D[java 9]) +* ++[[painless-api-reference-Optional-orElseThrow-1]]def link:{java8-javadoc}/java/util/Optional.html#orElseThrow%2Djava.util.function.Supplier%2D[orElseThrow](<>)++ (link:{java9-javadoc}/java/util/Optional.html#orElseThrow%2Djava.util.function.Supplier%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/OptionalDouble.asciidoc b/docs/painless/painless-api-reference/OptionalDouble.asciidoc new file mode 100644 index 0000000000000..56ac37deaa336 --- /dev/null +++ b/docs/painless/painless-api-reference/OptionalDouble.asciidoc @@ -0,0 +1,15 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-OptionalDouble]]++OptionalDouble++:: +* ++[[painless-api-reference-OptionalDouble-empty-0]]static <> link:{java8-javadoc}/java/util/OptionalDouble.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/OptionalDouble.html#empty%2D%2D[java 9]) +* ++[[painless-api-reference-OptionalDouble-of-1]]static <> link:{java8-javadoc}/java/util/OptionalDouble.html#of%2Ddouble%2D[of](double)++ (link:{java9-javadoc}/java/util/OptionalDouble.html#of%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-OptionalDouble-getAsDouble-0]]double link:{java8-javadoc}/java/util/OptionalDouble.html#getAsDouble%2D%2D[getAsDouble]()++ (link:{java9-javadoc}/java/util/OptionalDouble.html#getAsDouble%2D%2D[java 9]) +* ++[[painless-api-reference-OptionalDouble-ifPresent-1]]void link:{java8-javadoc}/java/util/OptionalDouble.html#ifPresent%2Djava.util.function.DoubleConsumer%2D[ifPresent](<>)++ (link:{java9-javadoc}/java/util/OptionalDouble.html#ifPresent%2Djava.util.function.DoubleConsumer%2D[java 9]) +* ++[[painless-api-reference-OptionalDouble-isPresent-0]]boolean link:{java8-javadoc}/java/util/OptionalDouble.html#isPresent%2D%2D[isPresent]()++ (link:{java9-javadoc}/java/util/OptionalDouble.html#isPresent%2D%2D[java 9]) +* ++[[painless-api-reference-OptionalDouble-orElse-1]]double link:{java8-javadoc}/java/util/OptionalDouble.html#orElse%2Ddouble%2D[orElse](double)++ (link:{java9-javadoc}/java/util/OptionalDouble.html#orElse%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-OptionalDouble-orElseGet-1]]double link:{java8-javadoc}/java/util/OptionalDouble.html#orElseGet%2Djava.util.function.DoubleSupplier%2D[orElseGet](<>)++ (link:{java9-javadoc}/java/util/OptionalDouble.html#orElseGet%2Djava.util.function.DoubleSupplier%2D[java 9]) +* ++[[painless-api-reference-OptionalDouble-orElseThrow-1]]double link:{java8-javadoc}/java/util/OptionalDouble.html#orElseThrow%2Djava.util.function.Supplier%2D[orElseThrow](<>)++ (link:{java9-javadoc}/java/util/OptionalDouble.html#orElseThrow%2Djava.util.function.Supplier%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/OptionalInt.asciidoc b/docs/painless/painless-api-reference/OptionalInt.asciidoc new file mode 100644 index 0000000000000..69f0059b85f9c --- /dev/null +++ b/docs/painless/painless-api-reference/OptionalInt.asciidoc @@ -0,0 +1,15 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-OptionalInt]]++OptionalInt++:: +* ++[[painless-api-reference-OptionalInt-empty-0]]static <> link:{java8-javadoc}/java/util/OptionalInt.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/OptionalInt.html#empty%2D%2D[java 9]) +* ++[[painless-api-reference-OptionalInt-of-1]]static <> link:{java8-javadoc}/java/util/OptionalInt.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/util/OptionalInt.html#of%2Dint%2D[java 9]) +* ++[[painless-api-reference-OptionalInt-getAsInt-0]]int link:{java8-javadoc}/java/util/OptionalInt.html#getAsInt%2D%2D[getAsInt]()++ (link:{java9-javadoc}/java/util/OptionalInt.html#getAsInt%2D%2D[java 9]) +* ++[[painless-api-reference-OptionalInt-ifPresent-1]]void link:{java8-javadoc}/java/util/OptionalInt.html#ifPresent%2Djava.util.function.IntConsumer%2D[ifPresent](<>)++ (link:{java9-javadoc}/java/util/OptionalInt.html#ifPresent%2Djava.util.function.IntConsumer%2D[java 9]) +* ++[[painless-api-reference-OptionalInt-isPresent-0]]boolean link:{java8-javadoc}/java/util/OptionalInt.html#isPresent%2D%2D[isPresent]()++ (link:{java9-javadoc}/java/util/OptionalInt.html#isPresent%2D%2D[java 9]) +* ++[[painless-api-reference-OptionalInt-orElse-1]]int link:{java8-javadoc}/java/util/OptionalInt.html#orElse%2Dint%2D[orElse](int)++ (link:{java9-javadoc}/java/util/OptionalInt.html#orElse%2Dint%2D[java 9]) +* ++[[painless-api-reference-OptionalInt-orElseGet-1]]int link:{java8-javadoc}/java/util/OptionalInt.html#orElseGet%2Djava.util.function.IntSupplier%2D[orElseGet](<>)++ (link:{java9-javadoc}/java/util/OptionalInt.html#orElseGet%2Djava.util.function.IntSupplier%2D[java 9]) +* ++[[painless-api-reference-OptionalInt-orElseThrow-1]]int link:{java8-javadoc}/java/util/OptionalInt.html#orElseThrow%2Djava.util.function.Supplier%2D[orElseThrow](<>)++ (link:{java9-javadoc}/java/util/OptionalInt.html#orElseThrow%2Djava.util.function.Supplier%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/OptionalLong.asciidoc b/docs/painless/painless-api-reference/OptionalLong.asciidoc new file mode 100644 index 0000000000000..a4b135e55115a --- /dev/null +++ b/docs/painless/painless-api-reference/OptionalLong.asciidoc @@ -0,0 +1,15 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-OptionalLong]]++OptionalLong++:: +* ++[[painless-api-reference-OptionalLong-empty-0]]static <> link:{java8-javadoc}/java/util/OptionalLong.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/OptionalLong.html#empty%2D%2D[java 9]) +* ++[[painless-api-reference-OptionalLong-of-1]]static <> link:{java8-javadoc}/java/util/OptionalLong.html#of%2Dlong%2D[of](long)++ (link:{java9-javadoc}/java/util/OptionalLong.html#of%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OptionalLong-getAsLong-0]]long link:{java8-javadoc}/java/util/OptionalLong.html#getAsLong%2D%2D[getAsLong]()++ (link:{java9-javadoc}/java/util/OptionalLong.html#getAsLong%2D%2D[java 9]) +* ++[[painless-api-reference-OptionalLong-ifPresent-1]]void link:{java8-javadoc}/java/util/OptionalLong.html#ifPresent%2Djava.util.function.LongConsumer%2D[ifPresent](<>)++ (link:{java9-javadoc}/java/util/OptionalLong.html#ifPresent%2Djava.util.function.LongConsumer%2D[java 9]) +* ++[[painless-api-reference-OptionalLong-isPresent-0]]boolean link:{java8-javadoc}/java/util/OptionalLong.html#isPresent%2D%2D[isPresent]()++ (link:{java9-javadoc}/java/util/OptionalLong.html#isPresent%2D%2D[java 9]) +* ++[[painless-api-reference-OptionalLong-orElse-1]]long link:{java8-javadoc}/java/util/OptionalLong.html#orElse%2Dlong%2D[orElse](long)++ (link:{java9-javadoc}/java/util/OptionalLong.html#orElse%2Dlong%2D[java 9]) +* ++[[painless-api-reference-OptionalLong-orElseGet-1]]long link:{java8-javadoc}/java/util/OptionalLong.html#orElseGet%2Djava.util.function.LongSupplier%2D[orElseGet](<>)++ (link:{java9-javadoc}/java/util/OptionalLong.html#orElseGet%2Djava.util.function.LongSupplier%2D[java 9]) +* ++[[painless-api-reference-OptionalLong-orElseThrow-1]]long link:{java8-javadoc}/java/util/OptionalLong.html#orElseThrow%2Djava.util.function.Supplier%2D[orElseThrow](<>)++ (link:{java9-javadoc}/java/util/OptionalLong.html#orElseThrow%2Djava.util.function.Supplier%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ParseException.asciidoc b/docs/painless/painless-api-reference/ParseException.asciidoc new file mode 100644 index 0000000000000..086bcd0b7aaed --- /dev/null +++ b/docs/painless/painless-api-reference/ParseException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ParseException]]++ParseException++:: +* ++[[painless-api-reference-ParseException-ParseException-2]]link:{java8-javadoc}/java/text/ParseException.html#ParseException%2Djava.lang.String%2Dint%2D[ParseException](<>, int)++ (link:{java9-javadoc}/java/text/ParseException.html#ParseException%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-ParseException-getErrorOffset-0]]int link:{java8-javadoc}/java/text/ParseException.html#getErrorOffset%2D%2D[getErrorOffset]()++ (link:{java9-javadoc}/java/text/ParseException.html#getErrorOffset%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ParsePosition.asciidoc b/docs/painless/painless-api-reference/ParsePosition.asciidoc new file mode 100644 index 0000000000000..88fe6369657bd --- /dev/null +++ b/docs/painless/painless-api-reference/ParsePosition.asciidoc @@ -0,0 +1,12 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ParsePosition]]++ParsePosition++:: +* ++[[painless-api-reference-ParsePosition-ParsePosition-1]]link:{java8-javadoc}/java/text/ParsePosition.html#ParsePosition%2Dint%2D[ParsePosition](int)++ (link:{java9-javadoc}/java/text/ParsePosition.html#ParsePosition%2Dint%2D[java 9]) +* ++[[painless-api-reference-ParsePosition-getErrorIndex-0]]int link:{java8-javadoc}/java/text/ParsePosition.html#getErrorIndex%2D%2D[getErrorIndex]()++ (link:{java9-javadoc}/java/text/ParsePosition.html#getErrorIndex%2D%2D[java 9]) +* ++[[painless-api-reference-ParsePosition-getIndex-0]]int link:{java8-javadoc}/java/text/ParsePosition.html#getIndex%2D%2D[getIndex]()++ (link:{java9-javadoc}/java/text/ParsePosition.html#getIndex%2D%2D[java 9]) +* ++[[painless-api-reference-ParsePosition-setErrorIndex-1]]void link:{java8-javadoc}/java/text/ParsePosition.html#setErrorIndex%2Dint%2D[setErrorIndex](int)++ (link:{java9-javadoc}/java/text/ParsePosition.html#setErrorIndex%2Dint%2D[java 9]) +* ++[[painless-api-reference-ParsePosition-setIndex-1]]void link:{java8-javadoc}/java/text/ParsePosition.html#setIndex%2Dint%2D[setIndex](int)++ (link:{java9-javadoc}/java/text/ParsePosition.html#setIndex%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Pattern.asciidoc b/docs/painless/painless-api-reference/Pattern.asciidoc new file mode 100644 index 0000000000000..ad7b3603036b1 --- /dev/null +++ b/docs/painless/painless-api-reference/Pattern.asciidoc @@ -0,0 +1,15 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Pattern]]++Pattern++:: +* ++[[painless-api-reference-Pattern-quote-1]]static <> link:{java8-javadoc}/java/util/regex/Pattern.html#quote%2Djava.lang.String%2D[quote](<>)++ (link:{java9-javadoc}/java/util/regex/Pattern.html#quote%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Pattern-asPredicate-0]]<> link:{java8-javadoc}/java/util/regex/Pattern.html#asPredicate%2D%2D[asPredicate]()++ (link:{java9-javadoc}/java/util/regex/Pattern.html#asPredicate%2D%2D[java 9]) +* ++[[painless-api-reference-Pattern-flags-0]]int link:{java8-javadoc}/java/util/regex/Pattern.html#flags%2D%2D[flags]()++ (link:{java9-javadoc}/java/util/regex/Pattern.html#flags%2D%2D[java 9]) +* ++[[painless-api-reference-Pattern-matcher-1]]<> link:{java8-javadoc}/java/util/regex/Pattern.html#matcher%2Djava.lang.CharSequence%2D[matcher](<>)++ (link:{java9-javadoc}/java/util/regex/Pattern.html#matcher%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-Pattern-pattern-0]]<> link:{java8-javadoc}/java/util/regex/Pattern.html#pattern%2D%2D[pattern]()++ (link:{java9-javadoc}/java/util/regex/Pattern.html#pattern%2D%2D[java 9]) +* ++[[painless-api-reference-Pattern-split-1]]<>[] link:{java8-javadoc}/java/util/regex/Pattern.html#split%2Djava.lang.CharSequence%2D[split](<>)++ (link:{java9-javadoc}/java/util/regex/Pattern.html#split%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-Pattern-split-2]]<>[] link:{java8-javadoc}/java/util/regex/Pattern.html#split%2Djava.lang.CharSequence%2Dint%2D[split](<>, int)++ (link:{java9-javadoc}/java/util/regex/Pattern.html#split%2Djava.lang.CharSequence%2Dint%2D[java 9]) +* ++[[painless-api-reference-Pattern-splitAsStream-1]]<> link:{java8-javadoc}/java/util/regex/Pattern.html#splitAsStream%2Djava.lang.CharSequence%2D[splitAsStream](<>)++ (link:{java9-javadoc}/java/util/regex/Pattern.html#splitAsStream%2Djava.lang.CharSequence%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Period.asciidoc b/docs/painless/painless-api-reference/Period.asciidoc new file mode 100644 index 0000000000000..bb20dddae60a8 --- /dev/null +++ b/docs/painless/painless-api-reference/Period.asciidoc @@ -0,0 +1,35 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Period]]++Period++:: +** [[painless-api-reference-Period-ZERO]]static <> link:{java8-javadoc}/java/time/Period.html#ZERO[ZERO] (link:{java9-javadoc}/java/time/Period.html#ZERO[java 9]) +* ++[[painless-api-reference-Period-between-2]]static <> link:{java8-javadoc}/java/time/Period.html#between%2Djava.time.LocalDate%2Djava.time.LocalDate%2D[between](<>, <>)++ (link:{java9-javadoc}/java/time/Period.html#between%2Djava.time.LocalDate%2Djava.time.LocalDate%2D[java 9]) +* ++[[painless-api-reference-Period-from-1]]static <> link:{java8-javadoc}/java/time/Period.html#from%2Djava.time.temporal.TemporalAmount%2D[from](<>)++ (link:{java9-javadoc}/java/time/Period.html#from%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-Period-of-3]]static <> link:{java8-javadoc}/java/time/Period.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/Period.html#of%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Period-ofDays-1]]static <> link:{java8-javadoc}/java/time/Period.html#ofDays%2Dint%2D[ofDays](int)++ (link:{java9-javadoc}/java/time/Period.html#ofDays%2Dint%2D[java 9]) +* ++[[painless-api-reference-Period-ofMonths-1]]static <> link:{java8-javadoc}/java/time/Period.html#ofMonths%2Dint%2D[ofMonths](int)++ (link:{java9-javadoc}/java/time/Period.html#ofMonths%2Dint%2D[java 9]) +* ++[[painless-api-reference-Period-ofWeeks-1]]static <> link:{java8-javadoc}/java/time/Period.html#ofWeeks%2Dint%2D[ofWeeks](int)++ (link:{java9-javadoc}/java/time/Period.html#ofWeeks%2Dint%2D[java 9]) +* ++[[painless-api-reference-Period-ofYears-1]]static <> link:{java8-javadoc}/java/time/Period.html#ofYears%2Dint%2D[ofYears](int)++ (link:{java9-javadoc}/java/time/Period.html#ofYears%2Dint%2D[java 9]) +* ++[[painless-api-reference-Period-parse-1]]static <> link:{java8-javadoc}/java/time/Period.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/Period.html#parse%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-Period-getChronology-0]]<> link:{java8-javadoc}/java/time/Period.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/Period.html#getChronology%2D%2D[java 9]) +* ++[[painless-api-reference-Period-getDays-0]]int link:{java8-javadoc}/java/time/Period.html#getDays%2D%2D[getDays]()++ (link:{java9-javadoc}/java/time/Period.html#getDays%2D%2D[java 9]) +* ++[[painless-api-reference-Period-getMonths-0]]int link:{java8-javadoc}/java/time/Period.html#getMonths%2D%2D[getMonths]()++ (link:{java9-javadoc}/java/time/Period.html#getMonths%2D%2D[java 9]) +* ++[[painless-api-reference-Period-getYears-0]]int link:{java8-javadoc}/java/time/Period.html#getYears%2D%2D[getYears]()++ (link:{java9-javadoc}/java/time/Period.html#getYears%2D%2D[java 9]) +* ++[[painless-api-reference-Period-minus-1]]<> link:{java8-javadoc}/java/time/Period.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/Period.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-Period-minusDays-1]]<> link:{java8-javadoc}/java/time/Period.html#minusDays%2Dlong%2D[minusDays](long)++ (link:{java9-javadoc}/java/time/Period.html#minusDays%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Period-minusMonths-1]]<> link:{java8-javadoc}/java/time/Period.html#minusMonths%2Dlong%2D[minusMonths](long)++ (link:{java9-javadoc}/java/time/Period.html#minusMonths%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Period-minusYears-1]]<> link:{java8-javadoc}/java/time/Period.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/Period.html#minusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Period-multipliedBy-1]]<> link:{java8-javadoc}/java/time/Period.html#multipliedBy%2Dint%2D[multipliedBy](int)++ (link:{java9-javadoc}/java/time/Period.html#multipliedBy%2Dint%2D[java 9]) +* ++[[painless-api-reference-Period-negated-0]]<> link:{java8-javadoc}/java/time/Period.html#negated%2D%2D[negated]()++ (link:{java9-javadoc}/java/time/Period.html#negated%2D%2D[java 9]) +* ++[[painless-api-reference-Period-normalized-0]]<> link:{java8-javadoc}/java/time/Period.html#normalized%2D%2D[normalized]()++ (link:{java9-javadoc}/java/time/Period.html#normalized%2D%2D[java 9]) +* ++[[painless-api-reference-Period-plus-1]]<> link:{java8-javadoc}/java/time/Period.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/Period.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-Period-plusDays-1]]<> link:{java8-javadoc}/java/time/Period.html#plusDays%2Dlong%2D[plusDays](long)++ (link:{java9-javadoc}/java/time/Period.html#plusDays%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Period-plusMonths-1]]<> link:{java8-javadoc}/java/time/Period.html#plusMonths%2Dlong%2D[plusMonths](long)++ (link:{java9-javadoc}/java/time/Period.html#plusMonths%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Period-plusYears-1]]<> link:{java8-javadoc}/java/time/Period.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/Period.html#plusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Period-toTotalMonths-0]]long link:{java8-javadoc}/java/time/Period.html#toTotalMonths%2D%2D[toTotalMonths]()++ (link:{java9-javadoc}/java/time/Period.html#toTotalMonths%2D%2D[java 9]) +* ++[[painless-api-reference-Period-withDays-1]]<> link:{java8-javadoc}/java/time/Period.html#withDays%2Dint%2D[withDays](int)++ (link:{java9-javadoc}/java/time/Period.html#withDays%2Dint%2D[java 9]) +* ++[[painless-api-reference-Period-withMonths-1]]<> link:{java8-javadoc}/java/time/Period.html#withMonths%2Dint%2D[withMonths](int)++ (link:{java9-javadoc}/java/time/Period.html#withMonths%2Dint%2D[java 9]) +* ++[[painless-api-reference-Period-withYears-1]]<> link:{java8-javadoc}/java/time/Period.html#withYears%2Dint%2D[withYears](int)++ (link:{java9-javadoc}/java/time/Period.html#withYears%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Predicate.asciidoc b/docs/painless/painless-api-reference/Predicate.asciidoc new file mode 100644 index 0000000000000..6eef93ea27c02 --- /dev/null +++ b/docs/painless/painless-api-reference/Predicate.asciidoc @@ -0,0 +1,12 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Predicate]]++Predicate++:: +* ++[[painless-api-reference-Predicate-isEqual-1]]static <> link:{java8-javadoc}/java/util/function/Predicate.html#isEqual%2Djava.lang.Object%2D[isEqual](def)++ (link:{java9-javadoc}/java/util/function/Predicate.html#isEqual%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Predicate-and-1]]<> link:{java8-javadoc}/java/util/function/Predicate.html#and%2Djava.util.function.Predicate%2D[and](<>)++ (link:{java9-javadoc}/java/util/function/Predicate.html#and%2Djava.util.function.Predicate%2D[java 9]) +* ++[[painless-api-reference-Predicate-negate-0]]<> link:{java8-javadoc}/java/util/function/Predicate.html#negate%2D%2D[negate]()++ (link:{java9-javadoc}/java/util/function/Predicate.html#negate%2D%2D[java 9]) +* ++[[painless-api-reference-Predicate-or-1]]<> link:{java8-javadoc}/java/util/function/Predicate.html#or%2Djava.util.function.Predicate%2D[or](<>)++ (link:{java9-javadoc}/java/util/function/Predicate.html#or%2Djava.util.function.Predicate%2D[java 9]) +* ++[[painless-api-reference-Predicate-test-1]]boolean link:{java8-javadoc}/java/util/function/Predicate.html#test%2Djava.lang.Object%2D[test](def)++ (link:{java9-javadoc}/java/util/function/Predicate.html#test%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/PrimitiveIterator.OfDouble.asciidoc b/docs/painless/painless-api-reference/PrimitiveIterator.OfDouble.asciidoc new file mode 100644 index 0000000000000..bb26cf3b51477 --- /dev/null +++ b/docs/painless/painless-api-reference/PrimitiveIterator.OfDouble.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-PrimitiveIterator-OfDouble]]++PrimitiveIterator.OfDouble++:: +* ++[[painless-api-reference-PrimitiveIterator-OfDouble-next-0]]<> link:{java8-javadoc}/java/util/PrimitiveIterator$OfDouble.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/util/PrimitiveIterator$OfDouble.html#next%2D%2D[java 9]) +* ++[[painless-api-reference-PrimitiveIterator-OfDouble-nextDouble-0]]double link:{java8-javadoc}/java/util/PrimitiveIterator$OfDouble.html#nextDouble%2D%2D[nextDouble]()++ (link:{java9-javadoc}/java/util/PrimitiveIterator$OfDouble.html#nextDouble%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/PrimitiveIterator.OfInt.asciidoc b/docs/painless/painless-api-reference/PrimitiveIterator.OfInt.asciidoc new file mode 100644 index 0000000000000..1cd90b8e13f02 --- /dev/null +++ b/docs/painless/painless-api-reference/PrimitiveIterator.OfInt.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-PrimitiveIterator-OfInt]]++PrimitiveIterator.OfInt++:: +* ++[[painless-api-reference-PrimitiveIterator-OfInt-next-0]]<> link:{java8-javadoc}/java/util/PrimitiveIterator$OfInt.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/util/PrimitiveIterator$OfInt.html#next%2D%2D[java 9]) +* ++[[painless-api-reference-PrimitiveIterator-OfInt-nextInt-0]]int link:{java8-javadoc}/java/util/PrimitiveIterator$OfInt.html#nextInt%2D%2D[nextInt]()++ (link:{java9-javadoc}/java/util/PrimitiveIterator$OfInt.html#nextInt%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/PrimitiveIterator.OfLong.asciidoc b/docs/painless/painless-api-reference/PrimitiveIterator.OfLong.asciidoc new file mode 100644 index 0000000000000..bcfff0e7edc14 --- /dev/null +++ b/docs/painless/painless-api-reference/PrimitiveIterator.OfLong.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-PrimitiveIterator-OfLong]]++PrimitiveIterator.OfLong++:: +* ++[[painless-api-reference-PrimitiveIterator-OfLong-next-0]]<> link:{java8-javadoc}/java/util/PrimitiveIterator$OfLong.html#next%2D%2D[next]()++ (link:{java9-javadoc}/java/util/PrimitiveIterator$OfLong.html#next%2D%2D[java 9]) +* ++[[painless-api-reference-PrimitiveIterator-OfLong-nextLong-0]]long link:{java8-javadoc}/java/util/PrimitiveIterator$OfLong.html#nextLong%2D%2D[nextLong]()++ (link:{java9-javadoc}/java/util/PrimitiveIterator$OfLong.html#nextLong%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/PrimitiveIterator.asciidoc b/docs/painless/painless-api-reference/PrimitiveIterator.asciidoc new file mode 100644 index 0000000000000..42742f79d3d40 --- /dev/null +++ b/docs/painless/painless-api-reference/PrimitiveIterator.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-PrimitiveIterator]]++PrimitiveIterator++:: +* ++[[painless-api-reference-PrimitiveIterator-forEachRemaining-1]]void link:{java8-javadoc}/java/util/PrimitiveIterator.html#forEachRemaining%2Djava.lang.Object%2D[forEachRemaining](def)++ (link:{java9-javadoc}/java/util/PrimitiveIterator.html#forEachRemaining%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/PriorityQueue.asciidoc b/docs/painless/painless-api-reference/PriorityQueue.asciidoc new file mode 100644 index 0000000000000..88334cd8b9c63 --- /dev/null +++ b/docs/painless/painless-api-reference/PriorityQueue.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-PriorityQueue]]++PriorityQueue++:: +* ++[[painless-api-reference-PriorityQueue-PriorityQueue-0]]link:{java8-javadoc}/java/util/PriorityQueue.html#PriorityQueue%2D%2D[PriorityQueue]()++ (link:{java9-javadoc}/java/util/PriorityQueue.html#PriorityQueue%2D%2D[java 9]) +* ++[[painless-api-reference-PriorityQueue-PriorityQueue-1]]link:{java8-javadoc}/java/util/PriorityQueue.html#PriorityQueue%2Djava.util.Comparator%2D[PriorityQueue](<>)++ (link:{java9-javadoc}/java/util/PriorityQueue.html#PriorityQueue%2Djava.util.Comparator%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Queue.asciidoc b/docs/painless/painless-api-reference/Queue.asciidoc new file mode 100644 index 0000000000000..7d323457591ff --- /dev/null +++ b/docs/painless/painless-api-reference/Queue.asciidoc @@ -0,0 +1,12 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Queue]]++Queue++:: +* ++[[painless-api-reference-Queue-element-0]]def link:{java8-javadoc}/java/util/Queue.html#element%2D%2D[element]()++ (link:{java9-javadoc}/java/util/Queue.html#element%2D%2D[java 9]) +* ++[[painless-api-reference-Queue-offer-1]]boolean link:{java8-javadoc}/java/util/Queue.html#offer%2Djava.lang.Object%2D[offer](def)++ (link:{java9-javadoc}/java/util/Queue.html#offer%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Queue-peek-0]]def link:{java8-javadoc}/java/util/Queue.html#peek%2D%2D[peek]()++ (link:{java9-javadoc}/java/util/Queue.html#peek%2D%2D[java 9]) +* ++[[painless-api-reference-Queue-poll-0]]def link:{java8-javadoc}/java/util/Queue.html#poll%2D%2D[poll]()++ (link:{java9-javadoc}/java/util/Queue.html#poll%2D%2D[java 9]) +* ++[[painless-api-reference-Queue-remove-0]]def link:{java8-javadoc}/java/util/Queue.html#remove%2D%2D[remove]()++ (link:{java9-javadoc}/java/util/Queue.html#remove%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Random.asciidoc b/docs/painless/painless-api-reference/Random.asciidoc new file mode 100644 index 0000000000000..4288c330850da --- /dev/null +++ b/docs/painless/painless-api-reference/Random.asciidoc @@ -0,0 +1,24 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Random]]++Random++:: +* ++[[painless-api-reference-Random-Random-0]]link:{java8-javadoc}/java/util/Random.html#Random%2D%2D[Random]()++ (link:{java9-javadoc}/java/util/Random.html#Random%2D%2D[java 9]) +* ++[[painless-api-reference-Random-Random-1]]link:{java8-javadoc}/java/util/Random.html#Random%2Dlong%2D[Random](long)++ (link:{java9-javadoc}/java/util/Random.html#Random%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Random-doubles-1]]<> link:{java8-javadoc}/java/util/Random.html#doubles%2Dlong%2D[doubles](long)++ (link:{java9-javadoc}/java/util/Random.html#doubles%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Random-doubles-3]]<> link:{java8-javadoc}/java/util/Random.html#doubles%2Dlong%2Ddouble%2Ddouble%2D[doubles](long, double, double)++ (link:{java9-javadoc}/java/util/Random.html#doubles%2Dlong%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-Random-ints-1]]<> link:{java8-javadoc}/java/util/Random.html#ints%2Dlong%2D[ints](long)++ (link:{java9-javadoc}/java/util/Random.html#ints%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Random-ints-3]]<> link:{java8-javadoc}/java/util/Random.html#ints%2Dlong%2Dint%2Dint%2D[ints](long, int, int)++ (link:{java9-javadoc}/java/util/Random.html#ints%2Dlong%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-Random-longs-1]]<> link:{java8-javadoc}/java/util/Random.html#longs%2Dlong%2D[longs](long)++ (link:{java9-javadoc}/java/util/Random.html#longs%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Random-longs-3]]<> link:{java8-javadoc}/java/util/Random.html#longs%2Dlong%2Dlong%2Dlong%2D[longs](long, long, long)++ (link:{java9-javadoc}/java/util/Random.html#longs%2Dlong%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Random-nextBoolean-0]]boolean link:{java8-javadoc}/java/util/Random.html#nextBoolean%2D%2D[nextBoolean]()++ (link:{java9-javadoc}/java/util/Random.html#nextBoolean%2D%2D[java 9]) +* ++[[painless-api-reference-Random-nextBytes-1]]void link:{java8-javadoc}/java/util/Random.html#nextBytes%2Dbyte:A%2D[nextBytes](byte[])++ (link:{java9-javadoc}/java/util/Random.html#nextBytes%2Dbyte:A%2D[java 9]) +* ++[[painless-api-reference-Random-nextDouble-0]]double link:{java8-javadoc}/java/util/Random.html#nextDouble%2D%2D[nextDouble]()++ (link:{java9-javadoc}/java/util/Random.html#nextDouble%2D%2D[java 9]) +* ++[[painless-api-reference-Random-nextFloat-0]]float link:{java8-javadoc}/java/util/Random.html#nextFloat%2D%2D[nextFloat]()++ (link:{java9-javadoc}/java/util/Random.html#nextFloat%2D%2D[java 9]) +* ++[[painless-api-reference-Random-nextGaussian-0]]double link:{java8-javadoc}/java/util/Random.html#nextGaussian%2D%2D[nextGaussian]()++ (link:{java9-javadoc}/java/util/Random.html#nextGaussian%2D%2D[java 9]) +* ++[[painless-api-reference-Random-nextInt-0]]int link:{java8-javadoc}/java/util/Random.html#nextInt%2D%2D[nextInt]()++ (link:{java9-javadoc}/java/util/Random.html#nextInt%2D%2D[java 9]) +* ++[[painless-api-reference-Random-nextInt-1]]int link:{java8-javadoc}/java/util/Random.html#nextInt%2Dint%2D[nextInt](int)++ (link:{java9-javadoc}/java/util/Random.html#nextInt%2Dint%2D[java 9]) +* ++[[painless-api-reference-Random-nextLong-0]]long link:{java8-javadoc}/java/util/Random.html#nextLong%2D%2D[nextLong]()++ (link:{java9-javadoc}/java/util/Random.html#nextLong%2D%2D[java 9]) +* ++[[painless-api-reference-Random-setSeed-1]]void link:{java8-javadoc}/java/util/Random.html#setSeed%2Dlong%2D[setSeed](long)++ (link:{java9-javadoc}/java/util/Random.html#setSeed%2Dlong%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/RandomAccess.asciidoc b/docs/painless/painless-api-reference/RandomAccess.asciidoc new file mode 100644 index 0000000000000..b7afb205f0c6c --- /dev/null +++ b/docs/painless/painless-api-reference/RandomAccess.asciidoc @@ -0,0 +1,7 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-RandomAccess]]++RandomAccess++:: +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ReflectiveOperationException.asciidoc b/docs/painless/painless-api-reference/ReflectiveOperationException.asciidoc new file mode 100644 index 0000000000000..eee134d373a93 --- /dev/null +++ b/docs/painless/painless-api-reference/ReflectiveOperationException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ReflectiveOperationException]]++ReflectiveOperationException++:: +* ++[[painless-api-reference-ReflectiveOperationException-ReflectiveOperationException-0]]link:{java8-javadoc}/java/lang/ReflectiveOperationException.html#ReflectiveOperationException%2D%2D[ReflectiveOperationException]()++ (link:{java9-javadoc}/java/lang/ReflectiveOperationException.html#ReflectiveOperationException%2D%2D[java 9]) +* ++[[painless-api-reference-ReflectiveOperationException-ReflectiveOperationException-1]]link:{java8-javadoc}/java/lang/ReflectiveOperationException.html#ReflectiveOperationException%2Djava.lang.String%2D[ReflectiveOperationException](<>)++ (link:{java9-javadoc}/java/lang/ReflectiveOperationException.html#ReflectiveOperationException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ResolverStyle.asciidoc b/docs/painless/painless-api-reference/ResolverStyle.asciidoc new file mode 100644 index 0000000000000..00576a2d93a0e --- /dev/null +++ b/docs/painless/painless-api-reference/ResolverStyle.asciidoc @@ -0,0 +1,12 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ResolverStyle]]++ResolverStyle++:: +** [[painless-api-reference-ResolverStyle-LENIENT]]static <> link:{java8-javadoc}/java/time/format/ResolverStyle.html#LENIENT[LENIENT] (link:{java9-javadoc}/java/time/format/ResolverStyle.html#LENIENT[java 9]) +** [[painless-api-reference-ResolverStyle-SMART]]static <> link:{java8-javadoc}/java/time/format/ResolverStyle.html#SMART[SMART] (link:{java9-javadoc}/java/time/format/ResolverStyle.html#SMART[java 9]) +** [[painless-api-reference-ResolverStyle-STRICT]]static <> link:{java8-javadoc}/java/time/format/ResolverStyle.html#STRICT[STRICT] (link:{java9-javadoc}/java/time/format/ResolverStyle.html#STRICT[java 9]) +* ++[[painless-api-reference-ResolverStyle-valueOf-1]]static <> link:{java8-javadoc}/java/time/format/ResolverStyle.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/format/ResolverStyle.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-ResolverStyle-values-0]]static <>[] link:{java8-javadoc}/java/time/format/ResolverStyle.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/format/ResolverStyle.html#values%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/RoundingMode.asciidoc b/docs/painless/painless-api-reference/RoundingMode.asciidoc new file mode 100644 index 0000000000000..e0b4d579835e9 --- /dev/null +++ b/docs/painless/painless-api-reference/RoundingMode.asciidoc @@ -0,0 +1,17 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-RoundingMode]]++RoundingMode++:: +** [[painless-api-reference-RoundingMode-CEILING]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#CEILING[CEILING] (link:{java9-javadoc}/java/math/RoundingMode.html#CEILING[java 9]) +** [[painless-api-reference-RoundingMode-DOWN]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#DOWN[DOWN] (link:{java9-javadoc}/java/math/RoundingMode.html#DOWN[java 9]) +** [[painless-api-reference-RoundingMode-FLOOR]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#FLOOR[FLOOR] (link:{java9-javadoc}/java/math/RoundingMode.html#FLOOR[java 9]) +** [[painless-api-reference-RoundingMode-HALF_DOWN]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#HALF_DOWN[HALF_DOWN] (link:{java9-javadoc}/java/math/RoundingMode.html#HALF_DOWN[java 9]) +** [[painless-api-reference-RoundingMode-HALF_EVEN]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#HALF_EVEN[HALF_EVEN] (link:{java9-javadoc}/java/math/RoundingMode.html#HALF_EVEN[java 9]) +** [[painless-api-reference-RoundingMode-HALF_UP]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#HALF_UP[HALF_UP] (link:{java9-javadoc}/java/math/RoundingMode.html#HALF_UP[java 9]) +** [[painless-api-reference-RoundingMode-UNNECESSARY]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#UNNECESSARY[UNNECESSARY] (link:{java9-javadoc}/java/math/RoundingMode.html#UNNECESSARY[java 9]) +** [[painless-api-reference-RoundingMode-UP]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#UP[UP] (link:{java9-javadoc}/java/math/RoundingMode.html#UP[java 9]) +* ++[[painless-api-reference-RoundingMode-valueOf-1]]static <> link:{java8-javadoc}/java/math/RoundingMode.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/math/RoundingMode.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-RoundingMode-values-0]]static <>[] link:{java8-javadoc}/java/math/RoundingMode.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/math/RoundingMode.html#values%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/RuleBasedCollator.asciidoc b/docs/painless/painless-api-reference/RuleBasedCollator.asciidoc new file mode 100644 index 0000000000000..5581768e2f300 --- /dev/null +++ b/docs/painless/painless-api-reference/RuleBasedCollator.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-RuleBasedCollator]]++RuleBasedCollator++:: +* ++[[painless-api-reference-RuleBasedCollator-RuleBasedCollator-1]]link:{java8-javadoc}/java/text/RuleBasedCollator.html#RuleBasedCollator%2Djava.lang.String%2D[RuleBasedCollator](<>)++ (link:{java9-javadoc}/java/text/RuleBasedCollator.html#RuleBasedCollator%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-RuleBasedCollator-getCollationElementIterator-1]]<> link:{java8-javadoc}/java/text/RuleBasedCollator.html#getCollationElementIterator%2Djava.lang.String%2D[getCollationElementIterator](<>)++ (link:{java9-javadoc}/java/text/RuleBasedCollator.html#getCollationElementIterator%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-RuleBasedCollator-getRules-0]]<> link:{java8-javadoc}/java/text/RuleBasedCollator.html#getRules%2D%2D[getRules]()++ (link:{java9-javadoc}/java/text/RuleBasedCollator.html#getRules%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/RuntimeException.asciidoc b/docs/painless/painless-api-reference/RuntimeException.asciidoc new file mode 100644 index 0000000000000..fc8523169e33d --- /dev/null +++ b/docs/painless/painless-api-reference/RuntimeException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-RuntimeException]]++RuntimeException++:: +* ++[[painless-api-reference-RuntimeException-RuntimeException-0]]link:{java8-javadoc}/java/lang/RuntimeException.html#RuntimeException%2D%2D[RuntimeException]()++ (link:{java9-javadoc}/java/lang/RuntimeException.html#RuntimeException%2D%2D[java 9]) +* ++[[painless-api-reference-RuntimeException-RuntimeException-1]]link:{java8-javadoc}/java/lang/RuntimeException.html#RuntimeException%2Djava.lang.String%2D[RuntimeException](<>)++ (link:{java9-javadoc}/java/lang/RuntimeException.html#RuntimeException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/SecurityException.asciidoc b/docs/painless/painless-api-reference/SecurityException.asciidoc new file mode 100644 index 0000000000000..ec6ea157e5587 --- /dev/null +++ b/docs/painless/painless-api-reference/SecurityException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-SecurityException]]++SecurityException++:: +* ++[[painless-api-reference-SecurityException-SecurityException-0]]link:{java8-javadoc}/java/lang/SecurityException.html#SecurityException%2D%2D[SecurityException]()++ (link:{java9-javadoc}/java/lang/SecurityException.html#SecurityException%2D%2D[java 9]) +* ++[[painless-api-reference-SecurityException-SecurityException-1]]link:{java8-javadoc}/java/lang/SecurityException.html#SecurityException%2Djava.lang.String%2D[SecurityException](<>)++ (link:{java9-javadoc}/java/lang/SecurityException.html#SecurityException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Set.asciidoc b/docs/painless/painless-api-reference/Set.asciidoc new file mode 100644 index 0000000000000..23d8a352f231f --- /dev/null +++ b/docs/painless/painless-api-reference/Set.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Set]]++Set++:: +* ++[[painless-api-reference-Set-equals-1]]boolean link:{java8-javadoc}/java/util/Set.html#equals%2Djava.lang.Object%2D[equals](<>)++ (link:{java9-javadoc}/java/util/Set.html#equals%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Set-hashCode-0]]int link:{java8-javadoc}/java/util/Set.html#hashCode%2D%2D[hashCode]()++ (link:{java9-javadoc}/java/util/Set.html#hashCode%2D%2D[java 9]) +* ++[[painless-api-reference-Set-remove-1]]boolean link:{java8-javadoc}/java/util/Set.html#remove%2Djava.lang.Object%2D[remove](def)++ (link:{java9-javadoc}/java/util/Set.html#remove%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Short.asciidoc b/docs/painless/painless-api-reference/Short.asciidoc new file mode 100644 index 0000000000000..12f0336e8fd40 --- /dev/null +++ b/docs/painless/painless-api-reference/Short.asciidoc @@ -0,0 +1,23 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Short]]++Short++:: +** [[painless-api-reference-Short-BYTES]]static int link:{java8-javadoc}/java/lang/Short.html#BYTES[BYTES] (link:{java9-javadoc}/java/lang/Short.html#BYTES[java 9]) +** [[painless-api-reference-Short-MAX_VALUE]]static short link:{java8-javadoc}/java/lang/Short.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/lang/Short.html#MAX_VALUE[java 9]) +** [[painless-api-reference-Short-MIN_VALUE]]static short link:{java8-javadoc}/java/lang/Short.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/lang/Short.html#MIN_VALUE[java 9]) +** [[painless-api-reference-Short-SIZE]]static int link:{java8-javadoc}/java/lang/Short.html#SIZE[SIZE] (link:{java9-javadoc}/java/lang/Short.html#SIZE[java 9]) +* ++[[painless-api-reference-Short-compare-2]]static int link:{java8-javadoc}/java/lang/Short.html#compare%2Dshort%2Dshort%2D[compare](short, short)++ (link:{java9-javadoc}/java/lang/Short.html#compare%2Dshort%2Dshort%2D[java 9]) +* ++[[painless-api-reference-Short-decode-1]]static <> link:{java8-javadoc}/java/lang/Short.html#decode%2Djava.lang.String%2D[decode](<>)++ (link:{java9-javadoc}/java/lang/Short.html#decode%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Short-hashCode-1]]static int link:{java8-javadoc}/java/lang/Short.html#hashCode%2Dshort%2D[hashCode](short)++ (link:{java9-javadoc}/java/lang/Short.html#hashCode%2Dshort%2D[java 9]) +* ++[[painless-api-reference-Short-parseShort-1]]static short link:{java8-javadoc}/java/lang/Short.html#parseShort%2Djava.lang.String%2D[parseShort](<>)++ (link:{java9-javadoc}/java/lang/Short.html#parseShort%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-Short-parseShort-2]]static short link:{java8-javadoc}/java/lang/Short.html#parseShort%2Djava.lang.String%2Dint%2D[parseShort](<>, int)++ (link:{java9-javadoc}/java/lang/Short.html#parseShort%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-Short-reverseBytes-1]]static short link:{java8-javadoc}/java/lang/Short.html#reverseBytes%2Dshort%2D[reverseBytes](short)++ (link:{java9-javadoc}/java/lang/Short.html#reverseBytes%2Dshort%2D[java 9]) +* ++[[painless-api-reference-Short-toString-1]]static <> link:{java8-javadoc}/java/lang/Short.html#toString%2Dshort%2D[toString](short)++ (link:{java9-javadoc}/java/lang/Short.html#toString%2Dshort%2D[java 9]) +* ++[[painless-api-reference-Short-toUnsignedInt-1]]static int link:{java8-javadoc}/java/lang/Short.html#toUnsignedInt%2Dshort%2D[toUnsignedInt](short)++ (link:{java9-javadoc}/java/lang/Short.html#toUnsignedInt%2Dshort%2D[java 9]) +* ++[[painless-api-reference-Short-toUnsignedLong-1]]static long link:{java8-javadoc}/java/lang/Short.html#toUnsignedLong%2Dshort%2D[toUnsignedLong](short)++ (link:{java9-javadoc}/java/lang/Short.html#toUnsignedLong%2Dshort%2D[java 9]) +* ++[[painless-api-reference-Short-valueOf-1]]static <> link:{java8-javadoc}/java/lang/Short.html#valueOf%2Dshort%2D[valueOf](short)++ (link:{java9-javadoc}/java/lang/Short.html#valueOf%2Dshort%2D[java 9]) +* ++[[painless-api-reference-Short-valueOf-2]]static <> link:{java8-javadoc}/java/lang/Short.html#valueOf%2Djava.lang.String%2Dint%2D[valueOf](<>, int)++ (link:{java9-javadoc}/java/lang/Short.html#valueOf%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-Short-compareTo-1]]int link:{java8-javadoc}/java/lang/Short.html#compareTo%2Djava.lang.Short%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/Short.html#compareTo%2Djava.lang.Short%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/SignStyle.asciidoc b/docs/painless/painless-api-reference/SignStyle.asciidoc new file mode 100644 index 0000000000000..a42ce94ebfa33 --- /dev/null +++ b/docs/painless/painless-api-reference/SignStyle.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-SignStyle]]++SignStyle++:: +** [[painless-api-reference-SignStyle-ALWAYS]]static <> link:{java8-javadoc}/java/time/format/SignStyle.html#ALWAYS[ALWAYS] (link:{java9-javadoc}/java/time/format/SignStyle.html#ALWAYS[java 9]) +** [[painless-api-reference-SignStyle-EXCEEDS_PAD]]static <> link:{java8-javadoc}/java/time/format/SignStyle.html#EXCEEDS_PAD[EXCEEDS_PAD] (link:{java9-javadoc}/java/time/format/SignStyle.html#EXCEEDS_PAD[java 9]) +** [[painless-api-reference-SignStyle-NEVER]]static <> link:{java8-javadoc}/java/time/format/SignStyle.html#NEVER[NEVER] (link:{java9-javadoc}/java/time/format/SignStyle.html#NEVER[java 9]) +** [[painless-api-reference-SignStyle-NORMAL]]static <> link:{java8-javadoc}/java/time/format/SignStyle.html#NORMAL[NORMAL] (link:{java9-javadoc}/java/time/format/SignStyle.html#NORMAL[java 9]) +** [[painless-api-reference-SignStyle-NOT_NEGATIVE]]static <> link:{java8-javadoc}/java/time/format/SignStyle.html#NOT_NEGATIVE[NOT_NEGATIVE] (link:{java9-javadoc}/java/time/format/SignStyle.html#NOT_NEGATIVE[java 9]) +* ++[[painless-api-reference-SignStyle-valueOf-1]]static <> link:{java8-javadoc}/java/time/format/SignStyle.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/format/SignStyle.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-SignStyle-values-0]]static <>[] link:{java8-javadoc}/java/time/format/SignStyle.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/format/SignStyle.html#values%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/SimpleDateFormat.asciidoc b/docs/painless/painless-api-reference/SimpleDateFormat.asciidoc new file mode 100644 index 0000000000000..b7a8ffc191c45 --- /dev/null +++ b/docs/painless/painless-api-reference/SimpleDateFormat.asciidoc @@ -0,0 +1,18 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-SimpleDateFormat]]++SimpleDateFormat++:: +* ++[[painless-api-reference-SimpleDateFormat-SimpleDateFormat-0]]link:{java8-javadoc}/java/text/SimpleDateFormat.html#SimpleDateFormat%2D%2D[SimpleDateFormat]()++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#SimpleDateFormat%2D%2D[java 9]) +* ++[[painless-api-reference-SimpleDateFormat-SimpleDateFormat-1]]link:{java8-javadoc}/java/text/SimpleDateFormat.html#SimpleDateFormat%2Djava.lang.String%2D[SimpleDateFormat](<>)++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#SimpleDateFormat%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-SimpleDateFormat-SimpleDateFormat-2]]link:{java8-javadoc}/java/text/SimpleDateFormat.html#SimpleDateFormat%2Djava.lang.String%2Djava.util.Locale%2D[SimpleDateFormat](<>, <>)++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#SimpleDateFormat%2Djava.lang.String%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-SimpleDateFormat-applyLocalizedPattern-1]]void link:{java8-javadoc}/java/text/SimpleDateFormat.html#applyLocalizedPattern%2Djava.lang.String%2D[applyLocalizedPattern](<>)++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#applyLocalizedPattern%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-SimpleDateFormat-applyPattern-1]]void link:{java8-javadoc}/java/text/SimpleDateFormat.html#applyPattern%2Djava.lang.String%2D[applyPattern](<>)++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#applyPattern%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-SimpleDateFormat-get2DigitYearStart-0]]<> link:{java8-javadoc}/java/text/SimpleDateFormat.html#get2DigitYearStart%2D%2D[get2DigitYearStart]()++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#get2DigitYearStart%2D%2D[java 9]) +* ++[[painless-api-reference-SimpleDateFormat-getDateFormatSymbols-0]]<> link:{java8-javadoc}/java/text/SimpleDateFormat.html#getDateFormatSymbols%2D%2D[getDateFormatSymbols]()++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#getDateFormatSymbols%2D%2D[java 9]) +* ++[[painless-api-reference-SimpleDateFormat-set2DigitYearStart-1]]void link:{java8-javadoc}/java/text/SimpleDateFormat.html#set2DigitYearStart%2Djava.util.Date%2D[set2DigitYearStart](<>)++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#set2DigitYearStart%2Djava.util.Date%2D[java 9]) +* ++[[painless-api-reference-SimpleDateFormat-setDateFormatSymbols-1]]void link:{java8-javadoc}/java/text/SimpleDateFormat.html#setDateFormatSymbols%2Djava.text.DateFormatSymbols%2D[setDateFormatSymbols](<>)++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#setDateFormatSymbols%2Djava.text.DateFormatSymbols%2D[java 9]) +* ++[[painless-api-reference-SimpleDateFormat-toLocalizedPattern-0]]<> link:{java8-javadoc}/java/text/SimpleDateFormat.html#toLocalizedPattern%2D%2D[toLocalizedPattern]()++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#toLocalizedPattern%2D%2D[java 9]) +* ++[[painless-api-reference-SimpleDateFormat-toPattern-0]]<> link:{java8-javadoc}/java/text/SimpleDateFormat.html#toPattern%2D%2D[toPattern]()++ (link:{java9-javadoc}/java/text/SimpleDateFormat.html#toPattern%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/SimpleTimeZone.asciidoc b/docs/painless/painless-api-reference/SimpleTimeZone.asciidoc new file mode 100644 index 0000000000000..eff23a40723a3 --- /dev/null +++ b/docs/painless/painless-api-reference/SimpleTimeZone.asciidoc @@ -0,0 +1,23 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-SimpleTimeZone]]++SimpleTimeZone++:: +** [[painless-api-reference-SimpleTimeZone-STANDARD_TIME]]static int link:{java8-javadoc}/java/util/SimpleTimeZone.html#STANDARD_TIME[STANDARD_TIME] (link:{java9-javadoc}/java/util/SimpleTimeZone.html#STANDARD_TIME[java 9]) +** [[painless-api-reference-SimpleTimeZone-UTC_TIME]]static int link:{java8-javadoc}/java/util/SimpleTimeZone.html#UTC_TIME[UTC_TIME] (link:{java9-javadoc}/java/util/SimpleTimeZone.html#UTC_TIME[java 9]) +** [[painless-api-reference-SimpleTimeZone-WALL_TIME]]static int link:{java8-javadoc}/java/util/SimpleTimeZone.html#WALL_TIME[WALL_TIME] (link:{java9-javadoc}/java/util/SimpleTimeZone.html#WALL_TIME[java 9]) +* ++[[painless-api-reference-SimpleTimeZone-SimpleTimeZone-2]]link:{java8-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2D[SimpleTimeZone](int, <>)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-SimpleTimeZone-SimpleTimeZone-10]]link:{java8-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[SimpleTimeZone](int, <>, int, int, int, int, int, int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-SimpleTimeZone-SimpleTimeZone-11]]link:{java8-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[SimpleTimeZone](int, <>, int, int, int, int, int, int, int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-SimpleTimeZone-SimpleTimeZone-13]]link:{java8-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[SimpleTimeZone](int, <>, int, int, int, int, int, int, int, int, int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#SimpleTimeZone%2Dint%2Djava.lang.String%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-SimpleTimeZone-getDSTSavings-0]]int link:{java8-javadoc}/java/util/SimpleTimeZone.html#getDSTSavings%2D%2D[getDSTSavings]()++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#getDSTSavings%2D%2D[java 9]) +* ++[[painless-api-reference-SimpleTimeZone-setDSTSavings-1]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setDSTSavings%2Dint%2D[setDSTSavings](int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setDSTSavings%2Dint%2D[java 9]) +* ++[[painless-api-reference-SimpleTimeZone-setEndRule-3]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setEndRule%2Dint%2Dint%2Dint%2D[setEndRule](int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setEndRule%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-SimpleTimeZone-setEndRule-4]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setEndRule%2Dint%2Dint%2Dint%2Dint%2D[setEndRule](int, int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setEndRule%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-SimpleTimeZone-setEndRule-5]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setEndRule%2Dint%2Dint%2Dint%2Dint%2Dboolean%2D[setEndRule](int, int, int, int, boolean)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setEndRule%2Dint%2Dint%2Dint%2Dint%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-SimpleTimeZone-setStartRule-3]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setStartRule%2Dint%2Dint%2Dint%2D[setStartRule](int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setStartRule%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-SimpleTimeZone-setStartRule-4]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setStartRule%2Dint%2Dint%2Dint%2Dint%2D[setStartRule](int, int, int, int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setStartRule%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-SimpleTimeZone-setStartRule-5]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setStartRule%2Dint%2Dint%2Dint%2Dint%2Dboolean%2D[setStartRule](int, int, int, int, boolean)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setStartRule%2Dint%2Dint%2Dint%2Dint%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-SimpleTimeZone-setStartYear-1]]void link:{java8-javadoc}/java/util/SimpleTimeZone.html#setStartYear%2Dint%2D[setStartYear](int)++ (link:{java9-javadoc}/java/util/SimpleTimeZone.html#setStartYear%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/SortedMap.asciidoc b/docs/painless/painless-api-reference/SortedMap.asciidoc new file mode 100644 index 0000000000000..d4e31e6c8fa37 --- /dev/null +++ b/docs/painless/painless-api-reference/SortedMap.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-SortedMap]]++SortedMap++:: +* ++[[painless-api-reference-SortedMap-comparator-0]]<> link:{java8-javadoc}/java/util/SortedMap.html#comparator%2D%2D[comparator]()++ (link:{java9-javadoc}/java/util/SortedMap.html#comparator%2D%2D[java 9]) +* ++[[painless-api-reference-SortedMap-firstKey-0]]def link:{java8-javadoc}/java/util/SortedMap.html#firstKey%2D%2D[firstKey]()++ (link:{java9-javadoc}/java/util/SortedMap.html#firstKey%2D%2D[java 9]) +* ++[[painless-api-reference-SortedMap-headMap-1]]<> link:{java8-javadoc}/java/util/SortedMap.html#headMap%2Djava.lang.Object%2D[headMap](def)++ (link:{java9-javadoc}/java/util/SortedMap.html#headMap%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-SortedMap-lastKey-0]]def link:{java8-javadoc}/java/util/SortedMap.html#lastKey%2D%2D[lastKey]()++ (link:{java9-javadoc}/java/util/SortedMap.html#lastKey%2D%2D[java 9]) +* ++[[painless-api-reference-SortedMap-subMap-2]]<> link:{java8-javadoc}/java/util/SortedMap.html#subMap%2Djava.lang.Object%2Djava.lang.Object%2D[subMap](def, def)++ (link:{java9-javadoc}/java/util/SortedMap.html#subMap%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-SortedMap-tailMap-1]]<> link:{java8-javadoc}/java/util/SortedMap.html#tailMap%2Djava.lang.Object%2D[tailMap](def)++ (link:{java9-javadoc}/java/util/SortedMap.html#tailMap%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/SortedSet.asciidoc b/docs/painless/painless-api-reference/SortedSet.asciidoc new file mode 100644 index 0000000000000..2a7160aa2ba44 --- /dev/null +++ b/docs/painless/painless-api-reference/SortedSet.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-SortedSet]]++SortedSet++:: +* ++[[painless-api-reference-SortedSet-comparator-0]]<> link:{java8-javadoc}/java/util/SortedSet.html#comparator%2D%2D[comparator]()++ (link:{java9-javadoc}/java/util/SortedSet.html#comparator%2D%2D[java 9]) +* ++[[painless-api-reference-SortedSet-first-0]]def link:{java8-javadoc}/java/util/SortedSet.html#first%2D%2D[first]()++ (link:{java9-javadoc}/java/util/SortedSet.html#first%2D%2D[java 9]) +* ++[[painless-api-reference-SortedSet-headSet-1]]<> link:{java8-javadoc}/java/util/SortedSet.html#headSet%2Djava.lang.Object%2D[headSet](def)++ (link:{java9-javadoc}/java/util/SortedSet.html#headSet%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-SortedSet-last-0]]def link:{java8-javadoc}/java/util/SortedSet.html#last%2D%2D[last]()++ (link:{java9-javadoc}/java/util/SortedSet.html#last%2D%2D[java 9]) +* ++[[painless-api-reference-SortedSet-subSet-2]]<> link:{java8-javadoc}/java/util/SortedSet.html#subSet%2Djava.lang.Object%2Djava.lang.Object%2D[subSet](def, def)++ (link:{java9-javadoc}/java/util/SortedSet.html#subSet%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-SortedSet-tailSet-1]]<> link:{java8-javadoc}/java/util/SortedSet.html#tailSet%2Djava.lang.Object%2D[tailSet](def)++ (link:{java9-javadoc}/java/util/SortedSet.html#tailSet%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Spliterator.OfDouble.asciidoc b/docs/painless/painless-api-reference/Spliterator.OfDouble.asciidoc new file mode 100644 index 0000000000000..7292d2bffca8a --- /dev/null +++ b/docs/painless/painless-api-reference/Spliterator.OfDouble.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Spliterator-OfDouble]]++Spliterator.OfDouble++:: +* ++[[painless-api-reference-Spliterator-OfDouble-trySplit-0]]<> link:{java8-javadoc}/java/util/Spliterator$OfDouble.html#trySplit%2D%2D[trySplit]()++ (link:{java9-javadoc}/java/util/Spliterator$OfDouble.html#trySplit%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Spliterator.OfInt.asciidoc b/docs/painless/painless-api-reference/Spliterator.OfInt.asciidoc new file mode 100644 index 0000000000000..1f19e7013aa6c --- /dev/null +++ b/docs/painless/painless-api-reference/Spliterator.OfInt.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Spliterator-OfInt]]++Spliterator.OfInt++:: +* ++[[painless-api-reference-Spliterator-OfInt-trySplit-0]]<> link:{java8-javadoc}/java/util/Spliterator$OfInt.html#trySplit%2D%2D[trySplit]()++ (link:{java9-javadoc}/java/util/Spliterator$OfInt.html#trySplit%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Spliterator.OfLong.asciidoc b/docs/painless/painless-api-reference/Spliterator.OfLong.asciidoc new file mode 100644 index 0000000000000..a1d56d5e1ee3c --- /dev/null +++ b/docs/painless/painless-api-reference/Spliterator.OfLong.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Spliterator-OfLong]]++Spliterator.OfLong++:: +* ++[[painless-api-reference-Spliterator-OfLong-trySplit-0]]<> link:{java8-javadoc}/java/util/Spliterator$OfLong.html#trySplit%2D%2D[trySplit]()++ (link:{java9-javadoc}/java/util/Spliterator$OfLong.html#trySplit%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Spliterator.OfPrimitive.asciidoc b/docs/painless/painless-api-reference/Spliterator.OfPrimitive.asciidoc new file mode 100644 index 0000000000000..00cd8b472618b --- /dev/null +++ b/docs/painless/painless-api-reference/Spliterator.OfPrimitive.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Spliterator-OfPrimitive]]++Spliterator.OfPrimitive++:: +* ++[[painless-api-reference-Spliterator-OfPrimitive-forEachRemaining-1]]void link:{java8-javadoc}/java/util/Spliterator$OfPrimitive.html#forEachRemaining%2Djava.lang.Object%2D[forEachRemaining](def)++ (link:{java9-javadoc}/java/util/Spliterator$OfPrimitive.html#forEachRemaining%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Spliterator-OfPrimitive-tryAdvance-1]]boolean link:{java8-javadoc}/java/util/Spliterator$OfPrimitive.html#tryAdvance%2Djava.lang.Object%2D[tryAdvance](def)++ (link:{java9-javadoc}/java/util/Spliterator$OfPrimitive.html#tryAdvance%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Spliterator-OfPrimitive-trySplit-0]]<> link:{java8-javadoc}/java/util/Spliterator$OfPrimitive.html#trySplit%2D%2D[trySplit]()++ (link:{java9-javadoc}/java/util/Spliterator$OfPrimitive.html#trySplit%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Spliterator.asciidoc b/docs/painless/painless-api-reference/Spliterator.asciidoc new file mode 100644 index 0000000000000..fcf919409858d --- /dev/null +++ b/docs/painless/painless-api-reference/Spliterator.asciidoc @@ -0,0 +1,23 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Spliterator]]++Spliterator++:: +** [[painless-api-reference-Spliterator-CONCURRENT]]static int link:{java8-javadoc}/java/util/Spliterator.html#CONCURRENT[CONCURRENT] (link:{java9-javadoc}/java/util/Spliterator.html#CONCURRENT[java 9]) +** [[painless-api-reference-Spliterator-DISTINCT]]static int link:{java8-javadoc}/java/util/Spliterator.html#DISTINCT[DISTINCT] (link:{java9-javadoc}/java/util/Spliterator.html#DISTINCT[java 9]) +** [[painless-api-reference-Spliterator-IMMUTABLE]]static int link:{java8-javadoc}/java/util/Spliterator.html#IMMUTABLE[IMMUTABLE] (link:{java9-javadoc}/java/util/Spliterator.html#IMMUTABLE[java 9]) +** [[painless-api-reference-Spliterator-NONNULL]]static int link:{java8-javadoc}/java/util/Spliterator.html#NONNULL[NONNULL] (link:{java9-javadoc}/java/util/Spliterator.html#NONNULL[java 9]) +** [[painless-api-reference-Spliterator-ORDERED]]static int link:{java8-javadoc}/java/util/Spliterator.html#ORDERED[ORDERED] (link:{java9-javadoc}/java/util/Spliterator.html#ORDERED[java 9]) +** [[painless-api-reference-Spliterator-SIZED]]static int link:{java8-javadoc}/java/util/Spliterator.html#SIZED[SIZED] (link:{java9-javadoc}/java/util/Spliterator.html#SIZED[java 9]) +** [[painless-api-reference-Spliterator-SORTED]]static int link:{java8-javadoc}/java/util/Spliterator.html#SORTED[SORTED] (link:{java9-javadoc}/java/util/Spliterator.html#SORTED[java 9]) +** [[painless-api-reference-Spliterator-SUBSIZED]]static int link:{java8-javadoc}/java/util/Spliterator.html#SUBSIZED[SUBSIZED] (link:{java9-javadoc}/java/util/Spliterator.html#SUBSIZED[java 9]) +* ++[[painless-api-reference-Spliterator-characteristics-0]]int link:{java8-javadoc}/java/util/Spliterator.html#characteristics%2D%2D[characteristics]()++ (link:{java9-javadoc}/java/util/Spliterator.html#characteristics%2D%2D[java 9]) +* ++[[painless-api-reference-Spliterator-estimateSize-0]]long link:{java8-javadoc}/java/util/Spliterator.html#estimateSize%2D%2D[estimateSize]()++ (link:{java9-javadoc}/java/util/Spliterator.html#estimateSize%2D%2D[java 9]) +* ++[[painless-api-reference-Spliterator-forEachRemaining-1]]void link:{java8-javadoc}/java/util/Spliterator.html#forEachRemaining%2Djava.util.function.Consumer%2D[forEachRemaining](<>)++ (link:{java9-javadoc}/java/util/Spliterator.html#forEachRemaining%2Djava.util.function.Consumer%2D[java 9]) +* ++[[painless-api-reference-Spliterator-getComparator-0]]<> link:{java8-javadoc}/java/util/Spliterator.html#getComparator%2D%2D[getComparator]()++ (link:{java9-javadoc}/java/util/Spliterator.html#getComparator%2D%2D[java 9]) +* ++[[painless-api-reference-Spliterator-getExactSizeIfKnown-0]]long link:{java8-javadoc}/java/util/Spliterator.html#getExactSizeIfKnown%2D%2D[getExactSizeIfKnown]()++ (link:{java9-javadoc}/java/util/Spliterator.html#getExactSizeIfKnown%2D%2D[java 9]) +* ++[[painless-api-reference-Spliterator-hasCharacteristics-1]]boolean link:{java8-javadoc}/java/util/Spliterator.html#hasCharacteristics%2Dint%2D[hasCharacteristics](int)++ (link:{java9-javadoc}/java/util/Spliterator.html#hasCharacteristics%2Dint%2D[java 9]) +* ++[[painless-api-reference-Spliterator-tryAdvance-1]]boolean link:{java8-javadoc}/java/util/Spliterator.html#tryAdvance%2Djava.util.function.Consumer%2D[tryAdvance](<>)++ (link:{java9-javadoc}/java/util/Spliterator.html#tryAdvance%2Djava.util.function.Consumer%2D[java 9]) +* ++[[painless-api-reference-Spliterator-trySplit-0]]<> link:{java8-javadoc}/java/util/Spliterator.html#trySplit%2D%2D[trySplit]()++ (link:{java9-javadoc}/java/util/Spliterator.html#trySplit%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Spliterators.asciidoc b/docs/painless/painless-api-reference/Spliterators.asciidoc new file mode 100644 index 0000000000000..6d8202a567411 --- /dev/null +++ b/docs/painless/painless-api-reference/Spliterators.asciidoc @@ -0,0 +1,15 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Spliterators]]++Spliterators++:: +* ++[[painless-api-reference-Spliterators-emptyDoubleSpliterator-0]]static <> link:{java8-javadoc}/java/util/Spliterators.html#emptyDoubleSpliterator%2D%2D[emptyDoubleSpliterator]()++ (link:{java9-javadoc}/java/util/Spliterators.html#emptyDoubleSpliterator%2D%2D[java 9]) +* ++[[painless-api-reference-Spliterators-emptyIntSpliterator-0]]static <> link:{java8-javadoc}/java/util/Spliterators.html#emptyIntSpliterator%2D%2D[emptyIntSpliterator]()++ (link:{java9-javadoc}/java/util/Spliterators.html#emptyIntSpliterator%2D%2D[java 9]) +* ++[[painless-api-reference-Spliterators-emptyLongSpliterator-0]]static <> link:{java8-javadoc}/java/util/Spliterators.html#emptyLongSpliterator%2D%2D[emptyLongSpliterator]()++ (link:{java9-javadoc}/java/util/Spliterators.html#emptyLongSpliterator%2D%2D[java 9]) +* ++[[painless-api-reference-Spliterators-emptySpliterator-0]]static <> link:{java8-javadoc}/java/util/Spliterators.html#emptySpliterator%2D%2D[emptySpliterator]()++ (link:{java9-javadoc}/java/util/Spliterators.html#emptySpliterator%2D%2D[java 9]) +* ++[[painless-api-reference-Spliterators-iterator-1]]static <> link:{java8-javadoc}/java/util/Spliterators.html#iterator%2Djava.util.Spliterator%2D[iterator](<>)++ (link:{java9-javadoc}/java/util/Spliterators.html#iterator%2Djava.util.Spliterator%2D[java 9]) +* ++[[painless-api-reference-Spliterators-spliterator-2]]static <> link:{java8-javadoc}/java/util/Spliterators.html#spliterator%2Djava.util.Collection%2Dint%2D[spliterator](<>, int)++ (link:{java9-javadoc}/java/util/Spliterators.html#spliterator%2Djava.util.Collection%2Dint%2D[java 9]) +* ++[[painless-api-reference-Spliterators-spliterator-3]]static <> link:{java8-javadoc}/java/util/Spliterators.html#spliterator%2Djava.util.Iterator%2Dlong%2Dint%2D[spliterator](<>, long, int)++ (link:{java9-javadoc}/java/util/Spliterators.html#spliterator%2Djava.util.Iterator%2Dlong%2Dint%2D[java 9]) +* ++[[painless-api-reference-Spliterators-spliteratorUnknownSize-2]]static <> link:{java8-javadoc}/java/util/Spliterators.html#spliteratorUnknownSize%2Djava.util.Iterator%2Dint%2D[spliteratorUnknownSize](<>, int)++ (link:{java9-javadoc}/java/util/Spliterators.html#spliteratorUnknownSize%2Djava.util.Iterator%2Dint%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Stack.asciidoc b/docs/painless/painless-api-reference/Stack.asciidoc new file mode 100644 index 0000000000000..ad1520d9d885d --- /dev/null +++ b/docs/painless/painless-api-reference/Stack.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Stack]]++Stack++:: +* ++[[painless-api-reference-Stack-Stack-0]]link:{java8-javadoc}/java/util/Stack.html#Stack%2D%2D[Stack]()++ (link:{java9-javadoc}/java/util/Stack.html#Stack%2D%2D[java 9]) +* ++[[painless-api-reference-Stack-empty-0]]boolean link:{java8-javadoc}/java/util/Stack.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/Stack.html#empty%2D%2D[java 9]) +* ++[[painless-api-reference-Stack-peek-0]]def link:{java8-javadoc}/java/util/Stack.html#peek%2D%2D[peek]()++ (link:{java9-javadoc}/java/util/Stack.html#peek%2D%2D[java 9]) +* ++[[painless-api-reference-Stack-pop-0]]def link:{java8-javadoc}/java/util/Stack.html#pop%2D%2D[pop]()++ (link:{java9-javadoc}/java/util/Stack.html#pop%2D%2D[java 9]) +* ++[[painless-api-reference-Stack-push-1]]def link:{java8-javadoc}/java/util/Stack.html#push%2Djava.lang.Object%2D[push](def)++ (link:{java9-javadoc}/java/util/Stack.html#push%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Stack-search-1]]int link:{java8-javadoc}/java/util/Stack.html#search%2Djava.lang.Object%2D[search](def)++ (link:{java9-javadoc}/java/util/Stack.html#search%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/StackTraceElement.asciidoc b/docs/painless/painless-api-reference/StackTraceElement.asciidoc new file mode 100644 index 0000000000000..046e56ef1f016 --- /dev/null +++ b/docs/painless/painless-api-reference/StackTraceElement.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-StackTraceElement]]++StackTraceElement++:: +* ++[[painless-api-reference-StackTraceElement-StackTraceElement-4]]link:{java8-javadoc}/java/lang/StackTraceElement.html#StackTraceElement%2Djava.lang.String%2Djava.lang.String%2Djava.lang.String%2Dint%2D[StackTraceElement](<>, <>, <>, int)++ (link:{java9-javadoc}/java/lang/StackTraceElement.html#StackTraceElement%2Djava.lang.String%2Djava.lang.String%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-StackTraceElement-getClassName-0]]<> link:{java8-javadoc}/java/lang/StackTraceElement.html#getClassName%2D%2D[getClassName]()++ (link:{java9-javadoc}/java/lang/StackTraceElement.html#getClassName%2D%2D[java 9]) +* ++[[painless-api-reference-StackTraceElement-getFileName-0]]<> link:{java8-javadoc}/java/lang/StackTraceElement.html#getFileName%2D%2D[getFileName]()++ (link:{java9-javadoc}/java/lang/StackTraceElement.html#getFileName%2D%2D[java 9]) +* ++[[painless-api-reference-StackTraceElement-getLineNumber-0]]int link:{java8-javadoc}/java/lang/StackTraceElement.html#getLineNumber%2D%2D[getLineNumber]()++ (link:{java9-javadoc}/java/lang/StackTraceElement.html#getLineNumber%2D%2D[java 9]) +* ++[[painless-api-reference-StackTraceElement-getMethodName-0]]<> link:{java8-javadoc}/java/lang/StackTraceElement.html#getMethodName%2D%2D[getMethodName]()++ (link:{java9-javadoc}/java/lang/StackTraceElement.html#getMethodName%2D%2D[java 9]) +* ++[[painless-api-reference-StackTraceElement-isNativeMethod-0]]boolean link:{java8-javadoc}/java/lang/StackTraceElement.html#isNativeMethod%2D%2D[isNativeMethod]()++ (link:{java9-javadoc}/java/lang/StackTraceElement.html#isNativeMethod%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Stream.Builder.asciidoc b/docs/painless/painless-api-reference/Stream.Builder.asciidoc new file mode 100644 index 0000000000000..aa9146ce8ed61 --- /dev/null +++ b/docs/painless/painless-api-reference/Stream.Builder.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Stream-Builder]]++Stream.Builder++:: +* ++[[painless-api-reference-Stream-Builder-add-1]]<> link:{java8-javadoc}/java/util/stream/Stream$Builder.html#add%2Djava.lang.Object%2D[add](def)++ (link:{java9-javadoc}/java/util/stream/Stream$Builder.html#add%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Stream-Builder-build-0]]<> link:{java8-javadoc}/java/util/stream/Stream$Builder.html#build%2D%2D[build]()++ (link:{java9-javadoc}/java/util/stream/Stream$Builder.html#build%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Stream.asciidoc b/docs/painless/painless-api-reference/Stream.asciidoc new file mode 100644 index 0000000000000..0713ad1ce9d90 --- /dev/null +++ b/docs/painless/painless-api-reference/Stream.asciidoc @@ -0,0 +1,43 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Stream]]++Stream++:: +* ++[[painless-api-reference-Stream-builder-0]]static <> link:{java8-javadoc}/java/util/stream/Stream.html#builder%2D%2D[builder]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#builder%2D%2D[java 9]) +* ++[[painless-api-reference-Stream-concat-2]]static <> link:{java8-javadoc}/java/util/stream/Stream.html#concat%2Djava.util.stream.Stream%2Djava.util.stream.Stream%2D[concat](<>, <>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#concat%2Djava.util.stream.Stream%2Djava.util.stream.Stream%2D[java 9]) +* ++[[painless-api-reference-Stream-empty-0]]static <> link:{java8-javadoc}/java/util/stream/Stream.html#empty%2D%2D[empty]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#empty%2D%2D[java 9]) +* ++[[painless-api-reference-Stream-of-1]]static <> link:{java8-javadoc}/java/util/stream/Stream.html#of%2Djava.lang.Object:A%2D[of](def[])++ (link:{java9-javadoc}/java/util/stream/Stream.html#of%2Djava.lang.Object:A%2D[java 9]) +* ++[[painless-api-reference-Stream-allMatch-1]]boolean link:{java8-javadoc}/java/util/stream/Stream.html#allMatch%2Djava.util.function.Predicate%2D[allMatch](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#allMatch%2Djava.util.function.Predicate%2D[java 9]) +* ++[[painless-api-reference-Stream-anyMatch-1]]boolean link:{java8-javadoc}/java/util/stream/Stream.html#anyMatch%2Djava.util.function.Predicate%2D[anyMatch](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#anyMatch%2Djava.util.function.Predicate%2D[java 9]) +* ++[[painless-api-reference-Stream-collect-1]]def link:{java8-javadoc}/java/util/stream/Stream.html#collect%2Djava.util.stream.Collector%2D[collect](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#collect%2Djava.util.stream.Collector%2D[java 9]) +* ++[[painless-api-reference-Stream-collect-3]]def link:{java8-javadoc}/java/util/stream/Stream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.BiConsumer%2Djava.util.function.BiConsumer%2D[collect](<>, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#collect%2Djava.util.function.Supplier%2Djava.util.function.BiConsumer%2Djava.util.function.BiConsumer%2D[java 9]) +* ++[[painless-api-reference-Stream-count-0]]long link:{java8-javadoc}/java/util/stream/Stream.html#count%2D%2D[count]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#count%2D%2D[java 9]) +* ++[[painless-api-reference-Stream-distinct-0]]<> link:{java8-javadoc}/java/util/stream/Stream.html#distinct%2D%2D[distinct]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#distinct%2D%2D[java 9]) +* ++[[painless-api-reference-Stream-filter-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#filter%2Djava.util.function.Predicate%2D[filter](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#filter%2Djava.util.function.Predicate%2D[java 9]) +* ++[[painless-api-reference-Stream-findAny-0]]<> link:{java8-javadoc}/java/util/stream/Stream.html#findAny%2D%2D[findAny]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#findAny%2D%2D[java 9]) +* ++[[painless-api-reference-Stream-findFirst-0]]<> link:{java8-javadoc}/java/util/stream/Stream.html#findFirst%2D%2D[findFirst]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#findFirst%2D%2D[java 9]) +* ++[[painless-api-reference-Stream-flatMap-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#flatMap%2Djava.util.function.Function%2D[flatMap](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#flatMap%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-Stream-flatMapToDouble-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#flatMapToDouble%2Djava.util.function.Function%2D[flatMapToDouble](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#flatMapToDouble%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-Stream-flatMapToInt-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#flatMapToInt%2Djava.util.function.Function%2D[flatMapToInt](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#flatMapToInt%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-Stream-flatMapToLong-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#flatMapToLong%2Djava.util.function.Function%2D[flatMapToLong](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#flatMapToLong%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-Stream-forEach-1]]void link:{java8-javadoc}/java/util/stream/Stream.html#forEach%2Djava.util.function.Consumer%2D[forEach](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#forEach%2Djava.util.function.Consumer%2D[java 9]) +* ++[[painless-api-reference-Stream-forEachOrdered-1]]void link:{java8-javadoc}/java/util/stream/Stream.html#forEachOrdered%2Djava.util.function.Consumer%2D[forEachOrdered](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#forEachOrdered%2Djava.util.function.Consumer%2D[java 9]) +* ++[[painless-api-reference-Stream-limit-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#limit%2Dlong%2D[limit](long)++ (link:{java9-javadoc}/java/util/stream/Stream.html#limit%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Stream-map-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#map%2Djava.util.function.Function%2D[map](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#map%2Djava.util.function.Function%2D[java 9]) +* ++[[painless-api-reference-Stream-mapToDouble-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#mapToDouble%2Djava.util.function.ToDoubleFunction%2D[mapToDouble](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#mapToDouble%2Djava.util.function.ToDoubleFunction%2D[java 9]) +* ++[[painless-api-reference-Stream-mapToInt-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#mapToInt%2Djava.util.function.ToIntFunction%2D[mapToInt](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#mapToInt%2Djava.util.function.ToIntFunction%2D[java 9]) +* ++[[painless-api-reference-Stream-mapToLong-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#mapToLong%2Djava.util.function.ToLongFunction%2D[mapToLong](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#mapToLong%2Djava.util.function.ToLongFunction%2D[java 9]) +* ++[[painless-api-reference-Stream-max-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#max%2Djava.util.Comparator%2D[max](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#max%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Stream-min-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#min%2Djava.util.Comparator%2D[min](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#min%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Stream-noneMatch-1]]boolean link:{java8-javadoc}/java/util/stream/Stream.html#noneMatch%2Djava.util.function.Predicate%2D[noneMatch](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#noneMatch%2Djava.util.function.Predicate%2D[java 9]) +* ++[[painless-api-reference-Stream-peek-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#peek%2Djava.util.function.Consumer%2D[peek](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#peek%2Djava.util.function.Consumer%2D[java 9]) +* ++[[painless-api-reference-Stream-reduce-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#reduce%2Djava.util.function.BinaryOperator%2D[reduce](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#reduce%2Djava.util.function.BinaryOperator%2D[java 9]) +* ++[[painless-api-reference-Stream-reduce-2]]def link:{java8-javadoc}/java/util/stream/Stream.html#reduce%2Djava.lang.Object%2Djava.util.function.BinaryOperator%2D[reduce](def, <>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#reduce%2Djava.lang.Object%2Djava.util.function.BinaryOperator%2D[java 9]) +* ++[[painless-api-reference-Stream-reduce-3]]def link:{java8-javadoc}/java/util/stream/Stream.html#reduce%2Djava.lang.Object%2Djava.util.function.BiFunction%2Djava.util.function.BinaryOperator%2D[reduce](def, <>, <>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#reduce%2Djava.lang.Object%2Djava.util.function.BiFunction%2Djava.util.function.BinaryOperator%2D[java 9]) +* ++[[painless-api-reference-Stream-skip-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#skip%2Dlong%2D[skip](long)++ (link:{java9-javadoc}/java/util/stream/Stream.html#skip%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Stream-sorted-0]]<> link:{java8-javadoc}/java/util/stream/Stream.html#sorted%2D%2D[sorted]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#sorted%2D%2D[java 9]) +* ++[[painless-api-reference-Stream-sorted-1]]<> link:{java8-javadoc}/java/util/stream/Stream.html#sorted%2Djava.util.Comparator%2D[sorted](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#sorted%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-Stream-toArray-0]]def[] link:{java8-javadoc}/java/util/stream/Stream.html#toArray%2D%2D[toArray]()++ (link:{java9-javadoc}/java/util/stream/Stream.html#toArray%2D%2D[java 9]) +* ++[[painless-api-reference-Stream-toArray-1]]def[] link:{java8-javadoc}/java/util/stream/Stream.html#toArray%2Djava.util.function.IntFunction%2D[toArray](<>)++ (link:{java9-javadoc}/java/util/stream/Stream.html#toArray%2Djava.util.function.IntFunction%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/StrictMath.asciidoc b/docs/painless/painless-api-reference/StrictMath.asciidoc new file mode 100644 index 0000000000000..17346c33ba3f9 --- /dev/null +++ b/docs/painless/painless-api-reference/StrictMath.asciidoc @@ -0,0 +1,46 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-StrictMath]]++StrictMath++:: +** [[painless-api-reference-StrictMath-E]]static double link:{java8-javadoc}/java/lang/StrictMath.html#E[E] (link:{java9-javadoc}/java/lang/StrictMath.html#E[java 9]) +** [[painless-api-reference-StrictMath-PI]]static double link:{java8-javadoc}/java/lang/StrictMath.html#PI[PI] (link:{java9-javadoc}/java/lang/StrictMath.html#PI[java 9]) +* ++[[painless-api-reference-StrictMath-IEEEremainder-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#IEEEremainder%2Ddouble%2Ddouble%2D[IEEEremainder](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#IEEEremainder%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-abs-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#abs%2Ddouble%2D[abs](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#abs%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-acos-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#acos%2Ddouble%2D[acos](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#acos%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-asin-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#asin%2Ddouble%2D[asin](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#asin%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-atan-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#atan%2Ddouble%2D[atan](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#atan%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-atan2-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#atan2%2Ddouble%2Ddouble%2D[atan2](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#atan2%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-cbrt-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#cbrt%2Ddouble%2D[cbrt](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#cbrt%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-ceil-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#ceil%2Ddouble%2D[ceil](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#ceil%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-copySign-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#copySign%2Ddouble%2Ddouble%2D[copySign](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#copySign%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-cos-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#cos%2Ddouble%2D[cos](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#cos%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-cosh-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#cosh%2Ddouble%2D[cosh](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#cosh%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-exp-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#exp%2Ddouble%2D[exp](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#exp%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-expm1-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#expm1%2Ddouble%2D[expm1](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#expm1%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-floor-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#floor%2Ddouble%2D[floor](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#floor%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-hypot-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#hypot%2Ddouble%2Ddouble%2D[hypot](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#hypot%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-log-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#log%2Ddouble%2D[log](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#log%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-log10-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#log10%2Ddouble%2D[log10](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#log10%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-log1p-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#log1p%2Ddouble%2D[log1p](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#log1p%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-max-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#max%2Ddouble%2Ddouble%2D[max](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#max%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-min-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#min%2Ddouble%2Ddouble%2D[min](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#min%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-nextAfter-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#nextAfter%2Ddouble%2Ddouble%2D[nextAfter](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#nextAfter%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-nextDown-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#nextDown%2Ddouble%2D[nextDown](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#nextDown%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-nextUp-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#nextUp%2Ddouble%2D[nextUp](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#nextUp%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-pow-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#pow%2Ddouble%2Ddouble%2D[pow](double, double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#pow%2Ddouble%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-random-0]]static double link:{java8-javadoc}/java/lang/StrictMath.html#random%2D%2D[random]()++ (link:{java9-javadoc}/java/lang/StrictMath.html#random%2D%2D[java 9]) +* ++[[painless-api-reference-StrictMath-rint-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#rint%2Ddouble%2D[rint](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#rint%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-round-1]]static long link:{java8-javadoc}/java/lang/StrictMath.html#round%2Ddouble%2D[round](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#round%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-scalb-2]]static double link:{java8-javadoc}/java/lang/StrictMath.html#scalb%2Ddouble%2Dint%2D[scalb](double, int)++ (link:{java9-javadoc}/java/lang/StrictMath.html#scalb%2Ddouble%2Dint%2D[java 9]) +* ++[[painless-api-reference-StrictMath-signum-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#signum%2Ddouble%2D[signum](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#signum%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-sin-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#sin%2Ddouble%2D[sin](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#sin%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-sinh-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#sinh%2Ddouble%2D[sinh](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#sinh%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-sqrt-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#sqrt%2Ddouble%2D[sqrt](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#sqrt%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-tan-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#tan%2Ddouble%2D[tan](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#tan%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-tanh-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#tanh%2Ddouble%2D[tanh](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#tanh%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-toDegrees-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#toDegrees%2Ddouble%2D[toDegrees](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#toDegrees%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-toRadians-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#toRadians%2Ddouble%2D[toRadians](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#toRadians%2Ddouble%2D[java 9]) +* ++[[painless-api-reference-StrictMath-ulp-1]]static double link:{java8-javadoc}/java/lang/StrictMath.html#ulp%2Ddouble%2D[ulp](double)++ (link:{java9-javadoc}/java/lang/StrictMath.html#ulp%2Ddouble%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/String.asciidoc b/docs/painless/painless-api-reference/String.asciidoc new file mode 100644 index 0000000000000..a40e1811183d5 --- /dev/null +++ b/docs/painless/painless-api-reference/String.asciidoc @@ -0,0 +1,46 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-String]]++String++:: +* ++[[painless-api-reference-String-copyValueOf-1]]static <> link:{java8-javadoc}/java/lang/String.html#copyValueOf%2Dchar:A%2D[copyValueOf](char[])++ (link:{java9-javadoc}/java/lang/String.html#copyValueOf%2Dchar:A%2D[java 9]) +* ++[[painless-api-reference-String-copyValueOf-3]]static <> link:{java8-javadoc}/java/lang/String.html#copyValueOf%2Dchar:A%2Dint%2Dint%2D[copyValueOf](char[], int, int)++ (link:{java9-javadoc}/java/lang/String.html#copyValueOf%2Dchar:A%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-String-format-2]]static <> link:{java8-javadoc}/java/lang/String.html#format%2Djava.lang.String%2Djava.lang.Object:A%2D[format](<>, def[])++ (link:{java9-javadoc}/java/lang/String.html#format%2Djava.lang.String%2Djava.lang.Object:A%2D[java 9]) +* ++[[painless-api-reference-String-format-3]]static <> link:{java8-javadoc}/java/lang/String.html#format%2Djava.util.Locale%2Djava.lang.String%2Djava.lang.Object:A%2D[format](<>, <>, def[])++ (link:{java9-javadoc}/java/lang/String.html#format%2Djava.util.Locale%2Djava.lang.String%2Djava.lang.Object:A%2D[java 9]) +* ++[[painless-api-reference-String-join-2]]static <> link:{java8-javadoc}/java/lang/String.html#join%2Djava.lang.CharSequence%2Djava.lang.Iterable%2D[join](<>, <>)++ (link:{java9-javadoc}/java/lang/String.html#join%2Djava.lang.CharSequence%2Djava.lang.Iterable%2D[java 9]) +* ++[[painless-api-reference-String-valueOf-1]]static <> link:{java8-javadoc}/java/lang/String.html#valueOf%2Djava.lang.Object%2D[valueOf](def)++ (link:{java9-javadoc}/java/lang/String.html#valueOf%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-String-String-0]]link:{java8-javadoc}/java/lang/String.html#String%2D%2D[String]()++ (link:{java9-javadoc}/java/lang/String.html#String%2D%2D[java 9]) +* ++[[painless-api-reference-String-codePointAt-1]]int link:{java8-javadoc}/java/lang/String.html#codePointAt%2Dint%2D[codePointAt](int)++ (link:{java9-javadoc}/java/lang/String.html#codePointAt%2Dint%2D[java 9]) +* ++[[painless-api-reference-String-codePointBefore-1]]int link:{java8-javadoc}/java/lang/String.html#codePointBefore%2Dint%2D[codePointBefore](int)++ (link:{java9-javadoc}/java/lang/String.html#codePointBefore%2Dint%2D[java 9]) +* ++[[painless-api-reference-String-codePointCount-2]]int link:{java8-javadoc}/java/lang/String.html#codePointCount%2Dint%2Dint%2D[codePointCount](int, int)++ (link:{java9-javadoc}/java/lang/String.html#codePointCount%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-String-compareTo-1]]int link:{java8-javadoc}/java/lang/String.html#compareTo%2Djava.lang.String%2D[compareTo](<>)++ (link:{java9-javadoc}/java/lang/String.html#compareTo%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-String-compareToIgnoreCase-1]]int link:{java8-javadoc}/java/lang/String.html#compareToIgnoreCase%2Djava.lang.String%2D[compareToIgnoreCase](<>)++ (link:{java9-javadoc}/java/lang/String.html#compareToIgnoreCase%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-String-concat-1]]<> link:{java8-javadoc}/java/lang/String.html#concat%2Djava.lang.String%2D[concat](<>)++ (link:{java9-javadoc}/java/lang/String.html#concat%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-String-contains-1]]boolean link:{java8-javadoc}/java/lang/String.html#contains%2Djava.lang.CharSequence%2D[contains](<>)++ (link:{java9-javadoc}/java/lang/String.html#contains%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-String-contentEquals-1]]boolean link:{java8-javadoc}/java/lang/String.html#contentEquals%2Djava.lang.CharSequence%2D[contentEquals](<>)++ (link:{java9-javadoc}/java/lang/String.html#contentEquals%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-String-decodeBase64-0]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#decodeBase64%2Djava.lang.String%2D[decodeBase64]()++ +* ++[[painless-api-reference-String-encodeBase64-0]]<> link:{painless-javadoc}/org/elasticsearch/painless/api/Augmentation.html#encodeBase64%2Djava.lang.String%2D[encodeBase64]()++ +* ++[[painless-api-reference-String-endsWith-1]]boolean link:{java8-javadoc}/java/lang/String.html#endsWith%2Djava.lang.String%2D[endsWith](<>)++ (link:{java9-javadoc}/java/lang/String.html#endsWith%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-String-equalsIgnoreCase-1]]boolean link:{java8-javadoc}/java/lang/String.html#equalsIgnoreCase%2Djava.lang.String%2D[equalsIgnoreCase](<>)++ (link:{java9-javadoc}/java/lang/String.html#equalsIgnoreCase%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-String-getChars-4]]void link:{java8-javadoc}/java/lang/String.html#getChars%2Dint%2Dint%2Dchar:A%2Dint%2D[getChars](int, int, char[], int)++ (link:{java9-javadoc}/java/lang/String.html#getChars%2Dint%2Dint%2Dchar:A%2Dint%2D[java 9]) +* ++[[painless-api-reference-String-indexOf-1]]int link:{java8-javadoc}/java/lang/String.html#indexOf%2Djava.lang.String%2D[indexOf](<>)++ (link:{java9-javadoc}/java/lang/String.html#indexOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-String-indexOf-2]]int link:{java8-javadoc}/java/lang/String.html#indexOf%2Djava.lang.String%2Dint%2D[indexOf](<>, int)++ (link:{java9-javadoc}/java/lang/String.html#indexOf%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-String-isEmpty-0]]boolean link:{java8-javadoc}/java/lang/String.html#isEmpty%2D%2D[isEmpty]()++ (link:{java9-javadoc}/java/lang/String.html#isEmpty%2D%2D[java 9]) +* ++[[painless-api-reference-String-lastIndexOf-1]]int link:{java8-javadoc}/java/lang/String.html#lastIndexOf%2Djava.lang.String%2D[lastIndexOf](<>)++ (link:{java9-javadoc}/java/lang/String.html#lastIndexOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-String-lastIndexOf-2]]int link:{java8-javadoc}/java/lang/String.html#lastIndexOf%2Djava.lang.String%2Dint%2D[lastIndexOf](<>, int)++ (link:{java9-javadoc}/java/lang/String.html#lastIndexOf%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-String-offsetByCodePoints-2]]int link:{java8-javadoc}/java/lang/String.html#offsetByCodePoints%2Dint%2Dint%2D[offsetByCodePoints](int, int)++ (link:{java9-javadoc}/java/lang/String.html#offsetByCodePoints%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-String-regionMatches-4]]boolean link:{java8-javadoc}/java/lang/String.html#regionMatches%2Dint%2Djava.lang.String%2Dint%2Dint%2D[regionMatches](int, <>, int, int)++ (link:{java9-javadoc}/java/lang/String.html#regionMatches%2Dint%2Djava.lang.String%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-String-regionMatches-5]]boolean link:{java8-javadoc}/java/lang/String.html#regionMatches%2Dboolean%2Dint%2Djava.lang.String%2Dint%2Dint%2D[regionMatches](boolean, int, <>, int, int)++ (link:{java9-javadoc}/java/lang/String.html#regionMatches%2Dboolean%2Dint%2Djava.lang.String%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-String-replace-2]]<> link:{java8-javadoc}/java/lang/String.html#replace%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2D[replace](<>, <>)++ (link:{java9-javadoc}/java/lang/String.html#replace%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-String-startsWith-1]]boolean link:{java8-javadoc}/java/lang/String.html#startsWith%2Djava.lang.String%2D[startsWith](<>)++ (link:{java9-javadoc}/java/lang/String.html#startsWith%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-String-startsWith-2]]boolean link:{java8-javadoc}/java/lang/String.html#startsWith%2Djava.lang.String%2Dint%2D[startsWith](<>, int)++ (link:{java9-javadoc}/java/lang/String.html#startsWith%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-String-substring-1]]<> link:{java8-javadoc}/java/lang/String.html#substring%2Dint%2D[substring](int)++ (link:{java9-javadoc}/java/lang/String.html#substring%2Dint%2D[java 9]) +* ++[[painless-api-reference-String-substring-2]]<> link:{java8-javadoc}/java/lang/String.html#substring%2Dint%2Dint%2D[substring](int, int)++ (link:{java9-javadoc}/java/lang/String.html#substring%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-String-toCharArray-0]]char[] link:{java8-javadoc}/java/lang/String.html#toCharArray%2D%2D[toCharArray]()++ (link:{java9-javadoc}/java/lang/String.html#toCharArray%2D%2D[java 9]) +* ++[[painless-api-reference-String-toLowerCase-0]]<> link:{java8-javadoc}/java/lang/String.html#toLowerCase%2D%2D[toLowerCase]()++ (link:{java9-javadoc}/java/lang/String.html#toLowerCase%2D%2D[java 9]) +* ++[[painless-api-reference-String-toLowerCase-1]]<> link:{java8-javadoc}/java/lang/String.html#toLowerCase%2Djava.util.Locale%2D[toLowerCase](<>)++ (link:{java9-javadoc}/java/lang/String.html#toLowerCase%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-String-toUpperCase-0]]<> link:{java8-javadoc}/java/lang/String.html#toUpperCase%2D%2D[toUpperCase]()++ (link:{java9-javadoc}/java/lang/String.html#toUpperCase%2D%2D[java 9]) +* ++[[painless-api-reference-String-toUpperCase-1]]<> link:{java8-javadoc}/java/lang/String.html#toUpperCase%2Djava.util.Locale%2D[toUpperCase](<>)++ (link:{java9-javadoc}/java/lang/String.html#toUpperCase%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-String-trim-0]]<> link:{java8-javadoc}/java/lang/String.html#trim%2D%2D[trim]()++ (link:{java9-javadoc}/java/lang/String.html#trim%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/StringBuffer.asciidoc b/docs/painless/painless-api-reference/StringBuffer.asciidoc new file mode 100644 index 0000000000000..5993832a983da --- /dev/null +++ b/docs/painless/painless-api-reference/StringBuffer.asciidoc @@ -0,0 +1,31 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-StringBuffer]]++StringBuffer++:: +* ++[[painless-api-reference-StringBuffer-StringBuffer-0]]link:{java8-javadoc}/java/lang/StringBuffer.html#StringBuffer%2D%2D[StringBuffer]()++ (link:{java9-javadoc}/java/lang/StringBuffer.html#StringBuffer%2D%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-StringBuffer-1]]link:{java8-javadoc}/java/lang/StringBuffer.html#StringBuffer%2Djava.lang.CharSequence%2D[StringBuffer](<>)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#StringBuffer%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-append-1]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#append%2Djava.lang.Object%2D[append](def)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#append%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-append-3]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#append%2Djava.lang.CharSequence%2Dint%2Dint%2D[append](<>, int, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#append%2Djava.lang.CharSequence%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-appendCodePoint-1]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#appendCodePoint%2Dint%2D[appendCodePoint](int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#appendCodePoint%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-capacity-0]]int link:{java8-javadoc}/java/lang/StringBuffer.html#capacity%2D%2D[capacity]()++ (link:{java9-javadoc}/java/lang/StringBuffer.html#capacity%2D%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-codePointAt-1]]int link:{java8-javadoc}/java/lang/StringBuffer.html#codePointAt%2Dint%2D[codePointAt](int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#codePointAt%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-codePointBefore-1]]int link:{java8-javadoc}/java/lang/StringBuffer.html#codePointBefore%2Dint%2D[codePointBefore](int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#codePointBefore%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-codePointCount-2]]int link:{java8-javadoc}/java/lang/StringBuffer.html#codePointCount%2Dint%2Dint%2D[codePointCount](int, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#codePointCount%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-delete-2]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#delete%2Dint%2Dint%2D[delete](int, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#delete%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-deleteCharAt-1]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#deleteCharAt%2Dint%2D[deleteCharAt](int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#deleteCharAt%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-getChars-4]]void link:{java8-javadoc}/java/lang/StringBuffer.html#getChars%2Dint%2Dint%2Dchar:A%2Dint%2D[getChars](int, int, char[], int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#getChars%2Dint%2Dint%2Dchar:A%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-indexOf-1]]int link:{java8-javadoc}/java/lang/StringBuffer.html#indexOf%2Djava.lang.String%2D[indexOf](<>)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#indexOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-indexOf-2]]int link:{java8-javadoc}/java/lang/StringBuffer.html#indexOf%2Djava.lang.String%2Dint%2D[indexOf](<>, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#indexOf%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-insert-2]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#insert%2Dint%2Djava.lang.Object%2D[insert](int, def)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#insert%2Dint%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-lastIndexOf-1]]int link:{java8-javadoc}/java/lang/StringBuffer.html#lastIndexOf%2Djava.lang.String%2D[lastIndexOf](<>)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#lastIndexOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-lastIndexOf-2]]int link:{java8-javadoc}/java/lang/StringBuffer.html#lastIndexOf%2Djava.lang.String%2Dint%2D[lastIndexOf](<>, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#lastIndexOf%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-offsetByCodePoints-2]]int link:{java8-javadoc}/java/lang/StringBuffer.html#offsetByCodePoints%2Dint%2Dint%2D[offsetByCodePoints](int, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#offsetByCodePoints%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-replace-3]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#replace%2Dint%2Dint%2Djava.lang.String%2D[replace](int, int, <>)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#replace%2Dint%2Dint%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-reverse-0]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#reverse%2D%2D[reverse]()++ (link:{java9-javadoc}/java/lang/StringBuffer.html#reverse%2D%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-setCharAt-2]]void link:{java8-javadoc}/java/lang/StringBuffer.html#setCharAt%2Dint%2Dchar%2D[setCharAt](int, char)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#setCharAt%2Dint%2Dchar%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-setLength-1]]void link:{java8-javadoc}/java/lang/StringBuffer.html#setLength%2Dint%2D[setLength](int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#setLength%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-substring-1]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#substring%2Dint%2D[substring](int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#substring%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuffer-substring-2]]<> link:{java8-javadoc}/java/lang/StringBuffer.html#substring%2Dint%2Dint%2D[substring](int, int)++ (link:{java9-javadoc}/java/lang/StringBuffer.html#substring%2Dint%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/StringBuilder.asciidoc b/docs/painless/painless-api-reference/StringBuilder.asciidoc new file mode 100644 index 0000000000000..fd9c0c097cc03 --- /dev/null +++ b/docs/painless/painless-api-reference/StringBuilder.asciidoc @@ -0,0 +1,31 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-StringBuilder]]++StringBuilder++:: +* ++[[painless-api-reference-StringBuilder-StringBuilder-0]]link:{java8-javadoc}/java/lang/StringBuilder.html#StringBuilder%2D%2D[StringBuilder]()++ (link:{java9-javadoc}/java/lang/StringBuilder.html#StringBuilder%2D%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-StringBuilder-1]]link:{java8-javadoc}/java/lang/StringBuilder.html#StringBuilder%2Djava.lang.CharSequence%2D[StringBuilder](<>)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#StringBuilder%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-append-1]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#append%2Djava.lang.Object%2D[append](def)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#append%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-append-3]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#append%2Djava.lang.CharSequence%2Dint%2Dint%2D[append](<>, int, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#append%2Djava.lang.CharSequence%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-appendCodePoint-1]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#appendCodePoint%2Dint%2D[appendCodePoint](int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#appendCodePoint%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-capacity-0]]int link:{java8-javadoc}/java/lang/StringBuilder.html#capacity%2D%2D[capacity]()++ (link:{java9-javadoc}/java/lang/StringBuilder.html#capacity%2D%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-codePointAt-1]]int link:{java8-javadoc}/java/lang/StringBuilder.html#codePointAt%2Dint%2D[codePointAt](int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#codePointAt%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-codePointBefore-1]]int link:{java8-javadoc}/java/lang/StringBuilder.html#codePointBefore%2Dint%2D[codePointBefore](int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#codePointBefore%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-codePointCount-2]]int link:{java8-javadoc}/java/lang/StringBuilder.html#codePointCount%2Dint%2Dint%2D[codePointCount](int, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#codePointCount%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-delete-2]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#delete%2Dint%2Dint%2D[delete](int, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#delete%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-deleteCharAt-1]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#deleteCharAt%2Dint%2D[deleteCharAt](int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#deleteCharAt%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-getChars-4]]void link:{java8-javadoc}/java/lang/StringBuilder.html#getChars%2Dint%2Dint%2Dchar:A%2Dint%2D[getChars](int, int, char[], int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#getChars%2Dint%2Dint%2Dchar:A%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-indexOf-1]]int link:{java8-javadoc}/java/lang/StringBuilder.html#indexOf%2Djava.lang.String%2D[indexOf](<>)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#indexOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-indexOf-2]]int link:{java8-javadoc}/java/lang/StringBuilder.html#indexOf%2Djava.lang.String%2Dint%2D[indexOf](<>, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#indexOf%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-insert-2]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#insert%2Dint%2Djava.lang.Object%2D[insert](int, def)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#insert%2Dint%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-lastIndexOf-1]]int link:{java8-javadoc}/java/lang/StringBuilder.html#lastIndexOf%2Djava.lang.String%2D[lastIndexOf](<>)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#lastIndexOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-lastIndexOf-2]]int link:{java8-javadoc}/java/lang/StringBuilder.html#lastIndexOf%2Djava.lang.String%2Dint%2D[lastIndexOf](<>, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#lastIndexOf%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-offsetByCodePoints-2]]int link:{java8-javadoc}/java/lang/StringBuilder.html#offsetByCodePoints%2Dint%2Dint%2D[offsetByCodePoints](int, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#offsetByCodePoints%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-replace-3]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#replace%2Dint%2Dint%2Djava.lang.String%2D[replace](int, int, <>)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#replace%2Dint%2Dint%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-reverse-0]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#reverse%2D%2D[reverse]()++ (link:{java9-javadoc}/java/lang/StringBuilder.html#reverse%2D%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-setCharAt-2]]void link:{java8-javadoc}/java/lang/StringBuilder.html#setCharAt%2Dint%2Dchar%2D[setCharAt](int, char)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#setCharAt%2Dint%2Dchar%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-setLength-1]]void link:{java8-javadoc}/java/lang/StringBuilder.html#setLength%2Dint%2D[setLength](int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#setLength%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-substring-1]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#substring%2Dint%2D[substring](int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#substring%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringBuilder-substring-2]]<> link:{java8-javadoc}/java/lang/StringBuilder.html#substring%2Dint%2Dint%2D[substring](int, int)++ (link:{java9-javadoc}/java/lang/StringBuilder.html#substring%2Dint%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/StringCharacterIterator.asciidoc b/docs/painless/painless-api-reference/StringCharacterIterator.asciidoc new file mode 100644 index 0000000000000..8f65f2b6c5e53 --- /dev/null +++ b/docs/painless/painless-api-reference/StringCharacterIterator.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-StringCharacterIterator]]++StringCharacterIterator++:: +* ++[[painless-api-reference-StringCharacterIterator-StringCharacterIterator-1]]link:{java8-javadoc}/java/text/StringCharacterIterator.html#StringCharacterIterator%2Djava.lang.String%2D[StringCharacterIterator](<>)++ (link:{java9-javadoc}/java/text/StringCharacterIterator.html#StringCharacterIterator%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-StringCharacterIterator-StringCharacterIterator-2]]link:{java8-javadoc}/java/text/StringCharacterIterator.html#StringCharacterIterator%2Djava.lang.String%2Dint%2D[StringCharacterIterator](<>, int)++ (link:{java9-javadoc}/java/text/StringCharacterIterator.html#StringCharacterIterator%2Djava.lang.String%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringCharacterIterator-StringCharacterIterator-4]]link:{java8-javadoc}/java/text/StringCharacterIterator.html#StringCharacterIterator%2Djava.lang.String%2Dint%2Dint%2Dint%2D[StringCharacterIterator](<>, int, int, int)++ (link:{java9-javadoc}/java/text/StringCharacterIterator.html#StringCharacterIterator%2Djava.lang.String%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-StringCharacterIterator-setText-1]]void link:{java8-javadoc}/java/text/StringCharacterIterator.html#setText%2Djava.lang.String%2D[setText](<>)++ (link:{java9-javadoc}/java/text/StringCharacterIterator.html#setText%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/StringIndexOutOfBoundsException.asciidoc b/docs/painless/painless-api-reference/StringIndexOutOfBoundsException.asciidoc new file mode 100644 index 0000000000000..a29cae3f857f0 --- /dev/null +++ b/docs/painless/painless-api-reference/StringIndexOutOfBoundsException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-StringIndexOutOfBoundsException]]++StringIndexOutOfBoundsException++:: +* ++[[painless-api-reference-StringIndexOutOfBoundsException-StringIndexOutOfBoundsException-0]]link:{java8-javadoc}/java/lang/StringIndexOutOfBoundsException.html#StringIndexOutOfBoundsException%2D%2D[StringIndexOutOfBoundsException]()++ (link:{java9-javadoc}/java/lang/StringIndexOutOfBoundsException.html#StringIndexOutOfBoundsException%2D%2D[java 9]) +* ++[[painless-api-reference-StringIndexOutOfBoundsException-StringIndexOutOfBoundsException-1]]link:{java8-javadoc}/java/lang/StringIndexOutOfBoundsException.html#StringIndexOutOfBoundsException%2Djava.lang.String%2D[StringIndexOutOfBoundsException](<>)++ (link:{java9-javadoc}/java/lang/StringIndexOutOfBoundsException.html#StringIndexOutOfBoundsException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/StringJoiner.asciidoc b/docs/painless/painless-api-reference/StringJoiner.asciidoc new file mode 100644 index 0000000000000..7bd9baa4460ac --- /dev/null +++ b/docs/painless/painless-api-reference/StringJoiner.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-StringJoiner]]++StringJoiner++:: +* ++[[painless-api-reference-StringJoiner-StringJoiner-1]]link:{java8-javadoc}/java/util/StringJoiner.html#StringJoiner%2Djava.lang.CharSequence%2D[StringJoiner](<>)++ (link:{java9-javadoc}/java/util/StringJoiner.html#StringJoiner%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-StringJoiner-StringJoiner-3]]link:{java8-javadoc}/java/util/StringJoiner.html#StringJoiner%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2D[StringJoiner](<>, <>, <>)++ (link:{java9-javadoc}/java/util/StringJoiner.html#StringJoiner%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-StringJoiner-add-1]]<> link:{java8-javadoc}/java/util/StringJoiner.html#add%2Djava.lang.CharSequence%2D[add](<>)++ (link:{java9-javadoc}/java/util/StringJoiner.html#add%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-StringJoiner-length-0]]int link:{java8-javadoc}/java/util/StringJoiner.html#length%2D%2D[length]()++ (link:{java9-javadoc}/java/util/StringJoiner.html#length%2D%2D[java 9]) +* ++[[painless-api-reference-StringJoiner-merge-1]]<> link:{java8-javadoc}/java/util/StringJoiner.html#merge%2Djava.util.StringJoiner%2D[merge](<>)++ (link:{java9-javadoc}/java/util/StringJoiner.html#merge%2Djava.util.StringJoiner%2D[java 9]) +* ++[[painless-api-reference-StringJoiner-setEmptyValue-1]]<> link:{java8-javadoc}/java/util/StringJoiner.html#setEmptyValue%2Djava.lang.CharSequence%2D[setEmptyValue](<>)++ (link:{java9-javadoc}/java/util/StringJoiner.html#setEmptyValue%2Djava.lang.CharSequence%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/StringTokenizer.asciidoc b/docs/painless/painless-api-reference/StringTokenizer.asciidoc new file mode 100644 index 0000000000000..9462cecc33b4e --- /dev/null +++ b/docs/painless/painless-api-reference/StringTokenizer.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-StringTokenizer]]++StringTokenizer++:: +* ++[[painless-api-reference-StringTokenizer-StringTokenizer-1]]link:{java8-javadoc}/java/util/StringTokenizer.html#StringTokenizer%2Djava.lang.String%2D[StringTokenizer](<>)++ (link:{java9-javadoc}/java/util/StringTokenizer.html#StringTokenizer%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-StringTokenizer-StringTokenizer-2]]link:{java8-javadoc}/java/util/StringTokenizer.html#StringTokenizer%2Djava.lang.String%2Djava.lang.String%2D[StringTokenizer](<>, <>)++ (link:{java9-javadoc}/java/util/StringTokenizer.html#StringTokenizer%2Djava.lang.String%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-StringTokenizer-StringTokenizer-3]]link:{java8-javadoc}/java/util/StringTokenizer.html#StringTokenizer%2Djava.lang.String%2Djava.lang.String%2Dboolean%2D[StringTokenizer](<>, <>, boolean)++ (link:{java9-javadoc}/java/util/StringTokenizer.html#StringTokenizer%2Djava.lang.String%2Djava.lang.String%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-StringTokenizer-countTokens-0]]int link:{java8-javadoc}/java/util/StringTokenizer.html#countTokens%2D%2D[countTokens]()++ (link:{java9-javadoc}/java/util/StringTokenizer.html#countTokens%2D%2D[java 9]) +* ++[[painless-api-reference-StringTokenizer-hasMoreTokens-0]]boolean link:{java8-javadoc}/java/util/StringTokenizer.html#hasMoreTokens%2D%2D[hasMoreTokens]()++ (link:{java9-javadoc}/java/util/StringTokenizer.html#hasMoreTokens%2D%2D[java 9]) +* ++[[painless-api-reference-StringTokenizer-nextToken-0]]<> link:{java8-javadoc}/java/util/StringTokenizer.html#nextToken%2D%2D[nextToken]()++ (link:{java9-javadoc}/java/util/StringTokenizer.html#nextToken%2D%2D[java 9]) +* ++[[painless-api-reference-StringTokenizer-nextToken-1]]<> link:{java8-javadoc}/java/util/StringTokenizer.html#nextToken%2Djava.lang.String%2D[nextToken](<>)++ (link:{java9-javadoc}/java/util/StringTokenizer.html#nextToken%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/Supplier.asciidoc b/docs/painless/painless-api-reference/Supplier.asciidoc new file mode 100644 index 0000000000000..ba3cd9da951ec --- /dev/null +++ b/docs/painless/painless-api-reference/Supplier.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Supplier]]++Supplier++:: +* ++[[painless-api-reference-Supplier-get-0]]def link:{java8-javadoc}/java/util/function/Supplier.html#get%2D%2D[get]()++ (link:{java9-javadoc}/java/util/function/Supplier.html#get%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/System.asciidoc b/docs/painless/painless-api-reference/System.asciidoc new file mode 100644 index 0000000000000..a791d5dc15966 --- /dev/null +++ b/docs/painless/painless-api-reference/System.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-System]]++System++:: +* ++[[painless-api-reference-System-arraycopy-5]]static void link:{java8-javadoc}/java/lang/System.html#arraycopy%2Djava.lang.Object%2Dint%2Djava.lang.Object%2Dint%2Dint%2D[arraycopy](<>, int, <>, int, int)++ (link:{java9-javadoc}/java/lang/System.html#arraycopy%2Djava.lang.Object%2Dint%2Djava.lang.Object%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-System-currentTimeMillis-0]]static long link:{java8-javadoc}/java/lang/System.html#currentTimeMillis%2D%2D[currentTimeMillis]()++ (link:{java9-javadoc}/java/lang/System.html#currentTimeMillis%2D%2D[java 9]) +* ++[[painless-api-reference-System-nanoTime-0]]static long link:{java8-javadoc}/java/lang/System.html#nanoTime%2D%2D[nanoTime]()++ (link:{java9-javadoc}/java/lang/System.html#nanoTime%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Temporal.asciidoc b/docs/painless/painless-api-reference/Temporal.asciidoc new file mode 100644 index 0000000000000..ce693cfde51fa --- /dev/null +++ b/docs/painless/painless-api-reference/Temporal.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Temporal]]++Temporal++:: +* ++[[painless-api-reference-Temporal-minus-1]]<> link:{java8-javadoc}/java/time/temporal/Temporal.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-Temporal-minus-2]]<> link:{java8-javadoc}/java/time/temporal/Temporal.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-Temporal-plus-1]]<> link:{java8-javadoc}/java/time/temporal/Temporal.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-Temporal-plus-2]]<> link:{java8-javadoc}/java/time/temporal/Temporal.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-Temporal-until-2]]long link:{java8-javadoc}/java/time/temporal/Temporal.html#until%2Djava.time.temporal.Temporal%2Djava.time.temporal.TemporalUnit%2D[until](<>, <>)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#until%2Djava.time.temporal.Temporal%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-Temporal-with-1]]<> link:{java8-javadoc}/java/time/temporal/Temporal.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-Temporal-with-2]]<> link:{java8-javadoc}/java/time/temporal/Temporal.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/temporal/Temporal.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/TemporalAccessor.asciidoc b/docs/painless/painless-api-reference/TemporalAccessor.asciidoc new file mode 100644 index 0000000000000..7583b2180d940 --- /dev/null +++ b/docs/painless/painless-api-reference/TemporalAccessor.asciidoc @@ -0,0 +1,12 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TemporalAccessor]]++TemporalAccessor++:: +* ++[[painless-api-reference-TemporalAccessor-get-1]]int link:{java8-javadoc}/java/time/temporal/TemporalAccessor.html#get%2Djava.time.temporal.TemporalField%2D[get](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAccessor.html#get%2Djava.time.temporal.TemporalField%2D[java 9]) +* ++[[painless-api-reference-TemporalAccessor-getLong-1]]long link:{java8-javadoc}/java/time/temporal/TemporalAccessor.html#getLong%2Djava.time.temporal.TemporalField%2D[getLong](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAccessor.html#getLong%2Djava.time.temporal.TemporalField%2D[java 9]) +* ++[[painless-api-reference-TemporalAccessor-isSupported-1]]boolean link:{java8-javadoc}/java/time/temporal/TemporalAccessor.html#isSupported%2Djava.time.temporal.TemporalField%2D[isSupported](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAccessor.html#isSupported%2Djava.time.temporal.TemporalField%2D[java 9]) +* ++[[painless-api-reference-TemporalAccessor-query-1]]def link:{java8-javadoc}/java/time/temporal/TemporalAccessor.html#query%2Djava.time.temporal.TemporalQuery%2D[query](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAccessor.html#query%2Djava.time.temporal.TemporalQuery%2D[java 9]) +* ++[[painless-api-reference-TemporalAccessor-range-1]]<> link:{java8-javadoc}/java/time/temporal/TemporalAccessor.html#range%2Djava.time.temporal.TemporalField%2D[range](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAccessor.html#range%2Djava.time.temporal.TemporalField%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/TemporalAdjuster.asciidoc b/docs/painless/painless-api-reference/TemporalAdjuster.asciidoc new file mode 100644 index 0000000000000..b80f0d7afbfae --- /dev/null +++ b/docs/painless/painless-api-reference/TemporalAdjuster.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TemporalAdjuster]]++TemporalAdjuster++:: +* ++[[painless-api-reference-TemporalAdjuster-adjustInto-1]]<> link:{java8-javadoc}/java/time/temporal/TemporalAdjuster.html#adjustInto%2Djava.time.temporal.Temporal%2D[adjustInto](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjuster.html#adjustInto%2Djava.time.temporal.Temporal%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/TemporalAdjusters.asciidoc b/docs/painless/painless-api-reference/TemporalAdjusters.asciidoc new file mode 100644 index 0000000000000..7299ae9256abc --- /dev/null +++ b/docs/painless/painless-api-reference/TemporalAdjusters.asciidoc @@ -0,0 +1,21 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TemporalAdjusters]]++TemporalAdjusters++:: +* ++[[painless-api-reference-TemporalAdjusters-dayOfWeekInMonth-2]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#dayOfWeekInMonth%2Dint%2Djava.time.DayOfWeek%2D[dayOfWeekInMonth](int, <>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#dayOfWeekInMonth%2Dint%2Djava.time.DayOfWeek%2D[java 9]) +* ++[[painless-api-reference-TemporalAdjusters-firstDayOfMonth-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfMonth%2D%2D[firstDayOfMonth]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfMonth%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalAdjusters-firstDayOfNextMonth-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfNextMonth%2D%2D[firstDayOfNextMonth]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfNextMonth%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalAdjusters-firstDayOfNextYear-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfNextYear%2D%2D[firstDayOfNextYear]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfNextYear%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalAdjusters-firstDayOfYear-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfYear%2D%2D[firstDayOfYear]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#firstDayOfYear%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalAdjusters-firstInMonth-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#firstInMonth%2Djava.time.DayOfWeek%2D[firstInMonth](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#firstInMonth%2Djava.time.DayOfWeek%2D[java 9]) +* ++[[painless-api-reference-TemporalAdjusters-lastDayOfMonth-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#lastDayOfMonth%2D%2D[lastDayOfMonth]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#lastDayOfMonth%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalAdjusters-lastDayOfYear-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#lastDayOfYear%2D%2D[lastDayOfYear]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#lastDayOfYear%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalAdjusters-lastInMonth-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#lastInMonth%2Djava.time.DayOfWeek%2D[lastInMonth](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#lastInMonth%2Djava.time.DayOfWeek%2D[java 9]) +* ++[[painless-api-reference-TemporalAdjusters-next-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#next%2Djava.time.DayOfWeek%2D[next](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#next%2Djava.time.DayOfWeek%2D[java 9]) +* ++[[painless-api-reference-TemporalAdjusters-nextOrSame-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#nextOrSame%2Djava.time.DayOfWeek%2D[nextOrSame](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#nextOrSame%2Djava.time.DayOfWeek%2D[java 9]) +* ++[[painless-api-reference-TemporalAdjusters-ofDateAdjuster-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#ofDateAdjuster%2Djava.util.function.UnaryOperator%2D[ofDateAdjuster](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#ofDateAdjuster%2Djava.util.function.UnaryOperator%2D[java 9]) +* ++[[painless-api-reference-TemporalAdjusters-previous-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#previous%2Djava.time.DayOfWeek%2D[previous](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#previous%2Djava.time.DayOfWeek%2D[java 9]) +* ++[[painless-api-reference-TemporalAdjusters-previousOrSame-1]]static <> link:{java8-javadoc}/java/time/temporal/TemporalAdjusters.html#previousOrSame%2Djava.time.DayOfWeek%2D[previousOrSame](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAdjusters.html#previousOrSame%2Djava.time.DayOfWeek%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/TemporalAmount.asciidoc b/docs/painless/painless-api-reference/TemporalAmount.asciidoc new file mode 100644 index 0000000000000..e27954b4db3e4 --- /dev/null +++ b/docs/painless/painless-api-reference/TemporalAmount.asciidoc @@ -0,0 +1,11 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TemporalAmount]]++TemporalAmount++:: +* ++[[painless-api-reference-TemporalAmount-addTo-1]]<> link:{java8-javadoc}/java/time/temporal/TemporalAmount.html#addTo%2Djava.time.temporal.Temporal%2D[addTo](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAmount.html#addTo%2Djava.time.temporal.Temporal%2D[java 9]) +* ++[[painless-api-reference-TemporalAmount-get-1]]long link:{java8-javadoc}/java/time/temporal/TemporalAmount.html#get%2Djava.time.temporal.TemporalUnit%2D[get](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAmount.html#get%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-TemporalAmount-getUnits-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalAmount.html#getUnits%2D%2D[getUnits]()++ (link:{java9-javadoc}/java/time/temporal/TemporalAmount.html#getUnits%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalAmount-subtractFrom-1]]<> link:{java8-javadoc}/java/time/temporal/TemporalAmount.html#subtractFrom%2Djava.time.temporal.Temporal%2D[subtractFrom](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalAmount.html#subtractFrom%2Djava.time.temporal.Temporal%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/TemporalField.asciidoc b/docs/painless/painless-api-reference/TemporalField.asciidoc new file mode 100644 index 0000000000000..dd38544d46e4c --- /dev/null +++ b/docs/painless/painless-api-reference/TemporalField.asciidoc @@ -0,0 +1,19 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TemporalField]]++TemporalField++:: +* ++[[painless-api-reference-TemporalField-adjustInto-2]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#adjustInto%2Djava.time.temporal.Temporal%2Dlong%2D[adjustInto](<>, long)++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#adjustInto%2Djava.time.temporal.Temporal%2Dlong%2D[java 9]) +* ++[[painless-api-reference-TemporalField-getBaseUnit-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#getBaseUnit%2D%2D[getBaseUnit]()++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#getBaseUnit%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalField-getDisplayName-1]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#getDisplayName%2Djava.util.Locale%2D[getDisplayName](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#getDisplayName%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-TemporalField-getFrom-1]]long link:{java8-javadoc}/java/time/temporal/TemporalField.html#getFrom%2Djava.time.temporal.TemporalAccessor%2D[getFrom](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#getFrom%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-TemporalField-getRangeUnit-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#getRangeUnit%2D%2D[getRangeUnit]()++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#getRangeUnit%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalField-isDateBased-0]]boolean link:{java8-javadoc}/java/time/temporal/TemporalField.html#isDateBased%2D%2D[isDateBased]()++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#isDateBased%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalField-isSupportedBy-1]]boolean link:{java8-javadoc}/java/time/temporal/TemporalField.html#isSupportedBy%2Djava.time.temporal.TemporalAccessor%2D[isSupportedBy](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#isSupportedBy%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-TemporalField-isTimeBased-0]]boolean link:{java8-javadoc}/java/time/temporal/TemporalField.html#isTimeBased%2D%2D[isTimeBased]()++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#isTimeBased%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalField-range-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#range%2D%2D[range]()++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#range%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalField-rangeRefinedBy-1]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#rangeRefinedBy%2Djava.time.temporal.TemporalAccessor%2D[rangeRefinedBy](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#rangeRefinedBy%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-TemporalField-resolve-3]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#resolve%2Djava.util.Map%2Djava.time.temporal.TemporalAccessor%2Djava.time.format.ResolverStyle%2D[resolve](<>, <>, <>)++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#resolve%2Djava.util.Map%2Djava.time.temporal.TemporalAccessor%2Djava.time.format.ResolverStyle%2D[java 9]) +* ++[[painless-api-reference-TemporalField-toString-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalField.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/temporal/TemporalField.html#toString%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/TemporalQueries.asciidoc b/docs/painless/painless-api-reference/TemporalQueries.asciidoc new file mode 100644 index 0000000000000..bd01ba6c2e0ed --- /dev/null +++ b/docs/painless/painless-api-reference/TemporalQueries.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TemporalQueries]]++TemporalQueries++:: +* ++[[painless-api-reference-TemporalQueries-chronology-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#chronology%2D%2D[chronology]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#chronology%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalQueries-localDate-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#localDate%2D%2D[localDate]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#localDate%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalQueries-localTime-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#localTime%2D%2D[localTime]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#localTime%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalQueries-offset-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#offset%2D%2D[offset]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#offset%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalQueries-precision-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#precision%2D%2D[precision]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#precision%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalQueries-zone-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#zone%2D%2D[zone]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#zone%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalQueries-zoneId-0]]static <> link:{java8-javadoc}/java/time/temporal/TemporalQueries.html#zoneId%2D%2D[zoneId]()++ (link:{java9-javadoc}/java/time/temporal/TemporalQueries.html#zoneId%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/TemporalQuery.asciidoc b/docs/painless/painless-api-reference/TemporalQuery.asciidoc new file mode 100644 index 0000000000000..ca09eff368de3 --- /dev/null +++ b/docs/painless/painless-api-reference/TemporalQuery.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TemporalQuery]]++TemporalQuery++:: +* ++[[painless-api-reference-TemporalQuery-queryFrom-1]]def link:{java8-javadoc}/java/time/temporal/TemporalQuery.html#queryFrom%2Djava.time.temporal.TemporalAccessor%2D[queryFrom](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalQuery.html#queryFrom%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/TemporalUnit.asciidoc b/docs/painless/painless-api-reference/TemporalUnit.asciidoc new file mode 100644 index 0000000000000..cc940d838efc2 --- /dev/null +++ b/docs/painless/painless-api-reference/TemporalUnit.asciidoc @@ -0,0 +1,15 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TemporalUnit]]++TemporalUnit++:: +* ++[[painless-api-reference-TemporalUnit-addTo-2]]<> link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#addTo%2Djava.time.temporal.Temporal%2Dlong%2D[addTo](<>, long)++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#addTo%2Djava.time.temporal.Temporal%2Dlong%2D[java 9]) +* ++[[painless-api-reference-TemporalUnit-between-2]]long link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#between%2Djava.time.temporal.Temporal%2Djava.time.temporal.Temporal%2D[between](<>, <>)++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#between%2Djava.time.temporal.Temporal%2Djava.time.temporal.Temporal%2D[java 9]) +* ++[[painless-api-reference-TemporalUnit-getDuration-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#getDuration%2D%2D[getDuration]()++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#getDuration%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalUnit-isDateBased-0]]boolean link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#isDateBased%2D%2D[isDateBased]()++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#isDateBased%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalUnit-isDurationEstimated-0]]boolean link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#isDurationEstimated%2D%2D[isDurationEstimated]()++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#isDurationEstimated%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalUnit-isSupportedBy-1]]boolean link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#isSupportedBy%2Djava.time.temporal.Temporal%2D[isSupportedBy](<>)++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#isSupportedBy%2Djava.time.temporal.Temporal%2D[java 9]) +* ++[[painless-api-reference-TemporalUnit-isTimeBased-0]]boolean link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#isTimeBased%2D%2D[isTimeBased]()++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#isTimeBased%2D%2D[java 9]) +* ++[[painless-api-reference-TemporalUnit-toString-0]]<> link:{java8-javadoc}/java/time/temporal/TemporalUnit.html#toString%2D%2D[toString]()++ (link:{java9-javadoc}/java/time/temporal/TemporalUnit.html#toString%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/TextStyle.asciidoc b/docs/painless/painless-api-reference/TextStyle.asciidoc new file mode 100644 index 0000000000000..c7b122f934592 --- /dev/null +++ b/docs/painless/painless-api-reference/TextStyle.asciidoc @@ -0,0 +1,18 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TextStyle]]++TextStyle++:: +** [[painless-api-reference-TextStyle-FULL]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#FULL[FULL] (link:{java9-javadoc}/java/time/format/TextStyle.html#FULL[java 9]) +** [[painless-api-reference-TextStyle-FULL_STANDALONE]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#FULL_STANDALONE[FULL_STANDALONE] (link:{java9-javadoc}/java/time/format/TextStyle.html#FULL_STANDALONE[java 9]) +** [[painless-api-reference-TextStyle-NARROW]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#NARROW[NARROW] (link:{java9-javadoc}/java/time/format/TextStyle.html#NARROW[java 9]) +** [[painless-api-reference-TextStyle-NARROW_STANDALONE]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#NARROW_STANDALONE[NARROW_STANDALONE] (link:{java9-javadoc}/java/time/format/TextStyle.html#NARROW_STANDALONE[java 9]) +** [[painless-api-reference-TextStyle-SHORT]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#SHORT[SHORT] (link:{java9-javadoc}/java/time/format/TextStyle.html#SHORT[java 9]) +** [[painless-api-reference-TextStyle-SHORT_STANDALONE]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#SHORT_STANDALONE[SHORT_STANDALONE] (link:{java9-javadoc}/java/time/format/TextStyle.html#SHORT_STANDALONE[java 9]) +* ++[[painless-api-reference-TextStyle-valueOf-1]]static <> link:{java8-javadoc}/java/time/format/TextStyle.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/format/TextStyle.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-TextStyle-values-0]]static <>[] link:{java8-javadoc}/java/time/format/TextStyle.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/format/TextStyle.html#values%2D%2D[java 9]) +* ++[[painless-api-reference-TextStyle-asNormal-0]]<> link:{java8-javadoc}/java/time/format/TextStyle.html#asNormal%2D%2D[asNormal]()++ (link:{java9-javadoc}/java/time/format/TextStyle.html#asNormal%2D%2D[java 9]) +* ++[[painless-api-reference-TextStyle-asStandalone-0]]<> link:{java8-javadoc}/java/time/format/TextStyle.html#asStandalone%2D%2D[asStandalone]()++ (link:{java9-javadoc}/java/time/format/TextStyle.html#asStandalone%2D%2D[java 9]) +* ++[[painless-api-reference-TextStyle-isStandalone-0]]boolean link:{java8-javadoc}/java/time/format/TextStyle.html#isStandalone%2D%2D[isStandalone]()++ (link:{java9-javadoc}/java/time/format/TextStyle.html#isStandalone%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ThaiBuddhistChronology.asciidoc b/docs/painless/painless-api-reference/ThaiBuddhistChronology.asciidoc new file mode 100644 index 0000000000000..86c721fda7ada --- /dev/null +++ b/docs/painless/painless-api-reference/ThaiBuddhistChronology.asciidoc @@ -0,0 +1,16 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ThaiBuddhistChronology]]++ThaiBuddhistChronology++:: +** [[painless-api-reference-ThaiBuddhistChronology-INSTANCE]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#INSTANCE[INSTANCE] (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#INSTANCE[java 9]) +* ++[[painless-api-reference-ThaiBuddhistChronology-date-1]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[date](<>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#date%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistChronology-date-3]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#date%2Dint%2Dint%2Dint%2D[date](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#date%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistChronology-date-4]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[date](<>, int, int, int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#date%2Djava.time.chrono.Era%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistChronology-dateEpochDay-1]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#dateEpochDay%2Dlong%2D[dateEpochDay](long)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#dateEpochDay%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistChronology-dateYearDay-2]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#dateYearDay%2Dint%2Dint%2D[dateYearDay](int, int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#dateYearDay%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistChronology-dateYearDay-3]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[dateYearDay](<>, int, int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#dateYearDay%2Djava.time.chrono.Era%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistChronology-eraOf-1]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#eraOf%2Dint%2D[eraOf](int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#eraOf%2Dint%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistChronology-resolveDate-2]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[resolveDate](<>, <>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistChronology.html#resolveDate%2Djava.util.Map%2Djava.time.format.ResolverStyle%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ThaiBuddhistDate.asciidoc b/docs/painless/painless-api-reference/ThaiBuddhistDate.asciidoc new file mode 100644 index 0000000000000..94d620182dff4 --- /dev/null +++ b/docs/painless/painless-api-reference/ThaiBuddhistDate.asciidoc @@ -0,0 +1,17 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ThaiBuddhistDate]]++ThaiBuddhistDate++:: +* ++[[painless-api-reference-ThaiBuddhistDate-from-1]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistDate-of-3]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#of%2Dint%2Dint%2Dint%2D[of](int, int, int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#of%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistDate-getChronology-0]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#getChronology%2D%2D[getChronology]()++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#getChronology%2D%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistDate-getEra-0]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#getEra%2D%2D[getEra]()++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#getEra%2D%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistDate-minus-1]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistDate-minus-2]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistDate-plus-1]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistDate-plus-2]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistDate-with-1]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistDate-with-2]]<> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistDate.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ThaiBuddhistEra.asciidoc b/docs/painless/painless-api-reference/ThaiBuddhistEra.asciidoc new file mode 100644 index 0000000000000..b6c070503a9ee --- /dev/null +++ b/docs/painless/painless-api-reference/ThaiBuddhistEra.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ThaiBuddhistEra]]++ThaiBuddhistEra++:: +** [[painless-api-reference-ThaiBuddhistEra-BE]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistEra.html#BE[BE] (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistEra.html#BE[java 9]) +** [[painless-api-reference-ThaiBuddhistEra-BEFORE_BE]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistEra.html#BEFORE_BE[BEFORE_BE] (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistEra.html#BEFORE_BE[java 9]) +* ++[[painless-api-reference-ThaiBuddhistEra-of-1]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistEra.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistEra.html#of%2Dint%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistEra-valueOf-1]]static <> link:{java8-javadoc}/java/time/chrono/ThaiBuddhistEra.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistEra.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistEra-values-0]]static <>[] link:{java8-javadoc}/java/time/chrono/ThaiBuddhistEra.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistEra.html#values%2D%2D[java 9]) +* ++[[painless-api-reference-ThaiBuddhistEra-getValue-0]]int link:{java8-javadoc}/java/time/chrono/ThaiBuddhistEra.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/chrono/ThaiBuddhistEra.html#getValue%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/TimeZone.asciidoc b/docs/painless/painless-api-reference/TimeZone.asciidoc new file mode 100644 index 0000000000000..5618e388a4429 --- /dev/null +++ b/docs/painless/painless-api-reference/TimeZone.asciidoc @@ -0,0 +1,29 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TimeZone]]++TimeZone++:: +** [[painless-api-reference-TimeZone-LONG]]static int link:{java8-javadoc}/java/util/TimeZone.html#LONG[LONG] (link:{java9-javadoc}/java/util/TimeZone.html#LONG[java 9]) +** [[painless-api-reference-TimeZone-SHORT]]static int link:{java8-javadoc}/java/util/TimeZone.html#SHORT[SHORT] (link:{java9-javadoc}/java/util/TimeZone.html#SHORT[java 9]) +* ++[[painless-api-reference-TimeZone-getAvailableIDs-0]]static <>[] link:{java8-javadoc}/java/util/TimeZone.html#getAvailableIDs%2D%2D[getAvailableIDs]()++ (link:{java9-javadoc}/java/util/TimeZone.html#getAvailableIDs%2D%2D[java 9]) +* ++[[painless-api-reference-TimeZone-getAvailableIDs-1]]static <>[] link:{java8-javadoc}/java/util/TimeZone.html#getAvailableIDs%2Dint%2D[getAvailableIDs](int)++ (link:{java9-javadoc}/java/util/TimeZone.html#getAvailableIDs%2Dint%2D[java 9]) +* ++[[painless-api-reference-TimeZone-getDefault-0]]static <> link:{java8-javadoc}/java/util/TimeZone.html#getDefault%2D%2D[getDefault]()++ (link:{java9-javadoc}/java/util/TimeZone.html#getDefault%2D%2D[java 9]) +* ++[[painless-api-reference-TimeZone-getTimeZone-1]]static <> link:{java8-javadoc}/java/util/TimeZone.html#getTimeZone%2Djava.lang.String%2D[getTimeZone](<>)++ (link:{java9-javadoc}/java/util/TimeZone.html#getTimeZone%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-TimeZone-clone-0]]def link:{java8-javadoc}/java/util/TimeZone.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/TimeZone.html#clone%2D%2D[java 9]) +* ++[[painless-api-reference-TimeZone-getDSTSavings-0]]int link:{java8-javadoc}/java/util/TimeZone.html#getDSTSavings%2D%2D[getDSTSavings]()++ (link:{java9-javadoc}/java/util/TimeZone.html#getDSTSavings%2D%2D[java 9]) +* ++[[painless-api-reference-TimeZone-getDisplayName-0]]<> link:{java8-javadoc}/java/util/TimeZone.html#getDisplayName%2D%2D[getDisplayName]()++ (link:{java9-javadoc}/java/util/TimeZone.html#getDisplayName%2D%2D[java 9]) +* ++[[painless-api-reference-TimeZone-getDisplayName-1]]<> link:{java8-javadoc}/java/util/TimeZone.html#getDisplayName%2Djava.util.Locale%2D[getDisplayName](<>)++ (link:{java9-javadoc}/java/util/TimeZone.html#getDisplayName%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-TimeZone-getDisplayName-2]]<> link:{java8-javadoc}/java/util/TimeZone.html#getDisplayName%2Dboolean%2Dint%2D[getDisplayName](boolean, int)++ (link:{java9-javadoc}/java/util/TimeZone.html#getDisplayName%2Dboolean%2Dint%2D[java 9]) +* ++[[painless-api-reference-TimeZone-getDisplayName-3]]<> link:{java8-javadoc}/java/util/TimeZone.html#getDisplayName%2Dboolean%2Dint%2Djava.util.Locale%2D[getDisplayName](boolean, int, <>)++ (link:{java9-javadoc}/java/util/TimeZone.html#getDisplayName%2Dboolean%2Dint%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-TimeZone-getID-0]]<> link:{java8-javadoc}/java/util/TimeZone.html#getID%2D%2D[getID]()++ (link:{java9-javadoc}/java/util/TimeZone.html#getID%2D%2D[java 9]) +* ++[[painless-api-reference-TimeZone-getOffset-1]]int link:{java8-javadoc}/java/util/TimeZone.html#getOffset%2Dlong%2D[getOffset](long)++ (link:{java9-javadoc}/java/util/TimeZone.html#getOffset%2Dlong%2D[java 9]) +* ++[[painless-api-reference-TimeZone-getOffset-6]]int link:{java8-javadoc}/java/util/TimeZone.html#getOffset%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[getOffset](int, int, int, int, int, int)++ (link:{java9-javadoc}/java/util/TimeZone.html#getOffset%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-TimeZone-getRawOffset-0]]int link:{java8-javadoc}/java/util/TimeZone.html#getRawOffset%2D%2D[getRawOffset]()++ (link:{java9-javadoc}/java/util/TimeZone.html#getRawOffset%2D%2D[java 9]) +* ++[[painless-api-reference-TimeZone-hasSameRules-1]]boolean link:{java8-javadoc}/java/util/TimeZone.html#hasSameRules%2Djava.util.TimeZone%2D[hasSameRules](<>)++ (link:{java9-javadoc}/java/util/TimeZone.html#hasSameRules%2Djava.util.TimeZone%2D[java 9]) +* ++[[painless-api-reference-TimeZone-inDaylightTime-1]]boolean link:{java8-javadoc}/java/util/TimeZone.html#inDaylightTime%2Djava.util.Date%2D[inDaylightTime](<>)++ (link:{java9-javadoc}/java/util/TimeZone.html#inDaylightTime%2Djava.util.Date%2D[java 9]) +* ++[[painless-api-reference-TimeZone-observesDaylightTime-0]]boolean link:{java8-javadoc}/java/util/TimeZone.html#observesDaylightTime%2D%2D[observesDaylightTime]()++ (link:{java9-javadoc}/java/util/TimeZone.html#observesDaylightTime%2D%2D[java 9]) +* ++[[painless-api-reference-TimeZone-setRawOffset-1]]void link:{java8-javadoc}/java/util/TimeZone.html#setRawOffset%2Dint%2D[setRawOffset](int)++ (link:{java9-javadoc}/java/util/TimeZone.html#setRawOffset%2Dint%2D[java 9]) +* ++[[painless-api-reference-TimeZone-toZoneId-0]]<> link:{java8-javadoc}/java/util/TimeZone.html#toZoneId%2D%2D[toZoneId]()++ (link:{java9-javadoc}/java/util/TimeZone.html#toZoneId%2D%2D[java 9]) +* ++[[painless-api-reference-TimeZone-useDaylightTime-0]]boolean link:{java8-javadoc}/java/util/TimeZone.html#useDaylightTime%2D%2D[useDaylightTime]()++ (link:{java9-javadoc}/java/util/TimeZone.html#useDaylightTime%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ToDoubleBiFunction.asciidoc b/docs/painless/painless-api-reference/ToDoubleBiFunction.asciidoc new file mode 100644 index 0000000000000..8ef0873fb5e15 --- /dev/null +++ b/docs/painless/painless-api-reference/ToDoubleBiFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ToDoubleBiFunction]]++ToDoubleBiFunction++:: +* ++[[painless-api-reference-ToDoubleBiFunction-applyAsDouble-2]]double link:{java8-javadoc}/java/util/function/ToDoubleBiFunction.html#applyAsDouble%2Djava.lang.Object%2Djava.lang.Object%2D[applyAsDouble](def, def)++ (link:{java9-javadoc}/java/util/function/ToDoubleBiFunction.html#applyAsDouble%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ToDoubleFunction.asciidoc b/docs/painless/painless-api-reference/ToDoubleFunction.asciidoc new file mode 100644 index 0000000000000..b0d66be9dc09e --- /dev/null +++ b/docs/painless/painless-api-reference/ToDoubleFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ToDoubleFunction]]++ToDoubleFunction++:: +* ++[[painless-api-reference-ToDoubleFunction-applyAsDouble-1]]double link:{java8-javadoc}/java/util/function/ToDoubleFunction.html#applyAsDouble%2Djava.lang.Object%2D[applyAsDouble](def)++ (link:{java9-javadoc}/java/util/function/ToDoubleFunction.html#applyAsDouble%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ToIntBiFunction.asciidoc b/docs/painless/painless-api-reference/ToIntBiFunction.asciidoc new file mode 100644 index 0000000000000..42cc396a1a989 --- /dev/null +++ b/docs/painless/painless-api-reference/ToIntBiFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ToIntBiFunction]]++ToIntBiFunction++:: +* ++[[painless-api-reference-ToIntBiFunction-applyAsInt-2]]int link:{java8-javadoc}/java/util/function/ToIntBiFunction.html#applyAsInt%2Djava.lang.Object%2Djava.lang.Object%2D[applyAsInt](def, def)++ (link:{java9-javadoc}/java/util/function/ToIntBiFunction.html#applyAsInt%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ToIntFunction.asciidoc b/docs/painless/painless-api-reference/ToIntFunction.asciidoc new file mode 100644 index 0000000000000..c471de6b77dc4 --- /dev/null +++ b/docs/painless/painless-api-reference/ToIntFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ToIntFunction]]++ToIntFunction++:: +* ++[[painless-api-reference-ToIntFunction-applyAsInt-1]]int link:{java8-javadoc}/java/util/function/ToIntFunction.html#applyAsInt%2Djava.lang.Object%2D[applyAsInt](def)++ (link:{java9-javadoc}/java/util/function/ToIntFunction.html#applyAsInt%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ToLongBiFunction.asciidoc b/docs/painless/painless-api-reference/ToLongBiFunction.asciidoc new file mode 100644 index 0000000000000..ffda1353210a8 --- /dev/null +++ b/docs/painless/painless-api-reference/ToLongBiFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ToLongBiFunction]]++ToLongBiFunction++:: +* ++[[painless-api-reference-ToLongBiFunction-applyAsLong-2]]long link:{java8-javadoc}/java/util/function/ToLongBiFunction.html#applyAsLong%2Djava.lang.Object%2Djava.lang.Object%2D[applyAsLong](def, def)++ (link:{java9-javadoc}/java/util/function/ToLongBiFunction.html#applyAsLong%2Djava.lang.Object%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ToLongFunction.asciidoc b/docs/painless/painless-api-reference/ToLongFunction.asciidoc new file mode 100644 index 0000000000000..785c414f8c906 --- /dev/null +++ b/docs/painless/painless-api-reference/ToLongFunction.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ToLongFunction]]++ToLongFunction++:: +* ++[[painless-api-reference-ToLongFunction-applyAsLong-1]]long link:{java8-javadoc}/java/util/function/ToLongFunction.html#applyAsLong%2Djava.lang.Object%2D[applyAsLong](def)++ (link:{java9-javadoc}/java/util/function/ToLongFunction.html#applyAsLong%2Djava.lang.Object%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/TooManyListenersException.asciidoc b/docs/painless/painless-api-reference/TooManyListenersException.asciidoc new file mode 100644 index 0000000000000..5a2d3d57b314d --- /dev/null +++ b/docs/painless/painless-api-reference/TooManyListenersException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TooManyListenersException]]++TooManyListenersException++:: +* ++[[painless-api-reference-TooManyListenersException-TooManyListenersException-0]]link:{java8-javadoc}/java/util/TooManyListenersException.html#TooManyListenersException%2D%2D[TooManyListenersException]()++ (link:{java9-javadoc}/java/util/TooManyListenersException.html#TooManyListenersException%2D%2D[java 9]) +* ++[[painless-api-reference-TooManyListenersException-TooManyListenersException-1]]link:{java8-javadoc}/java/util/TooManyListenersException.html#TooManyListenersException%2Djava.lang.String%2D[TooManyListenersException](<>)++ (link:{java9-javadoc}/java/util/TooManyListenersException.html#TooManyListenersException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/TreeMap.asciidoc b/docs/painless/painless-api-reference/TreeMap.asciidoc new file mode 100644 index 0000000000000..f91bae69162f6 --- /dev/null +++ b/docs/painless/painless-api-reference/TreeMap.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TreeMap]]++TreeMap++:: +* ++[[painless-api-reference-TreeMap-TreeMap-0]]link:{java8-javadoc}/java/util/TreeMap.html#TreeMap%2D%2D[TreeMap]()++ (link:{java9-javadoc}/java/util/TreeMap.html#TreeMap%2D%2D[java 9]) +* ++[[painless-api-reference-TreeMap-TreeMap-1]]link:{java8-javadoc}/java/util/TreeMap.html#TreeMap%2Djava.util.Comparator%2D[TreeMap](<>)++ (link:{java9-javadoc}/java/util/TreeMap.html#TreeMap%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-TreeMap-clone-0]]def link:{java8-javadoc}/java/util/TreeMap.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/TreeMap.html#clone%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/TreeSet.asciidoc b/docs/painless/painless-api-reference/TreeSet.asciidoc new file mode 100644 index 0000000000000..84e8a339fe3bf --- /dev/null +++ b/docs/painless/painless-api-reference/TreeSet.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TreeSet]]++TreeSet++:: +* ++[[painless-api-reference-TreeSet-TreeSet-0]]link:{java8-javadoc}/java/util/TreeSet.html#TreeSet%2D%2D[TreeSet]()++ (link:{java9-javadoc}/java/util/TreeSet.html#TreeSet%2D%2D[java 9]) +* ++[[painless-api-reference-TreeSet-TreeSet-1]]link:{java8-javadoc}/java/util/TreeSet.html#TreeSet%2Djava.util.Comparator%2D[TreeSet](<>)++ (link:{java9-javadoc}/java/util/TreeSet.html#TreeSet%2Djava.util.Comparator%2D[java 9]) +* ++[[painless-api-reference-TreeSet-clone-0]]def link:{java8-javadoc}/java/util/TreeSet.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/TreeSet.html#clone%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/TypeNotPresentException.asciidoc b/docs/painless/painless-api-reference/TypeNotPresentException.asciidoc new file mode 100644 index 0000000000000..cecb588d83b17 --- /dev/null +++ b/docs/painless/painless-api-reference/TypeNotPresentException.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-TypeNotPresentException]]++TypeNotPresentException++:: +* ++[[painless-api-reference-TypeNotPresentException-typeName-0]]<> link:{java8-javadoc}/java/lang/TypeNotPresentException.html#typeName%2D%2D[typeName]()++ (link:{java9-javadoc}/java/lang/TypeNotPresentException.html#typeName%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/UUID.asciidoc b/docs/painless/painless-api-reference/UUID.asciidoc new file mode 100644 index 0000000000000..a47620f9bc1d6 --- /dev/null +++ b/docs/painless/painless-api-reference/UUID.asciidoc @@ -0,0 +1,18 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-UUID]]++UUID++:: +* ++[[painless-api-reference-UUID-fromString-1]]static <> link:{java8-javadoc}/java/util/UUID.html#fromString%2Djava.lang.String%2D[fromString](<>)++ (link:{java9-javadoc}/java/util/UUID.html#fromString%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-UUID-nameUUIDFromBytes-1]]static <> link:{java8-javadoc}/java/util/UUID.html#nameUUIDFromBytes%2Dbyte:A%2D[nameUUIDFromBytes](byte[])++ (link:{java9-javadoc}/java/util/UUID.html#nameUUIDFromBytes%2Dbyte:A%2D[java 9]) +* ++[[painless-api-reference-UUID-UUID-2]]link:{java8-javadoc}/java/util/UUID.html#UUID%2Dlong%2Dlong%2D[UUID](long, long)++ (link:{java9-javadoc}/java/util/UUID.html#UUID%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-UUID-clockSequence-0]]int link:{java8-javadoc}/java/util/UUID.html#clockSequence%2D%2D[clockSequence]()++ (link:{java9-javadoc}/java/util/UUID.html#clockSequence%2D%2D[java 9]) +* ++[[painless-api-reference-UUID-compareTo-1]]int link:{java8-javadoc}/java/util/UUID.html#compareTo%2Djava.util.UUID%2D[compareTo](<>)++ (link:{java9-javadoc}/java/util/UUID.html#compareTo%2Djava.util.UUID%2D[java 9]) +* ++[[painless-api-reference-UUID-getLeastSignificantBits-0]]long link:{java8-javadoc}/java/util/UUID.html#getLeastSignificantBits%2D%2D[getLeastSignificantBits]()++ (link:{java9-javadoc}/java/util/UUID.html#getLeastSignificantBits%2D%2D[java 9]) +* ++[[painless-api-reference-UUID-getMostSignificantBits-0]]long link:{java8-javadoc}/java/util/UUID.html#getMostSignificantBits%2D%2D[getMostSignificantBits]()++ (link:{java9-javadoc}/java/util/UUID.html#getMostSignificantBits%2D%2D[java 9]) +* ++[[painless-api-reference-UUID-node-0]]long link:{java8-javadoc}/java/util/UUID.html#node%2D%2D[node]()++ (link:{java9-javadoc}/java/util/UUID.html#node%2D%2D[java 9]) +* ++[[painless-api-reference-UUID-timestamp-0]]long link:{java8-javadoc}/java/util/UUID.html#timestamp%2D%2D[timestamp]()++ (link:{java9-javadoc}/java/util/UUID.html#timestamp%2D%2D[java 9]) +* ++[[painless-api-reference-UUID-variant-0]]int link:{java8-javadoc}/java/util/UUID.html#variant%2D%2D[variant]()++ (link:{java9-javadoc}/java/util/UUID.html#variant%2D%2D[java 9]) +* ++[[painless-api-reference-UUID-version-0]]int link:{java8-javadoc}/java/util/UUID.html#version%2D%2D[version]()++ (link:{java9-javadoc}/java/util/UUID.html#version%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/UnaryOperator.asciidoc b/docs/painless/painless-api-reference/UnaryOperator.asciidoc new file mode 100644 index 0000000000000..fb7c6ed70d25d --- /dev/null +++ b/docs/painless/painless-api-reference/UnaryOperator.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-UnaryOperator]]++UnaryOperator++:: +* ++[[painless-api-reference-UnaryOperator-identity-0]]static <> link:{java8-javadoc}/java/util/function/UnaryOperator.html#identity%2D%2D[identity]()++ (link:{java9-javadoc}/java/util/function/UnaryOperator.html#identity%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/UnknownFormatConversionException.asciidoc b/docs/painless/painless-api-reference/UnknownFormatConversionException.asciidoc new file mode 100644 index 0000000000000..95dd348f422c1 --- /dev/null +++ b/docs/painless/painless-api-reference/UnknownFormatConversionException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-UnknownFormatConversionException]]++UnknownFormatConversionException++:: +* ++[[painless-api-reference-UnknownFormatConversionException-UnknownFormatConversionException-1]]link:{java8-javadoc}/java/util/UnknownFormatConversionException.html#UnknownFormatConversionException%2Djava.lang.String%2D[UnknownFormatConversionException](<>)++ (link:{java9-javadoc}/java/util/UnknownFormatConversionException.html#UnknownFormatConversionException%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-UnknownFormatConversionException-getConversion-0]]<> link:{java8-javadoc}/java/util/UnknownFormatConversionException.html#getConversion%2D%2D[getConversion]()++ (link:{java9-javadoc}/java/util/UnknownFormatConversionException.html#getConversion%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/UnknownFormatFlagsException.asciidoc b/docs/painless/painless-api-reference/UnknownFormatFlagsException.asciidoc new file mode 100644 index 0000000000000..f14d986c05b38 --- /dev/null +++ b/docs/painless/painless-api-reference/UnknownFormatFlagsException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-UnknownFormatFlagsException]]++UnknownFormatFlagsException++:: +* ++[[painless-api-reference-UnknownFormatFlagsException-UnknownFormatFlagsException-1]]link:{java8-javadoc}/java/util/UnknownFormatFlagsException.html#UnknownFormatFlagsException%2Djava.lang.String%2D[UnknownFormatFlagsException](<>)++ (link:{java9-javadoc}/java/util/UnknownFormatFlagsException.html#UnknownFormatFlagsException%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-UnknownFormatFlagsException-getFlags-0]]<> link:{java8-javadoc}/java/util/UnknownFormatFlagsException.html#getFlags%2D%2D[getFlags]()++ (link:{java9-javadoc}/java/util/UnknownFormatFlagsException.html#getFlags%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/UnsupportedOperationException.asciidoc b/docs/painless/painless-api-reference/UnsupportedOperationException.asciidoc new file mode 100644 index 0000000000000..ad9cbdb1478bf --- /dev/null +++ b/docs/painless/painless-api-reference/UnsupportedOperationException.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-UnsupportedOperationException]]++UnsupportedOperationException++:: +* ++[[painless-api-reference-UnsupportedOperationException-UnsupportedOperationException-0]]link:{java8-javadoc}/java/lang/UnsupportedOperationException.html#UnsupportedOperationException%2D%2D[UnsupportedOperationException]()++ (link:{java9-javadoc}/java/lang/UnsupportedOperationException.html#UnsupportedOperationException%2D%2D[java 9]) +* ++[[painless-api-reference-UnsupportedOperationException-UnsupportedOperationException-1]]link:{java8-javadoc}/java/lang/UnsupportedOperationException.html#UnsupportedOperationException%2Djava.lang.String%2D[UnsupportedOperationException](<>)++ (link:{java9-javadoc}/java/lang/UnsupportedOperationException.html#UnsupportedOperationException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/UnsupportedTemporalTypeException.asciidoc b/docs/painless/painless-api-reference/UnsupportedTemporalTypeException.asciidoc new file mode 100644 index 0000000000000..96e4c1d840220 --- /dev/null +++ b/docs/painless/painless-api-reference/UnsupportedTemporalTypeException.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-UnsupportedTemporalTypeException]]++UnsupportedTemporalTypeException++:: +* ++[[painless-api-reference-UnsupportedTemporalTypeException-UnsupportedTemporalTypeException-1]]link:{java8-javadoc}/java/time/temporal/UnsupportedTemporalTypeException.html#UnsupportedTemporalTypeException%2Djava.lang.String%2D[UnsupportedTemporalTypeException](<>)++ (link:{java9-javadoc}/java/time/temporal/UnsupportedTemporalTypeException.html#UnsupportedTemporalTypeException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ValueRange.asciidoc b/docs/painless/painless-api-reference/ValueRange.asciidoc new file mode 100644 index 0000000000000..409d155f9546a --- /dev/null +++ b/docs/painless/painless-api-reference/ValueRange.asciidoc @@ -0,0 +1,20 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ValueRange]]++ValueRange++:: +* ++[[painless-api-reference-ValueRange-of-2]]static <> link:{java8-javadoc}/java/time/temporal/ValueRange.html#of%2Dlong%2Dlong%2D[of](long, long)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#of%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ValueRange-of-3]]static <> link:{java8-javadoc}/java/time/temporal/ValueRange.html#of%2Dlong%2Dlong%2Dlong%2D[of](long, long, long)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#of%2Dlong%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ValueRange-of-4]]static <> link:{java8-javadoc}/java/time/temporal/ValueRange.html#of%2Dlong%2Dlong%2Dlong%2Dlong%2D[of](long, long, long, long)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#of%2Dlong%2Dlong%2Dlong%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ValueRange-checkValidIntValue-2]]int link:{java8-javadoc}/java/time/temporal/ValueRange.html#checkValidIntValue%2Dlong%2Djava.time.temporal.TemporalField%2D[checkValidIntValue](long, <>)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#checkValidIntValue%2Dlong%2Djava.time.temporal.TemporalField%2D[java 9]) +* ++[[painless-api-reference-ValueRange-checkValidValue-2]]long link:{java8-javadoc}/java/time/temporal/ValueRange.html#checkValidValue%2Dlong%2Djava.time.temporal.TemporalField%2D[checkValidValue](long, <>)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#checkValidValue%2Dlong%2Djava.time.temporal.TemporalField%2D[java 9]) +* ++[[painless-api-reference-ValueRange-getLargestMinimum-0]]long link:{java8-javadoc}/java/time/temporal/ValueRange.html#getLargestMinimum%2D%2D[getLargestMinimum]()++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#getLargestMinimum%2D%2D[java 9]) +* ++[[painless-api-reference-ValueRange-getMaximum-0]]long link:{java8-javadoc}/java/time/temporal/ValueRange.html#getMaximum%2D%2D[getMaximum]()++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#getMaximum%2D%2D[java 9]) +* ++[[painless-api-reference-ValueRange-getMinimum-0]]long link:{java8-javadoc}/java/time/temporal/ValueRange.html#getMinimum%2D%2D[getMinimum]()++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#getMinimum%2D%2D[java 9]) +* ++[[painless-api-reference-ValueRange-getSmallestMaximum-0]]long link:{java8-javadoc}/java/time/temporal/ValueRange.html#getSmallestMaximum%2D%2D[getSmallestMaximum]()++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#getSmallestMaximum%2D%2D[java 9]) +* ++[[painless-api-reference-ValueRange-isFixed-0]]boolean link:{java8-javadoc}/java/time/temporal/ValueRange.html#isFixed%2D%2D[isFixed]()++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#isFixed%2D%2D[java 9]) +* ++[[painless-api-reference-ValueRange-isIntValue-0]]boolean link:{java8-javadoc}/java/time/temporal/ValueRange.html#isIntValue%2D%2D[isIntValue]()++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#isIntValue%2D%2D[java 9]) +* ++[[painless-api-reference-ValueRange-isValidIntValue-1]]boolean link:{java8-javadoc}/java/time/temporal/ValueRange.html#isValidIntValue%2Dlong%2D[isValidIntValue](long)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#isValidIntValue%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ValueRange-isValidValue-1]]boolean link:{java8-javadoc}/java/time/temporal/ValueRange.html#isValidValue%2Dlong%2D[isValidValue](long)++ (link:{java9-javadoc}/java/time/temporal/ValueRange.html#isValidValue%2Dlong%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Vector.asciidoc b/docs/painless/painless-api-reference/Vector.asciidoc new file mode 100644 index 0000000000000..b1d9ed88b6f9a --- /dev/null +++ b/docs/painless/painless-api-reference/Vector.asciidoc @@ -0,0 +1,22 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Vector]]++Vector++:: +* ++[[painless-api-reference-Vector-Vector-0]]link:{java8-javadoc}/java/util/Vector.html#Vector%2D%2D[Vector]()++ (link:{java9-javadoc}/java/util/Vector.html#Vector%2D%2D[java 9]) +* ++[[painless-api-reference-Vector-Vector-1]]link:{java8-javadoc}/java/util/Vector.html#Vector%2Djava.util.Collection%2D[Vector](<>)++ (link:{java9-javadoc}/java/util/Vector.html#Vector%2Djava.util.Collection%2D[java 9]) +* ++[[painless-api-reference-Vector-addElement-1]]void link:{java8-javadoc}/java/util/Vector.html#addElement%2Djava.lang.Object%2D[addElement](def)++ (link:{java9-javadoc}/java/util/Vector.html#addElement%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Vector-clone-0]]def link:{java8-javadoc}/java/util/Vector.html#clone%2D%2D[clone]()++ (link:{java9-javadoc}/java/util/Vector.html#clone%2D%2D[java 9]) +* ++[[painless-api-reference-Vector-copyInto-1]]void link:{java8-javadoc}/java/util/Vector.html#copyInto%2Djava.lang.Object:A%2D[copyInto](<>[])++ (link:{java9-javadoc}/java/util/Vector.html#copyInto%2Djava.lang.Object:A%2D[java 9]) +* ++[[painless-api-reference-Vector-elementAt-1]]def link:{java8-javadoc}/java/util/Vector.html#elementAt%2Dint%2D[elementAt](int)++ (link:{java9-javadoc}/java/util/Vector.html#elementAt%2Dint%2D[java 9]) +* ++[[painless-api-reference-Vector-elements-0]]<> link:{java8-javadoc}/java/util/Vector.html#elements%2D%2D[elements]()++ (link:{java9-javadoc}/java/util/Vector.html#elements%2D%2D[java 9]) +* ++[[painless-api-reference-Vector-firstElement-0]]def link:{java8-javadoc}/java/util/Vector.html#firstElement%2D%2D[firstElement]()++ (link:{java9-javadoc}/java/util/Vector.html#firstElement%2D%2D[java 9]) +* ++[[painless-api-reference-Vector-insertElementAt-2]]void link:{java8-javadoc}/java/util/Vector.html#insertElementAt%2Djava.lang.Object%2Dint%2D[insertElementAt](def, int)++ (link:{java9-javadoc}/java/util/Vector.html#insertElementAt%2Djava.lang.Object%2Dint%2D[java 9]) +* ++[[painless-api-reference-Vector-lastElement-0]]def link:{java8-javadoc}/java/util/Vector.html#lastElement%2D%2D[lastElement]()++ (link:{java9-javadoc}/java/util/Vector.html#lastElement%2D%2D[java 9]) +* ++[[painless-api-reference-Vector-lastIndexOf-2]]int link:{java8-javadoc}/java/util/Vector.html#lastIndexOf%2Djava.lang.Object%2Dint%2D[lastIndexOf](def, int)++ (link:{java9-javadoc}/java/util/Vector.html#lastIndexOf%2Djava.lang.Object%2Dint%2D[java 9]) +* ++[[painless-api-reference-Vector-removeAllElements-0]]void link:{java8-javadoc}/java/util/Vector.html#removeAllElements%2D%2D[removeAllElements]()++ (link:{java9-javadoc}/java/util/Vector.html#removeAllElements%2D%2D[java 9]) +* ++[[painless-api-reference-Vector-removeElement-1]]boolean link:{java8-javadoc}/java/util/Vector.html#removeElement%2Djava.lang.Object%2D[removeElement](def)++ (link:{java9-javadoc}/java/util/Vector.html#removeElement%2Djava.lang.Object%2D[java 9]) +* ++[[painless-api-reference-Vector-removeElementAt-1]]void link:{java8-javadoc}/java/util/Vector.html#removeElementAt%2Dint%2D[removeElementAt](int)++ (link:{java9-javadoc}/java/util/Vector.html#removeElementAt%2Dint%2D[java 9]) +* ++[[painless-api-reference-Vector-setElementAt-2]]void link:{java8-javadoc}/java/util/Vector.html#setElementAt%2Djava.lang.Object%2Dint%2D[setElementAt](def, int)++ (link:{java9-javadoc}/java/util/Vector.html#setElementAt%2Djava.lang.Object%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/WeekFields.asciidoc b/docs/painless/painless-api-reference/WeekFields.asciidoc new file mode 100644 index 0000000000000..330b2e98d2e7a --- /dev/null +++ b/docs/painless/painless-api-reference/WeekFields.asciidoc @@ -0,0 +1,19 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-WeekFields]]++WeekFields++:: +** [[painless-api-reference-WeekFields-ISO]]static <> link:{java8-javadoc}/java/time/temporal/WeekFields.html#ISO[ISO] (link:{java9-javadoc}/java/time/temporal/WeekFields.html#ISO[java 9]) +** [[painless-api-reference-WeekFields-SUNDAY_START]]static <> link:{java8-javadoc}/java/time/temporal/WeekFields.html#SUNDAY_START[SUNDAY_START] (link:{java9-javadoc}/java/time/temporal/WeekFields.html#SUNDAY_START[java 9]) +** [[painless-api-reference-WeekFields-WEEK_BASED_YEARS]]static <> link:{java8-javadoc}/java/time/temporal/WeekFields.html#WEEK_BASED_YEARS[WEEK_BASED_YEARS] (link:{java9-javadoc}/java/time/temporal/WeekFields.html#WEEK_BASED_YEARS[java 9]) +* ++[[painless-api-reference-WeekFields-of-1]]static <> link:{java8-javadoc}/java/time/temporal/WeekFields.html#of%2Djava.util.Locale%2D[of](<>)++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#of%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-WeekFields-of-2]]static <> link:{java8-javadoc}/java/time/temporal/WeekFields.html#of%2Djava.time.DayOfWeek%2Dint%2D[of](<>, int)++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#of%2Djava.time.DayOfWeek%2Dint%2D[java 9]) +* ++[[painless-api-reference-WeekFields-dayOfWeek-0]]<> link:{java8-javadoc}/java/time/temporal/WeekFields.html#dayOfWeek%2D%2D[dayOfWeek]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#dayOfWeek%2D%2D[java 9]) +* ++[[painless-api-reference-WeekFields-getFirstDayOfWeek-0]]<> link:{java8-javadoc}/java/time/temporal/WeekFields.html#getFirstDayOfWeek%2D%2D[getFirstDayOfWeek]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#getFirstDayOfWeek%2D%2D[java 9]) +* ++[[painless-api-reference-WeekFields-getMinimalDaysInFirstWeek-0]]int link:{java8-javadoc}/java/time/temporal/WeekFields.html#getMinimalDaysInFirstWeek%2D%2D[getMinimalDaysInFirstWeek]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#getMinimalDaysInFirstWeek%2D%2D[java 9]) +* ++[[painless-api-reference-WeekFields-weekBasedYear-0]]<> link:{java8-javadoc}/java/time/temporal/WeekFields.html#weekBasedYear%2D%2D[weekBasedYear]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#weekBasedYear%2D%2D[java 9]) +* ++[[painless-api-reference-WeekFields-weekOfMonth-0]]<> link:{java8-javadoc}/java/time/temporal/WeekFields.html#weekOfMonth%2D%2D[weekOfMonth]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#weekOfMonth%2D%2D[java 9]) +* ++[[painless-api-reference-WeekFields-weekOfWeekBasedYear-0]]<> link:{java8-javadoc}/java/time/temporal/WeekFields.html#weekOfWeekBasedYear%2D%2D[weekOfWeekBasedYear]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#weekOfWeekBasedYear%2D%2D[java 9]) +* ++[[painless-api-reference-WeekFields-weekOfYear-0]]<> link:{java8-javadoc}/java/time/temporal/WeekFields.html#weekOfYear%2D%2D[weekOfYear]()++ (link:{java9-javadoc}/java/time/temporal/WeekFields.html#weekOfYear%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/Year.asciidoc b/docs/painless/painless-api-reference/Year.asciidoc new file mode 100644 index 0000000000000..e3800991039d6 --- /dev/null +++ b/docs/painless/painless-api-reference/Year.asciidoc @@ -0,0 +1,32 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-Year]]++Year++:: +** [[painless-api-reference-Year-MAX_VALUE]]static int link:{java8-javadoc}/java/time/Year.html#MAX_VALUE[MAX_VALUE] (link:{java9-javadoc}/java/time/Year.html#MAX_VALUE[java 9]) +** [[painless-api-reference-Year-MIN_VALUE]]static int link:{java8-javadoc}/java/time/Year.html#MIN_VALUE[MIN_VALUE] (link:{java9-javadoc}/java/time/Year.html#MIN_VALUE[java 9]) +* ++[[painless-api-reference-Year-from-1]]static <> link:{java8-javadoc}/java/time/Year.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/Year.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-Year-isLeap-1]]static boolean link:{java8-javadoc}/java/time/Year.html#isLeap%2Dlong%2D[isLeap](long)++ (link:{java9-javadoc}/java/time/Year.html#isLeap%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Year-of-1]]static <> link:{java8-javadoc}/java/time/Year.html#of%2Dint%2D[of](int)++ (link:{java9-javadoc}/java/time/Year.html#of%2Dint%2D[java 9]) +* ++[[painless-api-reference-Year-parse-1]]static <> link:{java8-javadoc}/java/time/Year.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/Year.html#parse%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-Year-parse-2]]static <> link:{java8-javadoc}/java/time/Year.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/Year.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-Year-atDay-1]]<> link:{java8-javadoc}/java/time/Year.html#atDay%2Dint%2D[atDay](int)++ (link:{java9-javadoc}/java/time/Year.html#atDay%2Dint%2D[java 9]) +* ++[[painless-api-reference-Year-atMonth-1]]<> link:{java8-javadoc}/java/time/Year.html#atMonth%2Dint%2D[atMonth](int)++ (link:{java9-javadoc}/java/time/Year.html#atMonth%2Dint%2D[java 9]) +* ++[[painless-api-reference-Year-atMonthDay-1]]<> link:{java8-javadoc}/java/time/Year.html#atMonthDay%2Djava.time.MonthDay%2D[atMonthDay](<>)++ (link:{java9-javadoc}/java/time/Year.html#atMonthDay%2Djava.time.MonthDay%2D[java 9]) +* ++[[painless-api-reference-Year-compareTo-1]]int link:{java8-javadoc}/java/time/Year.html#compareTo%2Djava.time.Year%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/Year.html#compareTo%2Djava.time.Year%2D[java 9]) +* ++[[painless-api-reference-Year-format-1]]<> link:{java8-javadoc}/java/time/Year.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/Year.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-Year-getValue-0]]int link:{java8-javadoc}/java/time/Year.html#getValue%2D%2D[getValue]()++ (link:{java9-javadoc}/java/time/Year.html#getValue%2D%2D[java 9]) +* ++[[painless-api-reference-Year-isAfter-1]]boolean link:{java8-javadoc}/java/time/Year.html#isAfter%2Djava.time.Year%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/Year.html#isAfter%2Djava.time.Year%2D[java 9]) +* ++[[painless-api-reference-Year-isLeap-0]]boolean link:{java8-javadoc}/java/time/Year.html#isLeap%2D%2D[isLeap]()++ (link:{java9-javadoc}/java/time/Year.html#isLeap%2D%2D[java 9]) +* ++[[painless-api-reference-Year-isValidMonthDay-1]]boolean link:{java8-javadoc}/java/time/Year.html#isValidMonthDay%2Djava.time.MonthDay%2D[isValidMonthDay](<>)++ (link:{java9-javadoc}/java/time/Year.html#isValidMonthDay%2Djava.time.MonthDay%2D[java 9]) +* ++[[painless-api-reference-Year-length-0]]int link:{java8-javadoc}/java/time/Year.html#length%2D%2D[length]()++ (link:{java9-javadoc}/java/time/Year.html#length%2D%2D[java 9]) +* ++[[painless-api-reference-Year-minus-1]]<> link:{java8-javadoc}/java/time/Year.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/Year.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-Year-minus-2]]<> link:{java8-javadoc}/java/time/Year.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/Year.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-Year-minusYears-1]]<> link:{java8-javadoc}/java/time/Year.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/Year.html#minusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Year-plus-1]]<> link:{java8-javadoc}/java/time/Year.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/Year.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-Year-plus-2]]<> link:{java8-javadoc}/java/time/Year.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/Year.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-Year-plusYears-1]]<> link:{java8-javadoc}/java/time/Year.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/Year.html#plusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-Year-with-1]]<> link:{java8-javadoc}/java/time/Year.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/Year.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-Year-with-2]]<> link:{java8-javadoc}/java/time/Year.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/Year.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/YearMonth.asciidoc b/docs/painless/painless-api-reference/YearMonth.asciidoc new file mode 100644 index 0000000000000..5573b16c0640c --- /dev/null +++ b/docs/painless/painless-api-reference/YearMonth.asciidoc @@ -0,0 +1,36 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-YearMonth]]++YearMonth++:: +* ++[[painless-api-reference-YearMonth-from-1]]static <> link:{java8-javadoc}/java/time/YearMonth.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-YearMonth-of-2]]static <> link:{java8-javadoc}/java/time/YearMonth.html#of%2Dint%2Dint%2D[of](int, int)++ (link:{java9-javadoc}/java/time/YearMonth.html#of%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-YearMonth-parse-1]]static <> link:{java8-javadoc}/java/time/YearMonth.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#parse%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-YearMonth-parse-2]]static <> link:{java8-javadoc}/java/time/YearMonth.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/YearMonth.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-YearMonth-atDay-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#atDay%2Dint%2D[atDay](int)++ (link:{java9-javadoc}/java/time/YearMonth.html#atDay%2Dint%2D[java 9]) +* ++[[painless-api-reference-YearMonth-atEndOfMonth-0]]<> link:{java8-javadoc}/java/time/YearMonth.html#atEndOfMonth%2D%2D[atEndOfMonth]()++ (link:{java9-javadoc}/java/time/YearMonth.html#atEndOfMonth%2D%2D[java 9]) +* ++[[painless-api-reference-YearMonth-compareTo-1]]int link:{java8-javadoc}/java/time/YearMonth.html#compareTo%2Djava.time.YearMonth%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#compareTo%2Djava.time.YearMonth%2D[java 9]) +* ++[[painless-api-reference-YearMonth-format-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#format%2Djava.time.format.DateTimeFormatter%2D[format](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#format%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-YearMonth-getMonth-0]]<> link:{java8-javadoc}/java/time/YearMonth.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/YearMonth.html#getMonth%2D%2D[java 9]) +* ++[[painless-api-reference-YearMonth-getMonthValue-0]]int link:{java8-javadoc}/java/time/YearMonth.html#getMonthValue%2D%2D[getMonthValue]()++ (link:{java9-javadoc}/java/time/YearMonth.html#getMonthValue%2D%2D[java 9]) +* ++[[painless-api-reference-YearMonth-getYear-0]]int link:{java8-javadoc}/java/time/YearMonth.html#getYear%2D%2D[getYear]()++ (link:{java9-javadoc}/java/time/YearMonth.html#getYear%2D%2D[java 9]) +* ++[[painless-api-reference-YearMonth-isAfter-1]]boolean link:{java8-javadoc}/java/time/YearMonth.html#isAfter%2Djava.time.YearMonth%2D[isAfter](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#isAfter%2Djava.time.YearMonth%2D[java 9]) +* ++[[painless-api-reference-YearMonth-isBefore-1]]boolean link:{java8-javadoc}/java/time/YearMonth.html#isBefore%2Djava.time.YearMonth%2D[isBefore](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#isBefore%2Djava.time.YearMonth%2D[java 9]) +* ++[[painless-api-reference-YearMonth-isLeapYear-0]]boolean link:{java8-javadoc}/java/time/YearMonth.html#isLeapYear%2D%2D[isLeapYear]()++ (link:{java9-javadoc}/java/time/YearMonth.html#isLeapYear%2D%2D[java 9]) +* ++[[painless-api-reference-YearMonth-isValidDay-1]]boolean link:{java8-javadoc}/java/time/YearMonth.html#isValidDay%2Dint%2D[isValidDay](int)++ (link:{java9-javadoc}/java/time/YearMonth.html#isValidDay%2Dint%2D[java 9]) +* ++[[painless-api-reference-YearMonth-lengthOfMonth-0]]int link:{java8-javadoc}/java/time/YearMonth.html#lengthOfMonth%2D%2D[lengthOfMonth]()++ (link:{java9-javadoc}/java/time/YearMonth.html#lengthOfMonth%2D%2D[java 9]) +* ++[[painless-api-reference-YearMonth-lengthOfYear-0]]int link:{java8-javadoc}/java/time/YearMonth.html#lengthOfYear%2D%2D[lengthOfYear]()++ (link:{java9-javadoc}/java/time/YearMonth.html#lengthOfYear%2D%2D[java 9]) +* ++[[painless-api-reference-YearMonth-minus-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-YearMonth-minus-2]]<> link:{java8-javadoc}/java/time/YearMonth.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/YearMonth.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-YearMonth-minusMonths-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#minusMonths%2Dlong%2D[minusMonths](long)++ (link:{java9-javadoc}/java/time/YearMonth.html#minusMonths%2Dlong%2D[java 9]) +* ++[[painless-api-reference-YearMonth-minusYears-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/YearMonth.html#minusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-YearMonth-plus-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-YearMonth-plus-2]]<> link:{java8-javadoc}/java/time/YearMonth.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/YearMonth.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-YearMonth-plusMonths-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#plusMonths%2Dlong%2D[plusMonths](long)++ (link:{java9-javadoc}/java/time/YearMonth.html#plusMonths%2Dlong%2D[java 9]) +* ++[[painless-api-reference-YearMonth-plusYears-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/YearMonth.html#plusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-YearMonth-with-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/YearMonth.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-YearMonth-with-2]]<> link:{java8-javadoc}/java/time/YearMonth.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/YearMonth.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* ++[[painless-api-reference-YearMonth-withMonth-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#withMonth%2Dint%2D[withMonth](int)++ (link:{java9-javadoc}/java/time/YearMonth.html#withMonth%2Dint%2D[java 9]) +* ++[[painless-api-reference-YearMonth-withYear-1]]<> link:{java8-javadoc}/java/time/YearMonth.html#withYear%2Dint%2D[withYear](int)++ (link:{java9-javadoc}/java/time/YearMonth.html#withYear%2Dint%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ZoneId.asciidoc b/docs/painless/painless-api-reference/ZoneId.asciidoc new file mode 100644 index 0000000000000..fe31c9df00bb1 --- /dev/null +++ b/docs/painless/painless-api-reference/ZoneId.asciidoc @@ -0,0 +1,18 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ZoneId]]++ZoneId++:: +** [[painless-api-reference-ZoneId-SHORT_IDS]]static <> link:{java8-javadoc}/java/time/ZoneId.html#SHORT_IDS[SHORT_IDS] (link:{java9-javadoc}/java/time/ZoneId.html#SHORT_IDS[java 9]) +* ++[[painless-api-reference-ZoneId-from-1]]static <> link:{java8-javadoc}/java/time/ZoneId.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/ZoneId.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-ZoneId-getAvailableZoneIds-0]]static <> link:{java8-javadoc}/java/time/ZoneId.html#getAvailableZoneIds%2D%2D[getAvailableZoneIds]()++ (link:{java9-javadoc}/java/time/ZoneId.html#getAvailableZoneIds%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneId-of-1]]static <> link:{java8-javadoc}/java/time/ZoneId.html#of%2Djava.lang.String%2D[of](<>)++ (link:{java9-javadoc}/java/time/ZoneId.html#of%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-ZoneId-of-2]]static <> link:{java8-javadoc}/java/time/ZoneId.html#of%2Djava.lang.String%2Djava.util.Map%2D[of](<>, <>)++ (link:{java9-javadoc}/java/time/ZoneId.html#of%2Djava.lang.String%2Djava.util.Map%2D[java 9]) +* ++[[painless-api-reference-ZoneId-ofOffset-2]]static <> link:{java8-javadoc}/java/time/ZoneId.html#ofOffset%2Djava.lang.String%2Djava.time.ZoneOffset%2D[ofOffset](<>, <>)++ (link:{java9-javadoc}/java/time/ZoneId.html#ofOffset%2Djava.lang.String%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-ZoneId-systemDefault-0]]static <> link:{java8-javadoc}/java/time/ZoneId.html#systemDefault%2D%2D[systemDefault]()++ (link:{java9-javadoc}/java/time/ZoneId.html#systemDefault%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneId-getDisplayName-2]]<> link:{java8-javadoc}/java/time/ZoneId.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[getDisplayName](<>, <>)++ (link:{java9-javadoc}/java/time/ZoneId.html#getDisplayName%2Djava.time.format.TextStyle%2Djava.util.Locale%2D[java 9]) +* ++[[painless-api-reference-ZoneId-getId-0]]<> link:{java8-javadoc}/java/time/ZoneId.html#getId%2D%2D[getId]()++ (link:{java9-javadoc}/java/time/ZoneId.html#getId%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneId-getRules-0]]<> link:{java8-javadoc}/java/time/ZoneId.html#getRules%2D%2D[getRules]()++ (link:{java9-javadoc}/java/time/ZoneId.html#getRules%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneId-normalized-0]]<> link:{java8-javadoc}/java/time/ZoneId.html#normalized%2D%2D[normalized]()++ (link:{java9-javadoc}/java/time/ZoneId.html#normalized%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ZoneOffset.asciidoc b/docs/painless/painless-api-reference/ZoneOffset.asciidoc new file mode 100644 index 0000000000000..84e8530f5938b --- /dev/null +++ b/docs/painless/painless-api-reference/ZoneOffset.asciidoc @@ -0,0 +1,17 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ZoneOffset]]++ZoneOffset++:: +** [[painless-api-reference-ZoneOffset-MAX]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#MAX[MAX] (link:{java9-javadoc}/java/time/ZoneOffset.html#MAX[java 9]) +** [[painless-api-reference-ZoneOffset-MIN]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#MIN[MIN] (link:{java9-javadoc}/java/time/ZoneOffset.html#MIN[java 9]) +** [[painless-api-reference-ZoneOffset-UTC]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#UTC[UTC] (link:{java9-javadoc}/java/time/ZoneOffset.html#UTC[java 9]) +* ++[[painless-api-reference-ZoneOffset-from-1]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/ZoneOffset.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-ZoneOffset-of-1]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#of%2Djava.lang.String%2D[of](<>)++ (link:{java9-javadoc}/java/time/ZoneOffset.html#of%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-ZoneOffset-ofHours-1]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#ofHours%2Dint%2D[ofHours](int)++ (link:{java9-javadoc}/java/time/ZoneOffset.html#ofHours%2Dint%2D[java 9]) +* ++[[painless-api-reference-ZoneOffset-ofHoursMinutes-2]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#ofHoursMinutes%2Dint%2Dint%2D[ofHoursMinutes](int, int)++ (link:{java9-javadoc}/java/time/ZoneOffset.html#ofHoursMinutes%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-ZoneOffset-ofHoursMinutesSeconds-3]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#ofHoursMinutesSeconds%2Dint%2Dint%2Dint%2D[ofHoursMinutesSeconds](int, int, int)++ (link:{java9-javadoc}/java/time/ZoneOffset.html#ofHoursMinutesSeconds%2Dint%2Dint%2Dint%2D[java 9]) +* ++[[painless-api-reference-ZoneOffset-ofTotalSeconds-1]]static <> link:{java8-javadoc}/java/time/ZoneOffset.html#ofTotalSeconds%2Dint%2D[ofTotalSeconds](int)++ (link:{java9-javadoc}/java/time/ZoneOffset.html#ofTotalSeconds%2Dint%2D[java 9]) +* ++[[painless-api-reference-ZoneOffset-getTotalSeconds-0]]int link:{java8-javadoc}/java/time/ZoneOffset.html#getTotalSeconds%2D%2D[getTotalSeconds]()++ (link:{java9-javadoc}/java/time/ZoneOffset.html#getTotalSeconds%2D%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ZoneOffsetTransition.asciidoc b/docs/painless/painless-api-reference/ZoneOffsetTransition.asciidoc new file mode 100644 index 0000000000000..43be4f56ee4b3 --- /dev/null +++ b/docs/painless/painless-api-reference/ZoneOffsetTransition.asciidoc @@ -0,0 +1,19 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ZoneOffsetTransition]]++ZoneOffsetTransition++:: +* ++[[painless-api-reference-ZoneOffsetTransition-of-3]]static <> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#of%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2D[of](<>, <>, <>)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#of%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransition-compareTo-1]]int link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#compareTo%2Djava.time.zone.ZoneOffsetTransition%2D[compareTo](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#compareTo%2Djava.time.zone.ZoneOffsetTransition%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransition-getDateTimeAfter-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#getDateTimeAfter%2D%2D[getDateTimeAfter]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#getDateTimeAfter%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransition-getDateTimeBefore-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#getDateTimeBefore%2D%2D[getDateTimeBefore]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#getDateTimeBefore%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransition-getDuration-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#getDuration%2D%2D[getDuration]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#getDuration%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransition-getInstant-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#getInstant%2D%2D[getInstant]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#getInstant%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransition-getOffsetAfter-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#getOffsetAfter%2D%2D[getOffsetAfter]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#getOffsetAfter%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransition-getOffsetBefore-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#getOffsetBefore%2D%2D[getOffsetBefore]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#getOffsetBefore%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransition-isGap-0]]boolean link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#isGap%2D%2D[isGap]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#isGap%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransition-isOverlap-0]]boolean link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#isOverlap%2D%2D[isOverlap]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#isOverlap%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransition-isValidOffset-1]]boolean link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#isValidOffset%2Djava.time.ZoneOffset%2D[isValidOffset](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#isValidOffset%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransition-toEpochSecond-0]]long link:{java8-javadoc}/java/time/zone/ZoneOffsetTransition.html#toEpochSecond%2D%2D[toEpochSecond]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransition.html#toEpochSecond%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ZoneOffsetTransitionRule.TimeDefinition.asciidoc b/docs/painless/painless-api-reference/ZoneOffsetTransitionRule.TimeDefinition.asciidoc new file mode 100644 index 0000000000000..45fb25e0f6e73 --- /dev/null +++ b/docs/painless/painless-api-reference/ZoneOffsetTransitionRule.TimeDefinition.asciidoc @@ -0,0 +1,13 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition]]++ZoneOffsetTransitionRule.TimeDefinition++:: +** [[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition-STANDARD]]static <> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#STANDARD[STANDARD] (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#STANDARD[java 9]) +** [[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition-UTC]]static <> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#UTC[UTC] (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#UTC[java 9]) +** [[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition-WALL]]static <> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#WALL[WALL] (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#WALL[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition-valueOf-1]]static <> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#valueOf%2Djava.lang.String%2D[valueOf](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#valueOf%2Djava.lang.String%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition-values-0]]static <>[] link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#values%2D%2D[values]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#values%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransitionRule-TimeDefinition-createDateTime-3]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#createDateTime%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2D[createDateTime](<>, <>, <>)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule$TimeDefinition.html#createDateTime%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ZoneOffsetTransitionRule.asciidoc b/docs/painless/painless-api-reference/ZoneOffsetTransitionRule.asciidoc new file mode 100644 index 0000000000000..8e84bff01722b --- /dev/null +++ b/docs/painless/painless-api-reference/ZoneOffsetTransitionRule.asciidoc @@ -0,0 +1,18 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ZoneOffsetTransitionRule]]++ZoneOffsetTransitionRule++:: +* ++[[painless-api-reference-ZoneOffsetTransitionRule-of-9]]static <> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#of%2Djava.time.Month%2Dint%2Djava.time.DayOfWeek%2Djava.time.LocalTime%2Dboolean%2Djava.time.zone.ZoneOffsetTransitionRule$TimeDefinition%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2D[of](<>, int, <>, <>, boolean, <>, <>, <>, <>)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#of%2Djava.time.Month%2Dint%2Djava.time.DayOfWeek%2Djava.time.LocalTime%2Dboolean%2Djava.time.zone.ZoneOffsetTransitionRule$TimeDefinition%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransitionRule-createTransition-1]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#createTransition%2Dint%2D[createTransition](int)++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#createTransition%2Dint%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransitionRule-getDayOfMonthIndicator-0]]int link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getDayOfMonthIndicator%2D%2D[getDayOfMonthIndicator]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getDayOfMonthIndicator%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransitionRule-getDayOfWeek-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getDayOfWeek%2D%2D[getDayOfWeek]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getDayOfWeek%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransitionRule-getLocalTime-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getLocalTime%2D%2D[getLocalTime]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getLocalTime%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransitionRule-getMonth-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getMonth%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransitionRule-getOffsetAfter-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getOffsetAfter%2D%2D[getOffsetAfter]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getOffsetAfter%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransitionRule-getOffsetBefore-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getOffsetBefore%2D%2D[getOffsetBefore]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getOffsetBefore%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransitionRule-getStandardOffset-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getStandardOffset%2D%2D[getStandardOffset]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getStandardOffset%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransitionRule-getTimeDefinition-0]]<> link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getTimeDefinition%2D%2D[getTimeDefinition]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#getTimeDefinition%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneOffsetTransitionRule-isMidnightEndOfDay-0]]boolean link:{java8-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#isMidnightEndOfDay%2D%2D[isMidnightEndOfDay]()++ (link:{java9-javadoc}/java/time/zone/ZoneOffsetTransitionRule.html#isMidnightEndOfDay%2D%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ZoneRules.asciidoc b/docs/painless/painless-api-reference/ZoneRules.asciidoc new file mode 100644 index 0000000000000..6c3104a7e02ab --- /dev/null +++ b/docs/painless/painless-api-reference/ZoneRules.asciidoc @@ -0,0 +1,21 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ZoneRules]]++ZoneRules++:: +* ++[[painless-api-reference-ZoneRules-of-1]]static <> link:{java8-javadoc}/java/time/zone/ZoneRules.html#of%2Djava.time.ZoneOffset%2D[of](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#of%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-ZoneRules-of-5]]static <> link:{java8-javadoc}/java/time/zone/ZoneRules.html#of%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2Djava.util.List%2Djava.util.List%2Djava.util.List%2D[of](<>, <>, <>, <>, <>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#of%2Djava.time.ZoneOffset%2Djava.time.ZoneOffset%2Djava.util.List%2Djava.util.List%2Djava.util.List%2D[java 9]) +* ++[[painless-api-reference-ZoneRules-getDaylightSavings-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getDaylightSavings%2Djava.time.Instant%2D[getDaylightSavings](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getDaylightSavings%2Djava.time.Instant%2D[java 9]) +* ++[[painless-api-reference-ZoneRules-getOffset-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getOffset%2Djava.time.Instant%2D[getOffset](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getOffset%2Djava.time.Instant%2D[java 9]) +* ++[[painless-api-reference-ZoneRules-getStandardOffset-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getStandardOffset%2Djava.time.Instant%2D[getStandardOffset](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getStandardOffset%2Djava.time.Instant%2D[java 9]) +* ++[[painless-api-reference-ZoneRules-getTransition-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getTransition%2Djava.time.LocalDateTime%2D[getTransition](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getTransition%2Djava.time.LocalDateTime%2D[java 9]) +* ++[[painless-api-reference-ZoneRules-getTransitionRules-0]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getTransitionRules%2D%2D[getTransitionRules]()++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getTransitionRules%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneRules-getTransitions-0]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getTransitions%2D%2D[getTransitions]()++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getTransitions%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneRules-getValidOffsets-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#getValidOffsets%2Djava.time.LocalDateTime%2D[getValidOffsets](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#getValidOffsets%2Djava.time.LocalDateTime%2D[java 9]) +* ++[[painless-api-reference-ZoneRules-isDaylightSavings-1]]boolean link:{java8-javadoc}/java/time/zone/ZoneRules.html#isDaylightSavings%2Djava.time.Instant%2D[isDaylightSavings](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#isDaylightSavings%2Djava.time.Instant%2D[java 9]) +* ++[[painless-api-reference-ZoneRules-isFixedOffset-0]]boolean link:{java8-javadoc}/java/time/zone/ZoneRules.html#isFixedOffset%2D%2D[isFixedOffset]()++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#isFixedOffset%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneRules-isValidOffset-2]]boolean link:{java8-javadoc}/java/time/zone/ZoneRules.html#isValidOffset%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2D[isValidOffset](<>, <>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#isValidOffset%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-ZoneRules-nextTransition-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#nextTransition%2Djava.time.Instant%2D[nextTransition](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#nextTransition%2Djava.time.Instant%2D[java 9]) +* ++[[painless-api-reference-ZoneRules-previousTransition-1]]<> link:{java8-javadoc}/java/time/zone/ZoneRules.html#previousTransition%2Djava.time.Instant%2D[previousTransition](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRules.html#previousTransition%2Djava.time.Instant%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ZoneRulesException.asciidoc b/docs/painless/painless-api-reference/ZoneRulesException.asciidoc new file mode 100644 index 0000000000000..8bb3ef0cc98b0 --- /dev/null +++ b/docs/painless/painless-api-reference/ZoneRulesException.asciidoc @@ -0,0 +1,8 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ZoneRulesException]]++ZoneRulesException++:: +* ++[[painless-api-reference-ZoneRulesException-ZoneRulesException-1]]link:{java8-javadoc}/java/time/zone/ZoneRulesException.html#ZoneRulesException%2Djava.lang.String%2D[ZoneRulesException](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRulesException.html#ZoneRulesException%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/ZoneRulesProvider.asciidoc b/docs/painless/painless-api-reference/ZoneRulesProvider.asciidoc new file mode 100644 index 0000000000000..be33f691f370e --- /dev/null +++ b/docs/painless/painless-api-reference/ZoneRulesProvider.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ZoneRulesProvider]]++ZoneRulesProvider++:: +* ++[[painless-api-reference-ZoneRulesProvider-getAvailableZoneIds-0]]static <> link:{java8-javadoc}/java/time/zone/ZoneRulesProvider.html#getAvailableZoneIds%2D%2D[getAvailableZoneIds]()++ (link:{java9-javadoc}/java/time/zone/ZoneRulesProvider.html#getAvailableZoneIds%2D%2D[java 9]) +* ++[[painless-api-reference-ZoneRulesProvider-getRules-2]]static <> link:{java8-javadoc}/java/time/zone/ZoneRulesProvider.html#getRules%2Djava.lang.String%2Dboolean%2D[getRules](<>, boolean)++ (link:{java9-javadoc}/java/time/zone/ZoneRulesProvider.html#getRules%2Djava.lang.String%2Dboolean%2D[java 9]) +* ++[[painless-api-reference-ZoneRulesProvider-getVersions-1]]static <> link:{java8-javadoc}/java/time/zone/ZoneRulesProvider.html#getVersions%2Djava.lang.String%2D[getVersions](<>)++ (link:{java9-javadoc}/java/time/zone/ZoneRulesProvider.html#getVersions%2Djava.lang.String%2D[java 9]) +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/ZonedDateTime.asciidoc b/docs/painless/painless-api-reference/ZonedDateTime.asciidoc new file mode 100644 index 0000000000000..e8ba7501c6c20 --- /dev/null +++ b/docs/painless/painless-api-reference/ZonedDateTime.asciidoc @@ -0,0 +1,66 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-ZonedDateTime]]++ZonedDateTime++:: +* ++[[painless-api-reference-ZonedDateTime-from-1]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[from](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#from%2Djava.time.temporal.TemporalAccessor%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-of-2]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#of%2Djava.time.LocalDateTime%2Djava.time.ZoneId%2D[of](<>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#of%2Djava.time.LocalDateTime%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-of-3]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#of%2Djava.time.LocalDate%2Djava.time.LocalTime%2Djava.time.ZoneId%2D[of](<>, <>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#of%2Djava.time.LocalDate%2Djava.time.LocalTime%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-of-8]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Djava.time.ZoneId%2D[of](int, int, int, int, int, int, int, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#of%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Dint%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-ofInstant-2]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[ofInstant](<>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#ofInstant%2Djava.time.Instant%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-ofInstant-3]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#ofInstant%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneId%2D[ofInstant](<>, <>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#ofInstant%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-ofLocal-3]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#ofLocal%2Djava.time.LocalDateTime%2Djava.time.ZoneId%2Djava.time.ZoneOffset%2D[ofLocal](<>, <>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#ofLocal%2Djava.time.LocalDateTime%2Djava.time.ZoneId%2Djava.time.ZoneOffset%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-ofStrict-3]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#ofStrict%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneId%2D[ofStrict](<>, <>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#ofStrict%2Djava.time.LocalDateTime%2Djava.time.ZoneOffset%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-parse-1]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#parse%2Djava.lang.CharSequence%2D[parse](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#parse%2Djava.lang.CharSequence%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-parse-2]]static <> link:{java8-javadoc}/java/time/ZonedDateTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[parse](<>, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#parse%2Djava.lang.CharSequence%2Djava.time.format.DateTimeFormatter%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-getDayOfMonth-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getDayOfMonth%2D%2D[getDayOfMonth]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getDayOfMonth%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-getDayOfWeek-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#getDayOfWeek%2D%2D[getDayOfWeek]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getDayOfWeek%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-getDayOfYear-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getDayOfYear%2D%2D[getDayOfYear]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getDayOfYear%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-getHour-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getHour%2D%2D[getHour]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getHour%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-getMinute-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getMinute%2D%2D[getMinute]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getMinute%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-getMonth-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#getMonth%2D%2D[getMonth]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getMonth%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-getMonthValue-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getMonthValue%2D%2D[getMonthValue]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getMonthValue%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-getNano-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getNano%2D%2D[getNano]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getNano%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-getSecond-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getSecond%2D%2D[getSecond]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getSecond%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-getYear-0]]int link:{java8-javadoc}/java/time/ZonedDateTime.html#getYear%2D%2D[getYear]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#getYear%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-minus-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[minus](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-minus-2]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[minus](long, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-minusDays-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusDays%2Dlong%2D[minusDays](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusDays%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-minusHours-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusHours%2Dlong%2D[minusHours](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusHours%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-minusMinutes-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusMinutes%2Dlong%2D[minusMinutes](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusMinutes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-minusMonths-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusMonths%2Dlong%2D[minusMonths](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusMonths%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-minusNanos-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusNanos%2Dlong%2D[minusNanos](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-minusSeconds-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusSeconds%2Dlong%2D[minusSeconds](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-minusWeeks-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusWeeks%2Dlong%2D[minusWeeks](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusWeeks%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-minusYears-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#minusYears%2Dlong%2D[minusYears](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#minusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-plus-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[plus](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plus%2Djava.time.temporal.TemporalAmount%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-plus-2]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[plus](long, <>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plus%2Dlong%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-plusDays-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusDays%2Dlong%2D[plusDays](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusDays%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-plusHours-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusHours%2Dlong%2D[plusHours](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusHours%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-plusMinutes-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusMinutes%2Dlong%2D[plusMinutes](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusMinutes%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-plusMonths-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusMonths%2Dlong%2D[plusMonths](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusMonths%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-plusNanos-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusNanos%2Dlong%2D[plusNanos](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusNanos%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-plusSeconds-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusSeconds%2Dlong%2D[plusSeconds](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusSeconds%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-plusWeeks-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusWeeks%2Dlong%2D[plusWeeks](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusWeeks%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-plusYears-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#plusYears%2Dlong%2D[plusYears](long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#plusYears%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-toLocalDate-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#toLocalDate%2D%2D[toLocalDate]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#toLocalDate%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-toLocalDateTime-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#toLocalDateTime%2D%2D[toLocalDateTime]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#toLocalDateTime%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-toOffsetDateTime-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#toOffsetDateTime%2D%2D[toOffsetDateTime]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#toOffsetDateTime%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-truncatedTo-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[truncatedTo](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#truncatedTo%2Djava.time.temporal.TemporalUnit%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-with-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[with](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#with%2Djava.time.temporal.TemporalAdjuster%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-with-2]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[with](<>, long)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#with%2Djava.time.temporal.TemporalField%2Dlong%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-withDayOfMonth-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withDayOfMonth%2Dint%2D[withDayOfMonth](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withDayOfMonth%2Dint%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-withDayOfYear-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withDayOfYear%2Dint%2D[withDayOfYear](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withDayOfYear%2Dint%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-withEarlierOffsetAtOverlap-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withEarlierOffsetAtOverlap%2D%2D[withEarlierOffsetAtOverlap]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withEarlierOffsetAtOverlap%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-withFixedOffsetZone-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withFixedOffsetZone%2D%2D[withFixedOffsetZone]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withFixedOffsetZone%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-withHour-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withHour%2Dint%2D[withHour](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withHour%2Dint%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-withLaterOffsetAtOverlap-0]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withLaterOffsetAtOverlap%2D%2D[withLaterOffsetAtOverlap]()++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withLaterOffsetAtOverlap%2D%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-withMinute-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withMinute%2Dint%2D[withMinute](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withMinute%2Dint%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-withMonth-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withMonth%2Dint%2D[withMonth](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withMonth%2Dint%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-withNano-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withNano%2Dint%2D[withNano](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withNano%2Dint%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-withSecond-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withSecond%2Dint%2D[withSecond](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withSecond%2Dint%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-withYear-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withYear%2Dint%2D[withYear](int)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withYear%2Dint%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-withZoneSameInstant-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withZoneSameInstant%2Djava.time.ZoneId%2D[withZoneSameInstant](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withZoneSameInstant%2Djava.time.ZoneId%2D[java 9]) +* ++[[painless-api-reference-ZonedDateTime-withZoneSameLocal-1]]<> link:{java8-javadoc}/java/time/ZonedDateTime.html#withZoneSameLocal%2Djava.time.ZoneId%2D[withZoneSameLocal](<>)++ (link:{java9-javadoc}/java/time/ZonedDateTime.html#withZoneSameLocal%2Djava.time.ZoneId%2D[java 9]) +* Inherits methods from ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/index.asciidoc b/docs/painless/painless-api-reference/index.asciidoc new file mode 100644 index 0000000000000..51e6271548410 --- /dev/null +++ b/docs/painless/painless-api-reference/index.asciidoc @@ -0,0 +1,338 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +include::AbstractChronology.asciidoc[] +include::AbstractCollection.asciidoc[] +include::AbstractList.asciidoc[] +include::AbstractMap.asciidoc[] +include::AbstractMap.SimpleEntry.asciidoc[] +include::AbstractMap.SimpleImmutableEntry.asciidoc[] +include::AbstractQueue.asciidoc[] +include::AbstractSequentialList.asciidoc[] +include::AbstractSet.asciidoc[] +include::Annotation.asciidoc[] +include::Appendable.asciidoc[] +include::ArithmeticException.asciidoc[] +include::ArrayDeque.asciidoc[] +include::ArrayIndexOutOfBoundsException.asciidoc[] +include::ArrayList.asciidoc[] +include::ArrayStoreException.asciidoc[] +include::Arrays.asciidoc[] +include::AttributedCharacterIterator.asciidoc[] +include::AttributedCharacterIterator.Attribute.asciidoc[] +include::AttributedString.asciidoc[] +include::Base64.asciidoc[] +include::Base64.Decoder.asciidoc[] +include::Base64.Encoder.asciidoc[] +include::BaseStream.asciidoc[] +include::BiConsumer.asciidoc[] +include::BiFunction.asciidoc[] +include::BiPredicate.asciidoc[] +include::Bidi.asciidoc[] +include::BigDecimal.asciidoc[] +include::BigInteger.asciidoc[] +include::BinaryOperator.asciidoc[] +include::BitSet.asciidoc[] +include::Boolean.asciidoc[] +include::BooleanSupplier.asciidoc[] +include::BreakIterator.asciidoc[] +include::Byte.asciidoc[] +include::BytesRef.asciidoc[] +include::Calendar.asciidoc[] +include::Calendar.Builder.asciidoc[] +include::CharSequence.asciidoc[] +include::Character.asciidoc[] +include::Character.Subset.asciidoc[] +include::Character.UnicodeBlock.asciidoc[] +include::Character.UnicodeScript.asciidoc[] +include::CharacterIterator.asciidoc[] +include::ChoiceFormat.asciidoc[] +include::ChronoField.asciidoc[] +include::ChronoLocalDate.asciidoc[] +include::ChronoLocalDateTime.asciidoc[] +include::ChronoPeriod.asciidoc[] +include::ChronoUnit.asciidoc[] +include::ChronoZonedDateTime.asciidoc[] +include::Chronology.asciidoc[] +include::ClassCastException.asciidoc[] +include::ClassNotFoundException.asciidoc[] +include::Clock.asciidoc[] +include::CloneNotSupportedException.asciidoc[] +include::CollationElementIterator.asciidoc[] +include::CollationKey.asciidoc[] +include::Collator.asciidoc[] +include::Collection.asciidoc[] +include::Collections.asciidoc[] +include::Collector.asciidoc[] +include::Collector.Characteristics.asciidoc[] +include::Collectors.asciidoc[] +include::Comparable.asciidoc[] +include::Comparator.asciidoc[] +include::ConcurrentModificationException.asciidoc[] +include::Consumer.asciidoc[] +include::Currency.asciidoc[] +include::Date.asciidoc[] +include::DateFormat.asciidoc[] +include::DateFormat.Field.asciidoc[] +include::DateFormatSymbols.asciidoc[] +include::DateTimeException.asciidoc[] +include::DateTimeFormatter.asciidoc[] +include::DateTimeFormatterBuilder.asciidoc[] +include::DateTimeParseException.asciidoc[] +include::DayOfWeek.asciidoc[] +include::Debug.asciidoc[] +include::DecimalFormat.asciidoc[] +include::DecimalFormatSymbols.asciidoc[] +include::DecimalStyle.asciidoc[] +include::Deque.asciidoc[] +include::Dictionary.asciidoc[] +include::Double.asciidoc[] +include::DoubleBinaryOperator.asciidoc[] +include::DoubleConsumer.asciidoc[] +include::DoubleFunction.asciidoc[] +include::DoublePredicate.asciidoc[] +include::DoubleStream.asciidoc[] +include::DoubleStream.Builder.asciidoc[] +include::DoubleSummaryStatistics.asciidoc[] +include::DoubleSupplier.asciidoc[] +include::DoubleToIntFunction.asciidoc[] +include::DoubleToLongFunction.asciidoc[] +include::DoubleUnaryOperator.asciidoc[] +include::DuplicateFormatFlagsException.asciidoc[] +include::Duration.asciidoc[] +include::EmptyStackException.asciidoc[] +include::Enum.asciidoc[] +include::EnumConstantNotPresentException.asciidoc[] +include::Enumeration.asciidoc[] +include::Era.asciidoc[] +include::EventListener.asciidoc[] +include::EventListenerProxy.asciidoc[] +include::EventObject.asciidoc[] +include::Exception.asciidoc[] +include::FieldPosition.asciidoc[] +include::Float.asciidoc[] +include::Format.asciidoc[] +include::Format.Field.asciidoc[] +include::FormatFlagsConversionMismatchException.asciidoc[] +include::FormatStyle.asciidoc[] +include::Formattable.asciidoc[] +include::FormattableFlags.asciidoc[] +include::Formatter.asciidoc[] +include::Formatter.BigDecimalLayoutForm.asciidoc[] +include::FormatterClosedException.asciidoc[] +include::Function.asciidoc[] +include::GregorianCalendar.asciidoc[] +include::HashMap.asciidoc[] +include::HashSet.asciidoc[] +include::Hashtable.asciidoc[] +include::HijrahChronology.asciidoc[] +include::HijrahDate.asciidoc[] +include::HijrahEra.asciidoc[] +include::IdentityHashMap.asciidoc[] +include::IllegalAccessException.asciidoc[] +include::IllegalArgumentException.asciidoc[] +include::IllegalFormatCodePointException.asciidoc[] +include::IllegalFormatConversionException.asciidoc[] +include::IllegalFormatException.asciidoc[] +include::IllegalFormatFlagsException.asciidoc[] +include::IllegalFormatPrecisionException.asciidoc[] +include::IllegalFormatWidthException.asciidoc[] +include::IllegalMonitorStateException.asciidoc[] +include::IllegalStateException.asciidoc[] +include::IllegalThreadStateException.asciidoc[] +include::IllformedLocaleException.asciidoc[] +include::IndexOutOfBoundsException.asciidoc[] +include::InputMismatchException.asciidoc[] +include::Instant.asciidoc[] +include::InstantiationException.asciidoc[] +include::IntBinaryOperator.asciidoc[] +include::IntConsumer.asciidoc[] +include::IntFunction.asciidoc[] +include::IntPredicate.asciidoc[] +include::IntStream.asciidoc[] +include::IntStream.Builder.asciidoc[] +include::IntSummaryStatistics.asciidoc[] +include::IntSupplier.asciidoc[] +include::IntToDoubleFunction.asciidoc[] +include::IntToLongFunction.asciidoc[] +include::IntUnaryOperator.asciidoc[] +include::Integer.asciidoc[] +include::InterruptedException.asciidoc[] +include::IsoChronology.asciidoc[] +include::IsoEra.asciidoc[] +include::IsoFields.asciidoc[] +include::Iterable.asciidoc[] +include::Iterator.asciidoc[] +include::JapaneseChronology.asciidoc[] +include::JapaneseDate.asciidoc[] +include::JapaneseEra.asciidoc[] +include::JulianFields.asciidoc[] +include::LinkedHashMap.asciidoc[] +include::LinkedHashSet.asciidoc[] +include::LinkedList.asciidoc[] +include::List.asciidoc[] +include::ListIterator.asciidoc[] +include::LocalDate.asciidoc[] +include::LocalDateTime.asciidoc[] +include::LocalTime.asciidoc[] +include::Locale.asciidoc[] +include::Locale.Builder.asciidoc[] +include::Locale.Category.asciidoc[] +include::Locale.FilteringMode.asciidoc[] +include::Locale.LanguageRange.asciidoc[] +include::Long.asciidoc[] +include::LongBinaryOperator.asciidoc[] +include::LongConsumer.asciidoc[] +include::LongFunction.asciidoc[] +include::LongPredicate.asciidoc[] +include::LongStream.asciidoc[] +include::LongStream.Builder.asciidoc[] +include::LongSummaryStatistics.asciidoc[] +include::LongSupplier.asciidoc[] +include::LongToDoubleFunction.asciidoc[] +include::LongToIntFunction.asciidoc[] +include::LongUnaryOperator.asciidoc[] +include::Map.asciidoc[] +include::Map.Entry.asciidoc[] +include::Matcher.asciidoc[] +include::Math.asciidoc[] +include::MathContext.asciidoc[] +include::MessageFormat.asciidoc[] +include::MessageFormat.Field.asciidoc[] +include::MinguoChronology.asciidoc[] +include::MinguoDate.asciidoc[] +include::MinguoEra.asciidoc[] +include::MissingFormatArgumentException.asciidoc[] +include::MissingFormatWidthException.asciidoc[] +include::MissingResourceException.asciidoc[] +include::Month.asciidoc[] +include::MonthDay.asciidoc[] +include::NavigableMap.asciidoc[] +include::NavigableSet.asciidoc[] +include::NegativeArraySizeException.asciidoc[] +include::NoSuchElementException.asciidoc[] +include::NoSuchFieldException.asciidoc[] +include::NoSuchMethodException.asciidoc[] +include::Normalizer.asciidoc[] +include::Normalizer.Form.asciidoc[] +include::NullPointerException.asciidoc[] +include::Number.asciidoc[] +include::NumberFormat.asciidoc[] +include::NumberFormat.Field.asciidoc[] +include::NumberFormatException.asciidoc[] +include::ObjDoubleConsumer.asciidoc[] +include::ObjIntConsumer.asciidoc[] +include::ObjLongConsumer.asciidoc[] +include::Object.asciidoc[] +include::Objects.asciidoc[] +include::Observable.asciidoc[] +include::Observer.asciidoc[] +include::OffsetDateTime.asciidoc[] +include::OffsetTime.asciidoc[] +include::Optional.asciidoc[] +include::OptionalDouble.asciidoc[] +include::OptionalInt.asciidoc[] +include::OptionalLong.asciidoc[] +include::ParseException.asciidoc[] +include::ParsePosition.asciidoc[] +include::Pattern.asciidoc[] +include::Period.asciidoc[] +include::Predicate.asciidoc[] +include::PrimitiveIterator.asciidoc[] +include::PrimitiveIterator.OfDouble.asciidoc[] +include::PrimitiveIterator.OfInt.asciidoc[] +include::PrimitiveIterator.OfLong.asciidoc[] +include::PriorityQueue.asciidoc[] +include::Queue.asciidoc[] +include::Random.asciidoc[] +include::RandomAccess.asciidoc[] +include::ReflectiveOperationException.asciidoc[] +include::ResolverStyle.asciidoc[] +include::RoundingMode.asciidoc[] +include::RuleBasedCollator.asciidoc[] +include::RuntimeException.asciidoc[] +include::SecurityException.asciidoc[] +include::Set.asciidoc[] +include::Short.asciidoc[] +include::SignStyle.asciidoc[] +include::SimpleDateFormat.asciidoc[] +include::SimpleTimeZone.asciidoc[] +include::SortedMap.asciidoc[] +include::SortedSet.asciidoc[] +include::Spliterator.asciidoc[] +include::Spliterator.OfDouble.asciidoc[] +include::Spliterator.OfInt.asciidoc[] +include::Spliterator.OfLong.asciidoc[] +include::Spliterator.OfPrimitive.asciidoc[] +include::Spliterators.asciidoc[] +include::Stack.asciidoc[] +include::StackTraceElement.asciidoc[] +include::Stream.asciidoc[] +include::Stream.Builder.asciidoc[] +include::StrictMath.asciidoc[] +include::String.asciidoc[] +include::StringBuffer.asciidoc[] +include::StringBuilder.asciidoc[] +include::StringCharacterIterator.asciidoc[] +include::StringIndexOutOfBoundsException.asciidoc[] +include::StringJoiner.asciidoc[] +include::StringTokenizer.asciidoc[] +include::Supplier.asciidoc[] +include::System.asciidoc[] +include::Temporal.asciidoc[] +include::TemporalAccessor.asciidoc[] +include::TemporalAdjuster.asciidoc[] +include::TemporalAdjusters.asciidoc[] +include::TemporalAmount.asciidoc[] +include::TemporalField.asciidoc[] +include::TemporalQueries.asciidoc[] +include::TemporalQuery.asciidoc[] +include::TemporalUnit.asciidoc[] +include::TextStyle.asciidoc[] +include::ThaiBuddhistChronology.asciidoc[] +include::ThaiBuddhistDate.asciidoc[] +include::ThaiBuddhistEra.asciidoc[] +include::TimeZone.asciidoc[] +include::ToDoubleBiFunction.asciidoc[] +include::ToDoubleFunction.asciidoc[] +include::ToIntBiFunction.asciidoc[] +include::ToIntFunction.asciidoc[] +include::ToLongBiFunction.asciidoc[] +include::ToLongFunction.asciidoc[] +include::TooManyListenersException.asciidoc[] +include::TreeMap.asciidoc[] +include::TreeSet.asciidoc[] +include::TypeNotPresentException.asciidoc[] +include::UUID.asciidoc[] +include::UnaryOperator.asciidoc[] +include::UnknownFormatConversionException.asciidoc[] +include::UnknownFormatFlagsException.asciidoc[] +include::UnsupportedOperationException.asciidoc[] +include::UnsupportedTemporalTypeException.asciidoc[] +include::ValueRange.asciidoc[] +include::Vector.asciidoc[] +include::WeekFields.asciidoc[] +include::Year.asciidoc[] +include::YearMonth.asciidoc[] +include::ZoneId.asciidoc[] +include::ZoneOffset.asciidoc[] +include::ZoneOffsetTransition.asciidoc[] +include::ZoneOffsetTransitionRule.asciidoc[] +include::ZoneOffsetTransitionRule.TimeDefinition.asciidoc[] +include::ZoneRules.asciidoc[] +include::ZoneRulesException.asciidoc[] +include::ZoneRulesProvider.asciidoc[] +include::ZonedDateTime.asciidoc[] +include::org.elasticsearch.common.geo.GeoPoint.asciidoc[] +include::org.elasticsearch.index.fielddata.ScriptDocValues.Booleans.asciidoc[] +include::org.elasticsearch.index.fielddata.ScriptDocValues.BytesRefs.asciidoc[] +include::org.elasticsearch.index.fielddata.ScriptDocValues.Doubles.asciidoc[] +include::org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints.asciidoc[] +include::org.elasticsearch.index.fielddata.ScriptDocValues.Longs.asciidoc[] +include::org.elasticsearch.index.fielddata.ScriptDocValues.Strings.asciidoc[] +include::org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues.asciidoc[] +include::org.elasticsearch.painless.FeatureTest.asciidoc[] +include::org.joda.time.ReadableDateTime.asciidoc[] +include::org.joda.time.ReadableInstant.asciidoc[] diff --git a/docs/painless/painless-api-reference/org.elasticsearch.common.geo.GeoPoint.asciidoc b/docs/painless/painless-api-reference/org.elasticsearch.common.geo.GeoPoint.asciidoc new file mode 100644 index 0000000000000..2a593d0ec6314 --- /dev/null +++ b/docs/painless/painless-api-reference/org.elasticsearch.common.geo.GeoPoint.asciidoc @@ -0,0 +1,9 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-org-elasticsearch-common-geo-GeoPoint]]++org.elasticsearch.common.geo.GeoPoint++:: +* ++[[painless-api-reference-org-elasticsearch-common-geo-GeoPoint-getLat-0]]double link:{elasticsearch-javadoc}/org/elasticsearch/common/geo/GeoPoint.html#getLat%2D%2D[getLat]()++ +* ++[[painless-api-reference-org-elasticsearch-common-geo-GeoPoint-getLon-0]]double link:{elasticsearch-javadoc}/org/elasticsearch/common/geo/GeoPoint.html#getLon%2D%2D[getLon]()++ +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Booleans.asciidoc b/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Booleans.asciidoc new file mode 100644 index 0000000000000..12aaed4003d97 --- /dev/null +++ b/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Booleans.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Booleans]]++org.elasticsearch.index.fielddata.ScriptDocValues.Booleans++:: +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Booleans-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Booleans.html#get%2Dint%2D[get](int)++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Booleans-getValue-0]]boolean link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Booleans.html#getValue%2D%2D[getValue]()++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Booleans-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Booleans.html#getValues%2D%2D[getValues]()++ +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.BytesRefs.asciidoc b/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.BytesRefs.asciidoc new file mode 100644 index 0000000000000..fe11f96874941 --- /dev/null +++ b/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.BytesRefs.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-BytesRefs]]++org.elasticsearch.index.fielddata.ScriptDocValues.BytesRefs++:: +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-BytesRefs-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$BytesRefs.html#get%2Dint%2D[get](int)++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-BytesRefs-getValue-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$BytesRefs.html#getValue%2D%2D[getValue]()++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-BytesRefs-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$BytesRefs.html#getValues%2D%2D[getValues]()++ +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Doubles.asciidoc b/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Doubles.asciidoc new file mode 100644 index 0000000000000..92d4307241483 --- /dev/null +++ b/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Doubles.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Doubles]]++org.elasticsearch.index.fielddata.ScriptDocValues.Doubles++:: +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Doubles-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Doubles.html#get%2Dint%2D[get](int)++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Doubles-getValue-0]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Doubles.html#getValue%2D%2D[getValue]()++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Doubles-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Doubles.html#getValues%2D%2D[getValues]()++ +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints.asciidoc b/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints.asciidoc new file mode 100644 index 0000000000000..e55afb2559430 --- /dev/null +++ b/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints.asciidoc @@ -0,0 +1,20 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints]]++org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints++:: +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-arcDistance-2]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#arcDistance%2Ddouble%2Ddouble%2D[arcDistance](double, double)++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-arcDistanceWithDefault-3]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#arcDistanceWithDefault%2Ddouble%2Ddouble%2Ddouble%2D[arcDistanceWithDefault](double, double, double)++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-geohashDistance-1]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#geohashDistance%2Djava.lang.String%2D[geohashDistance](<>)++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-geohashDistanceWithDefault-2]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#geohashDistanceWithDefault%2Djava.lang.String%2Ddouble%2D[geohashDistanceWithDefault](<>, double)++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#get%2Dint%2D[get](int)++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-getLat-0]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#getLat%2D%2D[getLat]()++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-getLats-0]]double[] link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#getLats%2D%2D[getLats]()++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-getLon-0]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#getLon%2D%2D[getLon]()++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-getLons-0]]double[] link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#getLons%2D%2D[getLons]()++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-getValue-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#getValue%2D%2D[getValue]()++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#getValues%2D%2D[getValues]()++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-planeDistance-2]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#planeDistance%2Ddouble%2Ddouble%2D[planeDistance](double, double)++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-GeoPoints-planeDistanceWithDefault-3]]double link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$GeoPoints.html#planeDistanceWithDefault%2Ddouble%2Ddouble%2Ddouble%2D[planeDistanceWithDefault](double, double, double)++ +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Longs.asciidoc b/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Longs.asciidoc new file mode 100644 index 0000000000000..85e6e9289ef5a --- /dev/null +++ b/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Longs.asciidoc @@ -0,0 +1,12 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Longs]]++org.elasticsearch.index.fielddata.ScriptDocValues.Longs++:: +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Longs-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Longs.html#get%2Dint%2D[get](int)++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Longs-getDate-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Longs.html#getDate%2D%2D[getDate]()++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Longs-getDates-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Longs.html#getDates%2D%2D[getDates]()++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Longs-getValue-0]]long link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Longs.html#getValue%2D%2D[getValue]()++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Longs-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Longs.html#getValues%2D%2D[getValues]()++ +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Strings.asciidoc b/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Strings.asciidoc new file mode 100644 index 0000000000000..c15c152cd65ba --- /dev/null +++ b/docs/painless/painless-api-reference/org.elasticsearch.index.fielddata.ScriptDocValues.Strings.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Strings]]++org.elasticsearch.index.fielddata.ScriptDocValues.Strings++:: +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Strings-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Strings.html#get%2Dint%2D[get](int)++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Strings-getValue-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Strings.html#getValue%2D%2D[getValue]()++ +* ++[[painless-api-reference-org-elasticsearch-index-fielddata-ScriptDocValues-Strings-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/fielddata/ScriptDocValues$Strings.html#getValues%2D%2D[getValues]()++ +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues.asciidoc b/docs/painless/painless-api-reference/org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues.asciidoc new file mode 100644 index 0000000000000..e6ce5d5a57a27 --- /dev/null +++ b/docs/painless/painless-api-reference/org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues.asciidoc @@ -0,0 +1,10 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-org-elasticsearch-index-mapper-IpFieldMapper-IpFieldType-IpScriptDocValues]]++org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues++:: +* ++[[painless-api-reference-org-elasticsearch-index-mapper-IpFieldMapper-IpFieldType-IpScriptDocValues-get-1]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/mapper/IpFieldMapper$IpFieldType$IpScriptDocValues.html#get%2Dint%2D[get](int)++ +* ++[[painless-api-reference-org-elasticsearch-index-mapper-IpFieldMapper-IpFieldType-IpScriptDocValues-getValue-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/mapper/IpFieldMapper$IpFieldType$IpScriptDocValues.html#getValue%2D%2D[getValue]()++ +* ++[[painless-api-reference-org-elasticsearch-index-mapper-IpFieldMapper-IpFieldType-IpScriptDocValues-getValues-0]]<> link:{elasticsearch-javadoc}/org/elasticsearch/index/mapper/IpFieldMapper$IpFieldType$IpScriptDocValues.html#getValues%2D%2D[getValues]()++ +* Inherits methods from ++<>++, ++<>++, ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/org.elasticsearch.painless.FeatureTest.asciidoc b/docs/painless/painless-api-reference/org.elasticsearch.painless.FeatureTest.asciidoc new file mode 100644 index 0000000000000..40fdec50793a9 --- /dev/null +++ b/docs/painless/painless-api-reference/org.elasticsearch.painless.FeatureTest.asciidoc @@ -0,0 +1,16 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-org-elasticsearch-painless-FeatureTest]]++org.elasticsearch.painless.FeatureTest++:: +* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-overloadedStatic-0]]static boolean link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#overloadedStatic%2D%2D[overloadedStatic]()++ +* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-overloadedStatic-1]]static boolean link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#overloadedStatic%2Dboolean%2D[overloadedStatic](boolean)++ +* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-org.elasticsearch.painless.FeatureTest-0]]link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#org.elasticsearch.painless.FeatureTest%2D%2D[org.elasticsearch.painless.FeatureTest]()++ +* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-org.elasticsearch.painless.FeatureTest-2]]link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#org.elasticsearch.painless.FeatureTest%2Dint%2Dint%2D[org.elasticsearch.painless.FeatureTest](int, int)++ +* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-getX-0]]int link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#getX%2D%2D[getX]()++ +* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-getY-0]]int link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#getY%2D%2D[getY]()++ +* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-setX-1]]void link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#setX%2Dint%2D[setX](int)++ +* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-setY-1]]void link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#setY%2Dint%2D[setY](int)++ +* ++[[painless-api-reference-org-elasticsearch-painless-FeatureTest-twoFunctionsOfX-2]]<> link:{painless-javadoc}/org/elasticsearch/painless/FeatureTest.html#twoFunctionsOfX%2Djava.util.function.Function%2Djava.util.function.Function%2D[twoFunctionsOfX](<>, <>)++ +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-api-reference/org.joda.time.ReadableDateTime.asciidoc b/docs/painless/painless-api-reference/org.joda.time.ReadableDateTime.asciidoc new file mode 100644 index 0000000000000..a7a0a17c1adb8 --- /dev/null +++ b/docs/painless/painless-api-reference/org.joda.time.ReadableDateTime.asciidoc @@ -0,0 +1,27 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-org-joda-time-ReadableDateTime]]++org.joda.time.ReadableDateTime++:: +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getCenturyOfEra-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getCenturyOfEra%2D%2D[getCenturyOfEra]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getDayOfMonth-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getDayOfMonth%2D%2D[getDayOfMonth]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getDayOfWeek-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getDayOfWeek%2D%2D[getDayOfWeek]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getDayOfYear-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getDayOfYear%2D%2D[getDayOfYear]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getEra-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getEra%2D%2D[getEra]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getHourOfDay-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getHourOfDay%2D%2D[getHourOfDay]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getMillisOfDay-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getMillisOfDay%2D%2D[getMillisOfDay]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getMillisOfSecond-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getMillisOfSecond%2D%2D[getMillisOfSecond]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getMinuteOfDay-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getMinuteOfDay%2D%2D[getMinuteOfDay]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getMinuteOfHour-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getMinuteOfHour%2D%2D[getMinuteOfHour]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getMonthOfYear-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getMonthOfYear%2D%2D[getMonthOfYear]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getSecondOfDay-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getSecondOfDay%2D%2D[getSecondOfDay]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getSecondOfMinute-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getSecondOfMinute%2D%2D[getSecondOfMinute]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getWeekOfWeekyear-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getWeekOfWeekyear%2D%2D[getWeekOfWeekyear]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getWeekyear-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getWeekyear%2D%2D[getWeekyear]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getYear-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getYear%2D%2D[getYear]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getYearOfCentury-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getYearOfCentury%2D%2D[getYearOfCentury]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-getYearOfEra-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#getYearOfEra%2D%2D[getYearOfEra]()++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-toString-1]]<> link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#toString%2Djava.lang.String%2D[toString](<>)++ +* ++[[painless-api-reference-org-joda-time-ReadableDateTime-toString-2]]<> link:{joda-time-javadoc}/org/joda/time/ReadableDateTime.html#toString%2Djava.lang.String%2Djava.util.Locale%2D[toString](<>, <>)++ +* Inherits methods from ++<>++, ++<>++ diff --git a/docs/painless/painless-api-reference/org.joda.time.ReadableInstant.asciidoc b/docs/painless/painless-api-reference/org.joda.time.ReadableInstant.asciidoc new file mode 100644 index 0000000000000..96d1f61a81f55 --- /dev/null +++ b/docs/painless/painless-api-reference/org.joda.time.ReadableInstant.asciidoc @@ -0,0 +1,14 @@ +//// +Automatically generated by PainlessDocGenerator. Do not edit. +Rebuild by running `gradle generatePainlessApi`. +//// + +[[painless-api-reference-org-joda-time-ReadableInstant]]++org.joda.time.ReadableInstant++:: +* ++[[painless-api-reference-org-joda-time-ReadableInstant-equals-1]]boolean link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#equals%2Djava.lang.Object%2D[equals](<>)++ +* ++[[painless-api-reference-org-joda-time-ReadableInstant-getMillis-0]]long link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#getMillis%2D%2D[getMillis]()++ +* ++[[painless-api-reference-org-joda-time-ReadableInstant-hashCode-0]]int link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#hashCode%2D%2D[hashCode]()++ +* ++[[painless-api-reference-org-joda-time-ReadableInstant-isAfter-1]]boolean link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#isAfter%2Dorg.joda.time.ReadableInstant%2D[isAfter](<>)++ +* ++[[painless-api-reference-org-joda-time-ReadableInstant-isBefore-1]]boolean link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#isBefore%2Dorg.joda.time.ReadableInstant%2D[isBefore](<>)++ +* ++[[painless-api-reference-org-joda-time-ReadableInstant-isEqual-1]]boolean link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#isEqual%2Dorg.joda.time.ReadableInstant%2D[isEqual](<>)++ +* ++[[painless-api-reference-org-joda-time-ReadableInstant-toString-0]]<> link:{joda-time-javadoc}/org/joda/time/ReadableInstant.html#toString%2D%2D[toString]()++ +* Inherits methods from ++<>++ diff --git a/docs/painless/painless-debugging.asciidoc b/docs/painless/painless-debugging.asciidoc new file mode 100644 index 0000000000000..7c4484938b03e --- /dev/null +++ b/docs/painless/painless-debugging.asciidoc @@ -0,0 +1,96 @@ +[[painless-debugging]] +=== Painless Debugging + +experimental[The Painless scripting language is new and is still marked as experimental. The syntax or API may be changed in the future in non-backwards compatible ways if required.] + +==== Debug.Explain + +Painless doesn't have a +https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop[REPL] +and while it'd be nice for it to have one one day, it wouldn't tell you the +whole story around debugging painless scripts embedded in Elasticsearch because +the data that the scripts have access to or "context" is so important. For now +the best way to debug embedded scripts is by throwing exceptions at choice +places. While you can throw your own exceptions +(`throw new Exception('whatever')`), Painless's sandbox prevents you from +accessing useful information like the type of an object. So Painless has a +utility method, `Debug.explain` which throws the exception for you. For +example, you can use {ref}/search-explain.html[`_explain`] to explore the +context available to a {ref}/query-dsl-script-query.html[script query]. + +[source,js] +--------------------------------------------------------- +PUT /hockey/player/1?refresh +{"first":"johnny","last":"gaudreau","goals":[9,27,1],"assists":[17,46,0],"gp":[26,82,1]} + +POST /hockey/player/1/_explain +{ + "query": { + "script": { + "script": "Debug.explain(doc.goals)" + } + } +} +--------------------------------------------------------- +// CONSOLE +// TEST[s/_explain/_explain?error_trace=false/ catch:/painless_explain_error/] +// The test system sends error_trace=true by default for easier debugging so +// we have to override it to get a normal shaped response + +Which shows that the class of `doc.first` is +`org.elasticsearch.index.fielddata.ScriptDocValues.Longs` by responding with: + +[source,js] +--------------------------------------------------------- +{ + "error": { + "type": "script_exception", + "to_string": "[1, 9, 27]", + "painless_class": "org.elasticsearch.index.fielddata.ScriptDocValues.Longs", + "java_class": "org.elasticsearch.index.fielddata.ScriptDocValues$Longs", + ... + }, + "status": 500 +} +--------------------------------------------------------- +// TESTRESPONSE[s/\.\.\./"script_stack": $body.error.script_stack, "script": $body.error.script, "lang": $body.error.lang, "caused_by": $body.error.caused_by, "root_cause": $body.error.root_cause, "reason": $body.error.reason/] + +You can use the same trick to see that `_source` is a `LinkedHashMap` +in the `_update` API: + +[source,js] +--------------------------------------------------------- +POST /hockey/player/1/_update +{ + "script": "Debug.explain(ctx._source)" +} +--------------------------------------------------------- +// CONSOLE +// TEST[continued s/_update/_update?error_trace=false/ catch:/painless_explain_error/] + +The response looks like: + +[source,js] +--------------------------------------------------------- +{ + "error" : { + "root_cause": ..., + "type": "illegal_argument_exception", + "reason": "failed to execute script", + "caused_by": { + "type": "script_exception", + "to_string": "{gp=[26, 82, 1], last=gaudreau, assists=[17, 46, 0], first=johnny, goals=[9, 27, 1]}", + "painless_class": "LinkedHashMap", + "java_class": "java.util.LinkedHashMap", + ... + } + }, + "status": 400 +} +--------------------------------------------------------- +// TESTRESPONSE[s/"root_cause": \.\.\./"root_cause": $body.error.root_cause/] +// TESTRESPONSE[s/\.\.\./"script_stack": $body.error.caused_by.script_stack, "script": $body.error.caused_by.script, "lang": $body.error.caused_by.lang, "caused_by": $body.error.caused_by.caused_by, "reason": $body.error.caused_by.reason/] +// TESTRESPONSE[s/"to_string": ".+"/"to_string": $body.error.caused_by.to_string/] + +Once you have a class you can go to <> to see a list of +available methods. diff --git a/docs/painless/painless-description.asciidoc b/docs/painless/painless-description.asciidoc new file mode 100644 index 0000000000000..874eab5632cfb --- /dev/null +++ b/docs/painless/painless-description.asciidoc @@ -0,0 +1,24 @@ +_Painless_ is a simple, secure scripting language designed specifically for use +with Elasticsearch. It is the default scripting language for Elasticsearch and +can safely be used for inline and stored scripts. For a detailed description of +the Painless syntax and language features, see the +{painless}/painless-specification.html[Painless Language Specification]. + +[[painless-features]] +You can use Painless anywhere scripts can be used in Elasticsearch. Painless +provides: + +* Fast performance: Painless scripts https://benchmarks.elastic.co/index.html#search_qps_scripts[ +run several times faster] than the alternatives. + +* Safety: Fine-grained whitelist with method call/field granularity. See the +{painless}/painless-api-reference.html[Painless API Reference] for a +complete list of available classes and methods. + +* Optional typing: Variables and parameters can use explicit types or the +dynamic `def` type. + +* Syntax: Extends Java's syntax to provide http://groovy-lang.org/index.html[ +Groovy-style] scripting language features that make scripts easier to write. + +* Optimizations: Designed specifically for Elasticsearch scripting. diff --git a/docs/painless/painless-getting-started.asciidoc b/docs/painless/painless-getting-started.asciidoc new file mode 100644 index 0000000000000..9a9c032164102 --- /dev/null +++ b/docs/painless/painless-getting-started.asciidoc @@ -0,0 +1,362 @@ +[[painless-getting-started]] +== Getting Started with Painless + +experimental[The Painless scripting language is new and is still marked as experimental. The syntax or API may be changed in the future in non-backwards compatible ways if required.] + +include::painless-description.asciidoc[] + +[[painless-examples]] +=== Painless Examples + +To illustrate how Painless works, let's load some hockey stats into an Elasticsearch index: + +[source,js] +---------------------------------------------------------------- +PUT hockey/player/_bulk?refresh +{"index":{"_id":1}} +{"first":"johnny","last":"gaudreau","goals":[9,27,1],"assists":[17,46,0],"gp":[26,82,1],"born":"1993/08/13"} +{"index":{"_id":2}} +{"first":"sean","last":"monohan","goals":[7,54,26],"assists":[11,26,13],"gp":[26,82,82],"born":"1994/10/12"} +{"index":{"_id":3}} +{"first":"jiri","last":"hudler","goals":[5,34,36],"assists":[11,62,42],"gp":[24,80,79],"born":"1984/01/04"} +{"index":{"_id":4}} +{"first":"micheal","last":"frolik","goals":[4,6,15],"assists":[8,23,15],"gp":[26,82,82],"born":"1988/02/17"} +{"index":{"_id":5}} +{"first":"sam","last":"bennett","goals":[5,0,0],"assists":[8,1,0],"gp":[26,1,0],"born":"1996/06/20"} +{"index":{"_id":6}} +{"first":"dennis","last":"wideman","goals":[0,26,15],"assists":[11,30,24],"gp":[26,81,82],"born":"1983/03/20"} +{"index":{"_id":7}} +{"first":"david","last":"jones","goals":[7,19,5],"assists":[3,17,4],"gp":[26,45,34],"born":"1984/08/10"} +{"index":{"_id":8}} +{"first":"tj","last":"brodie","goals":[2,14,7],"assists":[8,42,30],"gp":[26,82,82],"born":"1990/06/07"} +{"index":{"_id":39}} +{"first":"mark","last":"giordano","goals":[6,30,15],"assists":[3,30,24],"gp":[26,60,63],"born":"1983/10/03"} +{"index":{"_id":10}} +{"first":"mikael","last":"backlund","goals":[3,15,13],"assists":[6,24,18],"gp":[26,82,82],"born":"1989/03/17"} +{"index":{"_id":11}} +{"first":"joe","last":"colborne","goals":[3,18,13],"assists":[6,20,24],"gp":[26,67,82],"born":"1990/01/30"} +---------------------------------------------------------------- +// CONSOLE +// TESTSETUP + +[float] +==== Accessing Doc Values from Painless + +Document values can be accessed from a `Map` named `doc`. + +For example, the following script calculates a player's total goals. This example uses a strongly typed `int` and a `for` loop. + +[source,js] +---------------------------------------------------------------- +GET hockey/_search +{ + "query": { + "function_score": { + "script_score": { + "script": { + "lang": "painless", + "inline": "int total = 0; for (int i = 0; i < doc['goals'].length; ++i) { total += doc['goals'][i]; } return total;" + } + } + } + } +} +---------------------------------------------------------------- +// CONSOLE + +Alternatively, you could do the same thing using a script field instead of a function score: + +[source,js] +---------------------------------------------------------------- +GET hockey/_search +{ + "query": { + "match_all": {} + }, + "script_fields": { + "total_goals": { + "script": { + "lang": "painless", + "inline": "int total = 0; for (int i = 0; i < doc['goals'].length; ++i) { total += doc['goals'][i]; } return total;" + } + } + } +} +---------------------------------------------------------------- +// CONSOLE + +The following example uses a Painless script to sort the players by their combined first and last names. The names are accessed using +`doc['first'].value` and `doc['last'].value`. + +[source,js] +---------------------------------------------------------------- +GET hockey/_search +{ + "query": { + "match_all": {} + }, + "sort": { + "_script": { + "type": "string", + "order": "asc", + "script": { + "lang": "painless", + "inline": "doc['first.keyword'].value + ' ' + doc['last.keyword'].value" + } + } + } +} +---------------------------------------------------------------- +// CONSOLE + +[float] +==== Updating Fields with Painless + +You can also easily update fields. You access the original source for a field as `ctx._source.`. + +First, let's look at the source data for a player by submitting the following request: + +[source,js] +---------------------------------------------------------------- +GET hockey/_search +{ + "stored_fields": [ + "_id", + "_source" + ], + "query": { + "term": { + "_id": 1 + } + } +} +---------------------------------------------------------------- +// CONSOLE + +To change player 1's last name to `hockey`, simply set `ctx._source.last` to the new value: + +[source,js] +---------------------------------------------------------------- +POST hockey/player/1/_update +{ + "script": { + "lang": "painless", + "inline": "ctx._source.last = params.last", + "params": { + "last": "hockey" + } + } +} +---------------------------------------------------------------- +// CONSOLE + +You can also add fields to a document. For example, this script adds a new field that contains +the player's nickname, _hockey_. + +[source,js] +---------------------------------------------------------------- +POST hockey/player/1/_update +{ + "script": { + "lang": "painless", + "inline": "ctx._source.last = params.last; ctx._source.nick = params.nick", + "params": { + "last": "gaudreau", + "nick": "hockey" + } + } +} +---------------------------------------------------------------- +// CONSOLE + +[float] +[[modules-scripting-painless-dates]] +==== Dates + +Date fields are exposed as +<>s +so they support methods like +<>, +and +<>. +To get milliseconds since epoch call +<>. +For example, the following returns every hockey player's birth year: + +[source,js] +---------------------------------------------------------------- +GET hockey/_search +{ + "script_fields": { + "birth_year": { + "script": { + "inline": "doc.born.value.year" + } + } + } +} +---------------------------------------------------------------- +// CONSOLE + +[float] +[[modules-scripting-painless-regex]] +==== Regular expressions + +NOTE: Regexes are disabled by default because they circumvent Painless's +protection against long running and memory hungry scripts. To make matters +worse even innocuous looking regexes can have staggering performance and stack +depth behavior. They remain an amazing powerful tool but are too scary to enable +by default. To enable them yourself set `script.painless.regex.enabled: true` in +`elasticsearch.yml`. We'd like very much to have a safe alternative +implementation that can be enabled by default so check this space for later +developments! + +Painless's native support for regular expressions has syntax constructs: + +* `/pattern/`: Pattern literals create patterns. This is the only way to create +a pattern in painless. The pattern inside the ++/++'s are just +http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html[Java regular expressions]. +See <> for more. +* `=~`: The find operator return a `boolean`, `true` if a subsequence of the +text matches, `false` otherwise. +* `==~`: The match operator returns a `boolean`, `true` if the text matches, +`false` if it doesn't. + +Using the find operator (`=~`) you can update all hockey players with "b" in +their last name: + +[source,js] +---------------------------------------------------------------- +POST hockey/player/_update_by_query +{ + "script": { + "lang": "painless", + "inline": "if (ctx._source.last =~ /b/) {ctx._source.last += \"matched\"} else {ctx.op = 'noop'}" + } +} +---------------------------------------------------------------- +// CONSOLE + +Using the match operator (`==~`) you can update all the hockey players who's +names start with a consonant and end with a vowel: + +[source,js] +---------------------------------------------------------------- +POST hockey/player/_update_by_query +{ + "script": { + "lang": "painless", + "inline": "if (ctx._source.last ==~ /[^aeiou].*[aeiou]/) {ctx._source.last += \"matched\"} else {ctx.op = 'noop'}" + } +} +---------------------------------------------------------------- +// CONSOLE + +You can use the `Pattern.matcher` directly to get a `Matcher` instance and +remove all of the vowels in all of their last names: + +[source,js] +---------------------------------------------------------------- +POST hockey/player/_update_by_query +{ + "script": { + "lang": "painless", + "inline": "ctx._source.last = /[aeiou]/.matcher(ctx._source.last).replaceAll('')" + } +} +---------------------------------------------------------------- +// CONSOLE + +`Matcher.replaceAll` is just a call to Java's `Matcher`'s +http://docs.oracle.com/javase/8/docs/api/java/util/regex/Matcher.html#replaceAll-java.lang.String-[replaceAll] +method so it supports `$1` and `\1` for replacements: + +[source,js] +---------------------------------------------------------------- +POST hockey/player/_update_by_query +{ + "script": { + "lang": "painless", + "inline": "ctx._source.last = /n([aeiou])/.matcher(ctx._source.last).replaceAll('$1')" + } +} +---------------------------------------------------------------- +// CONSOLE + +If you need more control over replacements you can call `replaceAll` on a +`CharSequence` with a `Function` that builds the replacement. +This does not support `$1` or `\1` to access replacements because you already +have a reference to the matcher and can get them with `m.group(1)`. + +IMPORTANT: Calling `Matcher.find` inside of the function that builds the +replacement is rude and will likely break the replacement process. + +This will make all of the vowels in the hockey player's last names upper case: + +[source,js] +---------------------------------------------------------------- +POST hockey/player/_update_by_query +{ + "script": { + "lang": "painless", + "inline": "ctx._source.last = ctx._source.last.replaceAll(/[aeiou]/, m -> m.group().toUpperCase(Locale.ROOT))" + } +} +---------------------------------------------------------------- +// CONSOLE + +Or you can use the `CharSequence.replaceFirst` to make the first vowel in their +last names upper case: + +[source,js] +---------------------------------------------------------------- +POST hockey/player/_update_by_query +{ + "script": { + "lang": "painless", + "inline": "ctx._source.last = ctx._source.last.replaceFirst(/[aeiou]/, m -> m.group().toUpperCase(Locale.ROOT))" + } +} +---------------------------------------------------------------- +// CONSOLE + + +Note: all of the `_update_by_query` examples above could really do with a +`query` to limit the data that they pull back. While you *could* use a +See {ref}/query-dsl-script-query.html[script query] it wouldn't be as efficient +as using any other query because script queries aren't able to use the inverted +index to limit the documents that they have to check. + +[[modules-scripting-painless-dispatch]] +=== How painless dispatches functions + +Painless uses receiver, name, and https://en.wikipedia.org/wiki/Arity[arity] +for method dispatch. For example, `s.foo(a, b)` is resolved by first getting +the class of `s` and then looking up the method `foo` with two parameters. This +is different from Groovy which uses the +https://en.wikipedia.org/wiki/Multiple_dispatch[runtime types] of the +parameters and Java which uses the compile time types of the parameters. + +The consequence of this that Painless doesn't support overloaded methods like +Java, leading to some trouble when it whitelists classes from the Java +standard library. For example, in Java and Groovy, `Matcher` has two methods: +`group(int)` and `group(String)`. Painless can't whitelist both of them methods +because they have the same name and the same number of parameters. So instead it +has <> and +<>. + +We have a few justifications for this different way of dispatching methods: + +1. It makes operating on `def` types simpler and, presumably, faster. Using +receiver, name, and arity means when Painless sees a call on a `def` objects it +can dispatch the appropriate method without having to do expensive comparisons +of the types of the parameters. The same is true for invocations with `def` +typed parameters. +2. It keeps things consistent. It would be genuinely weird for Painless to +behave like Groovy if any `def` typed parameters were involved and Java +otherwise. It'd be slow for it to behave like Groovy all the time. +3. It keeps Painless maintainable. Adding the Java or Groovy like method +dispatch *feels* like it'd add a ton of complexity which'd make maintenance and +other improvements much more difficult. + +include::painless-debugging.asciidoc[] \ No newline at end of file diff --git a/docs/painless/painless-lang-spec.asciidoc b/docs/painless/painless-lang-spec.asciidoc new file mode 100644 index 0000000000000..dbad00931af20 --- /dev/null +++ b/docs/painless/painless-lang-spec.asciidoc @@ -0,0 +1,73 @@ +[[painless-specification]] +== Painless Language Specification + +Painless uses a Java-style syntax that is similar to Groovy. In fact, most +Painless scripts are also valid Groovy, and simple Groovy scripts are typically +valid Painless. This specification assumes you have at least a passing +familiarity with Java and related languages. + +Painless is essentially a subset of Java with some additional scripting +language features that make scripts easier to write. However, there are some +important differences, particularly with the casting model. For more detailed +conceptual information about the basic constructs that Java and Painless share, +refer to the corresponding topics in the +https://docs.oracle.com/javase/specs/jls/se8/html/index.html[Java Language +Specification]. + +Painless scripts are parsed and compiled using the http://www.antlr.org/[ANTLR4] +and http://asm.ow2.org/[ASM] libraries. Painless scripts are compiled directly +into Java byte code and executed against a standard Java Virtual Machine. This +specification uses ANTLR4 grammar notation to describe the allowed syntax. +However, the actual Painless grammar is more compact than what is shown here. + +[float] +[[comments]] +==== Comments + +Painless supports both single-line and multi-line comments. You can include +comments anywhere within a script. + +Single-line comments are preceded by two slashes: `// comment`. They can be +placed anywhere on a line. All characters from the two slashes to the end of +the line are ignored. + +Multi-line comments are preceded by a slash-star `/*` and closed by +star-slash `*/`. Multi-line comments can start anywhere on a line. All +characters from the opening `/*` to the closing `*/` are ignored. + +*Examples:* + +[source,Java] +---- +// single-line comment + + // single-line comment + +/* multi- + line + comment */ + + /* multi-line + comment */ + + /* multi-line comment */ +---- + +[float] +[[keywords]] +==== Keywords + +Painless reserves the following keywords for built-in language features. +These keywords cannot be used used in other contexts, such as identifiers. + +[cols="^1,^1,^1,^1,^1"] +|==== +| if | else | while | do | for +| in | continue | break | return | new +| try | catch | throw | this | instanceof +|==== + +include::painless-literals.asciidoc[] +include::painless-variables.asciidoc[] +include::painless-types.asciidoc[] +include::painless-operators.asciidoc[] diff --git a/docs/painless/painless-literals.asciidoc b/docs/painless/painless-literals.asciidoc new file mode 100644 index 0000000000000..43c5eb82f96a2 --- /dev/null +++ b/docs/painless/painless-literals.asciidoc @@ -0,0 +1,94 @@ +[[literals]] +=== Literals + +Literals are values that you can specify directly in Painless scripts. + +[[integers]] +==== Integers + +Specify integer literals in decimal, octal, or hex notation. Use the following +single letter designations to specify the primitive type: `l` for `long`, `f` +for `float`, and `d` for `double`. If not specified, the type defaults to +`int` (with the exception of certain assignments described later). + +*Grammar:* +[source,ANTLR4] +---- +INTEGER: '-'? ( '0' | [1-9] [0-9]* ) [lLfFdD]?; +OCTAL: '-'? '0' [0-7]+ [lL]?; +HEX: '-'? '0' [xX] [0-9a-fA-F]+ [lL]?; +---- + +*Examples:* +[source,Java] +---- +0 // integer literal of 0 +0D // double literal of 0.0 +1234L // long literal of 1234 +-90F // float literal of -90.0 +-022 // integer literal of -18 specified in octal +0xF2A // integer literal of 3882 +---- + +[[floating-point-values]] +==== Floating Point Values + +Specify floating point literals using the following single letter designations +for the primitive type: `f` for `float` and `d` for `double`. +If not specified, the type defaults to `double`. + +*Grammar:* +[source,ANTLR4] +---- +DECIMAL: '-'? ( '0' | [1-9] [0-9]* ) (DOT [0-9]+)? ( [eE] [+\-]? [0-9]+ )? [fFdD]?; +---- + +*Examples:* +[source,Java] +---- +0.0 // double value of 0.0 +1E6 // double value of 1000000 +0.977777 // double value of 0.97777 +-126.34 // double value of -126.34 +89.9F // float value of 89.9 +---- + +[[strings]] +==== Strings + +Specify literal string with either single or double quotes. In double-quoted +literal strings, you can escape double-quotes with a backslash to include them +in the string. Similarly, you escape single quotes with a backslash in +single-quoted literal strings. Backslashes themselves also need to be +escaped with a backslash. + +*Grammar:* +[source,ANTLR4] +---- +STRING: ( '"' ( '\\"' | '\\\\' | ~[\\"] )*? '"' ) | ( '\'' ( '\\\'' | '\\\\' | ~[\\'] )*? '\'' ); +---- + +*Examples:* +[source,Java] +---- +"double-quoted String literal" +'single-quoted String literal' +"\"double-quoted String with escaped double-quotes\" and backslash: \\" +'\'single-quoted String with escaped single-quotes\' and backslash \\' +"double-quoted String with non-escaped 'single-quotes'" +'single-quoted String with non-escaped "double-quotes"' +---- + +[[char]] +===== Char + +You cannot directly specify character literals in Painless. However, you can +cast single-character strings to char. Attempting to cast a multi-character +string to a char throws an error. + +*Examples:* +[source,Java] +---- +(char)"C" +(char)'c' +---- \ No newline at end of file diff --git a/docs/painless/painless-operators.asciidoc b/docs/painless/painless-operators.asciidoc new file mode 100644 index 0000000000000..58e2fab2b0a31 --- /dev/null +++ b/docs/painless/painless-operators.asciidoc @@ -0,0 +1,1774 @@ +=== Operators + +The following is a table of the available operators in Painless. Each operator will have further information and examples outside of the table. Many operators will have a promotion table as described by the documentation on promotion [MARK]. + +[options="header",cols="6,3,2,4"] +|==== +|Operator|Symbol(s)|Precedence|Associativity +|Precedence|()|0|left-to-right +|Field Access|.|1|left-to-right +|Method Call|. ()|1|left-to-right +|Null Safe|?.|1|left-to-right +|Function Call|()|1|left-to-right +|Array Initialization|[] {}|1|left-to-right +|Array Access|[]|1|left-to-right +|Array Length|.|1|left-to-right +|List Initialization|[]|1|left-to-right +|List Access|[]|1|left-to-right +|Map Initialization|[:]|1|left-to-right +|Map Access|[]|1|left-to-right +|Post Increment|++|1|left-to-right +|Post Decrement|--|1|left-to-right +|Pre Increment|++|2|right-to-left +|Pre Decrement|--|2|right-to-left +|Unary Positive|+|2|right-to-left +|Unary Negative|-|2|right-to-left +|Boolean Not|!|2|right-to-left +|Bitwise Not|~|2|right-to-left +|Cast|()|3|right-to-left +|Constructor Call|new ()|3|right-to-left +|New Array|new|3|right-to-left +|Multiplication|*|4|left-to-right +|Division|/|4|left-to-right +|Remainder|%|4|left-to-right +|String Concatenation|+|5|left-to-right +|Addition|+|5|left-to-right +|Subtraction|-|5|left-to-right +|Left Shift|<<|6|left-to-right +|Right Shift|>>|6|left-to-right +|Unsigned Right Shift|>>>|6|left-to-right +|Greater Than|>|7|left-to-right +|Greater Than Or Equal|>=|7|left-to-right +|Less Than|<|7|left-to-right +|Less Than Or Equal|<=|7|left-to-right +|Instance Of|instanceof|8|left-to-right +|Equality Equals|==|9|left-to-right +|Equality Not Equals|!=|9|left-to-right +|Identity Equals|===|9|left-to-right +|Identity Not Equals|!==|9|left-to-right +|Bitwise And|&|10|left-to-right +|Boolean Xor|^|11|left-to-right +|Bitwise Xor|^|11|left-to-right +|Bitwise Or|\||12|left-to-right +|Boolean And|&&|13|left-to-right +|Boolean Or|\|\||14|left-to-right +|Conditional|? :|15|right-to-left +|Elvis|?:|16|right-to-left +|Assignment|=|17|right-to-left +|Compound Assignment|$=|17|right-to-left +|==== + +[[precedence-operator]] +==== Precedence + +You group expressions using the precedence operator to guarantee +the order of evaluation and override existing precedence relationships between operators. The format is an opening parenthesis, one or more expressions, and +a closing parenthesis. For example, `(20+1)*2`. + +*Grammar:* +[source,ANTLR4] +---- +precedence: '(' expression ')'; +---- + +*Examples:* +[source,Java] +---- +int x = (5+4)*6; // declares the variable int x and sets it to (5+4)*6 + // where 5+4 is evaluated first due to the precedence operator +int y = 2*(x-4); // declares the variable int y and sets it to 2*(x-4) + // where x-4 is evaluated first due to the precedence operator +---- + + +[[dot-operator]] +==== Dot +You use the dot operator `.` to access a type's <> and <>. + +[[field-access]] +===== Accessing Fields +You access primitive and reference type members in a reference type using the +dot operator '.' followed by the id of the member. The accessed member behaves +the same way as the type it represents with one exception: if the reference +type is of type `def`, the member is also considered to be of type `def` and +resolved at runtime. + +*Grammar:* +[source,ANTLR4] +---- +field_access: ID '.' ID; +---- + +*Examples:* +[source,Java] +---- +FeatureTest ft = new FeatureTest(); // Declare FeatureTest variable ft and + // set it to a newly allocated FeatureTest +ft.x = 5; // Access int member x from ft and assign + // it the literal int value 5 +ft.y = ft.x; // Access int member y from ft and assign + // it the value of ft member x +int value = ft.x + ft.y; // Declare variable value as an int, + // add ft members x and y together, + // assign the sum to the variable value +---- + +[[method-access]] +===== Calling Methods + +You call reference type methods using the dot operator and the method id: +`.method_id(arg1,...,argn)`. The parentheses are required even if there are no +arguments. + +If the reference type is not type `def`, the argument types for the method +can be resolved at compile time. An error occurs if appropriate type +conversions (casting) cannot be performed. If the reference type is type `def`, the argument types for the method are all considered to be the type `def`. The +appropriate type conversions are performed at run-time. + +Automatic <> is performed when you pass in +arguments to a method. + +Method calls can be overloaded based on arity in Painless. The same method +name can be re-used for different methods as long as the number of arguments +differs. This differs from Java method overloading, where only the types must +differ. This has an effect on some of the provided reference type methods in +the <>. Where there are overloaded methods with +the same arity for a reference type in Java, Painless chooses a single method +to be provided. + +*Grammar:* +[source,ANTLR4] +---- +method_call: ID '.' ID '(' (expression (',' expression)*)? ')'; +---- + +*Examples:* +[source,Java] +---- +Map m = new HashMap(); // Declare Map variable m and set it a newly + // allocated HashMap +x.put(1, 2); // Call the put method on variable x to add key 1 + // with the value 2 to the Map +int z = x.get(1); // Declare int variable z, call the get method to + // retrieve the value of key 1, and assign the + // return value of the method call to variable z +def d = new ArrayList(); // Declare def variable m and set it a newly + // allocated ArrayList +d.add(1); // Call the add method on variable d and add the + // literal int 1 to the ArrayList. Note that + // the argument type is considered to be of + // type def since the reference type is also def +int i = Integer.parseInt('2'); // Declare int variable i and set it to the + // value returned by the static method parseInt +---- + +************************** +Painless describes the Map method arguments using the `def` type: + +[source,Java] +---- +put(def, def) +get(def) +---- + +When you call `x.put(1, 2)`, the key and value are implicitly converted from +the int type to the def type. + +Assume for a minute that the Map method arguments were described as Integers: + +[source,Java] +---- +put(Integer, Integer) +get(Integer) +---- + +In this case, the key and value would implicitly be _boxed_ from the primitive +int type to the Integer reference type. For more information about how Painless +casts between primitive types and reference types, see <>. +************************** + +==== Null Safe + +The null safe operator `?.` can be used in place of the dot operator +to check if a reference type instance is `null` before attempting to access +a field or make a method call against it. When using the null safe operator, +if the instance is `null`, the returned value is `null`. If the reference +type instance is non-null, it returns the value of the field or result of +the method call normally. + +// REVIEWER NOTE: The following paragraph doesn't make sense to me. Do you +All resultant types must be a reference type or be able to be implicitly cast +to a reference type or an error will occur. + +*Grammar:* +[source,ANTLR4] +---- +null_safe: null_safe_field_access + | null_safe_method_call; +null_safe_field_access: ID '?.' ID; +null_safe_method_call: ID '?.' ID '(' (expression (',' expression)*)? ')'; +---- + +*Examples:* +[source,Java] +---- +Map x = new HashMap(); // Declare the Map variable x and set it to a newly + // allocated HashMap +Map y = null; // Declare the Map variable y and set it to null +def z = new HashMap(); // Declares the def variable z and set it to a newly + // allocated HashMap + +x.put(1, 2); // Put the key-value pair 1 and 2 into x +z.put(5, 6); // Put the key-value pair 5 and 6 into z + +def value = x?.get(1); // Declare the def variable value and set it to the + // result of .get(1) since x is not null +value = y?.get(3); // Sets value to null since y is null +value = z?.get(5); // Sets value to the result of .get(5) since z is not null +---- + +==== Parenthesis + +User-defined function calls can be made in Painless using the parenthesis +operator. See Function Calls [MARK] for more information. + +==== Brackets and Braces + +The brackets operator `[]` is used to create and access arrays, lists, and maps. +The braces operator `{}` is used to intialize arrays. + +===== Creating and Initializing Arrays + +You create and initialize arrays using the brackets `[]` and braces `{}` +operators. Each set of brackets represents a dimension. The values you want to +initialize each dimension with are specified as a comma-separated list enclosed +in braces. For example, `new int[] {1, 2, 3}` creates a one dimensional `int` +array with a size of 3 and the values 1, 2, and 3. + +For more information about allocating and initializing arrays, see <>. + +===== Accessing Array Elements + +Elements in an array are stored and accessed using the brackets `[]` operator. +Elements are referenced by an expression enclosed in brackets. An error +occurs if the expression used to reference an element cannot be implicitly +cast to an `int`. + +The range of elements within an array that can be accessed is `[0, size)` where +size is the originally allocated size of the array. To access elements relative +to the last element in an array, you can use a negative numeric value from +`[-size, -1]`. An error occurs if you attempt to reference an element outside +of the array's range. + +*Grammar:* +[source,ANTLR4] +---- +brace_access: '[' expression ']' +---- + +*Examples:* +[source,Java] +---- + +int[] x = new int[2]; // Declare int array x and set it to a newly allocated + // array with a size of 2 +x[0] = 2; // Set the 0th element of array x to 2 +x[1] = 5; // Set the 1st element of array x to 5 +int y = x[0] + x[1]; // Declare the int variable y and set it to the sum + // of the first two elements of array x +int z = 1; // Declare the int variable z and set it to 1 +return x[z]; // Access the 1st element of array x using the + // variable z as an expression and return the value + +def d = new int[2]; // Declare def variable d and set it to a newly + // allocated array with a size of 2 +d[0] = 2; // Set the 0th element of array d to 2 +d[1] = 5; // Set the 1st element of array d to 2 +def y = d[0] + d[1]; // Declare def variable y and set it to the sum + // of the first two elements of array d +def z = 1; // Declare def variable z and set it to 1 +return d[z]; // Access the 1st element of array d using the + // variable z as an expression and return the value +---- + +NOTE: The use of the `def` type in the second example means that the types +cannot be resolved until runtime. + +===== Array Length + +Arrays contain a special member known as 'length' that is a read-only value that contains the size of the array. This member can be accessed from an array using the dot operator. + +*Examples:* +[source,Java] +---- +int[] x = new int[10]; // declares an int array variable x and sets it to a newly allocated array with a size of 10 +int l = x.length; // declares and int variable l and sets it to the field length of variable x +---- + +===== Creating and Initializing Lists + +You create and initialize lists using the brackets `[]` operator. The values +you want to initialize the list with are specified as a comma-separated list +of expressions enclosed in brackets. For example, `List l = [1, 2, 3]` creates +a new three item list. Each expression used to initialize the list is converted +a `def` type when the value is inserted into the list. The order of the +expressions is maintained. + +*Grammar:* +[source,ANTLR4] +---- +list_initialization: '[' expression (',' expression)* ']' + | '[' ']'; +---- + +*Examples:* +[source,Java] +---- +List empty = []; // declares the List variable empty and sets it to a newly initialized empty List +List l0 = [1, 2, 3]; // declares the List variable l0 and sets it to a newly initialized List with the values 1, 2, and 3 + +int i = 1; +long l = 2L; +float f = 3.0F; +double d = 4.0; +String s = "5"; +List l1 = [i, l, f*d, s]; // declares the List variable l1 and sets it to a newly initialized List with the values of i, l, and f*d and s +---- + +===== Accessing List Elements + +Elements in a List are stored or accessed using the brackets operator. The format begins with an opening bracket, followed by an expression, and finishes with a closing bracket. Storing elements in a List is equivalent to invoking a List's set method. Accessing elements in a List is equivalent to invoking a List's get method. Using this operator is strictly a shortcut for the previously mentioned methods. The range of elements within a List that can be accessed is [0, size) where size is the number of elements currently in the List. Elements may also be accessed from the last element in a List using a negative numeric value from [-size, -1]. The expression used to determine which element is accessed must be able to be implicitly cast to an int. An error will occur if the expression is outside of the legal range or is not of type int. + +*Grammar:* +[source,ANTLR4] +---- +list_access: '[' expression ']' +---- + +*Examples:* +[source,Java] +---- +List x = new ArrayList(); // declares a List variable x and sets it to a newly allocated ArrayList +x.add(1); // invokes the add method on the variable x and adds the constant int 1 to the List +x.add(2); // invokes the add method on the variable x and adds the constant int 2 to the List +x.add(3); // invokes the add method on the variable x and adds the constant int 3 to the List +x[0] = 2; // sets the 0th element of the variable x to the constant int 2 +x[1] = 5; // sets the 1st element of the variable x to the constant int 2 +int y = x[0] + x[1]; // declares the int variable y and sets it to the sum of the first two elements of the variable x +int z = 1; // declares the int variable z and sets it to the constant int 1 +return x[z]; // accesses the 1st element of the variable x using the variable z as an expression and returns the value + +def d = new ArrayList(); // declares a def variable d and sets it to a newly allocated ArrayList +d.add(1); // invokes the add method on the variable d and adds the constant int 1 to the List +d.add(2); // invokes the add method on the variable d and adds the constant int 2 to the List +d.add(3); // invokes the add method on the variable d and adds the constant int 3 to the List +d[0] = 2; // sets the 0th element of the variable d to the constant int 2 +d[1] = 5; // sets the 1st element of the variable d to the constant int 2 +def y = d[0] + d[1]; // declares the def variable y and sets it to the sum of the first two elements of the variable d +def z = 1; // declares the def variable z and sets it to the constant int 1 +return d[z]; // accesses the 1st element of the variable d using the variable z as an expression and returns the value +---- + +Note in the first example above all types can be resolved at compile-time, while in the second example all types must wait to be resolved until run-time. + +===== Creating and Initializing Maps + +A Map can be created and initialized using the brackets operator. The format begins with a bracket, followed by an arbitrary number of key-value pairs delimited with commas (except the last), and ends with a closing bracket. Each key-value pair is a set of two expressions separate by a colon. If there is only a single colon with no expressions, a new empty Map is created. + +*Grammar:* +[source,ANTLR4] +---- +map_initialization: '[' key_pair (',' key_pair)* ']' + | '[' ':' ']'; +key_pair: expression ':' expression +---- + +Each expression used as part of the initialization is converted to a `def` type +for insertion into the map. + +*Examples:* +[source,Java] +---- +Map empty = [:]; // declares the Map variable empty and sets it to a newly initialized empty Map +Map m0 = [1:2, 3:4, 5:6]; // declares the Map variable m0 and sets it to a newly initialized Map with the keys 1, 3, 5 and values 2, 4, 6, respectively + +byte b = 0; +int i = 1; +long l = 2L; +float f = 3.0F; +double d = 4.0; +String s = "5"; +Map m1 = [b:i, l:f*d, d:s]; // declares the Map variable m1 and sets it to a newly initialized Map with the keys b, l, d and values i, f*d, s, respectively +---- + +===== Accessing Map Elements + +Elements in a Map can be stored or accessed using the brackets operator. The format begins with an opening bracket, followed by an expression, and finishes with a closing bracket. Storing values in a Map is equivalent to invoking a Map's put method. Accessing values in a Map is equivalent to invoking a Map's get method. Using this operator is strictly a shortcut for the previously mentioned methods. Any element from a Map can be stored/accessed where the expression is the key. If a key has no corresponding value when accessing a Map then the value will be null. + +*Grammar:* +[source,ANTLR4] +---- +map_access: '[' expression ']' +---- + +*Examples:* +[source,Java] +---- +Map x = new HashMap(); // declares a Map variable x and sets it to a newly allocated HashMap +x['value2'] = 2; // puts the value of the key constant String value2 of the variable x to the constant int 2 +x['value5'] = 5; // puts the value of the key constant String value5 of the variable x to the constant int 5 +int y = x['value2'] + x['value5']; // declares the int variable y and sets it to the sum of the two values of the variable x +String z = 'value5'; // declares the String variable z and sets it to the constant String value5 +return x[z]; // accesses the value for the key value5 of the variable x using the variable z as an expression and returns the value + +def d = new HashMap(); // declares a def variable d and sets it to a newly allocated HashMap +d['value2'] = 2; // puts the value of the key constant String value2 of the variable d to the constant int 2 +d['value5'] = 5; // puts the value of the key constant String value5 of the variable d to the constant int 5 +int y = d['value2'] + d['value5']; // declares the int variable y and sets it to the sum of the two values of the variable d +String z = 'value5'; // declares the String variable z and sets it to the constant String value5 +return d[z]; // accesses the value for the key value5 of the variable x using the variable z as an expression and returns the value +---- + +Note in the first example above all types can be resolved at compile-time, while in the second example all types must wait to be resolved until run-time. + +==== Post Increment + +A variable/field representing a numerical value can be possibly evaluated as part of an expression, and then increased by 1 for its respective type. The format starts with a variable name followed by a plus and ends with a plus. + +*Grammar:* +[source,ANTLR4] +---- +post_increment: ( variable | member ) '++' +---- + +A numeric promotion may occur during a post-increment followed by a downcast if necessary. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. A downcast may be required after the type promotion to assign the appropriate value back into the variable/field. Non-numeric variables/members will result in an error. + +Promotion Table: + +|==== +|from|to|downcast +|byte|int|byte +|short|int|short +|char|int|char +|int|int| +|long|long| +|float|float| +|double|double| +|def|def| +|==== + +Examples(s): +[source,Java] +---- +int i = 0; // declares the int variable i and sets it to the constant 0 +i++; // increments the int variable i by 1 to a value of 1 +long l = 1; // declares the long variable l and set it the constant 1 +long k; // declares the long variable k +k = l++; // sets the long variable k to the value of l (1), and then increments the long variable l by 1 to a value of 2 +---- + +==== Post Decrement + +A variable/field representing a numerical value can be possibly evaluated as part of an expression, and then increased by 1 for its respective type. The format starts with a variable name followed by a minus and ends with a minus. + +*Grammar:* +[source,ANTLR4] +---- +post_increment: ( variable | member ) '--' +---- + +A numeric promotion may occur during a post-decrement followed by a downcast if necessary. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. A downcast may be required after the type promotion to assign the appropriate value back into the variable/field. Non-numeric variables/members will result in an error. + +Promotion Table: + +|==== +|from|to|downcast +|byte|int|byte +|short|int|short +|char|int|char +|int|int| +|long|long| +|float|float| +|double|double| +|def|def| +|==== + +Examples(s): +[source,Java] +---- +short i = 0; // declares the short variable i and sets it to the constant short 0 +i--; // decrements the short variable i by 1 to a value of -1 (promoted to int and downcast to short) +float l = 1.0f; // declares the float variable l and sets it the constant float 1.0f +float k; // declares the float variable k +k = l--; // sets the float variable k to the value of l (1.0f), and then decrements the float variable l by 1.0 to a value of 0.0 +---- + +==== Pre Increment + +A variable/field representing a numerical value can be increased by 1 for its respective type, and then possibly evaluated as part of an expression. The format starts with a plus followed by a plus and ends with a variable name. + +*Grammar:* +[source,ANTLR4] +---- +pre_increment: '++' ( variable | member ) +---- + +A numeric promotion may occur during a pre-increment followed by a downcast if necessary. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. A downcast may be required after the type promotion to assign the appropriate value back into the variable/field. Non-numeric variables/members will result in an error. + +Promotion Table: + +|==== +|from|to|downcast +|byte|int|byte +|short|int|short +|char|int|char +|int|int| +|long|long| +|float|float| +|double|double| +|def|def| +|==== + +Examples(s): +[source,Java] +---- +int i = 0; // declares the int variable i and sets it to the constant int 0 +++i; // increments the int variable i by 1 to a value of 1 +long l = 1; // declares the long variable l and sets it to the constant long 1 +long k; // declares the long variable k +k = ++l; // increments the long variable l by 1 to a value of 2, and then sets the long variable k to the value of l (2) +---- + +==== Pre Decrement + +A variable/field representing a numerical value can be decreased by 1 for its respective type, and then possibly evaluated as part of an expression. The format starts with a minus followed by a minus and ends with a variable name. + +*Grammar:* +[source,ANTLR4] +---- +pre_decrement: '--' ( variable | member ) +---- + +A numeric promotion may occur during a pre-decrement followed by a downcast if necessary. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. A downcast may be required after the type promotion to assign the appropriate value back into the variable/field. Non-numeric variables/members will result in an error. + +Promotion Table: +|==== +|from|to|downcast +|byte|int|byte +|short|int|short +|char|int|char +|int|int| +|long|long| +|float|float| +|double|double| +|def|def| +|==== + +Examples(s): +[source,Java] +---- +byte i = 1; // declares the byte variable i and sets it to the constant int 1 +--i; // decrements the byte variable i by 1 to a value of 0 (promoted to int and downcast to byte) +double l = 1.0; // declares the double variable l and sets it to the constant double 1.0 +double k; // declares the double variable k +k = --l; // decrements the double variable l by 1.0 to a value of 0.0, and then sets the double variable k to the value of l (0.0) +---- + +==== Unary Positive + +Unary positive gives the identity of a numerical value using the plus operator. In practice this is usually a no-op, but will cause some numeric types to be promoted. Format starts with a plus operator followed by a numerical expression. + +*Grammar:* +[source,ANTLR4] +---- +unary_positive: '+' expression +---- + +A numeric promotion may occur during a unary positive operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric expressions will result in an error. + +Promotion Table: +|==== +|from|to +|byte|int +|short|int +|char|int +|int|int +|long|long +|float|float +|double|double +|def|def +|==== + +*Examples:* +[source,Java] +---- +int x = +1; // declares the int variable x and sets it to positive 1 +long y = +x; // declares the long variable y and sets it to positive x (promoted to long from int) +def z = +y; // declares the def variable z and sets it to positive y +byte z = +2; //ERROR: cannot implicitly downcast an int to a byte +---- + +==== Unary Negative + +Unary negative negates a numeric value using the minus operator. Format starts with a minus followed by a numerical expression. + +*Grammar:* +[source,ANTLR4] +---- +unary_negative: '-' expression +---- + +A numeric promotion may occur during a unary negative operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric expressions will result in an error. + +Promotion Table: +|==== +|from|to +|byte|int +|short|int +|char|int +|int|int +|long|long +|float|float +|double|double +|def|def +|==== + +*Examples:* +[source,Java] +---- +int x = -1; // declares the int variable x and sets it to negative 1 +long y = -x; // declares the long variable y and sets it to negative x (promoted to long from int) +def z = -y; // declares the def variable z and sets it to negative y +byte z = -2; //ERROR: cannot implicitly downcast an int to a byte +---- + +==== Boolean Not + +Boolean not will flip a boolean value from true to false or false to true using the bang operator. The format is a bang operator followed by an expression. + +*Grammar:* +[source,ANTLR4] +---- +boolean_not: '!' expression; +---- + +Note that def types will be assumed to be of the boolean type. Any def type evaluated at run-time that does not represent a boolean will result in an error. Non-boolean expressions will result in an error. + +*Examples:* +[source,Java] +---- +boolean x = !false; // declares the boolean variable x and sets it to the the opposite of the false value +boolean y = !x; // declares the boolean variable y and sets it to the opposite of the boolean variable x +def z = !y; // declares the def variable z and sets it to the opposite of the boolean variable y +---- + +==== Bitwise Not + +Bitwise not will flip each bit of an integer type expression. The format is the tilde operator followed by an expression. + +*Grammar:* +[source,ANTLR4] +---- +bitwise_not: '~' expression; +---- + +A numeric promotion may occur during unary positive operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-integer expressions will result in an error. + +Promotion Table: +|==== +|from|to +|byte|int +|short|int +|char|int +|int|int +|long|long +|def|def +|==== + +*Examples:* +[source,Java] +---- +byte x = 1; // declares the byte variable x and sets it to a constant int 1 +int y = ~x; // declares the int variable y and sets it to the negation of x +long z = ~y; // declares the long variable z and sets it the negation of y +def d = ~z; // declares the def variable d and sets it the negation of z +def e; // declares the def variable e +e = ~d; // sets e the negation of d +---- + +==== Cast + +The cast operator can be used to explicitly convert one type to another. See casting [MARK] for more information. + +==== Constructor Call + +A constructor call is a special type of method call [MARK] used to allocate a reference type instance using the new operator. The format is the new operator followed by a type, an opening parenthesis, arguments if any, and a closing parenthesis. Arguments are a series of zero-to-many expressions delimited by commas. Auto-boxing and auto-unboxing will be applied automatically for arguments passed into a constructor call. See boxing and unboxing [MARK] for more information on this topic. Constructor argument types can always be resolved at run-time; if appropriate type conversions (casting) cannot be applied an error will occur. Once a reference type instance has been allocated, its members may be used as part of other expressions. + +Constructor calls may be overloaded based on arity in Painless. This means the same reference type may have multiple constructors as long as the number of arguments differs for each one. This does have an effect on some of the provided reference type constructors in the Painless API [MARK]. When there are overloaded constructors with the same arity for a reference type in Java a single constructor must be chosen to be provided in Painless. + +*Grammar:* +[source,ANTLR4] +---- +constructor_call: 'new' TYPE '(' (expression (',' expression)*)? ')'; +---- + +*Examples:* +[source,Java] +---- +Map m = new HashMap(); // declares the Map variable m and sets it to a newly allocated HashMap using an empty constructor +m.put(3, 3); // invokes the method call member put and adds the key-value pair of 3 to Map variable m +def d = new ArrayList(); // declares the def variable d and sets it to a newly allocated ArrayList using an empty constructor +def e; // declares the def variable e +e = new HashMap(m); // sets e to a newly allocated HashMap using the constructor with a single argument m +---- + +==== New Array + +An array type instance can be allocated using the new operator. The format starts with the new operator followed by the type followed by a series of opening and closing braces each containing an expression for the size of the dimension. + +*Grammar:* +[source,ANTLR4] +---- +new_array: 'new' TYPE ('[' expression ']')+; +---- + +*Examples:* +[source,Java] +---- +int[] x = new int[5]; // declares an int array variable x and sets it to a newly allocated array with a size of 5 +x = new int[10]; // sets the int array variable x to a newly allocated array with a size of 10 +def[][] y = new def[5][5]; // declares a 2-dimensional def array variable y and set it to a newly + // allocated 2-dimensional array where both dimensions have a size of 5 +---- + +==== Multiplication + +Multiplies two numerical expressions. Rules for resultant overflow and NaN values follow the Java specification. The format is an expression, followed by the star operator, and a closing expression. + +*Grammar:* +[source,ANTLR4] +---- +multiplication: expression '*' expression; +---- + +A numeric promotion may occur during a multiplication operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric numbers will result in an error. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +int x = 5*4; // declares the int variable x and sets it to the result of 5 multiplied by 4 +double y = x*7.0; // declares the double variable y and sets it to the result of x multiplied by 7.0 (x is promoted to a double) +def z = x*y; // declares the def variable z and sets it to the result of x multiplied by y (x is promoted to a double) +def a = z*x; // declares the def variable a and sets it to the result of z multiplied by x (x is promoted to def at compile-time and double at run-time) +---- + +==== Division + +Divides two numerical expressions. Rules for NaN values and division by zero follow the Java specification. Integer division will drop the remainder of the resultant value. The format is an expression, followed by the slash operator, and a closing expression. + +*Grammar:* +[source,ANTLR4] +---- +division: expression '/' expression; +---- + +A numeric promotion may occur during a division operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric expressions will result in an error. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +int x = 5/4; // declares the int variable x and sets it to the result of 5 divided by 4 +double y = x/7.0; // declares the double variable y and sets it to the result of x divided by 7.0 (x is promoted to a double) +def z = x/y; // declares the def variable z and sets it to the result of x divided by y (x is promoted to a double) +def a = z/x; // declares the def variable a and sets it to the result of z divided by x (x is promoted to def at compile-time and double at run-time) +---- + +==== Remainder + +Calculates the remainder for division between two numerical expressions. Rules for NaN values and division by zero follow the Java specification. The format is an expression, followed by the percent operator, and a closing expression. + +*Grammar:* +[source,ANTLR4] +---- +remainder: expression '%' expression; +---- + +A numeric promotion may occur during a remainder operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric expressions will result in an error. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +int x = 5%4; // declares the int variable x and sets it to the remainder of 5 divided by 4 +double y = x%7.0; // declares the double variable y and sets it to the remainder of x divided by 7.0 (x is promoted to a double) +def z = x%y; // declares the def variable z and sets it to the remainder of x divided by y (x is promoted to a double) +def a = z%x; // declares the def variable a and sets it to the remainder of z divided by x (x is promoted to def at compile-time and double at run-time) +---- + +==== String Concatenation + +Concatenates two expressions together as a single String where at least of one of the expressions is a String to begin with. The format is an expression, followed by a plus operator, and a closing expression. + +*Grammar:* +[source,ANTLR4] +---- +concatenate: expression '+' expression; +---- + +*Examples:* +[source,Java] +---- +String x = "con"; // declares the String variable x and sets it to the String constant "con" +String y = x + "cat"; // declares the String variable y and sets it to the concatenation of the String variable x and the String constant "cat" +String z = 4 + x; // declares the String variable z and sets it to the concatenation of the int constant 4 and the String variable x (4 is implicitly cast to a String) +def d = 2; // declares the def variable d and sets it to the int constant 2 +z = z + d; // sets the String variable z to the concatenation of the String variable z +d = "con" + x + y + "cat"; // sets the def variable d to the concatenation of String constant "con", x, y, and the String constant "cat" +---- + +==== Addition + +Adds two numerical expressions. Rules for resultant overflow and NaN values follow the Java specification. The format is an expression, followed by the plus operator, and a closing expression. + +*Grammar:* +[source,ANTLR4] +---- +addition: expression '+' expression; +---- + +A numeric promotion may occur during a addition operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric expressions will result in an error, except in the case of String which then implies the operation is string concatenation [MARK] rather than addition. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +int x = 5 + 4; // declares the int variable x and sets it to the result of 5 added to 4 +double y = x + 7.0; // declares the double variable y and sets it to the result of x added to 7.0 (x is promoted to a double) +def z = x + y; // declares the def variable z and sets it to the result of x added to y (x is promoted to a double) +def a = z + x; // declares the def variable a and sets it to the result of z added to x (x is promoted to def at compile-time and double at run-time) +---- + +==== Subtraction + +Subtracts two numerical expressions. Rules for resultant overflow and NaN values follow the Java specification. The format is an expression, followed by the minus operator, and a closing expression. + +*Grammar:* +[source,ANTLR4] +---- +subtraction: expression '-' expression; +---- + +A numeric promotion may occur during a subtraction operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric expressions will result in an error. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +int x = 5-4; // declares the int variable x and sets it to the result of 4 subtracted from 5 +double y = x-7.0; // declares the double variable y and sets it to the result of 7.0 subtracted from x (x is promoted to a double) +def z = x-y; // declares the def variable z and sets it to the result of y subtracted from x (x is promoted to a double) +def a = z-x; // declares the def variable a and sets it to the result of x subtracted from z (x is promoted to def at compile-time and double at run-time) +---- + +==== Left Shift + +Shifts lower order bits to higher order bits in the left-side expression by the distance specified in the right-side expression. The format is an expression followed by two left-carrots, and a closing expression. + +*Grammar:* +[source,ANTLR4] +---- +left_shift: expression '<<' expression; +---- + +A numeric promotion may occur during a left shift operation to the left-side expression. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric and floating point expressions will result in an error. + +Promotion Table: +|==== +|from|to +|byte|int +|short|int +|char|int +|int|int +|long|long +|def|def +|==== + +The right-side expression will be explicitly cast to an int value and truncated based on the promoted type of the left-side expression. If the left-side expression is of type int then the lowest order 5-bits will be taken as the distance to shift from the right-side expression (0-31). If the left-side expression is of type long then the lowest order 6-bits will be taken as the distance to shift from the right-side expression (0-63). Non-numeric and floating point expressions will result in an error. + +*Examples:* +[source,Java] +---- +int x = 5 << 4; // declares the int variable x and sets it to the result of 5 left shifted by 4 +long y = x << 7; // declares the long variable y and sets it to the result of x left shifted by 7 (x is promoted to a long) +def z = x << y; // declares the def variable z and sets it to the result of x left shifted by y +def a = z << x; // declares the def variable a and sets it to the result of z left shifted by x +---- + +==== Right Shift + +Shifts higher order bits to lower order bits in the left-side expression by the distance specified in the right-side expression. Right shift will preserve the signed bit (highest order bit) as part of the result. The format is an expression followed by two right-carrots, and a closing expression. + +*Grammar:* +[source,ANTLR4] +---- +right_shift: expression '>>' expression; +---- + +A numeric promotion may occur during a right shift operation to the left-side expression. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric and floating point expressions will result in an error. + +Promotion Table: +|==== +|from|to +|byte|int +|short|int +|char|int +|int|int +|long|long +|def|def +|==== + +The right-side expression will be explicitly cast to an int value and truncated based on the promoted type of the left-side expression. If the left-side expression is of type int then the lowest order 5-bits will be taken as the distance to shift from the right-side expression (0-31). If the left-side expression is of type long then the lowest order 6-bits will be taken as the distance to shift from the right-side expression (0-63). Non-numeric and floating point expressions will result in an error. + +*Examples:* +[source,Java] +---- +int x = 5 >> 4; // declares the int variable x and sets it to the result of 5 right shifted by 4 +long y = x >> 7; // declares the long variable y and sets it to the result of x right shifted by 7 (x is promoted to a long) +def z = x >> y; // declares the def variable z and sets it to the result of x right shifted by y +def a = z >> x; // declares the def variable a and sets it to the result of z right shifted by x +---- + +==== Unsigned Right Shift + +Shifts higher order bits to lower order bits in the left-side expression by the distance specified in the right-side expression. Unsigned right shift will not preserve the signed bit (highest order bit) as part of the result. The format is an expression followed by three right-carrots, and a closing expression. + +*Grammar:* +[source,ANTLR4] +---- +unsigned_right_shift: expression '>>>' expression; +---- + +A numeric promotion may occur during an unsigned right shift operation to the left-side expression. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric and floating point expressions will result in an error. + +Promotion Table: +|==== +|from|to +|byte|int +|short|int +|char|int +|int|int +|long|long +|def|def +|==== + +The right-side expression will be explicitly cast to an int value and truncated based on the promoted type of the left-side expression. If the left-side expression is of type int then the lowest order 5-bits will be taken as the distance to shift from the right-side expression (0-31). If the left-side expression is of type long then the lowest order 6-bits will be taken as the distance to shift from the right-side expression (0-63). Non-numeric and floating point expressions will result in an error. + +*Examples:* +[source,Java] +---- +int x = 5 >> 4; // declares the int variable x and sets it to the result of 5 unsigned right shifted by 4 +long y = x >> 7; // declares the long variable y and sets it to the result of x unsigned right shifted by 7 (x is promoted to a long) +def z = x >> y; // declares the def variable z and sets it to the result of x unsigned right shifted by y +def a = z >> x; // declares the def variable a and sets it to the result of z unsigned right shifted by x +---- + +==== Greater Than + +Greater than compares two numerical expressions where a resultant boolean value will be true if the left-side expression is a larger value than the right-side expression otherwise false. The format is an expression, followed by the right angle operator, and a closing expression. + +*Grammar:* +[source,ANTLR4] +---- +greater_than: expression '>' expression; +---- + +A numeric promotion may occur during a greater than operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric expressions will result in an error. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +boolean x = 5 > 4; // declares the int variable x and sets it to the result of 5 greater than 4 +double y = 7.0; // declares the double variable y and sets it to the double constant 7.0 +def z = y > 6.5; // declares the def variable z and sets it to the result of y greater than 6.5 +def a = y > x; // declares the def variable a and sets it to the result of y greater than z (x is promoted to double at compile-time) +---- + +==== Greater Than Or Equal + +Greater than or equal compares two numerical expressions where a resultant boolean value will be true if the left-side expression is a larger value than or equal to the right-side expression otherwise false. The format is an expression, followed by the right angle and equals operator, and a closing expression. + +*Grammar:* +[source,ANTLR4] +---- +greater_than_or_equal: expression '>=' expression; +---- + +A numeric promotion may occur during a greater than or equal operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric expressions will result in an error. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +boolean x = 5 >= 4; // declares the int variable x and sets it to the result of 5 greater than or equal to 4 +double y = 7.0; // declares the double variable y and sets it to the double constant 7.0 +def z = y >= 6.5; // declares the def variable z and sets it to the result of y greater than or equal to 6.5 +def a = y >= x; // declares the def variable a and sets it to the result of y greater than or equal to z (x is promoted to double at compile-time) +---- + +==== Less Than + +Less than compares two numerical expressions where a resultant boolean value will be true if the left-side expression is a smaller value than the right-side expression otherwise false. The format is an expression, followed by the left angle operator, and a closing expression. + +*Grammar:* +[source,ANTLR4] +---- +less_than: expression '<' expression; +---- + +A numeric promotion may occur during a less than operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric expressions will result in an error. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +boolean x = 5 < 4; // declares the int variable x and sets it to the result of 5 less than 4 +double y = 7.0; // declares the double variable y and sets it to the double constant 7.0 +def z = y < 6.5; // declares the def variable z and sets it to the result of y less than 6.5 +def a = y < x; // declares the def variable a and sets it to the result of y less than z (x is promoted to double at compile-time) +---- + +==== Less Than Or Equal + +Less than or equal compares two numerical expressions where a resultant boolean value will be true if the left-side expression is a larger value than or equal to the right-side expression otherwise false. The format is an expression, followed by the left angle and equals operator, and a closing expression. + +*Grammar:* +[source,ANTLR4] +---- +less_than_or_equal: expression '<=' expression; +---- + +A numeric promotion may occur during a less than or equal operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-numeric expressions will result in an error. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +boolean x = 5 <= 4; // declares the int variable x and sets it to the result of 5 less than or equal to 4 +double y = 7.0; // declares the double variable y and sets it to the double constant 7.0 +def z = y <= 6.5; // declares the def variable z and sets it to the result of y less than or equal to 6.5 +def a = y <= x; // declares the def variable a and sets it to the result of y less than or equal to z (x is promoted to double at compile-time) +---- + +==== Instance Of + +The instanceof operator can be used to compare a variable's type to a specified reference type where a resultant boolean value is true if the variable type is the same as or a descendant of the specified reference type and false otherwise. The format is an id, followed by the instanceof operator, and finished with a type. + +*Grammar:* +[source,ANTLR4] +---- +instance_of: ID 'instanceof' TYPE; +---- + +*Examples:* +[source,Java] +---- +Map x = new HashMap(); // declares the Map variable x and sets it to a newly allocated HashMap +List y = new ArrayList(); // declares the List variable y and sets it to a newly allocated ArrayList +def z = y; // declares the def variable z and sets it to y +boolean a = x instanceof HashMap; // declares the boolean variable a and sets it to true since x's type is the same type as HashMap +boolean b = y instanceof Map; // declares the boolean variable b and sets it to false since y's type is not the same type as Map or a descendant of Map +boolean c = z instanceof List; // declares the boolean variable c and sets it to true since z's type is a descendant of the type List +---- + +==== Equality Equals + +Equality equals compares two expressions where a resultant boolean value is true if the two expressions are equal and false otherwise. When reference types are compared using this operator the equivalent of the equals member method will be called against the first expression, where the second expression is the argument. Though the equals member method is used for reference types, this operation will always be null-safe. Valid comparisons are between boolean types, primitive numeric types, and reference types. If a comparison is made that is not listed as one of the valid comparisons an error will occur. The format is an expression, followed by the equals-equals operator, and finished with an expression. + +*Grammar:* +[source,ANTLR4] +---- +equality_equals: expression '==' expression; +---- + +A numeric type promotion may occur during a primitive numeric comparison. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +boolean b0 = true; // declares the boolean variable b0 and sets it the constant boolean true +boolean b1 = false; // declares the boolean variable b1 and sets it the constant boolean false +int i = 2; // declares the int variable i and sets it the constant int 2 +float f = 2.0f; // declares the float variable f and sets it the constant float 2.0 +List l0 = new ArrayList(); // declares the List variable l0 and sets it to a newly allocated ArrayList +ArrayList l1 = new ArrayList(); // declares the ArrayList variable l1 and sets it to a newly allocated ArrayList +def di0 = 2; // declares the def variable di0 and sets it the constant int 2 +def di1 = 3; // declares the def variable di1 and sets it the constant int 3 +def dl = new ArrayList(); // declares the def variable dl and sets it to a newly allocated ArrayList +boolean result; // declares the boolean variable result + +result = b0 == b1; // compares b0 to b1 and has a boolean result of false +result = i == f; // compares i to f where i is promoted to float and has a boolean result of true +result = b0 == i; // ERROR: a comparison between a boolean and a primitive numeric type is illegal +result = i == l0; // ERROR: a comparison between a primitive numeric type and a reference type is illegal + +l0.add(1); // adds a constant int 1 to the List l0 +l1.add(1); // adds a constant int 1 to the ArrayList l1 +result = l0 == l1; // compares l0 to l1 using l0.equals(l1) and has a boolean result of true +l0.add(1); // adds a constant int 1 to the List l0 +result = l0 == l1; // compares l0 to l1 using l0.equals(l1) and has a boolean result of false + +result = di0 == di1; // compares di0 to di1 and has a boolean result of false +result = di0 == i; // compares di0 to i where i is promoted to def and has a boolean result of true + +dl.add(1); // adds a constant int 1 to the def ArrayList dl +result = dl == l0; // compares dl to l0 using dl.equals(l0) with a boolean result of true + +result = null == dl; // compares null to dl with a boolean result of false +result = l1 == null; // compares l1 to null with a boolean result of false +---- + +==== Equality Not Equals + +Equality not equals compares two expressions where a resultant boolean value is true if the two expressions are not equal and false otherwise. When reference types are compared using this operator the equivalent of the equals member method will be called against the first expression, where the second expression is the argument, with the resultant boolean being reversed. Though the equals member method is used for reference types, this operation will always be null-safe. Valid comparisons are between boolean types, primitive numeric types, and reference types. If a comparison is made that is not listed as one of the valid comparisons an error will occur. The format is an expression, followed by the bang-equals operator, and finished with an expression. + +*Grammar:* +[source,ANTLR4] +---- +equality_not_equals: expression '!=' expression; +---- + +A numeric type promotion may occur during a primitive numeric comparison. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +boolean b0 = true; // declares the boolean variable b0 and sets it the constant boolean true +boolean b1 = false; // declares the boolean variable b1 and sets it the constant boolean false +int i = 2; // declares the int variable i and sets it the constant int 2 +float f = 2.0f; // declares the float variable f and sets it the constant float 2.0 +List l0 = new ArrayList(); // declares the List variable l0 and sets it to a newly allocated ArrayList +ArrayList l1 = new ArrayList(); // declares the ArrayList variable l1 and sets it to a newly allocated ArrayList +def di0 = 2; // declares the def variable di0 and sets it the constant int 2 +def di1 = 3; // declares the def variable di1 and sets it the constant int 3 +def dl = new ArrayList(); // declares the def variable dl and sets it to a newly allocated ArrayList +boolean result; // declares the boolean variable result + +result = b0 != b1; // compares b0 to b1 and has a boolean result of true +result = i != f; // compares i to f where i is promoted to float and has a boolean result of false +result = b0 != i; // ERROR: a comparison between a boolean and a primitive numeric type is illegal +result = i != l0; // ERROR: a comparison between a primitive numeric type and a reference type is illegal + +l0.add(1); // adds a constant int 1 to the List l0 +l1.add(1); // adds a constant int 1 to the ArrayList l1 +result = l0 != l1; // compares l0 to l1 using l0.equals(l1) and has a boolean result of false +l0.add(1); // adds a constant int 1 to the List l0 +result = l0 != l1; // compares l0 to l1 using l0.equals(l1) and has a boolean result of true + +result = di0 != di1; // compares di0 to di1 and has a boolean result of true +result = di0 != i; // compares di0 to i where i is promoted to def and has a boolean result of false + +dl.add(1); // adds a constant int 1 to the def ArrayList dl +result = dl != l0; // compares dl to l0 using dl.equals(l0) with a boolean result of false + +result = null != dl; // compares null to dl with a boolean result of true +result = l1 != null; // compares null to l1 with a boolean result of true +---- + +==== Identity Equals + +Identity equals compares two expressions where a resultant boolean value is true if the two expressions are equal and false otherwise. Two primitive types are considered to be equal if they have the same value. Two reference types are considered to be equal if they refer to the exact same instance in memory or are both null. Valid comparisons are between boolean types, primitive numeric types, and reference types. If a comparison is made that is not listed as one of the valid comparisons an error will occur. The format is an expression, followed by the equals-equals-equals operator, and finished with an expression. + +*Grammar:* +[source,ANTLR4] +---- +identity_equals: expression '===' expression; +---- + +A numeric type promotion may occur during a primitive numeric comparison. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +boolean b0 = true; // declares the boolean variable b0 and sets it the constant boolean true +boolean b1 = false; // declares the boolean variable b1 and sets it the constant boolean false +int i = 2; // declares the int variable i and sets it the constant int 2 +float f = 2.0f; // declares the float variable f and sets it the constant float 2.0 +List l0 = new ArrayList(); // declares the List variable l0 and sets it to a newly allocated ArrayList +ArrayList l1 = new ArrayList(); // declares the ArrayList variable l1 and sets it to a newly allocated ArrayList +List l2 = l1; // declares the List variable l2 and sets it to l1 +def di0 = 2; // declares the def variable di0 and sets it the constant int 2 +def di1 = 3; // declares the def variable di1 and sets it the constant int 3 +def dl = l0; // declares the def variable dl and sets it to l0 +boolean result; // declares the boolean variable result + +result = b0 === b1; // compares b0 to b1 and has a boolean result of false +result = i === f; // compares i to f where i is promoted to float and has a boolean result of true +result = b0 === i; // ERROR: a comparison between a boolean and a primitive numeric type is illegal +result = i === l0; // ERROR: a comparison between a primitive numeric type and a reference type is illegal + +l0.add(1); // adds a constant int 1 to the List l0 +l1.add(1); // adds a constant int 1 to the ArrayList l1 +result = l0 === l1; // compares l0 to l1 and has a boolean result of false +l0.add(1); // adds a constant int 1 to the List l0 +result = l0 === l1; // compares l0 to l1 and has a boolean result of false +result = l1 === l2; // compares l1 to l2 and has a boolean result of true + +result = di0 === di1; // compares di0 to di1 and has a boolean result of false +result = di0 === i; // compares di0 to i where i is promoted to def and has a boolean result of true + +result = dl === l0; // compares dl to l0 with a boolean result of true + +result = null === dl; // compares null to dl with a boolean result of false +result = l1 === null; // compares null to l1 with a boolean result of false +---- + +==== Identity Not Equals + +Identity not equals compares two expressions where a resultant boolean value is true if the two expressions are not equal and false otherwise. Two primitive types are considered to be not equal if they have different values. Two reference types are considered to be not equal if they refer to the different instances in memory or one is null and the other is not. Valid comparisons are between boolean types, primitive numeric types, and reference types. If a comparison is made that is not listed as one of the valid comparisons an error will occur. The format is an expression, followed by the bang-equals-equals operator, and finished with an expression. + +*Grammar:* +[source,ANTLR4] +---- +identity_not_equals: expression '!==' expression; +---- + +A numeric type promotion may occur during a primitive numeric comparison. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +boolean b0 = true; // declares the boolean variable b0 and sets it the constant boolean true +boolean b1 = false; // declares the boolean variable b1 and sets it the constant boolean false +int i = 2; // declares the int variable i and sets it the constant int 2 +float f = 2.0f; // declares the float variable f and sets it the constant float 2.0 +List l0 = new ArrayList(); // declares the List variable l0 and sets it to a newly allocated ArrayList +ArrayList l1 = new ArrayList(); // declares the ArrayList variable l1 and sets it to a newly allocated ArrayList +List l2 = l1; // declares the List variable l2 and sets it to l1 +def di0 = 2; // declares the def variable di0 and sets it the constant int 2 +def di1 = 3; // declares the def variable di1 and sets it the constant int 3 +def dl = l0; // declares the def variable dl and sets it to l0 +boolean result; // declares the boolean variable result + +result = b0 !== b1; // compares b0 to b1 and has a boolean result of true +result = i !== f; // compares i to f where i is promoted to float and has a boolean result of false +result = b0 !== i; // ERROR: a comparison between a boolean and a primitive numeric type is illegal +result = i !== l0; // ERROR: a comparison between a primitive numeric type and a reference type is illegal + +l0.add(1); // adds a constant int 1 to the List l0 +l1.add(1); // adds a constant int 1 to the ArrayList l1 +result = l0 !== l1; // compares l0 to l1 and has a boolean result of true +l0.add(1); // adds a constant int 1 to the List l0 +result = l0 !== l1; // compares l0 to l1 and has a boolean result of true +result = l1 !== l2; // compares l1 to l2 and has a boolean result of false + +result = di0 !== di1; // compares di0 to di1 and has a boolean result of true +result = di0 !== i; // compares di0 to i where i is promoted to def and has a boolean result of false + +result = dl !== l0; // compares dl to l0 with a boolean result of false + +result = null !== dl; // compares null to dl with a boolean result of true +result = l1 !== null; // compares null to l1 with a boolean result of true +---- + +==== Bitwise And + +Bitwise and will and together two integer type expressions. The table below shows what each resultant bit will in the resultant integer type value be based on the corresponding bit in each integer type expression. + +|==== +||1|0 +|1|1|0 +|0|0|0 +|==== + +The format starts with an expression, follows with the ampersand operator, and finishes with an expression. + +*Grammar:* +[source,ANTLR4] +---- +bitwise_and: expression '&' expression; +---- + +A numeric promotion may occur during a bitwise and operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-integer expressions will result in an error. + +Promotion Table: +|==== +||byte|short|char|int|long|def +|byte|int|int|int|int|long|def +|short|int|int|int|int|long|def +|char|int|int|int|int|long|def +|int|int|int|int|int|long|def +|long|long|long|long|long|long|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +byte x = 16; // declares the byte variable x and sets it to a constant int 1 +int y = x & 4; // declares the int variable y and sets it to the result of x and 4 +long z = y & x; // declares the long variable z and sets it the result of y and x +def d = z & 2; // declares the def variable d and sets it the result of z and 2 +def e; // declares the def variable e +e = d & z; // sets e to the result of d and z +---- + +==== Boolean Xor + +Boolean xor will xor together two boolean expressions. The table below shows what the resultant boolean value will be based on the two boolean expressions. + +|==== +||true|false +|true|false|true +|false|true|false +|==== + +The format starts with an expression, follows with the carrot operator, and finishes with an expression. + +*Grammar:* +[source,ANTLR4] +---- +boolean_xor: expression '^' expression; +---- + +Note that def types will be assumed to be of the boolean type. Any def type evaluated at run-time that does not represent a boolean will result in an error. Non-boolean expressions will result in an error. + +*Examples:* +[source,Java] +---- +boolean x = false; // declares the boolean variable x and sets the constant boolean false +boolean y = x ^ true; // declares the boolean variable y and sets it the result of x xor true +def z = y ^ x; // declares the def variable z and sets it to the result of y xor x +---- + +==== Bitwise Xor + +Bitwise xor will xor together two integer type expressions. The table below shows what each resultant bit will in the resultant integer type value be based on the corresponding bit in each integer type expression. + +|==== +||1|0 +|1|0|1 +|0|1|0 +|==== + +The format starts with an expression, follows with the carrot operator, and finishes with an expression. + +*Grammar:* +[source,ANTLR4] +---- +bitwise_xor: expression '^' expression; +---- + +A numeric promotion may occur during a bitwise xor operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-integer expressions will result in an error. + +Promotion Table: +|==== +||byte|short|char|int|long|def +|byte|int|int|int|int|long|def +|short|int|int|int|int|long|def +|char|int|int|int|int|long|def +|int|int|int|int|int|long|def +|long|long|long|long|long|long|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +byte x = 16; // declares the byte variable x and sets it to a constant int 1 +int y = x ^ 4; // declares the int variable y and sets it to the result of x xor 4 +long z = y ^ x; // declares the long variable z and sets it the result of y xor x +def d = z ^ 2; // declares the def variable d and sets it the result of z xor 2 +def e; // declares the def variable e +e = d ^ z; // sets e to the result of d xor z +---- + +==== Bitwise Or + +Bitwise or will or together two integer type expressions. The table below shows what each resultant bit will in the resultant integer type value be based on the corresponding bit in each integer type expression. + +|==== +||1|0 +|1|1|1 +|0|1|0 +|==== + +The format starts with an expression, follows with the pipe operator, and finishes with an expression. + +*Grammar:* +[source,ANTLR4] +---- +bitwise_or: expression '|' expression; +---- + +A numeric promotion may occur during a bitwise xor operation. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. Non-integer expressions will result in an error. + +Promotion Table: +|==== +||byte|short|char|int|long|def +|byte|int|int|int|int|long|def +|short|int|int|int|int|long|def +|char|int|int|int|int|long|def +|int|int|int|int|int|long|def +|long|long|long|long|long|long|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +byte x = 16; // declares the byte variable x and sets it to a constant int 1 +int y = x | 4; // declares the int variable y and sets it to the result of x or 4 +long z = y | x; // declares the long variable z and sets it the result of y or x +def d = z | 2; // declares the def variable d and sets it the result of z or 2 +def e; // declares the def variable e +e = d | z; // sets e to the result of d or z +---- + +==== Boolean And + +Boolean and will and together two boolean expressions. If the first expression is found to be false then it is known that the result will also be false, so evaluation of the second expression will be skipped. The table below shows what the resultant boolean value will be based on the two boolean expressions. + +||true|false +|true|true|false +|false|false|false + +The format starts with an expression, follows with the ampersand-ampersand operator, and finishes with an expression. + +*Grammar:* +[source,ANTLR4] +---- +boolean_and: expression '&&' expression; +---- + +Note that def types will be assumed to be of the boolean type. Any def type evaluated at run-time that does not represent a boolean will result in an error. Non-boolean expressions will result in an error. + +*Examples:* +[source,Java] +---- +boolean x = false; // declares the boolean variable x and sets the constant boolean false +boolean y = x && true; // declares the boolean variable y and sets it the result of x and true +def z = y && x; // declares the def variable z and sets it to the result of y and x +---- + +==== Boolean Or + +Boolean or will or together two boolean expressions. If the first expression is found to be true then it is known that the result will also be true, so evaluation of the second expression will be skipped. The table below shows what the resultant boolean value will be based on the two boolean expressions. + +|==== +||true|false +|true|true|true +|false|true|false +|==== + +The format starts with an expression, follows with the pipe-pipe operator, and finishes with an expression. + +*Grammar:* +[source,ANTLR4] +---- +boolean_and: expression '||' expression; +---- + +Note that def types will be assumed to be of the boolean type. Any def type evaluated at run-time that does not represent a boolean will result in an error. Non-boolean expressions will result in an error. + +*Examples:* +[source,Java] +---- +boolean x = false; // declares the boolean variable x and sets the constant boolean false +boolean y = x || true; // declares the boolean variable y and sets it the result of x or true +def z = y || x; // declares the def variable z and sets it to the result of y or x +---- + +==== Conditional + +A conditional operation consists of three expressions. The first expression is evaluated with an expected boolean result type. If the first expression evaluates to true then the second expression will be evaluated. If the first expression evaluates to false then the third expression will be evaluated. This can be used as a shortcut many different operations without requiring a full if/else branch. Errors will occur if the first expression does not evaluate to a boolean type or if one of the second or third expression cannot be converted to a type appropriate for the expected result. The format is an expression followed by a question-mark operator, another expression, a colon operator, and finishes with a final expression. + +*Grammar:* +[source,ANTLR4] +---- +conditional: expression '?' expression ':' expression; +---- + +A numeric type promotion may occur during the evaluation of a conditional with the second and third expressions if the expected result is a numeric type. A def type evaluated at run-time will follow the same promotion table at run-time following whatever type def represents. + +Promotion Table: +|==== +||byte|short|char|int|long|float|double|def +|byte|int|int|int|int|long|float|double|def +|short|int|int|int|int|long|float|double|def +|char|int|int|int|int|long|float|double|def +|int|int|int|int|int|long|float|double|def +|long|long|long|long|long|long|float|double|def +|float|float|float|float|float|float|float|double|def +|double|double|double|double|double|double|double|double|def +|def|def|def|def|def|def|def|def|def +|==== + +*Examples:* +[source,Java] +---- +boolean b = true; // declares the boolean variable b and sets it the constant boolean true + +int x = b ? 1 : 2; // declares the int variable x and sets it to the int constant 1 + // since the first expression of the conditional evaluates to true + // so the second expression is evaluated for a result + +List y = x > 1 ? new ArrayList() : null; // declares the List variable y and sets it to null + // since the first expression of the conditional evaluates to false + // so the third expression is evaluated for a result + +def z = x < 2 ? true : false; // declares the def variable z and sets it to the boolean constant true + // since the first expression of the conditional evaluates to true + // so the second expression is evaluated for a result +---- + +==== Elvis + +The elvis operator consists of two expressions. If the first expression is a non-null value then the resultant value will be the evaluated first expression otherwise the resultant value will be the evaluated second expression. This is typically used as a shortcut for a null check in a conditional. An error will occur if the expected result is a primitive type. The format is an expression, followed by the question-mark-colon operator, and finishes with an expression. + +*Grammar:* +[source,ANTLR4] +---- +elvis: expression '?:' expression; +---- + +*Examples:* +[source,Java] +---- +List l = new ArrayList(); // declares the List variable l and sets it to a newly allocated ArrayList +List y = l : new ArrayList(); // declares the List variable y and sets it to l since l is not null +y = null; // sets y to null +def z = y ?: new HashMap(); // declares the def variable z and sets it to a newly allocated HashMap since y is null +---- + +==== Assignment + +Assignment can be used to assign a value to a variable. See Variable Assignment [MARK] for more information. + +==== Compound Assignment + +Compound assignment can be used as a shortcut for an assignment where a binary operation would occur between the variable/field as the left-side expression and a separate right-side expression. The variable/field and right-side expression must be of appropriate types for the specific operation or an error will occur. A downcast may be necessary for certain operations to be able to assign the result back into the variable/field and will happen implicitly. The format is a variable/field, followed by one of the compound assignment operators, finished with an expression. + +*Grammar:* +[source,ANTLR4] +---- +compund_assignment: ID (. ID)? '$=' expression; // $ is a placeholder for the operation symbol +---- + +A compound assignment is equivalent to the expression below where V is the variable/field and T is the type of variable/member. + +[source,Java] +---- +V = (T)(V op expression); +---- + +The table below shows all available operators for compound assignment. All operators follow any casting/promotion rules according to their regular definition. + +|==== +|Operator|Compound Symbol +|Multiplication|*= +|Division|/= +|Remainder|%= +|String Concatenation|+= +|Addition|+= +|Subtraction|-= +|Left Shift|<<= +|Right Shift|>>= +|Unsigned Right Shift|>>>= +|Bitwise And|&= +|Boolean And|&= +|Bitwise Xor|^= +|Boolean Xor|^= +|Bitwise Or|\|= +|Boolean Or|\|= +|==== + +*Examples:* +[source,Java] +---- +int i = 10; // declares the variable i and sets it to constant int 10 +i *= 2; // multiplies i by 2 -- i = (int)(i * 2) +i /= 5; // divides i by 5 -- i = (int)(i / 5) +i %= 3; // gives the remainder for i/3 -- i = (int)(i % 3) +i += 5; // adds 5 to i -- i = (int)(i + 5) +i -= 5; // subtracts 5 from i -- i = (int)(i - 5) +i <<= 2; // left shifts i by 2 -- i = (int)(i << 2) +i >>= 1; // right shifts i by 1 -- i = (int)(i >> 1) +i >>>= 1; // unsigned right shifts i by 1 -- i = (int)(i >>> 1) +i &= 15; // ands i with 15 -- i = (int)(i & 15) +i ^= 12; // xors i with 12 -- i = (int)(i ^ 2) +i |= 4; // ors i with 4 -- i = (int)(i | 4) + +boolean b = true; // declares the boolean variable b and sets it to the constant boolean true +b &= false; // ands b with false -- b = (boolean)(b & false) +b ^= false; // xors b with false -- b = (boolean)(b & false) +b |= true; // ors be with true -- b = (boolean)(b & false) + +def x = 'compound'; // declares the def variable x and sets it to the constant String 'compound' +x += ' assignment'; // string concatenates ' assignment' to x -- x = (String)(x + ' assignment') +---- diff --git a/docs/painless/painless-syntax.asciidoc b/docs/painless/painless-syntax.asciidoc new file mode 100644 index 0000000000000..79e830c05d21c --- /dev/null +++ b/docs/painless/painless-syntax.asciidoc @@ -0,0 +1,119 @@ +[[painless-syntax]] +=== Painless Syntax + +experimental[The Painless scripting language is new and is still marked as experimental. The syntax or API may be changed in the future in non-backwards compatible ways if required.] + +[float] +[[control-flow]] +==== Control flow + +Painless supports all of Java's https://docs.oracle.com/javase/tutorial/java/nutsandbolts/flow.html[ +control flow statements] except the `switch` statement. + +Painless also supports the `for in` syntax from Groovy: + +[source,painless] +--------------------------------------------------------- +for (item : list) { + ... +} +--------------------------------------------------------- + +[float] +[[functions]] +==== Functions + +You can declare functions at the beginning of a Painless script, for example: + +[source,painless] +--------------------------------------------------------- +boolean isNegative(def x) { x < 0 } +... +if (isNegative(someVar)) { + ... +} +--------------------------------------------------------- + +[float] +[[lambda-expressions]] +==== Lambda expressions +Lambda expressions and method references work the same as in https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html[Java]. + +[source,painless] +--------------------------------------------------------- +list.removeIf(item -> item == 2); +list.removeIf((int item) -> item == 2); +list.removeIf((int item) -> { item == 2 }); +list.sort((x, y) -> x - y); +list.sort(Integer::compare); +--------------------------------------------------------- + +You can make method references to functions within the script with `this`, +for example `list.sort(this::mycompare)`. + +[float] +[[patterns]] +==== Patterns + +Regular expression constants are directly supported. To ensure fast performance, +this is the only mechanism for creating patterns. Regular expressions +are always constants and compiled efficiently a single time. + +[source,painless] +--------------------------------------------------------- +Pattern p = /[aeiou]/ +--------------------------------------------------------- + +[float] +[[pattern-flags]] +===== Pattern flags + +You can define flags on patterns in Painless by adding characters after the +trailing `/` like `/foo/i` or `/foo \w #comment/iUx`. Painless exposes all of +the flags from Java's +https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html[ +Pattern class] using these characters: + +[cols="<,<,<",options="header",] +|======================================================================= +| Character | Java Constant | Example +|`c` | CANON_EQ | `'å' ==~ /å/c` (open in hex editor to see) +|`i` | CASE_INSENSITIVE | `'A' ==~ /a/i` +|`l` | LITERAL | `'[a]' ==~ /[a]/l` +|`m` | MULTILINE | `'a\nb\nc' =~ /^b$/m` +|`s` | DOTALL (aka single line) | `'a\nb\nc' =~ /.b./s` +|`U` | UNICODE_CHARACTER_CLASS | `'Ɛ' ==~ /\\w/U` +|`u` | UNICODE_CASE | `'Ɛ' ==~ /ɛ/iu` +|`x` | COMMENTS (aka extended) | `'a' ==~ /a #comment/x` +|======================================================================= + +[float] +[[painless-deref]] +==== Dereferences + +Like lots of languages, Painless uses `.` to reference fields and call methods: + +[source,painless] +--------------------------------------------------------- +String foo = 'foo'; +TypeWithGetterOrPublicField bar = new TypeWithGetterOrPublicField() +return foo.length() + bar.x +--------------------------------------------------------- + +Like Groovy, Painless uses `?.` to perform null-safe references, with the +result being `null` if the left hand side is `null`: + +[source,painless] +--------------------------------------------------------- +String foo = null; +return foo?.length() // Returns null +--------------------------------------------------------- + +Unlike Groovy, Painless doesn't support writing to `null` values with this +operator: + +[source,painless] +--------------------------------------------------------- +TypeWithSetterOrPublicField foo = null; +foo?.x = 'bar' // Compile error +--------------------------------------------------------- diff --git a/docs/painless/painless-types.asciidoc b/docs/painless/painless-types.asciidoc new file mode 100644 index 0000000000000..36cf78312ea26 --- /dev/null +++ b/docs/painless/painless-types.asciidoc @@ -0,0 +1,442 @@ +[[types]] +=== Data Types + +Painless supports both dynamic and static types. Static types are split into +_primitive types_ and _reference types_. + +[[dynamic-types]] +==== Dynamic Types + +Painless supports one dynamic type: `def`. The `def` type can represent any +primitive or reference type. When you use the `def` type, it mimics the exact +behavior of whatever type it represents at runtime. The default value for the +def type is `null.` + +Internally, if the `def` type represents a primitive type, it is converted to the +corresponding reference type. It still behaves like the primitive type, however, +including within the casting model. The `def` type can be assigned to different +types during the course of script execution. + +IMPORTANT: Because a `def` type variable can be assigned to different types +during execution, type conversion errors that occur when using the `def` type +happen at runtime. + +Using the `def` type can have a slight impact on performance. If performance is +critical, it's better to declare static types. + +*Examples:* +[source,Java] +---- +def x = 1; // Declare def variable x and set it to the + // literal int 1 +def l = new ArrayList(); // Declare def variable l and set it a newly + // allocated ArrayList +---- + +[[primitive-types]] +==== Primitive Types + +Primitive types are allocated directly onto the stack according to the standard +Java memory model. + +Primitive types can behave as their corresponding (<>) +reference type. This means any piece of a reference type can be accessed or +called through the primitive type. Operations performed in this manner convert +the primitive type to its corresponding reference type at runtime and perform +the field access or method call without needing to perform any other +operations. + +Painless supports the following primitive types. + +byte:: +An 8-bit, signed, two's complement integer. +Range: [-128, 127]. +Default value: 0. +Reference type: Byte. + +short:: +A 16-bit, signed, two's complement integer. +Range: [-32768, 32767]. +Default value: 0. +Reference type: Short. + +char:: +A 16-bit Unicode character. +Range: [0, 65535]. +Default value: 0 or `\u0000`. +Reference type: Character. + +int:: +A 32-bit, signed, two's complement integer. +Range: [-2^32, 2^32-1]. +Default value: 0. +Reference type: Integer. + +long:: +A 64-bit, signed, two's complement integer. +Range: [-2^64, 2^64-1]. +Default value: 0. +Reference type: Long. + +float:: +A 32-bit, single-precision, IEEE 754 floating point number. +Range: Depends on multiple factors. +Default value: 0.0. +Reference type: Float. + +double:: +A 64-bit, double-precision, IEEE 754 floating point number. +Range: Depends on multiple factors. +Default value: 0.0. +Reference type: Double. + +boolean:: +A logical quanity with two possible values: true and false. +Range: true/false. +Default value: false. +Reference type: Boolean. + + +*Examples:* +[source,Java] +---- +int i = 1; // Declare variable i as an int and set it to the + // literal 1 +double d; // Declare variable d as a double and set it to the + // default value of 0.0 +boolean b = true; // Declare variable b as a boolean and set it to true +---- + +Using methods from the corresponding reference type on a primitive type. + +[source,Java] +---- +int i = 1; // Declare variable i as an int and set it to the + // literal 1 +i.toString(); // Invokes the Integer method toString on variable i +---- + +[[reference-types]] +==== Reference Types + +Reference types are similar to Java classes and can contain multiple pieces +known as _members_. However, reference types do not support access modifiers. +You allocate reference type instances on the heap using the `new` operator. + +Reference types can have both static and non-static members: + +* Static members are shared by all instances of the same reference type and +can be accessed without allocating an instance of the reference type. For +example `Integer.MAX_VALUE`. +* Non-static members are specific to an instance of the reference type +and can only be accessed through the allocated instance. + +The default value for a reference type is `null`, indicating that no memory has +been allocated for it. When you assign `null` to a reference type, its previous +value is discarded and garbage collected in accordance with the Java memory +model as long as there are no other references to that value. + +A reference type can contain: + +* Zero to many primitive types. Primitive type members can be static or +non-static and read-only or read-write. +* Zero to many reference types. Reference type members can be static or +non-static and read-only or read-write. +* Methods that call an internal function to return a value and/or manipulate +the primitive or reference type members. Method members can be static or +non-static. +* Constructors that call an internal function to return a newly-allocated +reference type instance. Constructors are non-static methods that can +optionally manipulate the primitive and reference type members. + +Reference types support a Java-style inheritance model. Consider types A and B. +Type A is considered to be a parent of B, and B a child of A, if B inherits +(is able to access as its own) all of A's fields and methods. Type B is +considered a descendant of A if there exists a recursive parent-child +relationship from B to A with none to many types in between. In this case, B +inherits all of A's fields and methods along with all of the fields and +methods of the types in between. Type B is also considered to be a type A +in both relationships. + +For the complete list of Painless reference types and their supported methods, +see the https://www.elastic.co/guide/en/elasticsearch/reference/current/painless-api-reference.html[Painless API Reference]. + +For more information about working with reference types, see +<> and <>. + +*Examples:* +[source,Java] +---- +ArrayList al = new ArrayList(); // Declare variable al as an ArrayList and + // set it to a newly allocated ArrayList +List l = new ArrayList(); // Declare variable l as a List and set + // it to a newly allocated ArrayList, which is + // allowed because ArrayList inherits from List +Map m; // Declare variable m as a Map and set it + // to the default value of null +---- + +Directly accessing static pieces of a reference type. + +[source,Java] +---- +Integer.MAX_VALUE // a static field access +Long.parseLong("123L") // a static function call +---- + +[[string-type]] +==== String Type + +A `String` is a specialized reference type that is immutable and does not have +to be explicitly allocated. You can directly assign to a `String` without first +allocating it with the `new` keyword. (Strings can be allocated with the `new` +keyword, but it's not required.) + +When assigning a value to a `String`, you must enclose the text in single or +double quotes. Strings are allocated according to the standard Java Memory Model. +The default value for a `String` is `null.` + +*Examples:* +[source,Java] +---- +String r = "some text"; // Declare String r and set it to the + // String "some text" +String s = 'some text'; // Declare String s and set it to the + // String 'some text' +String t = new String("some text"); // Declare String t and set it to the + // String "some text" +String u; // Declare String u and set it to the + // default value null +---- + +[[void-type]] +==== void Type + +The `void` type represents the concept of no type. In Painless, `void` declares +that a function has no return value. + +[[array-type]] +==== Array Type + +Arrays contain a series of elements of the same type that can be allocated +simultaneously. Painless supports both single and multi-dimensional arrays for +all types except void (including `def`). + +You declare an array by specifying a type followed by a series of empty brackets, +where each set of brackets represents a dimension. Declared arrays have a default +value of `null` and are themselves a reference type. + +To allocate an array, you use the `new` keyword followed by the type and a +set of brackets for each dimension. You can explicitly define the size of each dimension by specifying an expression within the brackets, or initialize each +dimension with the desired number of values. The allocated size of each +dimension is its permanent size. + +To initialize an array, specify the values you want to initialize +each dimension with as a comma-separated list of expressions enclosed in braces. +For example, `new int[] {1, 2, 3}` creates a one-dimensional `int` array with a +size of 3 and the values 1, 2, and 3. + +When you initialize an array, the order of the expressions is maintained. Each expression used as part of the initialization is converted to the +array's type. An error occurs if the types do not match. + +*Grammar:* +[source,ANTLR4] +---- +declare_array: TYPE ('[' ']')+; + +array_initialization: 'new' TYPE '[' ']' '{' expression (',' expression) '}' + | 'new' TYPE '[' ']' '{' '}'; +---- + +*Examples:* +[source,Java] +---- +int[] x = new int[5]; // Declare int array x and assign it a newly + // allocated int array with a size of 5 +def[][] y = new def[5][5]; // Declare the 2-dimensional def array y and + // assign it a newly allocated 2-dimensional + // array where both dimensions have a size of 5 +int[] x = new int[] {1, 2, 3}; // Declare int array x and set it to an int + // array with values 1, 2, 3 and a size of 3 +int i = 1; +long l = 2L; +float f = 3.0F; +double d = 4.0; +String s = "5"; +def[] da = new def[] {i, l, f*d, s}; // Declare def array da and set it to + // a def array with a size of 4 and the + // values i, l, f*d, and s +---- + +[[casting]] +=== Casting + +Casting is the conversion of one type to another. Implicit casts are casts that +occur automatically, such as during an assignment operation. Explicit casts are +casts where you use the casting operator to explicitly convert one type to +another. This is necessary during operations where the cast cannot be inferred. + +To cast to a new type, precede the expression by the new type enclosed in +parentheses, for example +`(int)x`. + +The following sections specify the implicit casts that can be performed and the +explicit casts that are allowed. The only other permitted cast is casting +a single character `String` to a `char`. + +*Grammar:* +[source,ANTLR4] +---- +cast: '(' TYPE ')' expression +---- + +[[numeric-casting]] +==== Numeric Casting + +The following table shows the allowed implicit and explicit casts between +numeric types. Read the table by row. To find out if you need to explicitly +cast from type A to type B, find the row for type A and scan across to the +column for type B. + +IMPORTANT: Explicit casts between numeric types can result in some data loss. A +smaller numeric type cannot necessarily accommodate the value from a larger +numeric type. You might also lose precision when casting from integer types +to floating point types. + +|==== +| | byte | short | char | int | long | float | double +| byte | | implicit | implicit | implicit | implicit | implicit | implicit +| short | explicit | | explicit | implicit | implicit | implicit | implicit +| char | explicit | explicit | | implicit | implicit | implicit | implicit +| int | explicit | explicit | explicit | | implicit | implicit | implicit +| long | explicit | explicit | explicit | explicit | | implicit | implicit +| float | explicit | explicit | explicit | explicit | explicit | | implicit +| float | explicit | explicit | explicit | explicit | explicit | explicit | +|==== + + +Example(s) +[source,Java] +---- +int a = 1; // Declare int variable a and set it to the literal + // value 1 +long b = a; // Declare long variable b and set it to int variable + // a with an implicit cast to convert from int to long +short c = (short)b; // Declare short variable c, explicitly cast b to a + // short, and assign b to c +byte d = a; // ERROR: Casting an int to a byte requires an explicit + // cast +double e = (double)a; // Explicitly cast int variable a to a double and assign + // it to the double variable e. The explicit cast is + // allowed, but it is not necessary. +---- + +[[reference-casting]] +==== Reference Casting + +A reference type can be implicitly cast to another reference type as long as +the type being cast _from_ is a descendant of the type being cast _to_. A +reference type can be explicitly cast _to_ if the type being cast to is a +descendant of the type being cast _from_. + +*Examples:* +[source,Java] +---- +List x; // Declare List variable x +ArrayList y = new ArrayList(); // Declare ArrayList variable y and assign it a + // newly allocated ArrayList [1] +x = y; // Assign Arraylist y to List x using an + // implicit cast +y = (ArrayList)x; // Explicitly cast List x to an ArrayList and + // assign it to ArrayList y +x = (List)y; // Set List x to ArrayList y using an explicit + // cast (the explicit cast is not necessary) +y = x; // ERROR: List x cannot be implicitly cast to + // an ArrayList, an explicit cast is required +Map m = y; // ERROR: Cannot implicitly or explicitly cast [2] + // an ArrayList to a Map, no relationship + // exists between the two types. +---- +[1] `ArrayList` is a descendant of the `List` type. +[2] `Map` is unrelated to the `List` and `ArrayList` types. + +[[def-type-casting]] +==== def Type Casting +All primitive and reference types can always be implicitly cast to +`def`. While it is possible to explicitly cast to `def`, it is not necessary. + +However, it is not always possible to implicitly cast a `def` to other +primitive and reference types. An explicit cast is required if an explicit +cast would normally be required between the non-def types. + + +*Examples:* +[source,Java] +---- +def x; // Declare def variable x and set it to null +x = 3; // Set the def variable x to the literal 3 with an implicit + // cast from int to def +double a = x; // Declare double variable y and set it to def variable x, + // which contains a double +int b = x; // ERROR: Results in a run-time error because an explicit cast is + // required to cast from a double to an int +int c = (int)x; // Declare int variable c, explicitly cast def variable x to an + // int, and assign x to c +---- + +[[boxing-unboxing]] +==== Boxing and Unboxing + +Boxing is where a cast is used to convert a primitive type to its corresponding +reference type. Unboxing is the reverse, converting a reference type to the +corresponding primitive type. + +There are two places Painless performs implicit boxing and unboxing: + +* When you call methods, Painless automatically boxes and unboxes arguments +so you can specify either primitive types or their corresponding reference +types. +* When you use the `def` type, Painless automatically boxes and unboxes as +needed when converting to and from `def`. + +The casting operator does not support any way to explicitly box a primitive +type or unbox a reference type. + +If a primitive type needs to be converted to a reference type, the Painless +reference type API supports methods that can do that. However, under normal +circumstances this should not be necessary. + +*Examples:* +[source,Java] +---- +Integer x = 1; // ERROR: not a legal implicit cast +Integer y = (Integer)1; // ERROR: not a legal explicit cast +int a = new Integer(1); // ERROR: not a legal implicit cast +int b = (int)new Integer(1); // ERROR: not a legal explicit cast +---- + +[[promotion]] +==== Promotion + +Promotion is where certain operations require types to be either a minimum +numerical type or for two (or more) types to be equivalent. +The documentation for each operation that has these requirements +includes promotion tables that describe how this is handled. + +When an operation promotes a type or types, the resultant type +of the operation is the promoted type. Types can be promoted to def +at compile-time; however, at run-time, the resultant type will be the +promotion of the types the `def` is representing. + +*Examples:* +[source,Java] +---- +2 + 2.0 // Add the literal int 2 and the literal double 2.0. The literal + // 2 is promoted to a double and the resulting value is a double. + +def x = 1; // Declare def variable x and set it to the literal int 1 through + // an implicit cast +x + 2.0F // Add def variable x and the literal float 2.0. + // At compile-time the types are promoted to def. + // At run-time the types are promoted to float. +---- diff --git a/docs/painless/painless-variables.asciidoc b/docs/painless/painless-variables.asciidoc new file mode 100644 index 0000000000000..2177b0bb91ba8 --- /dev/null +++ b/docs/painless/painless-variables.asciidoc @@ -0,0 +1,123 @@ +[[variables]] +=== Variables + +Variables in Painless must be declared and can be statically or <>. + +[[variable-identifiers]] +==== Variable Identifiers + +Specify variable identifiers using the following grammar. Variable identifiers +must start with a letter or underscore. You cannot use <> or +<> as identifiers. + +*Grammar:* +[source,ANTLR4] +---- +ID: [_a-zA-Z] [_a-zA-Z-0-9]*; +---- + +*Examples:* +[source,Java] +---- +_ +a +Z +id +list +list0 +MAP25 +_map25 +---- + +[[variable-declaration]] +==== Variable Declaration + +Variables must be declared before you use them. The format is `type-name +identifier-name`. To declare multiple variables of the same type, specify a +comma-separated list of identifier names. You can immediately assign a value to +a variable when you declare it. + +*Grammar:* +[source,ANTLR4] +---- +type: ID ('[' ']')*; +declaration : type ID (',' ID)*; +---- + +*Examples:* +[source,Java] +---- +int x; // Declare a variable with type int and id x +List y; // Declare a variable with type List and id y +int x, y, z; // Declare variables with type int and ids x, y, and z +def[] d; // Declare the variable d with type def[] +int i = 10; // Declare the int variable i and set it to the int literal 10 +---- + +[[variable-assignment]] +==== Variable Assignment + +Use the equals operator (`=`) to assign a value to a variable. The format is +`identifier-name = value`. Any value expression can be assigned to any variable +as long as the types match or the expression's type can be implicitly cast to +the variable's type. An error occurs if the types do not match. + +*Grammar:* +[source,ANTLR4] +---- +assignment: ID '=' expression +---- + + +*Examples:* + +Assigning a literal of the appropriate type directly to a declared variable. + +[source,Java] +---- +int i;   // Declare an int i +i = 10;  // Set the int i to the int literal 10 +---- + +Immediately assigning a value when declaring a variable. + +[source,Java] +---- +int i = 10; // Declare the int variable i and set it the int literal 1 +double j = 2.0; // Declare the double variable j and set it to the double + // literal 2.0 +---- + +Assigning a variable of one primitive type to another variable of the same +type. + +[source,Java] +---- +int i = 10; // Declare the int variable i and set it to the int literal 10 +int j = i;  // Declare the int variable j and set it to the int variable i +---- + +Assigning a reference type to a new heap allocation with the `new` operator. + +[source,Java] +---- +ArrayList l = new ArrayList();  // Declare an ArrayList variable l and set it + // to a newly allocated ArrayList +Map m = new HashMap(); // Declare a Map variable m and set it + // to a newly allocated HashMap +---- + +Assigning a variable of one reference type to another variable of the same type. + +[source,Java] +---- +List l = new ArrayList(); // Declare List variable l and set it a newly + // allocated ArrayList +List k = l;  // Declare List variable k and set it to the + // value of the List variable l +List m;                   // Declare List variable m and set it the + // default value null +m = k;                    // Set the value of List variable m to the value + // of List variable k +---- diff --git a/docs/painless/painless-xref.asciidoc b/docs/painless/painless-xref.asciidoc new file mode 100644 index 0000000000000..86407b3e697d6 --- /dev/null +++ b/docs/painless/painless-xref.asciidoc @@ -0,0 +1,2 @@ +Ready to start scripting with Painless? See {painless}/painless-getting-started.html[Getting Started with Painless] in the guide to the +{painless}/painless.html[Painless Scripting Language]. \ No newline at end of file From a2845c86fef5cef2435ef8cb29be186e1a4a76fa Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Tue, 16 May 2017 17:24:26 -0400 Subject: [PATCH 383/619] CONSOLEify some more aggregation docs Related #18160 --- docs/build.gradle | 2 - .../bucket/missing-aggregation.asciidoc | 9 ++-- .../metrics/cardinality-aggregation.asciidoc | 49 +++++++++++++++---- 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/docs/build.gradle b/docs/build.gradle index ef53bd135aba4..fd5c53769210e 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -25,14 +25,12 @@ apply plugin: 'elasticsearch.docs-test' * entirely and have a party! There will be cake and everything.... */ buildRestTests.expectedUnconvertedCandidates = [ 'reference/aggregations/bucket/iprange-aggregation.asciidoc', - 'reference/aggregations/bucket/missing-aggregation.asciidoc', 'reference/aggregations/bucket/nested-aggregation.asciidoc', 'reference/aggregations/bucket/range-aggregation.asciidoc', 'reference/aggregations/bucket/reverse-nested-aggregation.asciidoc', 'reference/aggregations/bucket/significantterms-aggregation.asciidoc', 'reference/aggregations/bucket/terms-aggregation.asciidoc', 'reference/aggregations/matrix/stats-aggregation.asciidoc', - 'reference/aggregations/metrics/cardinality-aggregation.asciidoc', 'reference/aggregations/metrics/extendedstats-aggregation.asciidoc', 'reference/aggregations/metrics/percentile-aggregation.asciidoc', 'reference/aggregations/metrics/percentile-rank-aggregation.asciidoc', diff --git a/docs/reference/aggregations/bucket/missing-aggregation.asciidoc b/docs/reference/aggregations/bucket/missing-aggregation.asciidoc index f0b8fb4ac347f..b670529119673 100644 --- a/docs/reference/aggregations/bucket/missing-aggregation.asciidoc +++ b/docs/reference/aggregations/bucket/missing-aggregation.asciidoc @@ -7,6 +7,7 @@ Example: [source,js] -------------------------------------------------- +POST /sales/_search?size=0 { "aggs" : { "products_without_a_price" : { @@ -15,6 +16,8 @@ Example: } } -------------------------------------------------- +// CONSOLE +// TEST[setup:sales] In the above example, we get the total number of products that do not have a price. @@ -24,11 +27,11 @@ Response: -------------------------------------------------- { ... - - "aggs" : { + "aggregations" : { "products_without_a_price" : { - "doc_count" : 10 + "doc_count" : 00 } } } -------------------------------------------------- +// TESTRESPONSE[s/\.\.\./"took": $body.took,"timed_out": false,"_shards": $body._shards,"hits": $body.hits,/] \ No newline at end of file diff --git a/docs/reference/aggregations/metrics/cardinality-aggregation.asciidoc b/docs/reference/aggregations/metrics/cardinality-aggregation.asciidoc index 77fc7dfcd5a01..9e24604769d16 100644 --- a/docs/reference/aggregations/metrics/cardinality-aggregation.asciidoc +++ b/docs/reference/aggregations/metrics/cardinality-aggregation.asciidoc @@ -10,16 +10,34 @@ match a query: [source,js] -------------------------------------------------- +POST /sales/_search?size=0 { "aggs" : { - "author_count" : { + "type_count" : { "cardinality" : { - "field" : "author" + "field" : "type" } } } } -------------------------------------------------- +// CONSOLE +// TEST[setup:sales] + +Response: + +[source,js] +-------------------------------------------------- +{ + ... + "aggregations" : { + "type_count" : { + "value" : 3 + } + } +} +-------------------------------------------------- +// TESTRESPONSE[s/\.\.\./"took": $body.took,"timed_out": false,"_shards": $body._shards,"hits": $body.hits,/] ==== Precision control @@ -29,17 +47,20 @@ experimental[The `precision_threshold` option is specific to the current interna [source,js] -------------------------------------------------- +POST /sales/_search?size=0 { "aggs" : { - "author_count" : { + "type_count" : { "cardinality" : { - "field" : "author_hash", + "field" : "type", "precision_threshold": 100 <1> } } } } -------------------------------------------------- +// CONSOLE +// TEST[setup:sales] <1> The `precision_threshold` options allows to trade memory for accuracy, and defines a unique count below which counts are expected to be close to @@ -159,33 +180,37 @@ however since hashes need to be computed on the fly. [source,js] -------------------------------------------------- +POST /sales/_search?size=0 { "aggs" : { - "author_count" : { + "type_promoted_count" : { "cardinality" : { "script": { "lang": "painless", - "inline": "doc['author.first_name'].value + ' ' + doc['author.last_name'].value" + "inline": "doc['type'].value + ' ' + doc['promoted'].value" } } } } } -------------------------------------------------- +// CONSOLE +// TEST[setup:sales] This will interpret the `script` parameter as an `inline` script with the `painless` script language and no script parameters. To use a file script use the following syntax: [source,js] -------------------------------------------------- +POST /sales/_search?size=0 { "aggs" : { - "author_count" : { + "type_promoted_count" : { "cardinality" : { "script" : { "file": "my_script", "params": { - "first_name_field": "author.first_name", - "last_name_field": "author.last_name" + "type_field": "type", + "promoted_field": "promoted" } } } @@ -193,6 +218,8 @@ This will interpret the `script` parameter as an `inline` script with the `painl } } -------------------------------------------------- +// CONSOLE +// TEST[skip:no script] TIP: for indexed scripts replace the `file` parameter with an `id` parameter. @@ -204,6 +231,7 @@ had a value. [source,js] -------------------------------------------------- +POST /sales/_search?size=0 { "aggs" : { "tag_cardinality" : { @@ -215,5 +243,6 @@ had a value. } } -------------------------------------------------- - +// CONSOLE +// TEST[setup:sales] <1> Documents without a value in the `tag` field will fall into the same bucket as documents that have the value `N/A`. From f80799acc2a0bdfbadfc2b4df876020a3a9ed591 Mon Sep 17 00:00:00 2001 From: debadair Date: Tue, 16 May 2017 15:05:06 -0700 Subject: [PATCH 384/619] [DOCS] Removed API xrefs from Painless GSG --- docs/painless/painless-getting-started.asciidoc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/painless/painless-getting-started.asciidoc b/docs/painless/painless-getting-started.asciidoc index 9a9c032164102..31ca284108f97 100644 --- a/docs/painless/painless-getting-started.asciidoc +++ b/docs/painless/painless-getting-started.asciidoc @@ -174,13 +174,12 @@ POST hockey/player/1/_update ==== Dates Date fields are exposed as -<>s +`ReadableDateTime` so they support methods like -<>, -and -<>. +`getYear`, +and `getDayOfWeek`. To get milliseconds since epoch call -<>. +`getMillis`. For example, the following returns every hockey player's birth year: [source,js] @@ -342,8 +341,7 @@ Java, leading to some trouble when it whitelists classes from the Java standard library. For example, in Java and Groovy, `Matcher` has two methods: `group(int)` and `group(String)`. Painless can't whitelist both of them methods because they have the same name and the same number of parameters. So instead it -has <> and -<>. +has `group(int)` and `namedGroup(String)`. We have a few justifications for this different way of dispatching methods: From d74760c30665e183650cf9a0f041702bf04962f4 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 16 May 2017 17:17:37 -0700 Subject: [PATCH 385/619] GCS Repository: Add secure storage of credentials (#24697) This commit adds gcs credential settings to the elasticsearch keystore. The setting name follows the same pattern as the s3 client settings, beginning with `gcs.client.`, followed by the client name, and then the setting name, in this case, `credentials_file`. Using the legacy service file setting is also deprecated. --- docs/plugins/repository-gcs.asciidoc | 46 ++++----- .../gcs/GoogleCloudStoragePlugin.java | 17 +++- .../gcs/GoogleCloudStorageRepository.java | 28 +++--- .../gcs/GoogleCloudStorageService.java | 94 +++++++++++++------ ...eCloudStorageBlobStoreRepositoryTests.java | 6 +- .../gcs/GoogleCloudStorageServiceTests.java | 85 +++++++++++++++++ .../src/test/resources/dummy-account.json | 12 +++ 7 files changed, 215 insertions(+), 73 deletions(-) create mode 100644 plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageServiceTests.java create mode 100644 plugins/repository-gcs/src/test/resources/dummy-account.json diff --git a/docs/plugins/repository-gcs.asciidoc b/docs/plugins/repository-gcs.asciidoc index 29661621486bb..54bdeafd0428f 100644 --- a/docs/plugins/repository-gcs.asciidoc +++ b/docs/plugins/repository-gcs.asciidoc @@ -41,34 +41,19 @@ The bucket should now be created. The plugin supports two authentication modes: -* the built-in <>. This mode is +* The built-in <>. This mode is recommended if your elasticsearch node is running on a Compute Engine virtual machine. -* the <> authentication mode. +* Specifying <> credentials. [[repository-gcs-using-compute-engine]] ===== Using Compute Engine -When running on Compute Engine, the plugin use the Google's built-in authentication mechanism to +When running on Compute Engine, the plugin use Google's built-in authentication mechanism to authenticate on the Storage service. Compute Engine virtual machines are usually associated to a default service account. This service account can be found in the VM instance details in the https://console.cloud.google.com/compute/[Compute Engine console]. -To indicate that a repository should use the built-in authentication, -the repository `service_account` setting must be set to `_default_`: - -[source,js] ----- -PUT _snapshot/my_gcs_repository_on_compute_engine -{ - "type": "gcs", - "settings": { - "bucket": "my_bucket", - "service_account": "_default_" - } -} ----- -// CONSOLE -// TEST[skip:we don't have gcs setup while testing this] +This is the default authentication mode and requires no configuration. NOTE: The Compute Engine VM must be allowed to use the Storage service. This can be done only at VM creation time, when "Storage" access can be configured to "Read/Write" permission. Check your @@ -76,7 +61,7 @@ instance details at the section "Cloud API access scopes". [[repository-gcs-using-service-account]] ===== Using a Service Account -If your elasticsearch node is not running on Compute Engine, or if you don't want to use Google +If your elasticsearch node is not running on Compute Engine, or if you don't want to use Google's built-in authentication mechanism, you can authenticate on the Storage service using a https://cloud.google.com/iam/docs/overview#service_account[Service Account] file. @@ -107,10 +92,14 @@ A service account file looks like this: ---- // NOTCONSOLE -This file must be copied in the `config` directory of the elasticsearch installation and on -every node of the cluster. +This file must be stored in the <>, under a setting name +of the form `gcs.client.NAME.credentials_file`, where `NAME` is the name of the client congiguration. +The default client name is `default`, but a different client name can be specified in repository +settings using `client`. -To indicate that a repository should use a service account file: +For example, if specifying the credentials file in the keystore under +`gcs.client.my_alternate_client.credentials_file`, you can configure a repository to use these +credentials like this: [source,js] ---- @@ -119,7 +108,7 @@ PUT _snapshot/my_gcs_repository "type": "gcs", "settings": { "bucket": "my_bucket", - "service_account": "service_account.json" + "client": "my_alternate_client" } } ---- @@ -150,8 +139,7 @@ PUT _snapshot/my_gcs_repository { "type": "gcs", "settings": { - "bucket": "my_bucket", - "service_account": "service_account.json" + "bucket": "my_bucket" } } ---- @@ -164,10 +152,10 @@ The following settings are supported: The name of the bucket to be used for snapshots. (Mandatory) -`service_account`:: +`client`:: - The service account to use. It can be a relative path to a service account JSON file - or the value `_default_` that indicate to use built-in Compute Engine service account. + The client congfiguration to use. This controls which credentials are used to connect + to Compute Engine. `base_path`:: diff --git a/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java b/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java index b1bacab22fd8f..fa43ab1bc3fcc 100644 --- a/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java +++ b/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java @@ -22,10 +22,12 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Collections; +import java.util.List; import java.util.Map; import com.google.api.client.auth.oauth2.TokenRequest; import com.google.api.client.auth.oauth2.TokenResponse; +import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.client.googleapis.json.GoogleJsonError; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpHeaders; @@ -39,6 +41,8 @@ import com.google.api.services.storage.model.Objects; import com.google.api.services.storage.model.StorageObject; import org.elasticsearch.SpecialPermission; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.env.Environment; import org.elasticsearch.plugins.Plugin; @@ -108,9 +112,15 @@ public class GoogleCloudStoragePlugin extends Plugin implements RepositoryPlugin }); } + private final Map credentials; + + public GoogleCloudStoragePlugin(Settings settings) { + credentials = GoogleCloudStorageService.loadClientCredentials(settings); + } + // overridable for tests protected GoogleCloudStorageService createStorageService(Environment environment) { - return new GoogleCloudStorageService.InternalGoogleCloudStorageService(environment); + return new GoogleCloudStorageService.InternalGoogleCloudStorageService(environment, credentials); } @Override @@ -118,4 +128,9 @@ public Map getRepositories(Environment env, NamedXCo return Collections.singletonMap(GoogleCloudStorageRepository.TYPE, (metadata) -> new GoogleCloudStorageRepository(metadata, env, namedXContentRegistry, createStorageService(env))); } + + @Override + public List> getSettings() { + return Collections.singletonList(GoogleCloudStorageService.CREDENTIALS_FILE_SETTING); + } } diff --git a/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java b/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java index 79cbbe5b156f8..500306687f65d 100644 --- a/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java +++ b/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java @@ -48,25 +48,26 @@ class GoogleCloudStorageRepository extends BlobStoreRepository { static final ByteSizeValue MIN_CHUNK_SIZE = new ByteSizeValue(1, ByteSizeUnit.BYTES); static final ByteSizeValue MAX_CHUNK_SIZE = new ByteSizeValue(100, ByteSizeUnit.MB); - public static final String TYPE = "gcs"; + static final String TYPE = "gcs"; - public static final TimeValue NO_TIMEOUT = timeValueMillis(-1); + static final TimeValue NO_TIMEOUT = timeValueMillis(-1); - public static final Setting BUCKET = + static final Setting BUCKET = simpleString("bucket", Property.NodeScope, Property.Dynamic); - public static final Setting BASE_PATH = + static final Setting BASE_PATH = simpleString("base_path", Property.NodeScope, Property.Dynamic); - public static final Setting COMPRESS = + static final Setting COMPRESS = boolSetting("compress", false, Property.NodeScope, Property.Dynamic); - public static final Setting CHUNK_SIZE = + static final Setting CHUNK_SIZE = byteSizeSetting("chunk_size", MAX_CHUNK_SIZE, MIN_CHUNK_SIZE, MAX_CHUNK_SIZE, Property.NodeScope, Property.Dynamic); - public static final Setting APPLICATION_NAME = + static final Setting APPLICATION_NAME = new Setting<>("application_name", GoogleCloudStoragePlugin.NAME, Function.identity(), Property.NodeScope, Property.Dynamic); - public static final Setting SERVICE_ACCOUNT = - simpleString("service_account", Property.NodeScope, Property.Dynamic); - public static final Setting HTTP_READ_TIMEOUT = + static final Setting SERVICE_ACCOUNT = + new Setting<>("service_account", "_default_", Function.identity(), Property.NodeScope, Property.Dynamic, Property.Deprecated); + static final Setting CLIENT_NAME = new Setting<>("client", "default", Function.identity()); + static final Setting HTTP_READ_TIMEOUT = timeSetting("http.read_timeout", NO_TIMEOUT, Property.NodeScope, Property.Dynamic); - public static final Setting HTTP_CONNECT_TIMEOUT = + static final Setting HTTP_CONNECT_TIMEOUT = timeSetting("http.connect_timeout", NO_TIMEOUT, Property.NodeScope, Property.Dynamic); private final ByteSizeValue chunkSize; @@ -81,7 +82,8 @@ class GoogleCloudStorageRepository extends BlobStoreRepository { String bucket = getSetting(BUCKET, metadata); String application = getSetting(APPLICATION_NAME, metadata); - String serviceAccount = getSetting(SERVICE_ACCOUNT, metadata); + String serviceAccount = SERVICE_ACCOUNT.get(metadata.settings()); + String clientName = CLIENT_NAME.get(metadata.settings()); String basePath = BASE_PATH.get(metadata.settings()); if (Strings.hasLength(basePath)) { @@ -113,7 +115,7 @@ class GoogleCloudStorageRepository extends BlobStoreRepository { logger.debug("using bucket [{}], base_path [{}], chunk_size [{}], compress [{}], application [{}]", bucket, basePath, chunkSize, compress, application); - Storage client = storageService.createClient(serviceAccount, application, connectTimeout, readTimeout); + Storage client = storageService.createClient(serviceAccount, clientName, application, connectTimeout, readTimeout); this.blobStore = new GoogleCloudStorageBlobStore(settings, bucket, client); } diff --git a/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java b/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java index d541a729a71d3..8da54a42ffd1b 100644 --- a/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java +++ b/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java @@ -35,28 +35,43 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.SecureSetting; +import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.env.Environment; import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; interface GoogleCloudStorageService { + String SETTINGS_PREFIX = "gcs.client."; + + /** A json credentials file loaded from secure settings. */ + Setting.AffixSetting CREDENTIALS_FILE_SETTING = Setting.affixKeySetting(SETTINGS_PREFIX, "credentials_file", + key -> SecureSetting.secureFile(key, null)); + /** * Creates a client that can be used to manage Google Cloud Storage objects. * * @param serviceAccount path to service account file + * @param clientName name of client settings to use from secure settings * @param application name of the application * @param connectTimeout connection timeout for HTTP requests * @param readTimeout read timeout for HTTP requests * @return a Client instance that can be used to manage objects */ - Storage createClient(String serviceAccount, String application, TimeValue connectTimeout, TimeValue readTimeout) throws Exception; + Storage createClient(String serviceAccount, String clientName, String application, + TimeValue connectTimeout, TimeValue readTimeout) throws Exception; /** * Default implementation @@ -67,58 +82,60 @@ class InternalGoogleCloudStorageService extends AbstractComponent implements Goo private final Environment environment; - InternalGoogleCloudStorageService(Environment environment) { + /** Credentials identified by client name. */ + private final Map credentials; + + InternalGoogleCloudStorageService(Environment environment, Map credentials) { super(environment.settings()); this.environment = environment; + this.credentials = credentials; } @Override - public Storage createClient(String serviceAccount, String application, TimeValue connectTimeout, TimeValue readTimeout) - throws Exception { + public Storage createClient(String serviceAccountFile, String clientName, String application, + TimeValue connectTimeout, TimeValue readTimeout) throws Exception { try { - GoogleCredential credentials = (DEFAULT.equalsIgnoreCase(serviceAccount)) ? loadDefault() : loadCredentials(serviceAccount); + GoogleCredential credential = getCredential(serviceAccountFile, clientName); NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport(); Storage.Builder storage = new Storage.Builder(httpTransport, JacksonFactory.getDefaultInstance(), - new DefaultHttpRequestInitializer(credentials, connectTimeout, readTimeout)); + new DefaultHttpRequestInitializer(credential, connectTimeout, readTimeout)); storage.setApplicationName(application); logger.debug("initializing client with service account [{}/{}]", - credentials.getServiceAccountId(), credentials.getServiceAccountUser()); + credential.getServiceAccountId(), credential.getServiceAccountUser()); return storage.build(); } catch (IOException e) { throw new ElasticsearchException("Error when loading Google Cloud Storage credentials file", e); } } - /** - * HTTP request initializer that loads credentials from the service account file - * and manages authentication for HTTP requests - */ - private GoogleCredential loadCredentials(String serviceAccount) throws IOException { - if (serviceAccount == null) { - throw new ElasticsearchException("Cannot load Google Cloud Storage service account file from a null path"); - } - - Path account = environment.configFile().resolve(serviceAccount); - if (Files.exists(account) == false) { - throw new ElasticsearchException("Unable to find service account file [" + serviceAccount + // pkg private for tests + GoogleCredential getCredential(String serviceAccountFile, String clientName) throws IOException { + if (DEFAULT.equalsIgnoreCase(serviceAccountFile) == false) { + deprecationLogger.deprecated("Using GCS service account file from disk is deprecated. " + + "Move the file into the elasticsearch keystore."); + Path account = environment.configFile().resolve(serviceAccountFile); + if (Files.exists(account) == false) { + throw new IllegalArgumentException("Unable to find service account file [" + serviceAccountFile + "] defined for repository"); - } + } - try (InputStream is = Files.newInputStream(account)) { - GoogleCredential credential = GoogleCredential.fromStream(is); - if (credential.createScopedRequired()) { - credential = credential.createScoped(Collections.singleton(StorageScopes.DEVSTORAGE_FULL_CONTROL)); + try (InputStream is = Files.newInputStream(account)) { + GoogleCredential credential = GoogleCredential.fromStream(is); + if (credential.createScopedRequired()) { + credential = credential.createScoped(Collections.singleton(StorageScopes.DEVSTORAGE_FULL_CONTROL)); + } + return credential; } - return credential; + } else if (credentials.containsKey(clientName)) { + return credentials.get(clientName); } + return getDefaultCredential(); } - /** - * HTTP request initializer that loads default credentials when running on Compute Engine - */ - private GoogleCredential loadDefault() throws IOException { + // pkg private for tests + GoogleCredential getDefaultCredential() throws IOException { return GoogleCredential.getApplicationDefault(); } @@ -172,4 +189,23 @@ private ExponentialBackOff newBackOff() { } } } + + /** Load all secure credentials from the settings. */ + static Map loadClientCredentials(Settings settings) { + Set clientNames = settings.getGroups(SETTINGS_PREFIX).keySet(); + Map credentials = new HashMap<>(); + for (String clientName : clientNames) { + Setting concreteSetting = CREDENTIALS_FILE_SETTING.getConcreteSettingForNamespace(clientName); + try (InputStream credStream = concreteSetting.get(settings)) { + GoogleCredential credential = GoogleCredential.fromStream(credStream); + if (credential.createScopedRequired()) { + credential = credential.createScoped(Collections.singleton(StorageScopes.DEVSTORAGE_FULL_CONTROL)); + } + credentials.put(clientName, credential); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + return credentials; + } } diff --git a/plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStoreRepositoryTests.java b/plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStoreRepositoryTests.java index b2ff8bfe1fd77..ceb8f8909e2b4 100644 --- a/plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStoreRepositoryTests.java +++ b/plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStoreRepositoryTests.java @@ -67,6 +67,9 @@ public static void setUpStorage() { } public static class MockGoogleCloudStoragePlugin extends GoogleCloudStoragePlugin { + public MockGoogleCloudStoragePlugin() { + super(Settings.EMPTY); + } @Override protected GoogleCloudStorageService createStorageService(Environment environment) { return new MockGoogleCloudStorageService(); @@ -75,7 +78,8 @@ protected GoogleCloudStorageService createStorageService(Environment environment public static class MockGoogleCloudStorageService implements GoogleCloudStorageService { @Override - public Storage createClient(String serviceAccount, String application, TimeValue connectTimeout, TimeValue readTimeout) throws + public Storage createClient(String serviceAccount, String accountName, String application, + TimeValue connectTimeout, TimeValue readTimeout) throws Exception { return storage.get(); } diff --git a/plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageServiceTests.java b/plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageServiceTests.java new file mode 100644 index 0000000000000..59f17fcd1c23d --- /dev/null +++ b/plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageServiceTests.java @@ -0,0 +1,85 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.repositories.gcs; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Map; + +import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.repositories.gcs.GoogleCloudStorageService.InternalGoogleCloudStorageService; +import org.elasticsearch.test.ESTestCase; + +import static org.hamcrest.Matchers.containsString; + +public class GoogleCloudStorageServiceTests extends ESTestCase { + + private InputStream getDummyCredentialStream() throws IOException { + return GoogleCloudStorageServiceTests.class.getResourceAsStream("/dummy-account.json"); + } + + public void testDefaultCredential() throws Exception { + Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build()); + GoogleCredential cred = GoogleCredential.fromStream(getDummyCredentialStream()); + InternalGoogleCloudStorageService service = new InternalGoogleCloudStorageService(env, Collections.emptyMap()) { + @Override + GoogleCredential getDefaultCredential() throws IOException { + return cred; + } + }; + assertSame(cred, service.getCredential("_default_", "default")); + } + + public void testFileCredentialBackcompat() throws Exception { + Path home = createTempDir(); + Path config = home.resolve("config"); + Files.createDirectories(config); + Settings settings = Settings.builder() + .put("path.home", home).build(); + Environment env = new Environment(settings); + Files.copy(getDummyCredentialStream(), config.resolve("test-cred.json")); + InternalGoogleCloudStorageService service = new InternalGoogleCloudStorageService(env, Collections.emptyMap()); + GoogleCredential cred = service.getCredential("test-cred.json", "default"); + assertEquals("some-project-name@appspot.gserviceaccount.com", cred.getServiceAccountId()); + assertWarnings("Using GCS service account file from disk is deprecated. Move the file into the elasticsearch keystore."); + } + + public void testFileCredentialMissing() throws Exception { + Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build()); + InternalGoogleCloudStorageService service = new InternalGoogleCloudStorageService(env, Collections.emptyMap()); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> + service.getCredential("test-cred.json", "default")); + assertThat(e.getMessage(), containsString("Unable to find service account file")); + assertWarnings("Using GCS service account file from disk is deprecated. Move the file into the elasticsearch keystore."); + } + + public void testClientCredential() throws Exception { + GoogleCredential cred = GoogleCredential.fromStream(getDummyCredentialStream()); + Map credentials = Collections.singletonMap("clientname", cred); + Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build()); + InternalGoogleCloudStorageService service = new InternalGoogleCloudStorageService(env, credentials); + assertSame(cred, service.getCredential("_default_", "clientname")); + } +} diff --git a/plugins/repository-gcs/src/test/resources/dummy-account.json b/plugins/repository-gcs/src/test/resources/dummy-account.json new file mode 100644 index 0000000000000..e282b6db0e3b0 --- /dev/null +++ b/plugins/repository-gcs/src/test/resources/dummy-account.json @@ -0,0 +1,12 @@ +{ + "type": "service_account", + "project_id": "some-project-name", + "private_key_id": "c7cefcb7c72a2880ecce49cb9d1095de5a61aff0", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDa+7r0RE1YykXC\n+d+DXlN3Dg3aL1YOfYuhy5PF/Vi0FFQHyXuPtAvkVHZD2NxMDZq2DxTu3AVLh1UE\nt2hMrWjDQDuArPl8FezpyYQwde04Qlx1YpQ1xUjTaFWd0hrOZEfsxY00h3ilxR3G\nJsofR3PZBKYI11VGruNemCgjiJg5hcoJDxLXUgcfpKJaiPeHutczCeZ1RANQwQF1\n/dPXhqbtWiaS/iu5so64P54TsrVX5DcXmbGr6hQAReIcI6cjA8QhSu6QBtdvEPhv\n27uTuSu4XRtTh3djVGFzFV9pamGZeGELkTiHVSDI8IkQ32s8yuP5Zys/4bFJk7nn\niqJpe0/DAgMBAAECggEAEexQnPWKLx4/H3o8JRBvXGs2DwmYzY7RAukaqzXVMMgJ\nKKoBBv4Biyquk1cIkOD8LLKHUBWKCWiGOOCaFMyMqo5zUFDYCqPwxCHOQ/ki9VvZ\nHXJ4Fv6Su1rqxwQPVZ03ldWFfSspYMgFa9Z47J54iOasgES/og1mZrOldWMUsoBu\nCKf0fH+vIsxWPwmRtyxKCMwqenqdc22nGGLhmpm8tuw1eQp6XtTXagqkPtAVMMga\nmgC0EGqhZA/IklGW1JuGWELjXVMgS/tLIPq+hYsmY14y6Ie032YoSMWkz6Z5p7i0\n/JwCzVZNO1mD0MwVj7nDmokXOpoyM7Qcbx8r1E4Q4QKBgQDxqAZ6D+A671mCNU0J\n6Qzc3cOZq7MBj4y7M/2qPXHC7i/DdbmnM7PPPriaBBch2nX7jZRlRmVDBsmrC4OG\n3m5+HAx7YPVbefwe6h5ki5O9wg1pLcgYY9uvgLSlD85lVZKAzO7QK2W5zfM19kPD\nSckIa+U7DKFbwKhtCsxcP6ARJwKBgQDn+zAPHepGP2Zf78pOLlVXcQqVA1uOu+bW\nrG4G+lPrytB0C4QdwWdBV3Lcqmnw/ae5PkQBs0dCbtWG8+MT8gA6k5kleflaZrAY\ngdUJIUP6J7ocWYxVTfqGFyFF1n5VT8/jbVucaT7izBZfZvlGyf7Vz7ewQzgWQWlK\nCQ0qstV2BQKBgQCajAQAYlDcQCC1dlMbqHDye91RVQ65S84MF1b+Xid4LA5d6dde\nyGERhKJY1Y7ZtrZHt6cVEe1G7XtiKY3nXi+59URCT6L66svEFaR0VxOYgxdCkeXr\nO0nPNvfQrIgqJIz6VJXSij6XktAdTa7OoUyxVxeWKSC05kSQ4BwMTyCWdwKBgQCW\noqlmZ4qE6w5TJaY8diG8kg7JDFEbsjAHHhikN1DfP+d0MzYrDDc8WsifOZlpf4y1\n4RTP9dZD8Sx+YUgG35H+d3FuwHGGnj+i6kunjg5SFhHn7s4NZoFTKRnV+541T4oy\nqARg4IaRRu0QLhGYQfpUZHlm339AFGGGTbJbE51A8QKBgQDTEN5O+3bRG3Fa1J6z\nU9PMrjjs6l8xhXFso10YEYG5KRnfhzCFujyWNiLE6WrlUL8invVBaCxsZr51GDgA\nhyEEdm4kXCRrv4JyhOvIuGxNcAIiQK/e91UQEM6u1t6hUI1rE7ZOyJQzBxj9hFlV\n7OvhBlHXQUtAOdq0XLHr9GzdSA==\n-----END PRIVATE KEY-----\n", + "client_email": "some-project-name@appspot.gserviceaccount.com", + "client_id": "123456789101112130594", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://accounts.google.com/o/oauth2/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/some-project-name%40appspot.gserviceaccount.com" +} From 2a65bed243a0387bdf1175c0ca4b73ed863d9792 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 16 May 2017 17:24:35 -0700 Subject: [PATCH 386/619] Tests: Change rest test extension from .yaml to .yml (#24659) This commit renames all rest test files to use the .yml extension instead of .yaml. This way the extension used within all of elasticsearch for yaml is consistent. --- .../doc/RestTestsFromSnippetsTask.groovy | 2 +- .../{10_modules.yaml => 10_modules.yml} | 0 .../{10_modules.yaml => 10_modules.yml} | 0 .../{10_modules.yaml => 10_modules.yml} | 0 .../plugins/InstallPluginCommandTests.java | 12 +++++------ .../{10_modules.yaml => 10_modules.yml} | 0 .../stats/{10_basic.yaml => 10_basic.yml} | 0 ..._empty_bucket.yaml => 20_empty_bucket.yml} | 0 ...e_field.yaml => 30_single_value_field.yml} | 0 ...ue_field.yaml => 40_multi_value_field.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_analyzers.yaml => 20_analyzers.yml} | 0 .../{30_tokenizers.yaml => 30_tokenizers.yml} | 0 ...oken_filters.yaml => 40_token_filters.yml} | 0 ..._char_filters.yaml => 50_char_filters.yml} | 0 ...yaml => 100_date_index_name_processor.yml} | 0 .../ingest/{10_basic.yaml => 10_basic.yml} | 0 .../ingest/{110_sort.yaml => 110_sort.yml} | 0 .../ingest/{120_grok.yaml => 120_grok.yml} | 0 ...130_escape_dot.yaml => 130_escape_dot.yml} | 0 .../ingest/{140_json.yaml => 140_json.yml} | 0 .../test/ingest/{150_kv.yaml => 150_kv.yml} | 0 .../test/ingest/{20_crud.yaml => 20_crud.yml} | 0 ...e_processor.yaml => 30_date_processor.yml} | 0 .../ingest/{40_mutate.yaml => 40_mutate.yml} | 0 .../{50_on_failure.yaml => 50_on_failure.yml} | 0 .../test/ingest/{60_fail.yaml => 60_fail.yml} | 0 .../test/ingest/{70_bulk.yaml => 70_bulk.yml} | 0 .../{80_foreach.yaml => 80_foreach.yml} | 0 .../{90_simulate.yaml => 90_simulate.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_search.yaml => 20_search.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...ate.yaml => 20_render_search_template.yml} | 0 ...functions.yaml => 25_custom_functions.yml} | 0 ...h_template.yaml => 30_search_template.yml} | 0 ...late.yaml => 50_multi_search_template.yml} | 0 .../{60_typed_keys.yaml => 60_typed_keys.yml} | 0 .../painless/{10_basic.yaml => 10_basic.yml} | 0 .../{15_update.yaml => 15_update.yml} | 0 .../{16_update2.yaml => 16_update2.yml} | 0 ...20_scriptfield.yaml => 20_scriptfield.yml} | 0 ...cript_upsert.yaml => 25_script_upsert.yml} | 0 .../{30_search.yaml => 30_search.yml} | 0 .../{40_disabled.yaml => 40_disabled.yml} | 0 ...c_values.yaml => 50_script_doc_values.yml} | 0 ...y.yaml => 60_script_doc_values_binary.yml} | 0 .../test/{10_basic.yaml => 10_basic.yml} | 0 .../test/{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_validation.yaml => 20_validation.yml} | 0 .../{30_by_type.yaml => 30_by_type.yml} | 0 .../{40_versioning.yaml => 40_versioning.yml} | 0 ...rds.yaml => 50_wait_for_active_shards.yml} | 0 .../{70_throttle.yaml => 70_throttle.yml} | 0 .../{80_slices.yaml => 80_slices.yml} | 0 .../reindex/{10_basic.yaml => 10_basic.yml} | 0 .../{20_validation.yaml => 20_validation.yml} | 0 ...auto_create.yaml => 25_no_auto_create.yml} | 0 .../reindex/{30_search.yaml => 30_search.yml} | 0 .../{40_versioning.yaml => 40_versioning.yml} | 0 .../{50_routing.yaml => 50_routing.yml} | 0 ...rds.yaml => 60_wait_for_active_shards.yml} | 0 .../{70_throttle.yaml => 70_throttle.yml} | 0 .../reindex/{80_slices.yaml => 80_slices.yml} | 0 .../reindex/{90_remote.yaml => 90_remote.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_validation.yaml => 20_validation.yml} | 0 .../{30_new_fields.yaml => 30_new_fields.yml} | 0 .../{40_versioning.yaml => 40_versioning.yml} | 0 ...50_consistency.yaml => 50_consistency.yml} | 0 .../{60_throttle.yaml => 60_throttle.yml} | 0 .../{70_slices.yaml => 70_slices.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_repository.yaml => 20_repository.yml} | 0 .../test/{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_search.yaml => 20_search.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_search.yaml => 20_search.yml} | 0 .../{10_metaphone.yaml => 10_metaphone.yml} | 0 ...metaphone.yaml => 20_double_metaphone.yml} | 0 ..._beider_morse.yaml => 30_beider_morse.yml} | 0 .../{40_search.yaml => 40_search.yml} | 0 ...h_mokotoff.yaml => 50_daitch_mokotoff.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_search.yaml => 20_search.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_search.yaml => 20_search.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_search.yaml => 20_search.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_score.yaml => 20_score.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...essor.yaml => 20_attachment_processor.yml} | 0 ..._supported.yaml => 30_files_supported.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ..._processor.yaml => 20_geoip_processor.yml} | 0 plugins/ingest-user-agent/build.gradle | 2 +- .../useragent/IngestUserAgentPlugin.java | 4 ++-- .../resources/{regexes.yaml => regexes.yml} | 0 .../UserAgentProcessorFactoryTests.java | 8 ++++---- .../useragent/UserAgentProcessorTests.java | 2 +- .../{10_basic.yaml => 10_basic.yml} | 0 ...cessor.yaml => 20_useragent_processor.yml} | 0 ..._custom_regex.yaml => 30_custom_regex.yml} | 2 +- .../{test-regexes.yaml => test-regexes.yml} | 0 .../main/config/{example.yaml => example.yml} | 0 .../example/ExamplePluginConfiguration.java | 2 +- .../{10_basic.yaml => 10_basic.yml} | 0 ...example.yaml => 20_configured_example.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_repository.yaml => 20_repository.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...y_create.yaml => 20_repository_create.yml} | 0 ...y_delete.yaml => 20_repository_delete.yml} | 0 ...y_verify.yaml => 20_repository_verify.yml} | 0 .../{30_snapshot.yaml => 30_snapshot.yml} | 0 ..._snapshot_get.yaml => 30_snapshot_get.yml} | 0 .../{40_restore.yaml => 40_restore.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...y_create.yaml => 20_repository_create.yml} | 0 ...y_delete.yaml => 20_repository_delete.yml} | 0 ...y_verify.yaml => 20_repository_verify.yml} | 0 .../{30_snapshot.yaml => 30_snapshot.yml} | 0 ..._snapshot_get.yaml => 30_snapshot_get.yml} | 0 .../{40_restore.yaml => 40_restore.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_repository.yaml => 20_repository.yml} | 0 .../store_smb/{10_basic.yaml => 10_basic.yml} | 0 ...ex_creation.yaml => 15_index_creation.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_info.yaml => 20_info.yml} | 0 .../{30_field_caps.yaml => 30_field_caps.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...t_disabled.yaml => 10_ingest_disabled.yml} | 0 ...> 10_pipeline_with_mustache_templates.yml} | 0 ...cessors.yaml => 20_combine_processors.yml} | 0 ...aml => 30_update_by_query_with_ingest.yml} | 0 ...ingest.yaml => 40_reindex_with_ingest.yml} | 0 ...=> 50_script_processor_using_painless.yml} | 0 ...=> 60_pipeline_timestamp_date_mapping.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../reindex/{10_script.yaml => 10_script.yml} | 0 .../reindex/{20_broken.yaml => 20_broken.yml} | 0 .../{30_timeout.yaml => 30_timeout.yml} | 0 ...h_failures.yaml => 40_search_failures.yml} | 0 ...d.yaml => 50_reindex_with_parentchild.yml} | 0 .../{10_script.yaml => 10_script.yml} | 0 .../{20_broken.yaml => 20_broken.yml} | 0 .../{30_timeout.yaml => 30_timeout.yml} | 0 ...rch_failure.yaml => 40_search_failure.yml} | 0 .../tribe/{10_basic.yaml => 10_basic.yml} | 0 .../test/bulk/{10_basic.yaml => 10_basic.yml} | 0 ...of_strings.yaml => 20_list_of_strings.yml} | 0 .../{30_big_string.yaml => 30_big_string.yml} | 0 .../bulk/{40_source.yaml => 40_source.yml} | 0 .../bulk/{50_refresh.yaml => 50_refresh.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_headers.yaml => 20_headers.yml} | 0 .../cat.aliases/{30_json.yaml => 30_json.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../cat.count/{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../cat.nodes/{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../cat.tasks/{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...st_timeout.yaml => 20_request_timeout.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{11_explain.yaml => 11_explain.yml} | 0 ...ltering.yaml => 20_response_filtering.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_filtering.yaml => 20_filtering.yml} | 0 ...wildcards.yaml => 30_expand_wildcards.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../count/{10_basic.yaml => 10_basic.yml} | 0 ..._query_string.yaml => 20_query_string.yml} | 0 .../{10_with_id.yaml => 10_with_id.yml} | 0 .../{15_without_id.yaml => 15_without_id.yml} | 0 ...l_version.yaml => 30_internal_version.yml} | 0 ...l_version.yaml => 35_external_version.yml} | 0 .../{40_routing.yaml => 40_routing.yml} | 0 .../create/{50_parent.yaml => 50_parent.yml} | 0 ...outing.yaml => 55_parent_with_routing.yml} | 0 .../{60_refresh.yaml => 60_refresh.yml} | 0 .../delete/{10_basic.yaml => 10_basic.yml} | 0 ..._shard_header.yaml => 11_shard_header.yml} | 0 .../delete/{12_result.yaml => 12_result.yml} | 0 ...l_version.yaml => 20_internal_version.yml} | 0 ...l_version.yaml => 25_external_version.yml} | 0 ...rsion.yaml => 26_external_gte_version.yml} | 0 .../{30_routing.yaml => 30_routing.yml} | 0 .../delete/{40_parent.yaml => 40_parent.yml} | 0 ...outing.yaml => 45_parent_with_routing.yml} | 0 .../{50_refresh.yaml => 50_refresh.yml} | 0 .../{60_missing.yaml => 60_missing.yml} | 0 .../exists/{10_basic.yaml => 10_basic.yml} | 0 .../exists/{30_parent.yaml => 30_parent.yml} | 0 .../{40_routing.yaml => 40_routing.yml} | 0 ...outing.yaml => 55_parent_with_routing.yml} | 0 ...e_refresh.yaml => 60_realtime_refresh.yml} | 0 .../{70_defaults.yaml => 70_defaults.yml} | 0 .../explain/{10_basic.yaml => 10_basic.yml} | 0 ...filtering.yaml => 20_source_filtering.yml} | 0 ..._query_string.yaml => 30_query_string.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basics.yaml => 10_basics.yml} | 0 .../test/get/{10_basic.yaml => 10_basic.yml} | 0 ...ault_values.yaml => 15_default_values.yml} | 0 ...tored_fields.yaml => 20_stored_fields.yml} | 0 .../get/{30_parent.yaml => 30_parent.yml} | 0 .../get/{40_routing.yaml => 40_routing.yml} | 0 ..._with_headers.yaml => 50_with_headers.yml} | 0 ...outing.yaml => 55_parent_with_routing.yml} | 0 ...e_refresh.yaml => 60_realtime_refresh.yml} | 0 ...filtering.yaml => 70_source_filtering.yml} | 0 .../get/{80_missing.yaml => 80_missing.yml} | 0 .../get/{90_versions.yaml => 90_versions.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...ault_values.yaml => 15_default_values.yml} | 0 .../{30_parent.yaml => 30_parent.yml} | 0 .../{40_routing.yaml => 40_routing.yml} | 0 ...outing.yaml => 55_parent_with_routing.yml} | 0 ...e_refresh.yaml => 60_realtime_refresh.yml} | 0 ...filtering.yaml => 70_source_filtering.yml} | 0 .../{80_missing.yaml => 80_missing.yml} | 0 ...rce_missing.yaml => 85_source_missing.yml} | 0 .../index/{10_with_id.yaml => 10_with_id.yml} | 0 .../index/{12_result.yaml => 12_result.yml} | 0 .../{15_without_id.yaml => 15_without_id.yml} | 0 .../index/{20_optype.yaml => 20_optype.yml} | 0 ...l_version.yaml => 30_internal_version.yml} | 0 ...l_version.yaml => 35_external_version.yml} | 0 ...rsion.yaml => 36_external_gte_version.yml} | 0 .../index/{40_routing.yaml => 40_routing.yml} | 0 .../index/{50_parent.yaml => 50_parent.yml} | 0 ...outing.yaml => 55_parent_with_routing.yml} | 0 .../index/{60_refresh.yaml => 60_refresh.yml} | 0 .../{10_analyze.yaml => 10_analyze.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...path_options.yaml => all_path_options.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...only_index.yaml => 20_read_only_index.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_empty.yaml => 20_empty.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...issing_field.yaml => 20_missing_field.yml} | 0 ..._missing_type.yaml => 30_missing_type.yml} | 0 ...issing_index.yaml => 40_missing_index.yml} | 0 ..._wildcards.yaml => 50_field_wildcards.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ..._missing_type.yaml => 20_missing_type.yml} | 0 ...issing_index.yaml => 30_missing_index.yml} | 0 .../{40_aliases.yaml => 40_aliases.yml} | 0 ...pansion.yaml => 50_wildcard_expansion.yml} | 0 .../{60_empty.yaml => 60_empty.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_aliases.yaml => 20_aliases.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...20_get_missing.yaml => 20_get_missing.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...e_indices.yaml => 20_multiple_indices.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...path_options.yaml => all_path_options.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...path_options.yaml => all_path_options.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{11_reset.yaml => 11_reset.yml} | 0 ...path_options.yaml => all_path_options.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_index.yaml => 10_index.yml} | 0 .../{11_metric.yaml => 11_metric.yml} | 0 .../{12_level.yaml => 12_level.yml} | 0 .../{13_fields.yaml => 13_fields.yml} | 0 .../{14_groups.yaml => 14_groups.yml} | 0 .../{15_types.yaml => 15_types.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_routing.yaml => 20_routing.yml} | 0 ...0_remove_index_and_replace_with_alias.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ..._query_string.yaml => 20_query_string.yml} | 0 .../test/info/{10_info.yaml => 10_info.yml} | 0 ...ene_version.yaml => 20_lucene_version.yml} | 0 .../ingest/{10_basic.yaml => 10_basic.yml} | 0 .../test/mget/{10_basic.yaml => 10_basic.yml} | 0 ...ex_type.yaml => 11_default_index_type.yml} | 0 ...t_index.yaml => 12_non_existent_index.yml} | 0 ..._metadata.yaml => 13_missing_metadata.yml} | 0 ....yaml => 14_alias_to_multiple_indices.yml} | 0 .../test/mget/{15_ids.yaml => 15_ids.yml} | 0 ...tored_fields.yaml => 20_stored_fields.yml} | 0 .../mget/{30_parent.yaml => 30_parent.yml} | 0 .../mget/{40_routing.yaml => 40_routing.yml} | 0 ...outing.yaml => 55_parent_with_routing.yml} | 0 ...e_refresh.yaml => 60_realtime_refresh.yml} | 0 ...filtering.yaml => 70_source_filtering.yml} | 0 .../test/mlt/{10_basic.yaml => 10_basic.yml} | 0 .../test/mlt/{20_docs.yaml => 20_docs.yml} | 0 .../mlt/{30_unlike.yaml => 30_unlike.yml} | 0 .../msearch/{10_basic.yaml => 10_basic.yml} | 0 .../msearch/{11_status.yaml => 11_status.yml} | 0 .../{20_typed_keys.yaml => 20_typed_keys.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_transport.yaml => 20_transport.yml} | 0 .../{30_settings.yaml => 30_settings.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...es_metrics.yaml => 11_indices_metrics.yml} | 0 ...ltering.yaml => 20_response_filtering.yml} | 0 .../{30_discovery.yaml => 30_discovery.yml} | 0 .../test/ping/{10_ping.yaml => 10_ping.yml} | 0 .../remote.info/{10_info.yaml => 10_info.yml} | 0 .../scroll/{10_basic.yaml => 10_basic.yml} | 0 .../scroll/{11_clear.yaml => 11_clear.yml} | 0 .../scroll/{12_slices.yaml => 12_slices.yml} | 0 .../{10_histogram.yaml => 10_histogram.yml} | 0 .../{20_terms.yaml => 20_terms.yml} | 0 .../{30_sig_terms.yaml => 30_sig_terms.yml} | 0 .../{40_range.yaml => 40_range.yml} | 0 .../{50_filter.yaml => 50_filter.yml} | 0 ...cy_matrix.yaml => 70_adjacency_matrix.yml} | 0 .../{80_typed_keys.yaml => 80_typed_keys.yml} | 0 .../{10_unified.yaml => 10_unified.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 ...ored_fields.yaml => 100_stored_fields.yml} | 0 ...filtering.yaml => 10_source_filtering.yml} | 0 ...llapsing.yaml => 110_field_collapsing.yml} | 0 ...ce_size.yaml => 120_batch_reduce_size.yml} | 0 ...ault_values.yaml => 20_default_values.yml} | 0 .../search/{30_limits.yaml => 30_limits.yml} | 0 ...ndices_boost.yaml => 40_indices_boost.yml} | 0 ..._query_string.yaml => 60_query_string.yml} | 0 ...ltering.yaml => 70_response_filtering.yml} | 0 ...es_options.yaml => 80_indices_options.yml} | 0 ..._search_after.yaml => 90_search_after.yml} | 0 .../search/{issue4895.yaml => issue4895.yml} | 0 .../search/{issue9606.yaml => issue9606.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../suggest/{10_basic.yaml => 10_basic.yml} | 0 .../{20_completion.yaml => 20_completion.yml} | 0 .../{30_context.yaml => 30_context.yml} | 0 .../{40_typed_keys.yaml => 40_typed_keys.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../tasks.get/{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{10_basic.yaml => 10_basic.yml} | 0 .../{20_issue7121.yaml => 20_issue7121.yml} | 0 .../{30_realtime.yaml => 30_realtime.yml} | 0 .../{40_versions.yaml => 40_versions.yml} | 0 .../test/update/{10_doc.yaml => 10_doc.yml} | 0 ..._shard_header.yaml => 11_shard_header.yml} | 0 .../update/{12_result.yaml => 12_result.yml} | 0 .../{20_doc_upsert.yaml => 20_doc_upsert.yml} | 0 ...oc_as_upsert.yaml => 22_doc_as_upsert.yml} | 0 ...l_version.yaml => 30_internal_version.yml} | 0 ...er_versions.yaml => 35_other_versions.yml} | 0 .../{40_routing.yaml => 40_routing.yml} | 0 .../update/{50_parent.yaml => 50_parent.yml} | 0 ...outing.yaml => 55_parent_with_routing.yml} | 0 .../{60_refresh.yaml => 60_refresh.yml} | 0 ...filtering.yaml => 80_source_filtering.yml} | 0 ...85_fields_meta.yaml => 85_fields_meta.yml} | 0 .../rest/yaml/ESClientYamlSuiteTestCase.java | 14 ++++++------- .../yaml/ESClientYamlSuiteTestCaseTests.java | 20 +++++++++---------- .../suite1/{10_basic.yaml => 10_basic.yml} | 0 ..._another_test.yaml => 20_another_test.yml} | 0 .../suite2/{10_basic.yaml => 10_basic.yml} | 0 .../suite2/{15_test2.yaml => 15_test2.yml} | 0 414 files changed, 34 insertions(+), 34 deletions(-) rename distribution/deb/src/test/resources/rest-api-spec/test/smoke_test_plugins/{10_modules.yaml => 10_modules.yml} (100%) rename distribution/rpm/src/test/resources/rest-api-spec/test/smoke_test_plugins/{10_modules.yaml => 10_modules.yml} (100%) rename distribution/tar/src/test/resources/rest-api-spec/test/smoke_test_plugins/{10_modules.yaml => 10_modules.yml} (100%) rename distribution/zip/src/test/resources/rest-api-spec/test/smoke_test_plugins/{10_modules.yaml => 10_modules.yml} (100%) rename modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/{10_basic.yaml => 10_basic.yml} (100%) rename modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/{20_empty_bucket.yaml => 20_empty_bucket.yml} (100%) rename modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/{30_single_value_field.yaml => 30_single_value_field.yml} (100%) rename modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/{40_multi_value_field.yaml => 40_multi_value_field.yml} (100%) rename modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/{10_basic.yaml => 10_basic.yml} (100%) rename modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/{20_analyzers.yaml => 20_analyzers.yml} (100%) rename modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/{30_tokenizers.yaml => 30_tokenizers.yml} (100%) rename modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/{40_token_filters.yaml => 40_token_filters.yml} (100%) rename modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/{50_char_filters.yaml => 50_char_filters.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{100_date_index_name_processor.yaml => 100_date_index_name_processor.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{10_basic.yaml => 10_basic.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{110_sort.yaml => 110_sort.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{120_grok.yaml => 120_grok.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{130_escape_dot.yaml => 130_escape_dot.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{140_json.yaml => 140_json.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{150_kv.yaml => 150_kv.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{20_crud.yaml => 20_crud.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{30_date_processor.yaml => 30_date_processor.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{40_mutate.yaml => 40_mutate.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{50_on_failure.yaml => 50_on_failure.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{60_fail.yaml => 60_fail.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{70_bulk.yaml => 70_bulk.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{80_foreach.yaml => 80_foreach.yml} (100%) rename modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/{90_simulate.yaml => 90_simulate.yml} (100%) rename modules/lang-expression/src/test/resources/rest-api-spec/test/lang_expression/{10_basic.yaml => 10_basic.yml} (100%) rename modules/lang-expression/src/test/resources/rest-api-spec/test/lang_expression/{20_search.yaml => 20_search.yml} (100%) rename modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/{10_basic.yaml => 10_basic.yml} (100%) rename modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/{20_render_search_template.yaml => 20_render_search_template.yml} (100%) rename modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/{25_custom_functions.yaml => 25_custom_functions.yml} (100%) rename modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/{30_search_template.yaml => 30_search_template.yml} (100%) rename modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/{50_multi_search_template.yaml => 50_multi_search_template.yml} (100%) rename modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/{60_typed_keys.yaml => 60_typed_keys.yml} (100%) rename modules/lang-painless/src/test/resources/rest-api-spec/test/painless/{10_basic.yaml => 10_basic.yml} (100%) rename modules/lang-painless/src/test/resources/rest-api-spec/test/painless/{15_update.yaml => 15_update.yml} (100%) rename modules/lang-painless/src/test/resources/rest-api-spec/test/painless/{16_update2.yaml => 16_update2.yml} (100%) rename modules/lang-painless/src/test/resources/rest-api-spec/test/painless/{20_scriptfield.yaml => 20_scriptfield.yml} (100%) rename modules/lang-painless/src/test/resources/rest-api-spec/test/painless/{25_script_upsert.yaml => 25_script_upsert.yml} (100%) rename modules/lang-painless/src/test/resources/rest-api-spec/test/painless/{30_search.yaml => 30_search.yml} (100%) rename modules/lang-painless/src/test/resources/rest-api-spec/test/painless/{40_disabled.yaml => 40_disabled.yml} (100%) rename modules/lang-painless/src/test/resources/rest-api-spec/test/painless/{50_script_doc_values.yaml => 50_script_doc_values.yml} (100%) rename modules/lang-painless/src/test/resources/rest-api-spec/test/painless/{60_script_doc_values_binary.yaml => 60_script_doc_values_binary.yml} (100%) rename modules/parent-join/src/test/resources/rest-api-spec/test/{10_basic.yaml => 10_basic.yml} (100%) rename modules/percolator/src/test/resources/rest-api-spec/test/{10_basic.yaml => 10_basic.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/{10_basic.yaml => 10_basic.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/{20_validation.yaml => 20_validation.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/{30_by_type.yaml => 30_by_type.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/{40_versioning.yaml => 40_versioning.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/{50_wait_for_active_shards.yaml => 50_wait_for_active_shards.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/{70_throttle.yaml => 70_throttle.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/{80_slices.yaml => 80_slices.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/reindex/{10_basic.yaml => 10_basic.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/reindex/{20_validation.yaml => 20_validation.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/reindex/{25_no_auto_create.yaml => 25_no_auto_create.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/reindex/{30_search.yaml => 30_search.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/reindex/{40_versioning.yaml => 40_versioning.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/reindex/{50_routing.yaml => 50_routing.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/reindex/{60_wait_for_active_shards.yaml => 60_wait_for_active_shards.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/reindex/{70_throttle.yaml => 70_throttle.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/reindex/{80_slices.yaml => 80_slices.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/reindex/{90_remote.yaml => 90_remote.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/{10_basic.yaml => 10_basic.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/{20_validation.yaml => 20_validation.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/{30_new_fields.yaml => 30_new_fields.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/{40_versioning.yaml => 40_versioning.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/{50_consistency.yaml => 50_consistency.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/{60_throttle.yaml => 60_throttle.yml} (100%) rename modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/{70_slices.yaml => 70_slices.yml} (100%) rename modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/{10_basic.yaml => 10_basic.yml} (100%) rename modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/{20_repository.yaml => 20_repository.yml} (100%) rename modules/transport-netty4/src/test/resources/rest-api-spec/test/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/analysis-icu/src/test/resources/rest-api-spec/test/analysis_icu/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/analysis-icu/src/test/resources/rest-api-spec/test/analysis_icu/{20_search.yaml => 20_search.yml} (100%) rename plugins/analysis-kuromoji/src/test/resources/rest-api-spec/test/analysis_kuromoji/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/analysis-kuromoji/src/test/resources/rest-api-spec/test/analysis_kuromoji/{20_search.yaml => 20_search.yml} (100%) rename plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/{10_metaphone.yaml => 10_metaphone.yml} (100%) rename plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/{20_double_metaphone.yaml => 20_double_metaphone.yml} (100%) rename plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/{30_beider_morse.yaml => 30_beider_morse.yml} (100%) rename plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/{40_search.yaml => 40_search.yml} (100%) rename plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/{50_daitch_mokotoff.yaml => 50_daitch_mokotoff.yml} (100%) rename plugins/analysis-smartcn/src/test/resources/rest-api-spec/test/analysis_smartcn/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/analysis-smartcn/src/test/resources/rest-api-spec/test/analysis_smartcn/{20_search.yaml => 20_search.yml} (100%) rename plugins/analysis-stempel/src/test/resources/rest-api-spec/test/analysis_stempel/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/analysis-stempel/src/test/resources/rest-api-spec/test/analysis_stempel/{20_search.yaml => 20_search.yml} (100%) rename plugins/analysis-ukrainian/src/test/resources/rest-api-spec/test/analysis_ukrainian/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/analysis-ukrainian/src/test/resources/rest-api-spec/test/analysis_ukrainian/{20_search.yaml => 20_search.yml} (100%) rename plugins/discovery-azure-classic/src/test/resources/rest-api-spec/test/discovery_azure_classic/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/discovery-ec2/src/test/resources/rest-api-spec/test/discovery_ec2/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/discovery-file/src/test/resources/rest-api-spec/test/discovery_file/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/discovery-gce/src/test/resources/rest-api-spec/test/discovery_gce/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/{20_score.yaml => 20_score.yml} (100%) rename plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/{20_attachment_processor.yaml => 20_attachment_processor.yml} (100%) rename plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/{30_files_supported.yaml => 30_files_supported.yml} (100%) rename plugins/ingest-geoip/src/test/resources/rest-api-spec/test/ingest_geoip/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/ingest-geoip/src/test/resources/rest-api-spec/test/ingest_geoip/{20_geoip_processor.yaml => 20_geoip_processor.yml} (100%) rename plugins/ingest-user-agent/src/main/resources/{regexes.yaml => regexes.yml} (100%) rename plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/{20_useragent_processor.yaml => 20_useragent_processor.yml} (100%) rename plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/{30_custom_regex.yaml => 30_custom_regex.yml} (95%) rename plugins/ingest-user-agent/test/{test-regexes.yaml => test-regexes.yml} (100%) rename plugins/jvm-example/src/main/config/{example.yaml => example.yml} (100%) rename plugins/jvm-example/src/test/resources/rest-api-spec/test/jvm_example/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/jvm-example/src/test/resources/rest-api-spec/test/jvm_example/{20_configured_example.yaml => 20_configured_example.yml} (100%) rename plugins/mapper-murmur3/src/test/resources/rest-api-spec/test/mapper_murmur3/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/mapper-size/src/test/resources/rest-api-spec/test/mapper_size/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/repository-azure/src/test/resources/rest-api-spec/test/repository_azure/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/repository-azure/src/test/resources/rest-api-spec/test/repository_azure/{20_repository.yaml => 20_repository.yml} (100%) rename plugins/repository-gcs/src/test/resources/rest-api-spec/test/repository_gcs/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/{20_repository_create.yaml => 20_repository_create.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/{20_repository_delete.yaml => 20_repository_delete.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/{20_repository_verify.yaml => 20_repository_verify.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/{30_snapshot.yaml => 30_snapshot.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/{30_snapshot_get.yaml => 30_snapshot_get.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/{40_restore.yaml => 40_restore.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/{20_repository_create.yaml => 20_repository_create.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/{20_repository_delete.yaml => 20_repository_delete.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/{20_repository_verify.yaml => 20_repository_verify.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/{30_snapshot.yaml => 30_snapshot.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/{30_snapshot_get.yaml => 30_snapshot_get.yml} (100%) rename plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/{40_restore.yaml => 40_restore.yml} (100%) rename plugins/repository-s3/src/test/resources/rest-api-spec/test/repository_s3/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/repository-s3/src/test/resources/rest-api-spec/test/repository_s3/{20_repository.yaml => 20_repository.yml} (100%) rename plugins/store-smb/src/test/resources/rest-api-spec/test/store_smb/{10_basic.yaml => 10_basic.yml} (100%) rename plugins/store-smb/src/test/resources/rest-api-spec/test/store_smb/{15_index_creation.yaml => 15_index_creation.yml} (100%) rename qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/{10_basic.yaml => 10_basic.yml} (100%) rename qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/{20_info.yaml => 20_info.yml} (100%) rename qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/{30_field_caps.yaml => 30_field_caps.yml} (100%) rename qa/multi-cluster-search/src/test/resources/rest-api-spec/test/remote_cluster/{10_basic.yaml => 10_basic.yml} (100%) rename qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/{10_basic.yaml => 10_basic.yml} (100%) rename qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/{10_basic.yaml => 10_basic.yml} (100%) rename qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/{10_basic.yaml => 10_basic.yml} (100%) rename qa/smoke-test-ingest-disabled/src/test/resources/rest-api-spec/test/ingest_mustache/{10_ingest_disabled.yaml => 10_ingest_disabled.yml} (100%) rename qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/{10_pipeline_with_mustache_templates.yaml => 10_pipeline_with_mustache_templates.yml} (100%) rename qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/{20_combine_processors.yaml => 20_combine_processors.yml} (100%) rename qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/{30_update_by_query_with_ingest.yaml => 30_update_by_query_with_ingest.yml} (100%) rename qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/{40_reindex_with_ingest.yaml => 40_reindex_with_ingest.yml} (100%) rename qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/{50_script_processor_using_painless.yaml => 50_script_processor_using_painless.yml} (100%) rename qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/{60_pipeline_timestamp_date_mapping.yaml => 60_pipeline_timestamp_date_mapping.yml} (100%) rename qa/smoke-test-multinode/src/test/resources/rest-api-spec/test/smoke_test_multinode/{10_basic.yaml => 10_basic.yml} (100%) rename qa/smoke-test-plugins/src/test/resources/rest-api-spec/test/smoke_test_plugins/{10_basic.yaml => 10_basic.yml} (100%) rename qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/{10_script.yaml => 10_script.yml} (100%) rename qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/{20_broken.yaml => 20_broken.yml} (100%) rename qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/{30_timeout.yaml => 30_timeout.yml} (100%) rename qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/{40_search_failures.yaml => 40_search_failures.yml} (100%) rename qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/{50_reindex_with_parentchild.yaml => 50_reindex_with_parentchild.yml} (100%) rename qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/{10_script.yaml => 10_script.yml} (100%) rename qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/{20_broken.yaml => 20_broken.yml} (100%) rename qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/{30_timeout.yaml => 30_timeout.yml} (100%) rename qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/{40_search_failure.yaml => 40_search_failure.yml} (100%) rename qa/smoke-test-tribe-node/src/test/resources/rest-api-spec/test/tribe/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/bulk/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/bulk/{20_list_of_strings.yaml => 20_list_of_strings.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/bulk/{30_big_string.yaml => 30_big_string.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/bulk/{40_source.yaml => 40_source.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/bulk/{50_refresh.yaml => 50_refresh.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/{20_headers.yaml => 20_headers.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/{30_json.yaml => 30_json.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.allocation/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.count/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.fielddata/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.health/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.indices/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodeattrs/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodes/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.plugins/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.recovery/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.repositories/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.segments/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.snapshots/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.tasks/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.templates/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cat.thread_pool/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cluster.allocation_explain/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cluster.health/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cluster.health/{20_request_timeout.yaml => 20_request_timeout.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cluster.pending_tasks/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cluster.put_settings/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/{11_explain.yaml => 11_explain.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/{20_response_filtering.yaml => 20_response_filtering.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/{20_filtering.yaml => 20_filtering.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/{30_expand_wildcards.yaml => 30_expand_wildcards.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/cluster.stats/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/count/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/count/{20_query_string.yaml => 20_query_string.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/create/{10_with_id.yaml => 10_with_id.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/create/{15_without_id.yaml => 15_without_id.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/create/{30_internal_version.yaml => 30_internal_version.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/create/{35_external_version.yaml => 35_external_version.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/create/{40_routing.yaml => 40_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/create/{50_parent.yaml => 50_parent.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/create/{55_parent_with_routing.yaml => 55_parent_with_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/create/{60_refresh.yaml => 60_refresh.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/delete/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/delete/{11_shard_header.yaml => 11_shard_header.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/delete/{12_result.yaml => 12_result.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/delete/{20_internal_version.yaml => 20_internal_version.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/delete/{25_external_version.yaml => 25_external_version.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/delete/{26_external_gte_version.yaml => 26_external_gte_version.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/delete/{30_routing.yaml => 30_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/delete/{40_parent.yaml => 40_parent.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/delete/{45_parent_with_routing.yaml => 45_parent_with_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/delete/{50_refresh.yaml => 50_refresh.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/delete/{60_missing.yaml => 60_missing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/exists/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/exists/{30_parent.yaml => 30_parent.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/exists/{40_routing.yaml => 40_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/exists/{55_parent_with_routing.yaml => 55_parent_with_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/exists/{60_realtime_refresh.yaml => 60_realtime_refresh.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/exists/{70_defaults.yaml => 70_defaults.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/explain/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/explain/{20_source_filtering.yaml => 20_source_filtering.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/explain/{30_query_string.yaml => 30_query_string.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/field_stats/{10_basics.yaml => 10_basics.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get/{15_default_values.yaml => 15_default_values.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get/{20_stored_fields.yaml => 20_stored_fields.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get/{30_parent.yaml => 30_parent.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get/{40_routing.yaml => 40_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get/{50_with_headers.yaml => 50_with_headers.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get/{55_parent_with_routing.yaml => 55_parent_with_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get/{60_realtime_refresh.yaml => 60_realtime_refresh.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get/{70_source_filtering.yaml => 70_source_filtering.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get/{80_missing.yaml => 80_missing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get/{90_versions.yaml => 90_versions.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get_source/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get_source/{15_default_values.yaml => 15_default_values.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get_source/{30_parent.yaml => 30_parent.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get_source/{40_routing.yaml => 40_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get_source/{55_parent_with_routing.yaml => 55_parent_with_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get_source/{60_realtime_refresh.yaml => 60_realtime_refresh.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get_source/{70_source_filtering.yaml => 70_source_filtering.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get_source/{80_missing.yaml => 80_missing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/get_source/{85_source_missing.yaml => 85_source_missing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/index/{10_with_id.yaml => 10_with_id.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/index/{12_result.yaml => 12_result.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/index/{15_without_id.yaml => 15_without_id.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/index/{20_optype.yaml => 20_optype.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/index/{30_internal_version.yaml => 30_internal_version.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/index/{35_external_version.yaml => 35_external_version.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/index/{36_external_gte_version.yaml => 36_external_gte_version.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/index/{40_routing.yaml => 40_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/index/{50_parent.yaml => 50_parent.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/index/{55_parent_with_routing.yaml => 55_parent_with_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/index/{60_refresh.yaml => 60_refresh.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.analyze/{10_analyze.yaml => 10_analyze.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.clear_cache/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/{all_path_options.yaml => all_path_options.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists/{20_read_only_index.yaml => 20_read_only_index.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_alias/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_template/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_type/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.flush/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.forcemerge/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_alias/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_alias/{20_empty.yaml => 20_empty.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/{20_missing_field.yaml => 20_missing_field.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/{30_missing_type.yaml => 30_missing_type.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/{40_missing_index.yaml => 40_missing_index.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/{50_field_wildcards.yaml => 50_field_wildcards.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/{20_missing_type.yaml => 20_missing_type.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/{30_missing_index.yaml => 30_missing_index.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/{40_aliases.yaml => 40_aliases.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/{50_wildcard_expansion.yaml => 50_wildcard_expansion.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/{60_empty.yaml => 60_empty.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/{20_aliases.yaml => 20_aliases.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/{20_get_missing.yaml => 20_get_missing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/{20_multiple_indices.yaml => 20_multiple_indices.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/{all_path_options.yaml => all_path_options.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/{all_path_options.yaml => all_path_options.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/{11_reset.yaml => 11_reset.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/{all_path_options.yaml => all_path_options.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.recovery/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.refresh/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.rollover/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.segments/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.shard_stores/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.sort/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/{10_index.yaml => 10_index.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/{11_metric.yaml => 11_metric.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/{12_level.yaml => 12_level.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/{13_fields.yaml => 13_fields.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/{14_groups.yaml => 14_groups.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/{15_types.yaml => 15_types.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/{20_routing.yaml => 20_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/{30_remove_index_and_replace_with_alias.yaml => 30_remove_index_and_replace_with_alias.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.upgrade/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.validate_query/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/indices.validate_query/{20_query_string.yaml => 20_query_string.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/info/{10_info.yaml => 10_info.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/info/{20_lucene_version.yaml => 20_lucene_version.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/ingest/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mget/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mget/{11_default_index_type.yaml => 11_default_index_type.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mget/{12_non_existent_index.yaml => 12_non_existent_index.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mget/{13_missing_metadata.yaml => 13_missing_metadata.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mget/{14_alias_to_multiple_indices.yaml => 14_alias_to_multiple_indices.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mget/{15_ids.yaml => 15_ids.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mget/{20_stored_fields.yaml => 20_stored_fields.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mget/{30_parent.yaml => 30_parent.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mget/{40_routing.yaml => 40_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mget/{55_parent_with_routing.yaml => 55_parent_with_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mget/{60_realtime_refresh.yaml => 60_realtime_refresh.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mget/{70_source_filtering.yaml => 70_source_filtering.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mlt/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mlt/{20_docs.yaml => 20_docs.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mlt/{30_unlike.yaml => 30_unlike.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/msearch/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/msearch/{11_status.yaml => 11_status.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/msearch/{20_typed_keys.yaml => 20_typed_keys.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/mtermvectors/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/{20_transport.yaml => 20_transport.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/{30_settings.yaml => 30_settings.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/{11_indices_metrics.yaml => 11_indices_metrics.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/{20_response_filtering.yaml => 20_response_filtering.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/{30_discovery.yaml => 30_discovery.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/ping/{10_ping.yaml => 10_ping.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/remote.info/{10_info.yaml => 10_info.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/scroll/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/scroll/{11_clear.yaml => 11_clear.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/scroll/{12_slices.yaml => 12_slices.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/{10_histogram.yaml => 10_histogram.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/{20_terms.yaml => 20_terms.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/{30_sig_terms.yaml => 30_sig_terms.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/{40_range.yaml => 40_range.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/{50_filter.yaml => 50_filter.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/{70_adjacency_matrix.yaml => 70_adjacency_matrix.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/{80_typed_keys.yaml => 80_typed_keys.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search.highlight/{10_unified.yaml => 10_unified.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search/{100_stored_fields.yaml => 100_stored_fields.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search/{10_source_filtering.yaml => 10_source_filtering.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search/{110_field_collapsing.yaml => 110_field_collapsing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search/{120_batch_reduce_size.yaml => 120_batch_reduce_size.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search/{20_default_values.yaml => 20_default_values.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search/{30_limits.yaml => 30_limits.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search/{40_indices_boost.yaml => 40_indices_boost.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search/{60_query_string.yaml => 60_query_string.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search/{70_response_filtering.yaml => 70_response_filtering.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search/{80_indices_options.yaml => 80_indices_options.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search/{90_search_after.yaml => 90_search_after.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search/{issue4895.yaml => issue4895.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search/{issue9606.yaml => issue9606.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.create/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get_repository/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.restore/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.status/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/suggest/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/suggest/{20_completion.yaml => 20_completion.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/suggest/{30_context.yaml => 30_context.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/suggest/{40_typed_keys.yaml => 40_typed_keys.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/tasks.cancel/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/tasks.get/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/{10_basic.yaml => 10_basic.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/{20_issue7121.yaml => 20_issue7121.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/{30_realtime.yaml => 30_realtime.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/{40_versions.yaml => 40_versions.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/update/{10_doc.yaml => 10_doc.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/update/{11_shard_header.yaml => 11_shard_header.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/update/{12_result.yaml => 12_result.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/update/{20_doc_upsert.yaml => 20_doc_upsert.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/update/{22_doc_as_upsert.yaml => 22_doc_as_upsert.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/update/{30_internal_version.yaml => 30_internal_version.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/update/{35_other_versions.yaml => 35_other_versions.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/update/{40_routing.yaml => 40_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/update/{50_parent.yaml => 50_parent.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/update/{55_parent_with_routing.yaml => 55_parent_with_routing.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/update/{60_refresh.yaml => 60_refresh.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/update/{80_source_filtering.yaml => 80_source_filtering.yml} (100%) rename rest-api-spec/src/main/resources/rest-api-spec/test/update/{85_fields_meta.yaml => 85_fields_meta.yml} (100%) rename test/framework/src/test/resources/rest-api-spec/test/suite1/{10_basic.yaml => 10_basic.yml} (100%) rename test/framework/src/test/resources/rest-api-spec/test/suite1/{20_another_test.yaml => 20_another_test.yml} (100%) rename test/framework/src/test/resources/rest-api-spec/test/suite2/{10_basic.yaml => 10_basic.yml} (100%) rename test/framework/src/test/resources/rest-api-spec/test/suite2/{15_test2.yaml => 15_test2.yml} (100%) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/doc/RestTestsFromSnippetsTask.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/doc/RestTestsFromSnippetsTask.groovy index af637267d119f..60e50a97ad0e3 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/doc/RestTestsFromSnippetsTask.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/doc/RestTestsFromSnippetsTask.groovy @@ -296,7 +296,7 @@ public class RestTestsFromSnippetsTask extends SnippetsTask { Path dest = outputRoot().toPath().resolve(test.path) // Replace the extension String fileName = dest.getName(dest.nameCount - 1) - dest = dest.parent.resolve(fileName.replace('.asciidoc', '.yaml')) + dest = dest.parent.resolve(fileName.replace('.asciidoc', '.yml')) // Now setup the writer Files.createDirectories(dest.parent) diff --git a/distribution/deb/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yaml b/distribution/deb/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yml similarity index 100% rename from distribution/deb/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yaml rename to distribution/deb/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yml diff --git a/distribution/rpm/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yaml b/distribution/rpm/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yml similarity index 100% rename from distribution/rpm/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yaml rename to distribution/rpm/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yml diff --git a/distribution/tar/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yaml b/distribution/tar/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yml similarity index 100% rename from distribution/tar/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yaml rename to distribution/tar/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yml diff --git a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java index 67ccfd210a076..e631ee31a2423 100644 --- a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java +++ b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java @@ -535,7 +535,7 @@ public void testConfig() throws Exception { Path pluginDir = createPluginDir(temp); Path configDir = pluginDir.resolve("config"); Files.createDirectory(configDir); - Files.createFile(configDir.resolve("custom.yaml")); + Files.createFile(configDir.resolve("custom.yml")); String pluginZip = createPluginUrl("fake", pluginDir); installPlugin(pluginZip, env.v1()); assertPlugin("fake", pluginDir, env.v2()); @@ -545,19 +545,19 @@ public void testExistingConfig() throws Exception { Tuple env = createEnv(fs, temp); Path envConfigDir = env.v2().configFile().resolve("fake"); Files.createDirectories(envConfigDir); - Files.write(envConfigDir.resolve("custom.yaml"), "existing config".getBytes(StandardCharsets.UTF_8)); + Files.write(envConfigDir.resolve("custom.yml"), "existing config".getBytes(StandardCharsets.UTF_8)); Path pluginDir = createPluginDir(temp); Path configDir = pluginDir.resolve("config"); Files.createDirectory(configDir); - Files.write(configDir.resolve("custom.yaml"), "new config".getBytes(StandardCharsets.UTF_8)); - Files.createFile(configDir.resolve("other.yaml")); + Files.write(configDir.resolve("custom.yml"), "new config".getBytes(StandardCharsets.UTF_8)); + Files.createFile(configDir.resolve("other.yml")); String pluginZip = createPluginUrl("fake", pluginDir); installPlugin(pluginZip, env.v1()); assertPlugin("fake", pluginDir, env.v2()); - List configLines = Files.readAllLines(envConfigDir.resolve("custom.yaml"), StandardCharsets.UTF_8); + List configLines = Files.readAllLines(envConfigDir.resolve("custom.yml"), StandardCharsets.UTF_8); assertEquals(1, configLines.size()); assertEquals("existing config", configLines.get(0)); - assertTrue(Files.exists(envConfigDir.resolve("other.yaml"))); + assertTrue(Files.exists(envConfigDir.resolve("other.yml"))); } public void testConfigNotDir() throws Exception { diff --git a/distribution/zip/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yaml b/distribution/zip/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yml similarity index 100% rename from distribution/zip/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yaml rename to distribution/zip/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_modules.yml diff --git a/modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/10_basic.yaml b/modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/10_basic.yml similarity index 100% rename from modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/10_basic.yaml rename to modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/10_basic.yml diff --git a/modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/20_empty_bucket.yaml b/modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/20_empty_bucket.yml similarity index 100% rename from modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/20_empty_bucket.yaml rename to modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/20_empty_bucket.yml diff --git a/modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/30_single_value_field.yaml b/modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/30_single_value_field.yml similarity index 100% rename from modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/30_single_value_field.yaml rename to modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/30_single_value_field.yml diff --git a/modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/40_multi_value_field.yaml b/modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/40_multi_value_field.yml similarity index 100% rename from modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/40_multi_value_field.yaml rename to modules/aggs-matrix-stats/src/test/resources/rest-api-spec/test/stats/40_multi_value_field.yml diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/10_basic.yaml b/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/10_basic.yml similarity index 100% rename from modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/10_basic.yaml rename to modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/10_basic.yml diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/20_analyzers.yaml b/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/20_analyzers.yml similarity index 100% rename from modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/20_analyzers.yaml rename to modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/20_analyzers.yml diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/30_tokenizers.yaml b/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/30_tokenizers.yml similarity index 100% rename from modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/30_tokenizers.yaml rename to modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/30_tokenizers.yml diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/40_token_filters.yaml b/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/40_token_filters.yml similarity index 100% rename from modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/40_token_filters.yaml rename to modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/40_token_filters.yml diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/50_char_filters.yaml b/modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/50_char_filters.yml similarity index 100% rename from modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/50_char_filters.yaml rename to modules/analysis-common/src/test/resources/rest-api-spec/test/analysis-common/50_char_filters.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/100_date_index_name_processor.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/100_date_index_name_processor.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/100_date_index_name_processor.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/100_date_index_name_processor.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/10_basic.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/10_basic.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/10_basic.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/10_basic.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/110_sort.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/110_sort.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/110_sort.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/110_sort.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/120_grok.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/120_grok.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/120_grok.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/120_grok.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/130_escape_dot.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/130_escape_dot.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/130_escape_dot.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/130_escape_dot.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/140_json.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/140_json.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/140_json.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/140_json.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/150_kv.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/150_kv.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/150_kv.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/150_kv.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/20_crud.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/20_crud.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/20_crud.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/20_crud.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/30_date_processor.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/30_date_processor.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/30_date_processor.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/30_date_processor.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/40_mutate.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/40_mutate.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/40_mutate.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/40_mutate.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/50_on_failure.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/50_on_failure.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/50_on_failure.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/50_on_failure.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/60_fail.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/60_fail.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/60_fail.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/60_fail.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/70_bulk.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/70_bulk.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/70_bulk.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/70_bulk.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/80_foreach.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/80_foreach.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/80_foreach.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/80_foreach.yml diff --git a/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/90_simulate.yaml b/modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/90_simulate.yml similarity index 100% rename from modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/90_simulate.yaml rename to modules/ingest-common/src/test/resources/rest-api-spec/test/ingest/90_simulate.yml diff --git a/modules/lang-expression/src/test/resources/rest-api-spec/test/lang_expression/10_basic.yaml b/modules/lang-expression/src/test/resources/rest-api-spec/test/lang_expression/10_basic.yml similarity index 100% rename from modules/lang-expression/src/test/resources/rest-api-spec/test/lang_expression/10_basic.yaml rename to modules/lang-expression/src/test/resources/rest-api-spec/test/lang_expression/10_basic.yml diff --git a/modules/lang-expression/src/test/resources/rest-api-spec/test/lang_expression/20_search.yaml b/modules/lang-expression/src/test/resources/rest-api-spec/test/lang_expression/20_search.yml similarity index 100% rename from modules/lang-expression/src/test/resources/rest-api-spec/test/lang_expression/20_search.yaml rename to modules/lang-expression/src/test/resources/rest-api-spec/test/lang_expression/20_search.yml diff --git a/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/10_basic.yaml b/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/10_basic.yml similarity index 100% rename from modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/10_basic.yaml rename to modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/10_basic.yml diff --git a/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/20_render_search_template.yaml b/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/20_render_search_template.yml similarity index 100% rename from modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/20_render_search_template.yaml rename to modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/20_render_search_template.yml diff --git a/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/25_custom_functions.yaml b/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/25_custom_functions.yml similarity index 100% rename from modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/25_custom_functions.yaml rename to modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/25_custom_functions.yml diff --git a/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/30_search_template.yaml b/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/30_search_template.yml similarity index 100% rename from modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/30_search_template.yaml rename to modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/30_search_template.yml diff --git a/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/50_multi_search_template.yaml b/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/50_multi_search_template.yml similarity index 100% rename from modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/50_multi_search_template.yaml rename to modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/50_multi_search_template.yml diff --git a/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/60_typed_keys.yaml b/modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/60_typed_keys.yml similarity index 100% rename from modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/60_typed_keys.yaml rename to modules/lang-mustache/src/test/resources/rest-api-spec/test/lang_mustache/60_typed_keys.yml diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/10_basic.yaml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/10_basic.yml similarity index 100% rename from modules/lang-painless/src/test/resources/rest-api-spec/test/painless/10_basic.yaml rename to modules/lang-painless/src/test/resources/rest-api-spec/test/painless/10_basic.yml diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/15_update.yaml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/15_update.yml similarity index 100% rename from modules/lang-painless/src/test/resources/rest-api-spec/test/painless/15_update.yaml rename to modules/lang-painless/src/test/resources/rest-api-spec/test/painless/15_update.yml diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/16_update2.yaml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/16_update2.yml similarity index 100% rename from modules/lang-painless/src/test/resources/rest-api-spec/test/painless/16_update2.yaml rename to modules/lang-painless/src/test/resources/rest-api-spec/test/painless/16_update2.yml diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/20_scriptfield.yaml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/20_scriptfield.yml similarity index 100% rename from modules/lang-painless/src/test/resources/rest-api-spec/test/painless/20_scriptfield.yaml rename to modules/lang-painless/src/test/resources/rest-api-spec/test/painless/20_scriptfield.yml diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/25_script_upsert.yaml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/25_script_upsert.yml similarity index 100% rename from modules/lang-painless/src/test/resources/rest-api-spec/test/painless/25_script_upsert.yaml rename to modules/lang-painless/src/test/resources/rest-api-spec/test/painless/25_script_upsert.yml diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/30_search.yaml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/30_search.yml similarity index 100% rename from modules/lang-painless/src/test/resources/rest-api-spec/test/painless/30_search.yaml rename to modules/lang-painless/src/test/resources/rest-api-spec/test/painless/30_search.yml diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/40_disabled.yaml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/40_disabled.yml similarity index 100% rename from modules/lang-painless/src/test/resources/rest-api-spec/test/painless/40_disabled.yaml rename to modules/lang-painless/src/test/resources/rest-api-spec/test/painless/40_disabled.yml diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/50_script_doc_values.yaml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/50_script_doc_values.yml similarity index 100% rename from modules/lang-painless/src/test/resources/rest-api-spec/test/painless/50_script_doc_values.yaml rename to modules/lang-painless/src/test/resources/rest-api-spec/test/painless/50_script_doc_values.yml diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/60_script_doc_values_binary.yaml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/60_script_doc_values_binary.yml similarity index 100% rename from modules/lang-painless/src/test/resources/rest-api-spec/test/painless/60_script_doc_values_binary.yaml rename to modules/lang-painless/src/test/resources/rest-api-spec/test/painless/60_script_doc_values_binary.yml diff --git a/modules/parent-join/src/test/resources/rest-api-spec/test/10_basic.yaml b/modules/parent-join/src/test/resources/rest-api-spec/test/10_basic.yml similarity index 100% rename from modules/parent-join/src/test/resources/rest-api-spec/test/10_basic.yaml rename to modules/parent-join/src/test/resources/rest-api-spec/test/10_basic.yml diff --git a/modules/percolator/src/test/resources/rest-api-spec/test/10_basic.yaml b/modules/percolator/src/test/resources/rest-api-spec/test/10_basic.yml similarity index 100% rename from modules/percolator/src/test/resources/rest-api-spec/test/10_basic.yaml rename to modules/percolator/src/test/resources/rest-api-spec/test/10_basic.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/10_basic.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/10_basic.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/10_basic.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/10_basic.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/20_validation.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/20_validation.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/20_validation.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/20_validation.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/30_by_type.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/30_by_type.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/30_by_type.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/30_by_type.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/40_versioning.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/40_versioning.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/40_versioning.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/40_versioning.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/50_wait_for_active_shards.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/50_wait_for_active_shards.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/50_wait_for_active_shards.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/50_wait_for_active_shards.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/70_throttle.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/70_throttle.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/70_throttle.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/70_throttle.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/80_slices.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/80_slices.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/80_slices.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/80_slices.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/10_basic.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/10_basic.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/reindex/10_basic.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/reindex/10_basic.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/20_validation.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/20_validation.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/reindex/20_validation.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/reindex/20_validation.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/25_no_auto_create.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/25_no_auto_create.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/reindex/25_no_auto_create.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/reindex/25_no_auto_create.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/30_search.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/30_search.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/reindex/30_search.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/reindex/30_search.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/40_versioning.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/40_versioning.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/reindex/40_versioning.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/reindex/40_versioning.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/50_routing.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/50_routing.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/reindex/50_routing.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/reindex/50_routing.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/60_wait_for_active_shards.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/60_wait_for_active_shards.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/reindex/60_wait_for_active_shards.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/reindex/60_wait_for_active_shards.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/70_throttle.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/70_throttle.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/reindex/70_throttle.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/reindex/70_throttle.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/80_slices.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/80_slices.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/reindex/80_slices.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/reindex/80_slices.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/10_basic.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/10_basic.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/10_basic.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/10_basic.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/20_validation.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/20_validation.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/20_validation.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/20_validation.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/30_new_fields.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/30_new_fields.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/30_new_fields.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/30_new_fields.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/40_versioning.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/40_versioning.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/40_versioning.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/40_versioning.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/50_consistency.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/50_consistency.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/50_consistency.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/50_consistency.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/60_throttle.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/60_throttle.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/60_throttle.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/60_throttle.yml diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/70_slices.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/70_slices.yml similarity index 100% rename from modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/70_slices.yaml rename to modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/70_slices.yml diff --git a/modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/10_basic.yaml b/modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/10_basic.yml similarity index 100% rename from modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/10_basic.yaml rename to modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/10_basic.yml diff --git a/modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/20_repository.yaml b/modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/20_repository.yml similarity index 100% rename from modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/20_repository.yaml rename to modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/20_repository.yml diff --git a/modules/transport-netty4/src/test/resources/rest-api-spec/test/10_basic.yaml b/modules/transport-netty4/src/test/resources/rest-api-spec/test/10_basic.yml similarity index 100% rename from modules/transport-netty4/src/test/resources/rest-api-spec/test/10_basic.yaml rename to modules/transport-netty4/src/test/resources/rest-api-spec/test/10_basic.yml diff --git a/plugins/analysis-icu/src/test/resources/rest-api-spec/test/analysis_icu/10_basic.yaml b/plugins/analysis-icu/src/test/resources/rest-api-spec/test/analysis_icu/10_basic.yml similarity index 100% rename from plugins/analysis-icu/src/test/resources/rest-api-spec/test/analysis_icu/10_basic.yaml rename to plugins/analysis-icu/src/test/resources/rest-api-spec/test/analysis_icu/10_basic.yml diff --git a/plugins/analysis-icu/src/test/resources/rest-api-spec/test/analysis_icu/20_search.yaml b/plugins/analysis-icu/src/test/resources/rest-api-spec/test/analysis_icu/20_search.yml similarity index 100% rename from plugins/analysis-icu/src/test/resources/rest-api-spec/test/analysis_icu/20_search.yaml rename to plugins/analysis-icu/src/test/resources/rest-api-spec/test/analysis_icu/20_search.yml diff --git a/plugins/analysis-kuromoji/src/test/resources/rest-api-spec/test/analysis_kuromoji/10_basic.yaml b/plugins/analysis-kuromoji/src/test/resources/rest-api-spec/test/analysis_kuromoji/10_basic.yml similarity index 100% rename from plugins/analysis-kuromoji/src/test/resources/rest-api-spec/test/analysis_kuromoji/10_basic.yaml rename to plugins/analysis-kuromoji/src/test/resources/rest-api-spec/test/analysis_kuromoji/10_basic.yml diff --git a/plugins/analysis-kuromoji/src/test/resources/rest-api-spec/test/analysis_kuromoji/20_search.yaml b/plugins/analysis-kuromoji/src/test/resources/rest-api-spec/test/analysis_kuromoji/20_search.yml similarity index 100% rename from plugins/analysis-kuromoji/src/test/resources/rest-api-spec/test/analysis_kuromoji/20_search.yaml rename to plugins/analysis-kuromoji/src/test/resources/rest-api-spec/test/analysis_kuromoji/20_search.yml diff --git a/plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/10_metaphone.yaml b/plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/10_metaphone.yml similarity index 100% rename from plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/10_metaphone.yaml rename to plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/10_metaphone.yml diff --git a/plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/20_double_metaphone.yaml b/plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/20_double_metaphone.yml similarity index 100% rename from plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/20_double_metaphone.yaml rename to plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/20_double_metaphone.yml diff --git a/plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/30_beider_morse.yaml b/plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/30_beider_morse.yml similarity index 100% rename from plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/30_beider_morse.yaml rename to plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/30_beider_morse.yml diff --git a/plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/40_search.yaml b/plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/40_search.yml similarity index 100% rename from plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/40_search.yaml rename to plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/40_search.yml diff --git a/plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/50_daitch_mokotoff.yaml b/plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/50_daitch_mokotoff.yml similarity index 100% rename from plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/50_daitch_mokotoff.yaml rename to plugins/analysis-phonetic/src/test/resources/rest-api-spec/test/analysis_phonetic/50_daitch_mokotoff.yml diff --git a/plugins/analysis-smartcn/src/test/resources/rest-api-spec/test/analysis_smartcn/10_basic.yaml b/plugins/analysis-smartcn/src/test/resources/rest-api-spec/test/analysis_smartcn/10_basic.yml similarity index 100% rename from plugins/analysis-smartcn/src/test/resources/rest-api-spec/test/analysis_smartcn/10_basic.yaml rename to plugins/analysis-smartcn/src/test/resources/rest-api-spec/test/analysis_smartcn/10_basic.yml diff --git a/plugins/analysis-smartcn/src/test/resources/rest-api-spec/test/analysis_smartcn/20_search.yaml b/plugins/analysis-smartcn/src/test/resources/rest-api-spec/test/analysis_smartcn/20_search.yml similarity index 100% rename from plugins/analysis-smartcn/src/test/resources/rest-api-spec/test/analysis_smartcn/20_search.yaml rename to plugins/analysis-smartcn/src/test/resources/rest-api-spec/test/analysis_smartcn/20_search.yml diff --git a/plugins/analysis-stempel/src/test/resources/rest-api-spec/test/analysis_stempel/10_basic.yaml b/plugins/analysis-stempel/src/test/resources/rest-api-spec/test/analysis_stempel/10_basic.yml similarity index 100% rename from plugins/analysis-stempel/src/test/resources/rest-api-spec/test/analysis_stempel/10_basic.yaml rename to plugins/analysis-stempel/src/test/resources/rest-api-spec/test/analysis_stempel/10_basic.yml diff --git a/plugins/analysis-stempel/src/test/resources/rest-api-spec/test/analysis_stempel/20_search.yaml b/plugins/analysis-stempel/src/test/resources/rest-api-spec/test/analysis_stempel/20_search.yml similarity index 100% rename from plugins/analysis-stempel/src/test/resources/rest-api-spec/test/analysis_stempel/20_search.yaml rename to plugins/analysis-stempel/src/test/resources/rest-api-spec/test/analysis_stempel/20_search.yml diff --git a/plugins/analysis-ukrainian/src/test/resources/rest-api-spec/test/analysis_ukrainian/10_basic.yaml b/plugins/analysis-ukrainian/src/test/resources/rest-api-spec/test/analysis_ukrainian/10_basic.yml similarity index 100% rename from plugins/analysis-ukrainian/src/test/resources/rest-api-spec/test/analysis_ukrainian/10_basic.yaml rename to plugins/analysis-ukrainian/src/test/resources/rest-api-spec/test/analysis_ukrainian/10_basic.yml diff --git a/plugins/analysis-ukrainian/src/test/resources/rest-api-spec/test/analysis_ukrainian/20_search.yaml b/plugins/analysis-ukrainian/src/test/resources/rest-api-spec/test/analysis_ukrainian/20_search.yml similarity index 100% rename from plugins/analysis-ukrainian/src/test/resources/rest-api-spec/test/analysis_ukrainian/20_search.yaml rename to plugins/analysis-ukrainian/src/test/resources/rest-api-spec/test/analysis_ukrainian/20_search.yml diff --git a/plugins/discovery-azure-classic/src/test/resources/rest-api-spec/test/discovery_azure_classic/10_basic.yaml b/plugins/discovery-azure-classic/src/test/resources/rest-api-spec/test/discovery_azure_classic/10_basic.yml similarity index 100% rename from plugins/discovery-azure-classic/src/test/resources/rest-api-spec/test/discovery_azure_classic/10_basic.yaml rename to plugins/discovery-azure-classic/src/test/resources/rest-api-spec/test/discovery_azure_classic/10_basic.yml diff --git a/plugins/discovery-ec2/src/test/resources/rest-api-spec/test/discovery_ec2/10_basic.yaml b/plugins/discovery-ec2/src/test/resources/rest-api-spec/test/discovery_ec2/10_basic.yml similarity index 100% rename from plugins/discovery-ec2/src/test/resources/rest-api-spec/test/discovery_ec2/10_basic.yaml rename to plugins/discovery-ec2/src/test/resources/rest-api-spec/test/discovery_ec2/10_basic.yml diff --git a/plugins/discovery-file/src/test/resources/rest-api-spec/test/discovery_file/10_basic.yaml b/plugins/discovery-file/src/test/resources/rest-api-spec/test/discovery_file/10_basic.yml similarity index 100% rename from plugins/discovery-file/src/test/resources/rest-api-spec/test/discovery_file/10_basic.yaml rename to plugins/discovery-file/src/test/resources/rest-api-spec/test/discovery_file/10_basic.yml diff --git a/plugins/discovery-gce/src/test/resources/rest-api-spec/test/discovery_gce/10_basic.yaml b/plugins/discovery-gce/src/test/resources/rest-api-spec/test/discovery_gce/10_basic.yml similarity index 100% rename from plugins/discovery-gce/src/test/resources/rest-api-spec/test/discovery_gce/10_basic.yaml rename to plugins/discovery-gce/src/test/resources/rest-api-spec/test/discovery_gce/10_basic.yml diff --git a/plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/10_basic.yaml b/plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/10_basic.yml similarity index 100% rename from plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/10_basic.yaml rename to plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/10_basic.yml diff --git a/plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/20_score.yaml b/plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/20_score.yml similarity index 100% rename from plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/20_score.yaml rename to plugins/examples/script-expert-scoring/src/test/resources/rest-api-spec/test/script_expert_scoring/20_score.yml diff --git a/plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/10_basic.yaml b/plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/10_basic.yml similarity index 100% rename from plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/10_basic.yaml rename to plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/10_basic.yml diff --git a/plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/20_attachment_processor.yaml b/plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/20_attachment_processor.yml similarity index 100% rename from plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/20_attachment_processor.yaml rename to plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/20_attachment_processor.yml diff --git a/plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/30_files_supported.yaml b/plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/30_files_supported.yml similarity index 100% rename from plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/30_files_supported.yaml rename to plugins/ingest-attachment/src/test/resources/rest-api-spec/test/ingest_attachment/30_files_supported.yml diff --git a/plugins/ingest-geoip/src/test/resources/rest-api-spec/test/ingest_geoip/10_basic.yaml b/plugins/ingest-geoip/src/test/resources/rest-api-spec/test/ingest_geoip/10_basic.yml similarity index 100% rename from plugins/ingest-geoip/src/test/resources/rest-api-spec/test/ingest_geoip/10_basic.yaml rename to plugins/ingest-geoip/src/test/resources/rest-api-spec/test/ingest_geoip/10_basic.yml diff --git a/plugins/ingest-geoip/src/test/resources/rest-api-spec/test/ingest_geoip/20_geoip_processor.yaml b/plugins/ingest-geoip/src/test/resources/rest-api-spec/test/ingest_geoip/20_geoip_processor.yml similarity index 100% rename from plugins/ingest-geoip/src/test/resources/rest-api-spec/test/ingest_geoip/20_geoip_processor.yaml rename to plugins/ingest-geoip/src/test/resources/rest-api-spec/test/ingest_geoip/20_geoip_processor.yml diff --git a/plugins/ingest-user-agent/build.gradle b/plugins/ingest-user-agent/build.gradle index 2b2669a40042b..b78e8aa4c9d09 100644 --- a/plugins/ingest-user-agent/build.gradle +++ b/plugins/ingest-user-agent/build.gradle @@ -23,5 +23,5 @@ esplugin { } integTestCluster { - extraConfigFile 'ingest-user-agent/test-regexes.yaml', 'test/test-regexes.yaml' + extraConfigFile 'ingest-user-agent/test-regexes.yml', 'test/test-regexes.yml' } diff --git a/plugins/ingest-user-agent/src/main/java/org/elasticsearch/ingest/useragent/IngestUserAgentPlugin.java b/plugins/ingest-user-agent/src/main/java/org/elasticsearch/ingest/useragent/IngestUserAgentPlugin.java index ce82d6e1c4d67..899b3eb721c3d 100644 --- a/plugins/ingest-user-agent/src/main/java/org/elasticsearch/ingest/useragent/IngestUserAgentPlugin.java +++ b/plugins/ingest-user-agent/src/main/java/org/elasticsearch/ingest/useragent/IngestUserAgentPlugin.java @@ -65,11 +65,11 @@ static Map createUserAgentParsers(Path userAgentConfigD Map userAgentParsers = new HashMap<>(); UserAgentParser defaultParser = new UserAgentParser(DEFAULT_PARSER_NAME, - IngestUserAgentPlugin.class.getResourceAsStream("/regexes.yaml"), cache); + IngestUserAgentPlugin.class.getResourceAsStream("/regexes.yml"), cache); userAgentParsers.put(DEFAULT_PARSER_NAME, defaultParser); if (Files.exists(userAgentConfigDirectory) && Files.isDirectory(userAgentConfigDirectory)) { - PathMatcher pathMatcher = userAgentConfigDirectory.getFileSystem().getPathMatcher("glob:**.yaml"); + PathMatcher pathMatcher = userAgentConfigDirectory.getFileSystem().getPathMatcher("glob:**.yml"); try (Stream regexFiles = Files.find(userAgentConfigDirectory, 1, (path, attr) -> attr.isRegularFile() && pathMatcher.matches(path))) { diff --git a/plugins/ingest-user-agent/src/main/resources/regexes.yaml b/plugins/ingest-user-agent/src/main/resources/regexes.yml similarity index 100% rename from plugins/ingest-user-agent/src/main/resources/regexes.yaml rename to plugins/ingest-user-agent/src/main/resources/regexes.yml diff --git a/plugins/ingest-user-agent/src/test/java/org/elasticsearch/ingest/useragent/UserAgentProcessorFactoryTests.java b/plugins/ingest-user-agent/src/test/java/org/elasticsearch/ingest/useragent/UserAgentProcessorFactoryTests.java index 4e0d0fb3695c8..d9c6fc17620da 100644 --- a/plugins/ingest-user-agent/src/test/java/org/elasticsearch/ingest/useragent/UserAgentProcessorFactoryTests.java +++ b/plugins/ingest-user-agent/src/test/java/org/elasticsearch/ingest/useragent/UserAgentProcessorFactoryTests.java @@ -46,7 +46,7 @@ public class UserAgentProcessorFactoryTests extends ESTestCase { private static Map userAgentParsers; - private static String regexWithoutDevicesFilename = "regexes_without_devices.yaml"; + private static String regexWithoutDevicesFilename = "regexes_without_devices.yml"; private static Path userAgentConfigDir; @BeforeClass @@ -57,7 +57,7 @@ public static void createUserAgentParsers() throws IOException { // Copy file, leaving out the device parsers at the end try (BufferedReader reader = new BufferedReader( - new InputStreamReader(UserAgentProcessor.class.getResourceAsStream("/regexes.yaml"), StandardCharsets.UTF_8)); + new InputStreamReader(UserAgentProcessor.class.getResourceAsStream("/regexes.yml"), StandardCharsets.UTF_8)); BufferedWriter writer = Files.newBufferedWriter(userAgentConfigDir.resolve(regexWithoutDevicesFilename));) { String line; while ((line = reader.readLine()) != null) { @@ -143,10 +143,10 @@ public void testBuildNonExistingRegexFile() throws Exception { Map config = new HashMap<>(); config.put("field", "_field"); - config.put("regex_file", "does-not-exist.yaml"); + config.put("regex_file", "does-not-exist.yml"); ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, () -> factory.create(null, null, config)); - assertThat(e.getMessage(), equalTo("[regex_file] regex file [does-not-exist.yaml] doesn't exist (has to exist at node startup)")); + assertThat(e.getMessage(), equalTo("[regex_file] regex file [does-not-exist.yml] doesn't exist (has to exist at node startup)")); } public void testBuildFields() throws Exception { diff --git a/plugins/ingest-user-agent/src/test/java/org/elasticsearch/ingest/useragent/UserAgentProcessorTests.java b/plugins/ingest-user-agent/src/test/java/org/elasticsearch/ingest/useragent/UserAgentProcessorTests.java index 92e94434ccdbe..020daa5a1c7a6 100644 --- a/plugins/ingest-user-agent/src/test/java/org/elasticsearch/ingest/useragent/UserAgentProcessorTests.java +++ b/plugins/ingest-user-agent/src/test/java/org/elasticsearch/ingest/useragent/UserAgentProcessorTests.java @@ -42,7 +42,7 @@ public class UserAgentProcessorTests extends ESTestCase { @BeforeClass public static void setupProcessor() throws IOException { - InputStream regexStream = UserAgentProcessor.class.getResourceAsStream("/regexes.yaml"); + InputStream regexStream = UserAgentProcessor.class.getResourceAsStream("/regexes.yml"); assertNotNull(regexStream); UserAgentParser parser = new UserAgentParser(randomAlphaOfLength(10), regexStream, new UserAgentCache(1000)); diff --git a/plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/10_basic.yaml b/plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/10_basic.yml similarity index 100% rename from plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/10_basic.yaml rename to plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/10_basic.yml diff --git a/plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/20_useragent_processor.yaml b/plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/20_useragent_processor.yml similarity index 100% rename from plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/20_useragent_processor.yaml rename to plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/20_useragent_processor.yml diff --git a/plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/30_custom_regex.yaml b/plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/30_custom_regex.yml similarity index 95% rename from plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/30_custom_regex.yaml rename to plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/30_custom_regex.yml index 5613145b6641b..5a6cd8f7a86f6 100644 --- a/plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/30_custom_regex.yaml +++ b/plugins/ingest-user-agent/src/test/resources/rest-api-spec/test/ingest-useragent/30_custom_regex.yml @@ -10,7 +10,7 @@ { "user_agent" : { "field": "field1", - "regex_file": "test-regexes.yaml" + "regex_file": "test-regexes.yml" } } ] diff --git a/plugins/ingest-user-agent/test/test-regexes.yaml b/plugins/ingest-user-agent/test/test-regexes.yml similarity index 100% rename from plugins/ingest-user-agent/test/test-regexes.yaml rename to plugins/ingest-user-agent/test/test-regexes.yml diff --git a/plugins/jvm-example/src/main/config/example.yaml b/plugins/jvm-example/src/main/config/example.yml similarity index 100% rename from plugins/jvm-example/src/main/config/example.yaml rename to plugins/jvm-example/src/main/config/example.yml diff --git a/plugins/jvm-example/src/main/java/org/elasticsearch/plugin/example/ExamplePluginConfiguration.java b/plugins/jvm-example/src/main/java/org/elasticsearch/plugin/example/ExamplePluginConfiguration.java index de8cb09115662..802c2fc67a6cf 100644 --- a/plugins/jvm-example/src/main/java/org/elasticsearch/plugin/example/ExamplePluginConfiguration.java +++ b/plugins/jvm-example/src/main/java/org/elasticsearch/plugin/example/ExamplePluginConfiguration.java @@ -38,7 +38,7 @@ public class ExamplePluginConfiguration { public ExamplePluginConfiguration(Environment env) { // The directory part of the location matches the artifactId of this plugin - Path path = env.configFile().resolve("jvm-example/example.yaml"); + Path path = env.configFile().resolve("jvm-example/example.yml"); try { customSettings = Settings.builder().loadFromPath(path).build(); } catch (IOException e) { diff --git a/plugins/jvm-example/src/test/resources/rest-api-spec/test/jvm_example/10_basic.yaml b/plugins/jvm-example/src/test/resources/rest-api-spec/test/jvm_example/10_basic.yml similarity index 100% rename from plugins/jvm-example/src/test/resources/rest-api-spec/test/jvm_example/10_basic.yaml rename to plugins/jvm-example/src/test/resources/rest-api-spec/test/jvm_example/10_basic.yml diff --git a/plugins/jvm-example/src/test/resources/rest-api-spec/test/jvm_example/20_configured_example.yaml b/plugins/jvm-example/src/test/resources/rest-api-spec/test/jvm_example/20_configured_example.yml similarity index 100% rename from plugins/jvm-example/src/test/resources/rest-api-spec/test/jvm_example/20_configured_example.yaml rename to plugins/jvm-example/src/test/resources/rest-api-spec/test/jvm_example/20_configured_example.yml diff --git a/plugins/mapper-murmur3/src/test/resources/rest-api-spec/test/mapper_murmur3/10_basic.yaml b/plugins/mapper-murmur3/src/test/resources/rest-api-spec/test/mapper_murmur3/10_basic.yml similarity index 100% rename from plugins/mapper-murmur3/src/test/resources/rest-api-spec/test/mapper_murmur3/10_basic.yaml rename to plugins/mapper-murmur3/src/test/resources/rest-api-spec/test/mapper_murmur3/10_basic.yml diff --git a/plugins/mapper-size/src/test/resources/rest-api-spec/test/mapper_size/10_basic.yaml b/plugins/mapper-size/src/test/resources/rest-api-spec/test/mapper_size/10_basic.yml similarity index 100% rename from plugins/mapper-size/src/test/resources/rest-api-spec/test/mapper_size/10_basic.yaml rename to plugins/mapper-size/src/test/resources/rest-api-spec/test/mapper_size/10_basic.yml diff --git a/plugins/repository-azure/src/test/resources/rest-api-spec/test/repository_azure/10_basic.yaml b/plugins/repository-azure/src/test/resources/rest-api-spec/test/repository_azure/10_basic.yml similarity index 100% rename from plugins/repository-azure/src/test/resources/rest-api-spec/test/repository_azure/10_basic.yaml rename to plugins/repository-azure/src/test/resources/rest-api-spec/test/repository_azure/10_basic.yml diff --git a/plugins/repository-azure/src/test/resources/rest-api-spec/test/repository_azure/20_repository.yaml b/plugins/repository-azure/src/test/resources/rest-api-spec/test/repository_azure/20_repository.yml similarity index 100% rename from plugins/repository-azure/src/test/resources/rest-api-spec/test/repository_azure/20_repository.yaml rename to plugins/repository-azure/src/test/resources/rest-api-spec/test/repository_azure/20_repository.yml diff --git a/plugins/repository-gcs/src/test/resources/rest-api-spec/test/repository_gcs/10_basic.yaml b/plugins/repository-gcs/src/test/resources/rest-api-spec/test/repository_gcs/10_basic.yml similarity index 100% rename from plugins/repository-gcs/src/test/resources/rest-api-spec/test/repository_gcs/10_basic.yaml rename to plugins/repository-gcs/src/test/resources/rest-api-spec/test/repository_gcs/10_basic.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/10_basic.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/10_basic.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/10_basic.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/10_basic.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/20_repository_create.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/20_repository_create.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/20_repository_create.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/20_repository_create.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/20_repository_delete.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/20_repository_delete.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/20_repository_delete.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/20_repository_delete.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/20_repository_verify.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/20_repository_verify.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/20_repository_verify.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/20_repository_verify.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot_get.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot_get.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot_get.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot_get.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/40_restore.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/40_restore.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/40_restore.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/40_restore.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/10_basic.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/10_basic.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/10_basic.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/10_basic.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_create.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_create.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_create.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_create.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_delete.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_delete.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_delete.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_delete.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_verify.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_verify.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_verify.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/20_repository_verify.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot_get.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot_get.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot_get.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot_get.yml diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/40_restore.yaml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/40_restore.yml similarity index 100% rename from plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/40_restore.yaml rename to plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/40_restore.yml diff --git a/plugins/repository-s3/src/test/resources/rest-api-spec/test/repository_s3/10_basic.yaml b/plugins/repository-s3/src/test/resources/rest-api-spec/test/repository_s3/10_basic.yml similarity index 100% rename from plugins/repository-s3/src/test/resources/rest-api-spec/test/repository_s3/10_basic.yaml rename to plugins/repository-s3/src/test/resources/rest-api-spec/test/repository_s3/10_basic.yml diff --git a/plugins/repository-s3/src/test/resources/rest-api-spec/test/repository_s3/20_repository.yaml b/plugins/repository-s3/src/test/resources/rest-api-spec/test/repository_s3/20_repository.yml similarity index 100% rename from plugins/repository-s3/src/test/resources/rest-api-spec/test/repository_s3/20_repository.yaml rename to plugins/repository-s3/src/test/resources/rest-api-spec/test/repository_s3/20_repository.yml diff --git a/plugins/store-smb/src/test/resources/rest-api-spec/test/store_smb/10_basic.yaml b/plugins/store-smb/src/test/resources/rest-api-spec/test/store_smb/10_basic.yml similarity index 100% rename from plugins/store-smb/src/test/resources/rest-api-spec/test/store_smb/10_basic.yaml rename to plugins/store-smb/src/test/resources/rest-api-spec/test/store_smb/10_basic.yml diff --git a/plugins/store-smb/src/test/resources/rest-api-spec/test/store_smb/15_index_creation.yaml b/plugins/store-smb/src/test/resources/rest-api-spec/test/store_smb/15_index_creation.yml similarity index 100% rename from plugins/store-smb/src/test/resources/rest-api-spec/test/store_smb/15_index_creation.yaml rename to plugins/store-smb/src/test/resources/rest-api-spec/test/store_smb/15_index_creation.yml diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yaml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml similarity index 100% rename from qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yaml rename to qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/20_info.yaml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/20_info.yml similarity index 100% rename from qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/20_info.yaml rename to qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/20_info.yml diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yaml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yml similarity index 100% rename from qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yaml rename to qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yml diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/remote_cluster/10_basic.yaml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/remote_cluster/10_basic.yml similarity index 100% rename from qa/multi-cluster-search/src/test/resources/rest-api-spec/test/remote_cluster/10_basic.yaml rename to qa/multi-cluster-search/src/test/resources/rest-api-spec/test/remote_cluster/10_basic.yml diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yaml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml similarity index 100% rename from qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yaml rename to qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yaml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml similarity index 100% rename from qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yaml rename to qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yaml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml similarity index 100% rename from qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yaml rename to qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml diff --git a/qa/smoke-test-ingest-disabled/src/test/resources/rest-api-spec/test/ingest_mustache/10_ingest_disabled.yaml b/qa/smoke-test-ingest-disabled/src/test/resources/rest-api-spec/test/ingest_mustache/10_ingest_disabled.yml similarity index 100% rename from qa/smoke-test-ingest-disabled/src/test/resources/rest-api-spec/test/ingest_mustache/10_ingest_disabled.yaml rename to qa/smoke-test-ingest-disabled/src/test/resources/rest-api-spec/test/ingest_mustache/10_ingest_disabled.yml diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/10_pipeline_with_mustache_templates.yaml b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/10_pipeline_with_mustache_templates.yml similarity index 100% rename from qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/10_pipeline_with_mustache_templates.yaml rename to qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/10_pipeline_with_mustache_templates.yml diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/20_combine_processors.yaml b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/20_combine_processors.yml similarity index 100% rename from qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/20_combine_processors.yaml rename to qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/20_combine_processors.yml diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/30_update_by_query_with_ingest.yaml b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/30_update_by_query_with_ingest.yml similarity index 100% rename from qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/30_update_by_query_with_ingest.yaml rename to qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/30_update_by_query_with_ingest.yml diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/40_reindex_with_ingest.yaml b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/40_reindex_with_ingest.yml similarity index 100% rename from qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/40_reindex_with_ingest.yaml rename to qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/40_reindex_with_ingest.yml diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/50_script_processor_using_painless.yaml b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/50_script_processor_using_painless.yml similarity index 100% rename from qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/50_script_processor_using_painless.yaml rename to qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/50_script_processor_using_painless.yml diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yml similarity index 100% rename from qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yaml rename to qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yml diff --git a/qa/smoke-test-multinode/src/test/resources/rest-api-spec/test/smoke_test_multinode/10_basic.yaml b/qa/smoke-test-multinode/src/test/resources/rest-api-spec/test/smoke_test_multinode/10_basic.yml similarity index 100% rename from qa/smoke-test-multinode/src/test/resources/rest-api-spec/test/smoke_test_multinode/10_basic.yaml rename to qa/smoke-test-multinode/src/test/resources/rest-api-spec/test/smoke_test_multinode/10_basic.yml diff --git a/qa/smoke-test-plugins/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_basic.yaml b/qa/smoke-test-plugins/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_basic.yml similarity index 100% rename from qa/smoke-test-plugins/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_basic.yaml rename to qa/smoke-test-plugins/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_basic.yml diff --git a/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/10_script.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/10_script.yml similarity index 100% rename from qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/10_script.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/10_script.yml diff --git a/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/20_broken.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/20_broken.yml similarity index 100% rename from qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/20_broken.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/20_broken.yml diff --git a/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/30_timeout.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/30_timeout.yml similarity index 100% rename from qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/30_timeout.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/30_timeout.yml diff --git a/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/40_search_failures.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/40_search_failures.yml similarity index 100% rename from qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/40_search_failures.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/40_search_failures.yml diff --git a/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/50_reindex_with_parentchild.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/50_reindex_with_parentchild.yml similarity index 100% rename from qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/50_reindex_with_parentchild.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/50_reindex_with_parentchild.yml diff --git a/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/10_script.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/10_script.yml similarity index 100% rename from qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/10_script.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/10_script.yml diff --git a/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/20_broken.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/20_broken.yml similarity index 100% rename from qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/20_broken.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/20_broken.yml diff --git a/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/30_timeout.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/30_timeout.yml similarity index 100% rename from qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/30_timeout.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/30_timeout.yml diff --git a/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/40_search_failure.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/40_search_failure.yml similarity index 100% rename from qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/40_search_failure.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/40_search_failure.yml diff --git a/qa/smoke-test-tribe-node/src/test/resources/rest-api-spec/test/tribe/10_basic.yaml b/qa/smoke-test-tribe-node/src/test/resources/rest-api-spec/test/tribe/10_basic.yml similarity index 100% rename from qa/smoke-test-tribe-node/src/test/resources/rest-api-spec/test/tribe/10_basic.yaml rename to qa/smoke-test-tribe-node/src/test/resources/rest-api-spec/test/tribe/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/bulk/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/bulk/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/20_list_of_strings.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/20_list_of_strings.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/bulk/20_list_of_strings.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/bulk/20_list_of_strings.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/30_big_string.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/30_big_string.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/bulk/30_big_string.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/bulk/30_big_string.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/40_source.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/40_source.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/bulk/40_source.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/bulk/40_source.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/50_refresh.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/50_refresh.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/bulk/50_refresh.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/bulk/50_refresh.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/20_headers.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/20_headers.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/20_headers.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/20_headers.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/30_json.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/30_json.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/30_json.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.aliases/30_json.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.allocation/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.allocation/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.allocation/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.allocation/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.count/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.count/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.count/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.count/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.fielddata/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.fielddata/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.fielddata/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.fielddata/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.health/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.health/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.health/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.health/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.indices/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.indices/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.indices/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.indices/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodeattrs/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodeattrs/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodeattrs/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodeattrs/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodes/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodes/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodes/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodes/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.plugins/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.plugins/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.plugins/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.plugins/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.recovery/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.recovery/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.recovery/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.recovery/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.repositories/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.repositories/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.repositories/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.repositories/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.segments/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.segments/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.segments/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.segments/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.snapshots/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.snapshots/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.snapshots/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.snapshots/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.tasks/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.tasks/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.tasks/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.tasks/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.templates/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.templates/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.templates/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.templates/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.thread_pool/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.thread_pool/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cat.thread_pool/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cat.thread_pool/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.allocation_explain/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.allocation_explain/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cluster.allocation_explain/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cluster.allocation_explain/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.health/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.health/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cluster.health/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cluster.health/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.health/20_request_timeout.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.health/20_request_timeout.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cluster.health/20_request_timeout.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cluster.health/20_request_timeout.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.pending_tasks/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.pending_tasks/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cluster.pending_tasks/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cluster.pending_tasks/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.put_settings/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.put_settings/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cluster.put_settings/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cluster.put_settings/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/11_explain.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/11_explain.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/11_explain.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/11_explain.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/20_response_filtering.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/20_response_filtering.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/20_response_filtering.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/20_response_filtering.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/20_filtering.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/20_filtering.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/20_filtering.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/20_filtering.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/30_expand_wildcards.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/30_expand_wildcards.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/30_expand_wildcards.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/30_expand_wildcards.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.stats/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.stats/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/cluster.stats/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/cluster.stats/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/count/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/count/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/count/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/count/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/count/20_query_string.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/count/20_query_string.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/count/20_query_string.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/count/20_query_string.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/create/10_with_id.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/create/10_with_id.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/create/10_with_id.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/create/10_with_id.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/create/15_without_id.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/create/15_without_id.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/create/15_without_id.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/create/15_without_id.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/create/30_internal_version.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/create/30_internal_version.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/create/30_internal_version.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/create/30_internal_version.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/create/35_external_version.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/create/35_external_version.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/create/35_external_version.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/create/35_external_version.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/create/40_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/create/40_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/create/40_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/create/40_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/create/50_parent.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/create/50_parent.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/create/50_parent.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/create/50_parent.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/create/55_parent_with_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/create/55_parent_with_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/create/55_parent_with_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/create/55_parent_with_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/create/60_refresh.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/create/60_refresh.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/create/60_refresh.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/create/60_refresh.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/delete/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/delete/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/delete/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/delete/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/delete/11_shard_header.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/delete/11_shard_header.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/delete/11_shard_header.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/delete/11_shard_header.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/delete/12_result.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/delete/12_result.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/delete/12_result.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/delete/12_result.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/delete/20_internal_version.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/delete/20_internal_version.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/delete/20_internal_version.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/delete/20_internal_version.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/delete/25_external_version.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/delete/25_external_version.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/delete/25_external_version.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/delete/25_external_version.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/delete/26_external_gte_version.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/delete/26_external_gte_version.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/delete/26_external_gte_version.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/delete/26_external_gte_version.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/delete/30_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/delete/30_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/delete/30_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/delete/30_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/delete/40_parent.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/delete/40_parent.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/delete/40_parent.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/delete/40_parent.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/delete/45_parent_with_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/delete/45_parent_with_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/delete/45_parent_with_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/delete/45_parent_with_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/delete/50_refresh.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/delete/50_refresh.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/delete/50_refresh.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/delete/50_refresh.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/delete/60_missing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/delete/60_missing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/delete/60_missing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/delete/60_missing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/exists/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/exists/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/exists/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/exists/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/exists/30_parent.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/exists/30_parent.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/exists/30_parent.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/exists/30_parent.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/exists/40_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/exists/40_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/exists/40_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/exists/40_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/exists/55_parent_with_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/exists/55_parent_with_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/exists/55_parent_with_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/exists/55_parent_with_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/exists/60_realtime_refresh.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/exists/60_realtime_refresh.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/exists/60_realtime_refresh.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/exists/60_realtime_refresh.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/exists/70_defaults.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/exists/70_defaults.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/exists/70_defaults.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/exists/70_defaults.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/explain/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/explain/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/explain/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/explain/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/explain/20_source_filtering.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/explain/20_source_filtering.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/explain/20_source_filtering.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/explain/20_source_filtering.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/explain/30_query_string.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/explain/30_query_string.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/explain/30_query_string.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/explain/30_query_string.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/field_caps/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/field_stats/10_basics.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/field_stats/10_basics.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/field_stats/10_basics.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/field_stats/10_basics.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/15_default_values.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/15_default_values.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get/15_default_values.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get/15_default_values.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/20_stored_fields.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/20_stored_fields.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get/20_stored_fields.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get/20_stored_fields.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/30_parent.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/30_parent.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get/30_parent.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get/30_parent.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/40_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/40_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get/40_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get/40_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/50_with_headers.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/50_with_headers.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get/50_with_headers.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get/50_with_headers.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/55_parent_with_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/55_parent_with_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get/55_parent_with_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get/55_parent_with_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/60_realtime_refresh.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/60_realtime_refresh.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get/60_realtime_refresh.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get/60_realtime_refresh.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/70_source_filtering.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/70_source_filtering.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get/70_source_filtering.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get/70_source_filtering.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/80_missing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/80_missing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get/80_missing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get/80_missing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/90_versions.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/90_versions.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get/90_versions.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get/90_versions.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get_source/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get_source/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/15_default_values.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/15_default_values.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get_source/15_default_values.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get_source/15_default_values.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/30_parent.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/30_parent.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get_source/30_parent.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get_source/30_parent.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/40_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/40_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get_source/40_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get_source/40_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/55_parent_with_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/55_parent_with_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get_source/55_parent_with_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get_source/55_parent_with_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/60_realtime_refresh.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/60_realtime_refresh.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get_source/60_realtime_refresh.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get_source/60_realtime_refresh.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/70_source_filtering.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/70_source_filtering.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get_source/70_source_filtering.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get_source/70_source_filtering.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/80_missing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/80_missing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get_source/80_missing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get_source/80_missing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/85_source_missing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/get_source/85_source_missing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/get_source/85_source_missing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/get_source/85_source_missing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/10_with_id.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/10_with_id.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/index/10_with_id.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/index/10_with_id.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/12_result.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/12_result.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/index/12_result.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/index/12_result.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/15_without_id.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/15_without_id.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/index/15_without_id.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/index/15_without_id.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/20_optype.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/20_optype.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/index/20_optype.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/index/20_optype.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/30_internal_version.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/30_internal_version.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/index/30_internal_version.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/index/30_internal_version.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/35_external_version.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/35_external_version.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/index/35_external_version.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/index/35_external_version.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/36_external_gte_version.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/36_external_gte_version.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/index/36_external_gte_version.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/index/36_external_gte_version.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/40_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/40_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/index/40_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/index/40_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/50_parent.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/50_parent.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/index/50_parent.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/index/50_parent.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/55_parent_with_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/55_parent_with_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/index/55_parent_with_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/index/55_parent_with_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/60_refresh.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/60_refresh.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/index/60_refresh.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/index/60_refresh.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.analyze/10_analyze.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.analyze/10_analyze.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.analyze/10_analyze.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.analyze/10_analyze.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clear_cache/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clear_cache/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.clear_cache/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.clear_cache/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/all_path_options.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/all_path_options.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/all_path_options.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/all_path_options.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists/20_read_only_index.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists/20_read_only_index.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists/20_read_only_index.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists/20_read_only_index.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_alias/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_alias/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_alias/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_alias/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_template/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_template/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_template/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_template/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_type/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_type/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_type/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_type/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.flush/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.flush/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.flush/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.flush/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.forcemerge/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.forcemerge/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.forcemerge/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.forcemerge/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_alias/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_alias/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_alias/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_alias/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_alias/20_empty.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_alias/20_empty.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_alias/20_empty.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_alias/20_empty.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/30_missing_type.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/30_missing_type.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/30_missing_type.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/30_missing_type.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/20_missing_type.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/20_missing_type.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/20_missing_type.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/20_missing_type.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/30_missing_index.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/30_missing_index.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/30_missing_index.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/30_missing_index.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/40_aliases.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/40_aliases.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/40_aliases.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/40_aliases.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/50_wildcard_expansion.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/50_wildcard_expansion.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/50_wildcard_expansion.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/50_wildcard_expansion.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/60_empty.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/60_empty.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/60_empty.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/60_empty.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/20_aliases.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/20_aliases.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/20_aliases.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/20_aliases.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/20_get_missing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/20_get_missing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/20_get_missing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/20_get_missing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/20_multiple_indices.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/20_multiple_indices.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/20_multiple_indices.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/20_multiple_indices.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/all_path_options.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/all_path_options.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/all_path_options.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/all_path_options.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/11_reset.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/11_reset.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/11_reset.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/11_reset.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/all_path_options.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/all_path_options.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/all_path_options.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/all_path_options.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.recovery/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.recovery/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.recovery/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.recovery/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.refresh/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.refresh/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.refresh/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.refresh/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.rollover/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.rollover/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.rollover/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.rollover/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.segments/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.segments/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.segments/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.segments/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shard_stores/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shard_stores/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.shard_stores/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.shard_stores/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.sort/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.sort/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.sort/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.sort/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/10_index.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/10_index.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/10_index.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/10_index.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/11_metric.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/11_metric.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/11_metric.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/11_metric.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/12_level.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/12_level.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/12_level.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/12_level.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/13_fields.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/13_fields.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/13_fields.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/13_fields.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/14_groups.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/14_groups.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/14_groups.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/14_groups.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/15_types.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/15_types.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/15_types.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.stats/15_types.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/20_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/20_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/20_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/20_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/30_remove_index_and_replace_with_alias.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/30_remove_index_and_replace_with_alias.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/30_remove_index_and_replace_with_alias.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/30_remove_index_and_replace_with_alias.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.upgrade/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.upgrade/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.upgrade/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.upgrade/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.validate_query/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.validate_query/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.validate_query/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.validate_query/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.validate_query/20_query_string.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.validate_query/20_query_string.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/indices.validate_query/20_query_string.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/indices.validate_query/20_query_string.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/info/10_info.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/info/10_info.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/info/10_info.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/info/10_info.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/info/20_lucene_version.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/info/20_lucene_version.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/info/20_lucene_version.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/info/20_lucene_version.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/ingest/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/ingest/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/ingest/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/ingest/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mget/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mget/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/11_default_index_type.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/11_default_index_type.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mget/11_default_index_type.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mget/11_default_index_type.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/12_non_existent_index.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/12_non_existent_index.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mget/12_non_existent_index.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mget/12_non_existent_index.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/13_missing_metadata.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/13_missing_metadata.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mget/13_missing_metadata.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mget/13_missing_metadata.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/14_alias_to_multiple_indices.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/14_alias_to_multiple_indices.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mget/14_alias_to_multiple_indices.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mget/14_alias_to_multiple_indices.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/15_ids.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/15_ids.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mget/15_ids.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mget/15_ids.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/20_stored_fields.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/20_stored_fields.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mget/20_stored_fields.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mget/20_stored_fields.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/30_parent.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/30_parent.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mget/30_parent.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mget/30_parent.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/40_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/40_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mget/40_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mget/40_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/55_parent_with_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/55_parent_with_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mget/55_parent_with_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mget/55_parent_with_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/60_realtime_refresh.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/60_realtime_refresh.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mget/60_realtime_refresh.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mget/60_realtime_refresh.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mget/70_source_filtering.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mget/70_source_filtering.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mget/70_source_filtering.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mget/70_source_filtering.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mlt/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mlt/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mlt/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mlt/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mlt/20_docs.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mlt/20_docs.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mlt/20_docs.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mlt/20_docs.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mlt/30_unlike.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mlt/30_unlike.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mlt/30_unlike.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mlt/30_unlike.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/msearch/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/msearch/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/11_status.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/11_status.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/msearch/11_status.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/msearch/11_status.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/20_typed_keys.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/msearch/20_typed_keys.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/msearch/20_typed_keys.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/msearch/20_typed_keys.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/mtermvectors/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/mtermvectors/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/mtermvectors/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/mtermvectors/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/20_transport.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/20_transport.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/20_transport.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/20_transport.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/30_settings.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/30_settings.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/30_settings.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/30_settings.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/11_indices_metrics.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/11_indices_metrics.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/11_indices_metrics.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/11_indices_metrics.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/20_response_filtering.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/20_response_filtering.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/20_response_filtering.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/20_response_filtering.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/30_discovery.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/30_discovery.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/30_discovery.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/30_discovery.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/ping/10_ping.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/ping/10_ping.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/ping/10_ping.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/ping/10_ping.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/remote.info/10_info.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/remote.info/10_info.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/remote.info/10_info.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/remote.info/10_info.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/scroll/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/scroll/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/11_clear.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/11_clear.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/scroll/11_clear.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/scroll/11_clear.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/12_slices.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/12_slices.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/scroll/12_slices.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/scroll/12_slices.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/10_histogram.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/10_histogram.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/10_histogram.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/10_histogram.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/20_terms.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/20_terms.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/20_terms.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/20_terms.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/30_sig_terms.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/30_sig_terms.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/30_sig_terms.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/30_sig_terms.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/40_range.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/40_range.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/40_range.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/40_range.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/50_filter.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/50_filter.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/50_filter.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/50_filter.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/70_adjacency_matrix.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/70_adjacency_matrix.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/70_adjacency_matrix.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/70_adjacency_matrix.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/80_typed_keys.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/80_typed_keys.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/80_typed_keys.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/80_typed_keys.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.highlight/10_unified.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.highlight/10_unified.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search.highlight/10_unified.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search.highlight/10_unified.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/100_stored_fields.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/100_stored_fields.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search/100_stored_fields.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search/100_stored_fields.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/10_source_filtering.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/10_source_filtering.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search/10_source_filtering.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search/10_source_filtering.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/110_field_collapsing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/110_field_collapsing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search/110_field_collapsing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search/110_field_collapsing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/120_batch_reduce_size.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/120_batch_reduce_size.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search/120_batch_reduce_size.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search/120_batch_reduce_size.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/20_default_values.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/20_default_values.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search/20_default_values.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search/20_default_values.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/30_limits.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/30_limits.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search/30_limits.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search/30_limits.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/40_indices_boost.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/40_indices_boost.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search/40_indices_boost.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search/40_indices_boost.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/60_query_string.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/60_query_string.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search/60_query_string.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search/60_query_string.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/70_response_filtering.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/70_response_filtering.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search/70_response_filtering.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search/70_response_filtering.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/80_indices_options.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/80_indices_options.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search/80_indices_options.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search/80_indices_options.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/90_search_after.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/90_search_after.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search/90_search_after.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search/90_search_after.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/issue4895.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/issue4895.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search/issue4895.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search/issue4895.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/issue9606.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/issue9606.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search/issue9606.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search/issue9606.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/search_shards/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.create/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.create/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.create/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.create/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get_repository/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get_repository/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get_repository/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get_repository/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.restore/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.restore/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.restore/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.restore/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.status/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.status/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.status/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.status/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/suggest/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/suggest/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/suggest/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/suggest/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/suggest/20_completion.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/suggest/20_completion.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/suggest/20_completion.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/suggest/20_completion.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/suggest/30_context.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/suggest/30_context.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/suggest/30_context.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/suggest/30_context.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/suggest/40_typed_keys.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/suggest/40_typed_keys.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/suggest/40_typed_keys.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/suggest/40_typed_keys.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.cancel/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.cancel/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/tasks.cancel/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/tasks.cancel/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.get/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.get/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/tasks.get/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/tasks.get/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/10_basic.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/10_basic.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/10_basic.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/20_issue7121.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/20_issue7121.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/20_issue7121.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/20_issue7121.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/30_realtime.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/30_realtime.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/30_realtime.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/30_realtime.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/40_versions.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/40_versions.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/40_versions.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/termvectors/40_versions.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/10_doc.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/10_doc.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/update/10_doc.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/update/10_doc.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/11_shard_header.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/11_shard_header.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/update/11_shard_header.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/update/11_shard_header.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/12_result.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/12_result.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/update/12_result.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/update/12_result.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/20_doc_upsert.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/20_doc_upsert.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/update/20_doc_upsert.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/update/20_doc_upsert.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/22_doc_as_upsert.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/22_doc_as_upsert.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/update/22_doc_as_upsert.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/update/22_doc_as_upsert.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/30_internal_version.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/30_internal_version.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/update/30_internal_version.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/update/30_internal_version.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/35_other_versions.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/35_other_versions.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/update/35_other_versions.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/update/35_other_versions.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/40_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/40_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/update/40_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/update/40_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/50_parent.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/50_parent.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/update/50_parent.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/update/50_parent.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/55_parent_with_routing.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/55_parent_with_routing.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/update/55_parent_with_routing.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/update/55_parent_with_routing.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/60_refresh.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/60_refresh.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/update/60_refresh.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/update/60_refresh.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/80_source_filtering.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/80_source_filtering.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/update/80_source_filtering.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/update/80_source_filtering.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/85_fields_meta.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/85_fields_meta.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/update/85_fields_meta.yaml rename to rest-api-spec/src/main/resources/rest-api-spec/test/update/85_fields_meta.yml diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCase.java index 17ecff32653ed..28bfa80cf2757 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCase.java @@ -171,7 +171,7 @@ protected void afterIfFailed(List errors) { public static Iterable createParameters() throws Exception { String[] paths = resolvePathsProperty(REST_TESTS_SUITE, ""); // default to all tests under the test root List tests = new ArrayList<>(); - Map> yamlSuites = loadYamlSuites(paths); + Map> yamlSuites = loadSuites(paths); // yaml suites are grouped by directory (effectively by api) for (String api : yamlSuites.keySet()) { List yamlFiles = new ArrayList<>(yamlSuites.get(api)); @@ -191,28 +191,28 @@ public static Iterable createParameters() throws Exception { /** Find all yaml suites that match the given list of paths from the root test path. */ // pkg private for tests - static Map> loadYamlSuites(String... paths) throws Exception { + static Map> loadSuites(String... paths) throws Exception { Map> files = new HashMap<>(); Path root = PathUtils.get(ESClientYamlSuiteTestCase.class.getResource(TESTS_PATH).toURI()); for (String strPath : paths) { Path path = root.resolve(strPath); if (Files.isDirectory(path)) { Files.walk(path).forEach(file -> { - if (file.toString().endsWith(".yaml")) { - addYamlSuite(root, file, files); + if (file.toString().endsWith(".yml")) { + addSuite(root, file, files); } }); } else { - path = root.resolve(strPath + ".yaml"); + path = root.resolve(strPath + ".yml"); assert Files.exists(path); - addYamlSuite(root, path, files); + addSuite(root, path, files); } } return files; } /** Add a single suite file to the set of suites. */ - private static void addYamlSuite(Path root, Path file, Map> files) { + private static void addSuite(Path root, Path file, Map> files) { String groupName = root.relativize(file.getParent()).toString(); Set filesSet = files.get(groupName); if (filesSet == null) { diff --git a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCaseTests.java b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCaseTests.java index ee76ad351a62b..ae64dbc893d81 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCaseTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCaseTests.java @@ -32,29 +32,29 @@ public class ESClientYamlSuiteTestCaseTests extends ESTestCase { public void testLoadAllYamlSuites() throws Exception { - Map> yamlSuites = ESClientYamlSuiteTestCase.loadYamlSuites(""); + Map> yamlSuites = ESClientYamlSuiteTestCase.loadSuites(""); assertEquals(2, yamlSuites.size()); } public void testLoadSingleYamlSuite() throws Exception { - Map> yamlSuites = ESClientYamlSuiteTestCase.loadYamlSuites("suite1/10_basic"); - assertSingleFile(yamlSuites, "suite1", "10_basic.yaml"); + Map> yamlSuites = ESClientYamlSuiteTestCase.loadSuites("suite1/10_basic"); + assertSingleFile(yamlSuites, "suite1", "10_basic.yml"); //extension .yaml is optional - yamlSuites = ESClientYamlSuiteTestCase.loadYamlSuites("suite1/10_basic"); - assertSingleFile(yamlSuites, "suite1", "10_basic.yaml"); + yamlSuites = ESClientYamlSuiteTestCase.loadSuites("suite1/10_basic"); + assertSingleFile(yamlSuites, "suite1", "10_basic.yml"); } public void testLoadMultipleYamlSuites() throws Exception { //single directory - Map> yamlSuites = ESClientYamlSuiteTestCase.loadYamlSuites("suite1"); + Map> yamlSuites = ESClientYamlSuiteTestCase.loadSuites("suite1"); assertThat(yamlSuites, notNullValue()); assertThat(yamlSuites.size(), equalTo(1)); assertThat(yamlSuites.containsKey("suite1"), equalTo(true)); assertThat(yamlSuites.get("suite1").size(), greaterThan(1)); //multiple directories - yamlSuites = ESClientYamlSuiteTestCase.loadYamlSuites("suite1", "suite2"); + yamlSuites = ESClientYamlSuiteTestCase.loadSuites("suite1", "suite2"); assertThat(yamlSuites, notNullValue()); assertThat(yamlSuites.size(), equalTo(2)); assertThat(yamlSuites.containsKey("suite1"), equalTo(true)); @@ -63,18 +63,18 @@ public void testLoadMultipleYamlSuites() throws Exception { assertEquals(2, yamlSuites.get("suite2").size()); //multiple paths, which can be both directories or yaml test suites (with optional file extension) - yamlSuites = ESClientYamlSuiteTestCase.loadYamlSuites("suite2/10_basic", "suite1"); + yamlSuites = ESClientYamlSuiteTestCase.loadSuites("suite2/10_basic", "suite1"); assertThat(yamlSuites, notNullValue()); assertThat(yamlSuites.size(), equalTo(2)); assertThat(yamlSuites.containsKey("suite2"), equalTo(true)); assertThat(yamlSuites.get("suite2").size(), equalTo(1)); - assertSingleFile(yamlSuites.get("suite2"), "suite2", "10_basic.yaml"); + assertSingleFile(yamlSuites.get("suite2"), "suite2", "10_basic.yml"); assertThat(yamlSuites.containsKey("suite1"), equalTo(true)); assertThat(yamlSuites.get("suite1").size(), greaterThan(1)); //files can be loaded from classpath and from file system too Path dir = createTempDir(); - Path file = dir.resolve("test_loading.yaml"); + Path file = dir.resolve("test_loading.yml"); Files.createFile(file); } diff --git a/test/framework/src/test/resources/rest-api-spec/test/suite1/10_basic.yaml b/test/framework/src/test/resources/rest-api-spec/test/suite1/10_basic.yml similarity index 100% rename from test/framework/src/test/resources/rest-api-spec/test/suite1/10_basic.yaml rename to test/framework/src/test/resources/rest-api-spec/test/suite1/10_basic.yml diff --git a/test/framework/src/test/resources/rest-api-spec/test/suite1/20_another_test.yaml b/test/framework/src/test/resources/rest-api-spec/test/suite1/20_another_test.yml similarity index 100% rename from test/framework/src/test/resources/rest-api-spec/test/suite1/20_another_test.yaml rename to test/framework/src/test/resources/rest-api-spec/test/suite1/20_another_test.yml diff --git a/test/framework/src/test/resources/rest-api-spec/test/suite2/10_basic.yaml b/test/framework/src/test/resources/rest-api-spec/test/suite2/10_basic.yml similarity index 100% rename from test/framework/src/test/resources/rest-api-spec/test/suite2/10_basic.yaml rename to test/framework/src/test/resources/rest-api-spec/test/suite2/10_basic.yml diff --git a/test/framework/src/test/resources/rest-api-spec/test/suite2/15_test2.yaml b/test/framework/src/test/resources/rest-api-spec/test/suite2/15_test2.yml similarity index 100% rename from test/framework/src/test/resources/rest-api-spec/test/suite2/15_test2.yaml rename to test/framework/src/test/resources/rest-api-spec/test/suite2/15_test2.yml From bb3a59fa705f5276143f7e9b8e1f06ef519987e6 Mon Sep 17 00:00:00 2001 From: debadair Date: Tue, 16 May 2017 17:55:47 -0700 Subject: [PATCH 387/619] [DOCS] Fixed cross doc xref in plugin docs. --- docs/plugins/repository-gcs.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/repository-gcs.asciidoc b/docs/plugins/repository-gcs.asciidoc index 54bdeafd0428f..fa4e8ca39807f 100644 --- a/docs/plugins/repository-gcs.asciidoc +++ b/docs/plugins/repository-gcs.asciidoc @@ -92,7 +92,7 @@ A service account file looks like this: ---- // NOTCONSOLE -This file must be stored in the <>, under a setting name +This file must be stored in the {ref}/secure-settings.html[elasticsearch keystore], under a setting name of the form `gcs.client.NAME.credentials_file`, where `NAME` is the name of the client congiguration. The default client name is `default`, but a different client name can be specified in repository settings using `client`. From 8a4d8909a1e6020a8007097eb3d38f27067201f5 Mon Sep 17 00:00:00 2001 From: George Papadrosou Date: Wed, 17 May 2017 12:34:01 +0300 Subject: [PATCH 388/619] Fix ArrayIndexOutOfBoundsException when no ranges are specified in the query (#23241) * Fix ArrayIndexOutOfBoundsException in Range Aggregation when no ranges are specified in the query * Revert "Fix ArrayIndexOutOfBoundsException in Range Aggregation when no ranges are specified in the query" This reverts commit ad57d8feb3577a64b37de28c6f3df96a3a49fe93. * Fix range aggregation out of bounds exception when there are no ranges in a range or date_range query * Fix range aggregation out of bounds exception when there are no ranges in the query This fix is applied to range queries, date range queries, ip range queries and geo distance aggregation queries --- .../bucket/range/RangeAggregationBuilder.java | 3 ++ .../date/DateRangeAggregationBuilder.java | 5 ++- .../GeoDistanceAggregationBuilder.java | 3 ++ .../range/ip/IpRangeAggregationBuilder.java | 3 ++ .../aggregations/bucket/DateRangeIT.java | 15 ++++++++ .../aggregations/bucket/GeoDistanceIT.java | 15 ++++++++ .../search/aggregations/bucket/IpRangeIT.java | 38 +++++++++++++------ .../search/aggregations/bucket/RangeIT.java | 16 ++++++++ 8 files changed, 86 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregationBuilder.java index 5692b34c57f57..105dbbc545830 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregationBuilder.java @@ -140,6 +140,9 @@ protected RangeAggregatorFactory innerBuild(SearchContext context, ValuesSourceC AggregatorFactory parent, Builder subFactoriesBuilder) throws IOException { // We need to call processRanges here so they are parsed before we make the decision of whether to cache the request Range[] ranges = processRanges(context, config); + if (ranges.length == 0) { + throw new IllegalArgumentException("No [ranges] specified for the [" + this.getName() + "] aggregation"); + } return new RangeAggregatorFactory(name, config, ranges, keyed, rangeFactory, context, parent, subFactoriesBuilder, metaData); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/DateRangeAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/DateRangeAggregationBuilder.java index de5622299cfb5..2c686fbb97768 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/DateRangeAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/DateRangeAggregationBuilder.java @@ -283,9 +283,12 @@ public DateRangeAggregationBuilder addUnboundedFrom(DateTime from) { @Override protected DateRangeAggregatorFactory innerBuild(SearchContext context, ValuesSourceConfig config, AggregatorFactory parent, Builder subFactoriesBuilder) throws IOException { - // We need to call processRanges here so they are parsed and we know whether `now` has been used before we make + // We need to call processRanges here so they are parsed and we know whether `now` has been used before we make // the decision of whether to cache the request Range[] ranges = processRanges(context, config); + if (ranges.length == 0) { + throw new IllegalArgumentException("No [ranges] specified for the [" + this.getName() + "] aggregation"); + } return new DateRangeAggregatorFactory(name, config, ranges, keyed, rangeFactory, context, parent, subFactoriesBuilder, metaData); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/GeoDistanceAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/GeoDistanceAggregationBuilder.java index 9278a0b73b28f..1484fae8d44fb 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/GeoDistanceAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/GeoDistanceAggregationBuilder.java @@ -384,6 +384,9 @@ public boolean keyed() { ValuesSourceConfig config, AggregatorFactory parent, Builder subFactoriesBuilder) throws IOException { Range[] ranges = this.ranges.toArray(new Range[this.range().size()]); + if (ranges.length == 0) { + throw new IllegalArgumentException("No [ranges] specified for the [" + this.getName() + "] aggregation"); + } return new GeoDistanceRangeAggregatorFactory(name, config, origin, ranges, unit, distanceType, keyed, context, parent, subFactoriesBuilder, metaData); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ip/IpRangeAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ip/IpRangeAggregationBuilder.java index cb03ef7251af0..c530ecbfa9946 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ip/IpRangeAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ip/IpRangeAggregationBuilder.java @@ -369,6 +369,9 @@ private static BytesRef toBytesRef(String ip) { AggregatorFactory parent, Builder subFactoriesBuilder) throws IOException { List ranges = new ArrayList<>(); + if(this.ranges.size() == 0){ + throw new IllegalArgumentException("No [ranges] specified for the [" + this.getName() + "] aggregation"); + } for (Range range : this.ranges) { ranges.add(new BinaryRangeAggregator.Range(range.key, toBytesRef(range.from), toBytesRef(range.to))); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java index 8a167c0daf0a6..ce575964977e5 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java @@ -19,6 +19,7 @@ package org.elasticsearch.search.aggregations.bucket; import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.Plugin; @@ -51,6 +52,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; @@ -866,6 +868,19 @@ public void testEmptyAggregation() throws Exception { assertThat(buckets.get(0).getAggregations().asList().isEmpty(), is(true)); } + public void testNoRangesInQuery() { + try { + client().prepareSearch("idx") + .addAggregation(dateRange("my_date_range_agg").field("value")) + .execute().actionGet(); + fail(); + } catch (SearchPhaseExecutionException spee){ + Throwable rootCause = spee.getCause().getCause(); + assertThat(rootCause, instanceOf(IllegalArgumentException.class)); + assertEquals(rootCause.getMessage(), "No [ranges] specified for the [my_date_range_agg] aggregation"); + } + } + /** * Make sure that a request using a script does not get cached and a request * not using a script does get cached. diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java index d8aab691d2aa1..032bb8d59140c 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java @@ -20,6 +20,7 @@ import org.elasticsearch.Version; import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.geo.GeoPoint; @@ -52,6 +53,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.core.IsNull.notNullValue; @@ -441,6 +443,19 @@ public void testEmptyAggregation() throws Exception { assertThat(buckets.get(0).getDocCount(), equalTo(0L)); } + public void testNoRangesInQuery() { + try { + client().prepareSearch("idx") + .addAggregation(geoDistance("geo_dist", new GeoPoint(52.3760, 4.894))) + .execute().actionGet(); + fail(); + } catch (SearchPhaseExecutionException spee){ + Throwable rootCause = spee.getCause().getCause(); + assertThat(rootCause, instanceOf(IllegalArgumentException.class)); + assertEquals(rootCause.getMessage(), "No [ranges] specified for the [geo_dist] aggregation"); + } + } + public void testMultiValues() throws Exception { SearchResponse response = client().prepareSearch("idx-multi") .addAggregation(geoDistance("amsterdam_rings", new GeoPoint(52.3760, 4.894)) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java index cc4818963ad8a..b9bb46501db73 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java @@ -18,18 +18,9 @@ */ package org.elasticsearch.search.aggregations.bucket; -import org.elasticsearch.cluster.health.ClusterHealthStatus; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; -import static org.hamcrest.Matchers.containsString; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; - +import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.common.inject.internal.Nullable; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.ScriptPlugin; @@ -42,6 +33,17 @@ import org.elasticsearch.search.aggregations.bucket.range.Range; import org.elasticsearch.test.ESIntegTestCase; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; + @ESIntegTestCase.SuiteScopeTestCase public class IpRangeIT extends ESIntegTestCase { @@ -221,6 +223,20 @@ public void testRejectsValueScript() { assertThat(e.getMessage(), containsString("[ip_range] does not support scripts")); } + public void testNoRangesInQuery() { + try { + client().prepareSearch("idx").addAggregation( + AggregationBuilders.ipRange("my_range") + .field("ip")) + .execute().actionGet(); + fail(); + } catch (SearchPhaseExecutionException spee){ + Throwable rootCause = spee.getCause().getCause(); + assertThat(rootCause, instanceOf(IllegalArgumentException.class)); + assertEquals(rootCause.getMessage(), "No [ranges] specified for the [my_range] aggregation"); + } + } + public static class DummyScriptPlugin extends Plugin implements ScriptPlugin { @Override public List getNativeScripts() { diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java index b4bb3c819d3fa..c2a2405098dab 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java @@ -19,6 +19,7 @@ package org.elasticsearch.search.aggregations.bucket; import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.fielddata.ScriptDocValues; @@ -53,6 +54,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; @@ -661,6 +663,20 @@ public void testEmptyRange() throws Exception { assertThat(bucket.getDocCount(), equalTo(0L)); } + public void testNoRangesInQuery() { + try { + client().prepareSearch("idx") + .addAggregation(range("foobar") + .field(SINGLE_VALUED_FIELD_NAME)) + .execute().actionGet(); + fail(); + } catch (SearchPhaseExecutionException spee){ + Throwable rootCause = spee.getCause().getCause(); + assertThat(rootCause, instanceOf(IllegalArgumentException.class)); + assertEquals(rootCause.getMessage(), "No [ranges] specified for the [foobar] aggregation"); + } + } + public void testScriptMultiValued() throws Exception { Script script = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['" + MULTI_VALUED_FIELD_NAME + "'].values", Collections.emptyMap()); From 026e94e6d94fae9ce76568776055f1852b2bcf4e Mon Sep 17 00:00:00 2001 From: Russ Cam Date: Wed, 17 May 2017 21:22:05 +1000 Subject: [PATCH 389/619] Handle parentheses in path The variable assignment needs to be quoted to correctly handle scenario where the path contains parentheses. Realtes #24731 --- distribution/src/main/resources/bin/elasticsearch.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/src/main/resources/bin/elasticsearch.bat b/distribution/src/main/resources/bin/elasticsearch.bat index 130b2fda5a4f5..5bf87a487b4e2 100644 --- a/distribution/src/main/resources/bin/elasticsearch.bat +++ b/distribution/src/main/resources/bin/elasticsearch.bat @@ -37,7 +37,7 @@ SET HOSTNAME=%COMPUTERNAME% if "%ES_JVM_OPTIONS%" == "" ( rem '0' is the batch file, '~dp' appends the drive and path -set ES_JVM_OPTIONS=%~dp0\..\config\jvm.options +set "ES_JVM_OPTIONS=%~dp0\..\config\jvm.options" ) @setlocal From 5fc6f171210ab5ace1aaf13edf8e9d33862bc070 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 17 May 2017 07:42:38 -0400 Subject: [PATCH 390/619] Fix new analysis-common yml tests These tests are broken because I added them with the `yml` extension and didn't realize that we weren't running tests with that extension until we merged #24659. I used that extension in anticipation of #24659 but didn't verify that the tests were actually running. Ooops! Closes #24734 --- .../test/search.query/10_match.yml | 16 +++++---- .../test/termvectors/10_payloads.yml | 36 +++++++++---------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/search.query/10_match.yml b/modules/analysis-common/src/test/resources/rest-api-spec/test/search.query/10_match.yml index d07e06865a1e9..fca70a57697f7 100644 --- a/modules/analysis-common/src/test/resources/rest-api-spec/test/search.query/10_match.yml +++ b/modules/analysis-common/src/test/resources/rest-api-spec/test/search.query/10_match.yml @@ -25,7 +25,7 @@ mappings: doc: properties: - body: + text: type: text analyzer: index search_analyzer: search @@ -43,9 +43,10 @@ body: query: match: - text: fox runs - operator: AND - - match: {hits.count: 1} + text: + query: fox runs + operator: AND + - match: {hits.total: 1} - do: index: @@ -60,6 +61,7 @@ body: query: match: - text: fox runs - operator: AND - - match: {hits.count: 2} + text: + query: fox runs + operator: AND + - match: {hits.total: 2} diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/termvectors/10_payloads.yml b/modules/analysis-common/src/test/resources/rest-api-spec/test/termvectors/10_payloads.yml index d0e31758340c3..489b55a63cd6f 100644 --- a/modules/analysis-common/src/test/resources/rest-api-spec/test/termvectors/10_payloads.yml +++ b/modules/analysis-common/src/test/resources/rest-api-spec/test/termvectors/10_payloads.yml @@ -3,23 +3,23 @@ # because there are no token filters that support payloads in core. - do: indices.create: - index: test - body: - mappings: - doc: - properties: - text: - type: text - term_vector: with_positions_offsets_payloads - analyzer: has_payloads - settings: - number_of_shards: 1 - number_of_replicas: 1 - analysis: - analyzer: - had_payloads: - tokenizer: standard - filter: [type_as_payload] + index: test + body: + settings: + number_of_shards: 1 + number_of_replicas: 1 + analysis: + analyzer: + has_payloads: + tokenizer: standard + filter: [type_as_payload] + mappings: + doc: + properties: + text: + type: text + term_vector: with_positions_offsets_payloads + analyzer: has_payloads - do: index: @@ -37,4 +37,4 @@ id: 1 payloads: true - match: {term_vectors.text.field_statistics.sum_doc_freq: 5} - - match: {term_vectors.text.terms.brown.tokens.0.payload: 10} + - match: {term_vectors.text.terms.brown.tokens.0.payload: PEFMUEhBTlVNPg==} From cf846af0e508e4626fde3b9b870118f64cc33cb9 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Wed, 17 May 2017 14:02:45 +0200 Subject: [PATCH 391/619] Fix `_field_caps` serialization in order to support cross cluster search (#24722) Today the `_field_caps` API doesn't implement its request serialization correctly since indices and indices options are not serialized at all. This will likely break with all transport clients etc. and if this request must be send across the network. This commit fixes this and adds correct handling if we have only remote indices to prevent the inclusion of all local indices. --- .../elasticsearch/action/OriginalIndices.java | 9 ++++ .../fieldcaps/FieldCapabilitiesRequest.java | 9 +++- .../TransportFieldCapabilitiesAction.java | 10 ++++- .../FieldCapabilitiesRequestTests.java | 43 +++++++++++++++++++ .../test/multi_cluster/30_field_caps.yml | 13 ++++++ 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/OriginalIndices.java b/core/src/main/java/org/elasticsearch/action/OriginalIndices.java index 39cf5c63242da..0642326d2b48e 100644 --- a/core/src/main/java/org/elasticsearch/action/OriginalIndices.java +++ b/core/src/main/java/org/elasticsearch/action/OriginalIndices.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.io.stream.StreamOutput; import java.io.IOException; +import java.util.Arrays; /** * Used to keep track of original indices within internal (e.g. shard level) requests @@ -64,4 +65,12 @@ public static void writeOriginalIndices(OriginalIndices originalIndices, StreamO out.writeStringArrayNullable(originalIndices.indices); originalIndices.indicesOptions.writeIndicesOptions(out); } + + @Override + public String toString() { + return "OriginalIndices{" + + "indices=" + Arrays.toString(indices) + + ", indicesOptions=" + indicesOptions + + '}'; + } } diff --git a/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java b/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java index ce1ba28289997..7a47405d92b95 100644 --- a/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java +++ b/core/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.HashSet; +import java.util.Objects; import java.util.Set; import static org.elasticsearch.common.xcontent.ObjectParser.fromList; @@ -78,6 +79,8 @@ public void readFrom(StreamInput in) throws IOException { super.readFrom(in); fields = in.readStringArray(); if (in.getVersion().onOrAfter(Version.V_5_5_0_UNRELEASED)) { + indices = in.readStringArray(); + indicesOptions = IndicesOptions.readIndicesOptions(in); mergeResults = in.readBoolean(); } else { mergeResults = true; @@ -89,6 +92,8 @@ public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeStringArray(fields); if (out.getVersion().onOrAfter(Version.V_5_5_0_UNRELEASED)) { + out.writeStringArray(indices); + indicesOptions.writeIndicesOptions(out); out.writeBoolean(mergeResults); } } @@ -118,12 +123,12 @@ public String[] fields() { * The list of indices to lookup */ public FieldCapabilitiesRequest indices(String... indices) { - this.indices = indices; + this.indices = Objects.requireNonNull(indices, "indices must not be null"); return this; } public FieldCapabilitiesRequest indicesOptions(IndicesOptions indicesOptions) { - this.indicesOptions = indicesOptions; + this.indicesOptions = Objects.requireNonNull(indicesOptions, "indices options must not be null"); return this; } diff --git a/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java b/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java index 6491b8ce4c757..8fad95257a879 100644 --- a/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java +++ b/core/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java @@ -26,6 +26,7 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.CountDown; @@ -72,7 +73,14 @@ protected void doExecute(FieldCapabilitiesRequest request, final Map remoteClusterIndices = remoteClusterService.groupIndices(request.indicesOptions(), request.indices(), idx -> indexNameExpressionResolver.hasIndexOrAlias(idx, clusterState)); final OriginalIndices localIndices = remoteClusterIndices.remove(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY); - final String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(clusterState, localIndices); + final String[] concreteIndices; + if (remoteClusterIndices.isEmpty() == false && localIndices.indices().length == 0) { + // in the case we have one or more remote indices but no local we don't expand to all local indices and just do remote + // indices + concreteIndices = Strings.EMPTY_ARRAY; + } else { + concreteIndices = indexNameExpressionResolver.concreteIndexNames(clusterState, localIndices); + } final int totalNumRequest = concreteIndices.length + remoteClusterIndices.size(); final CountDown completionCounter = new CountDown(totalNumRequest); final List indexResponses = Collections.synchronizedList(new ArrayList<>()); diff --git a/core/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequestTests.java b/core/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequestTests.java index 6b68112d5d52f..8543b35569a31 100644 --- a/core/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequestTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.action.fieldcaps; +import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.test.ESTestCase; @@ -33,10 +34,52 @@ private FieldCapabilitiesRequest randomRequest() { for (int i = 0; i < size; i++) { randomFields[i] = randomAlphaOfLengthBetween(5, 10); } + + size = randomIntBetween(0, 20); + String[] randomIndices = new String[size]; + for (int i = 0; i < size; i++) { + randomIndices[i] = randomAlphaOfLengthBetween(5, 10); + } request.fields(randomFields); + request.indices(randomIndices); + if (randomBoolean()) { + request.indicesOptions(randomBoolean() ? IndicesOptions.strictExpand() : IndicesOptions.lenientExpandOpen()); + } return request; } + public void testEqualsAndHashcode() { + FieldCapabilitiesRequest request = new FieldCapabilitiesRequest(); + request.indices("foo"); + request.indicesOptions(IndicesOptions.lenientExpandOpen()); + request.fields("bar"); + + FieldCapabilitiesRequest other = new FieldCapabilitiesRequest(); + other.indices("foo"); + other.indicesOptions(IndicesOptions.lenientExpandOpen()); + other.fields("bar"); + assertEquals(request, request); + assertEquals(request, other); + assertEquals(request.hashCode(), other.hashCode()); + + // change indices + other.indices("foo", "bar"); + assertNotEquals(request, other); + other.indices("foo"); + assertEquals(request, other); + + // change fields + other.fields("foo", "bar"); + assertNotEquals(request, other); + other.fields("bar"); + assertEquals(request, request); + + // change indices options + other.indicesOptions(IndicesOptions.strictExpand()); + assertNotEquals(request, other); + + } + public void testFieldCapsRequestSerialization() throws IOException { for (int i = 0; i < 20; i++) { FieldCapabilitiesRequest request = randomRequest(); diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yml index b5be2f7e124cd..e4463eb17a020 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/30_field_caps.yml @@ -64,3 +64,16 @@ - match: {fields.geo.keyword.indices: ["my_remote_cluster:field_caps_index_3"]} - is_false: fields.geo.keyword.non_searchable_indices - is_false: fields.geo.keyword.on_aggregatable_indices + + - do: + field_caps: + index: 'my_remote_cluster:some_index_that_doesnt_exist' + fields: [number] + - match: { 'fields': {} } # empty response - this index doesn't exists + + - do: + field_caps: + index: 'my_remote_cluster:field_caps_index_1' + fields: [number] + - match: {fields.number.double.searchable: true} + - match: {fields.number.double.aggregatable: true} From 67c41d2e77f5e27ab546ab5d3519639fb833b371 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Wed, 17 May 2017 14:15:40 +0200 Subject: [PATCH 392/619] Fix ExpandSearchPhase when response contains no hits (#24688) This change skips the expand search phase entirely when there is no search hits in the response. --- .../action/search/ExpandSearchPhase.java | 2 +- .../action/search/ExpandSearchPhaseTests.java | 31 +++++++++++++++++++ .../test/search/110_field_collapsing.yml | 20 +++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/search/ExpandSearchPhase.java b/core/src/main/java/org/elasticsearch/action/search/ExpandSearchPhase.java index 1ec21ab424901..a8c5bdeacf396 100644 --- a/core/src/main/java/org/elasticsearch/action/search/ExpandSearchPhase.java +++ b/core/src/main/java/org/elasticsearch/action/search/ExpandSearchPhase.java @@ -64,7 +64,7 @@ private boolean isCollapseRequest() { @Override public void run() throws IOException { - if (isCollapseRequest()) { + if (isCollapseRequest() && searchResponse.getHits().getHits().length > 0) { SearchRequest searchRequest = context.getRequest(); CollapseBuilder collapseBuilder = searchRequest.source().collapse(); MultiSearchRequest multiRequest = new MultiSearchRequest(); diff --git a/core/src/test/java/org/elasticsearch/action/search/ExpandSearchPhaseTests.java b/core/src/test/java/org/elasticsearch/action/search/ExpandSearchPhaseTests.java index b7f0e0785f982..255025302c78d 100644 --- a/core/src/test/java/org/elasticsearch/action/search/ExpandSearchPhaseTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/ExpandSearchPhaseTests.java @@ -196,4 +196,35 @@ public void run() throws IOException { assertNotNull(reference.get()); assertEquals(1, mockSearchPhaseContext.phasesExecuted.get()); } + + public void testSkipExpandCollapseNoHits() throws IOException { + MockSearchPhaseContext mockSearchPhaseContext = new MockSearchPhaseContext(1); + mockSearchPhaseContext.searchTransport = new SearchTransportService( + Settings.builder().put("search.remote.connect", false).build(), null) { + + @Override + void sendExecuteMultiSearch(MultiSearchRequest request, SearchTask task, ActionListener listener) { + fail("expand should not try to send empty multi search request"); + } + }; + mockSearchPhaseContext.getRequest().source(new SearchSourceBuilder() + .collapse(new CollapseBuilder("someField").setInnerHits(new InnerHitBuilder().setName("foobarbaz")))); + + SearchHits hits = new SearchHits(new SearchHit[0], 1, 1.0f); + InternalSearchResponse internalSearchResponse = new InternalSearchResponse(hits, null, null, null, false, null, 1); + SearchResponse response = mockSearchPhaseContext.buildSearchResponse(internalSearchResponse, null); + AtomicReference reference = new AtomicReference<>(); + ExpandSearchPhase phase = new ExpandSearchPhase(mockSearchPhaseContext, response, r -> + new SearchPhase("test") { + @Override + public void run() throws IOException { + reference.set(r); + } + } + ); + phase.run(); + mockSearchPhaseContext.assertNoFailure(); + assertNotNull(reference.get()); + assertEquals(1, mockSearchPhaseContext.phasesExecuted.get()); + } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/110_field_collapsing.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/110_field_collapsing.yml index dd17399d31ab9..9d3fc349a23ba 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search/110_field_collapsing.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/110_field_collapsing.yml @@ -147,7 +147,6 @@ setup: - match: { hits.hits.2.inner_hits.sub_hits.hits.hits.0._id: "5" } - match: { hits.hits.2.inner_hits.sub_hits.hits.hits.1._id: "4" } - --- "field collapsing, inner_hits and maxConcurrentGroupRequests": @@ -247,3 +246,22 @@ setup: match_all: {} query_weight: 1 rescore_query_weight: 2 + +--- +"no hits and inner_hits": + + - skip: + version: " - 5.4.0" + reason: "bug fixed in 5.4.1" + + - do: + search: + index: test + type: test + body: + size: 0 + collapse: { field: numeric_group, inner_hits: { name: sub_hits, size: 1} } + sort: [{ sort: desc }] + + - match: { hits.total: 6 } + - length: { hits.hits: 0 } From f56d8676cfef99722f299e0808974ee34485f30c Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 17 May 2017 09:32:07 -0400 Subject: [PATCH 393/619] Fix GetTermVectorsIT It was assuming that payloads were available in a context where they weren't. Relates to #24716 --- .../action/termvectors/GetTermVectorsIT.java | 39 +++++-------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/action/termvectors/GetTermVectorsIT.java b/core/src/test/java/org/elasticsearch/action/termvectors/GetTermVectorsIT.java index 2ab6292f2d2b6..cc7a73278efe0 100644 --- a/core/src/test/java/org/elasticsearch/action/termvectors/GetTermVectorsIT.java +++ b/core/src/test/java/org/elasticsearch/action/termvectors/GetTermVectorsIT.java @@ -216,10 +216,9 @@ public void testSimpleTermVectors() throws IOException { public void testRandomSingleTermVectors() throws IOException { FieldType ft = new FieldType(); - int config = randomInt(6); + int config = randomInt(4); boolean storePositions = false; boolean storeOffsets = false; - boolean storePayloads = false; boolean storeTermVectors = false; switch (config) { case 0: { @@ -246,23 +245,11 @@ public void testRandomSingleTermVectors() throws IOException { storeOffsets = true; break; } - case 5: { - storeTermVectors = true; - storePositions = true; - storePayloads = true; - break; - } - case 6: { - storeTermVectors = true; - storePositions = true; - storeOffsets = true; - storePayloads = true; - break; - } + default: + throw new IllegalArgumentException("Unsupported option: " + config); } ft.setStoreTermVectors(storeTermVectors); ft.setStoreTermVectorOffsets(storeOffsets); - ft.setStoreTermVectorPayloads(storePayloads); ft.setStoreTermVectorPositions(storePositions); String optionString = FieldMapper.termVectorOptionsToString(ft); @@ -293,13 +280,12 @@ public void testRandomSingleTermVectors() throws IOException { int[][] startOffset = {{10}, {40}, {16}, {20}, {35}, {26}, {4}, {0, 31}}; int[][] endOffset = {{15}, {43}, {19}, {25}, {39}, {30}, {9}, {3, 34}}; - boolean isPayloadRequested = randomBoolean(); boolean isOffsetRequested = randomBoolean(); boolean isPositionsRequested = randomBoolean(); - String infoString = createInfoString(isPositionsRequested, isOffsetRequested, isPayloadRequested, optionString); + String infoString = createInfoString(isPositionsRequested, isOffsetRequested, optionString); for (int i = 0; i < 10; i++) { TermVectorsRequestBuilder resp = client().prepareTermVectors("test", "type1", Integer.toString(i)) - .setPayloads(isPayloadRequested).setOffsets(isOffsetRequested).setPositions(isPositionsRequested).setSelectedFields(); + .setOffsets(isOffsetRequested).setPositions(isPositionsRequested).setSelectedFields(); TermVectorsResponse response = resp.execute().actionGet(); assertThat(infoString + "doc id: " + i + " doesn't exists but should", response.isExists(), equalTo(true)); Fields fields = response.getFields(); @@ -340,13 +326,8 @@ public void testRandomSingleTermVectors() throws IOException { } else { assertThat(infoString + "positions for term: ", nextPosition, equalTo(-1)); } - // only return something useful if requested and stored - if (isPayloadRequested && storePayloads) { - assertThat(infoString + "payloads for term: " + string, docsAndPositions.getPayload(), equalTo(new BytesRef( - "word"))); - } else { - assertThat(infoString + "payloads for term: " + string, docsAndPositions.getPayload(), equalTo(null)); - } + // payloads are never made by the mapping in this test + assertNull(infoString + "payloads for term: " + string, docsAndPositions.getPayload()); // only return something useful if requested and stored if (isOffsetRequested && storeOffsets) { @@ -365,11 +346,9 @@ public void testRandomSingleTermVectors() throws IOException { } } - private String createInfoString(boolean isPositionsRequested, boolean isOffsetRequested, boolean isPayloadRequested, - String optionString) { + private String createInfoString(boolean isPositionsRequested, boolean isOffsetRequested, String optionString) { String ret = "Store config: " + optionString + "\n" + "Requested: pos-" - + (isPositionsRequested ? "yes" : "no") + ", offsets-" + (isOffsetRequested ? "yes" : "no") + ", payload- " - + (isPayloadRequested ? "yes" : "no") + "\n"; + + (isPositionsRequested ? "yes" : "no") + ", offsets-" + (isOffsetRequested ? "yes" : "no") + "\n"; return ret; } From 0189a65e6bfc623813c6c088d19a0509fbeb64da Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 17 May 2017 10:24:57 -0400 Subject: [PATCH 394/619] Fail rest tests on yaml files (#24740) We've switched to supporting only `yml` files but anyone who didn't notice will commit a `yaml` file which won't be executed which is bad because it is easy not to notice. The test to catch this is simple enough that I think it is worth adding just to warn folks about their mistake. --- .../test/search.suggest/{20_phrase.yaml => 20_phrase.yml} | 0 .../elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCase.java | 2 ++ 2 files changed, 2 insertions(+) rename modules/analysis-common/src/test/resources/rest-api-spec/test/search.suggest/{20_phrase.yaml => 20_phrase.yml} (100%) diff --git a/modules/analysis-common/src/test/resources/rest-api-spec/test/search.suggest/20_phrase.yaml b/modules/analysis-common/src/test/resources/rest-api-spec/test/search.suggest/20_phrase.yml similarity index 100% rename from modules/analysis-common/src/test/resources/rest-api-spec/test/search.suggest/20_phrase.yaml rename to modules/analysis-common/src/test/resources/rest-api-spec/test/search.suggest/20_phrase.yml diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCase.java index 28bfa80cf2757..98f770d9cc757 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ESClientYamlSuiteTestCase.java @@ -200,6 +200,8 @@ static Map> loadSuites(String... paths) throws Exception { Files.walk(path).forEach(file -> { if (file.toString().endsWith(".yml")) { addSuite(root, file, files); + } else if (file.toString().endsWith(".yaml")) { + throw new IllegalArgumentException("yaml files are no longer supported: " + file); } }); } else { From 2ccc223ff761043807683f34b29c693af6c94d95 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Wed, 17 May 2017 17:27:09 +0200 Subject: [PATCH 395/619] Fix Version based BWC and set correct minCompatVersion (#24732) Approaching the release of 6.0 we need to sort out the usage of `Version#minimumCompatibilityVersion` which was still set to 5.0.0. Now this change moves it to the latest released version of 5.x (5.4 at this point) to ensure we are compatible with the latest minor of the previous major. This change also removes all the `_UNRELEASED` from the versions that where released and drops versions that were never released and are not expected to be released (bugfixes in minors that are not the latest in the previous major). --- .../elasticsearch/ElasticsearchException.java | 8 +- .../main/java/org/elasticsearch/Version.java | 93 ++++++++---------- .../ClusterAllocationExplainRequest.java | 4 +- .../shards/ClusterSearchShardsRequest.java | 4 +- .../shards/ClusterSearchShardsResponse.java | 4 +- .../GetStoredScriptResponse.java | 4 +- .../storedscripts/PutStoredScriptRequest.java | 4 +- .../indices/analyze/AnalyzeResponse.java | 4 +- .../validate/query/QueryExplanation.java | 4 +- .../validate/query/ValidateQueryRequest.java | 4 +- .../action/bulk/BulkItemResponse.java | 5 +- .../action/bulk/TransportShardBulkAction.java | 4 +- .../action/fieldstats/FieldStats.java | 6 +- .../action/fieldstats/FieldStatsResponse.java | 4 +- .../fieldstats/FieldStatsShardResponse.java | 2 +- .../action/index/IndexRequest.java | 6 +- .../action/ingest/PutPipelineRequest.java | 4 +- .../ingest/SimulatePipelineRequest.java | 4 +- .../action/search/SearchTransportService.java | 2 +- .../termvectors/TermVectorsRequest.java | 4 +- .../org/elasticsearch/cluster/NamedDiff.java | 2 +- .../elasticsearch/cluster/NamedDiffable.java | 2 +- .../cluster/SnapshotDeletionsInProgress.java | 2 +- .../cluster/SnapshotsInProgress.java | 2 +- .../cluster/node/DiscoveryNode.java | 3 +- .../allocation/NodeAllocationResult.java | 4 +- .../common/transport/TransportAddress.java | 8 +- .../gateway/GatewayMetaState.java | 4 +- .../index/query/InnerHitBuilder.java | 4 +- .../index/query/MoreLikeThisQueryBuilder.java | 4 +- .../index/query/QueryStringQueryBuilder.java | 12 +-- .../index/query/RangeQueryBuilder.java | 4 +- .../index/query/SimpleQueryStringBuilder.java | 16 +-- .../index/refresh/RefreshStats.java | 11 +-- .../reindex/AbstractBulkByScrollRequest.java | 6 +- .../index/reindex/BulkByScrollTask.java | 8 +- .../index/reindex/RemoteInfo.java | 4 +- .../ingest/PipelineConfiguration.java | 4 +- .../org/elasticsearch/monitor/os/OsStats.java | 4 +- .../org/elasticsearch/plugins/PluginInfo.java | 4 +- .../java/org/elasticsearch/script/Script.java | 8 +- .../elasticsearch/script/ScriptMetaData.java | 4 +- .../script/StoredScriptSource.java | 4 +- .../bucket/terms/support/IncludeExclude.java | 8 +- .../search/builder/SearchSourceBuilder.java | 4 +- .../highlight/AbstractHighlighterBuilder.java | 8 +- .../search/internal/AliasFilter.java | 4 +- .../internal/ShardSearchLocalRequest.java | 4 +- .../elasticsearch/snapshots/SnapshotInfo.java | 2 +- .../elasticsearch/transport/TcpTransport.java | 10 +- .../ExceptionSerializationTests.java | 2 +- .../java/org/elasticsearch/VersionTests.java | 43 ++++---- .../ClusterSearchShardsResponseTests.java | 2 +- .../PutStoredScriptRequestTests.java | 2 +- .../create/CreateIndexRequestTests.java | 4 +- .../mapping/put/PutMappingRequestTests.java | 2 +- .../put/PutIndexTemplateRequestTests.java | 2 +- .../fieldstats/FieldStatsRequestTests.java | 2 +- .../action/index/IndexRequestTests.java | 2 +- .../ingest/PutPipelineRequestTests.java | 2 +- .../ingest/SimulatePipelineRequestTests.java | 2 +- .../termvectors/TermVectorsUnitTests.java | 2 +- .../cluster/node/DiscoveryNodeTests.java | 8 +- .../ClusterSerializationTests.java | 2 +- .../common/io/stream/BytesStreamsTests.java | 2 +- .../discovery/zen/ZenDiscoveryUnitTests.java | 4 +- .../fieldstats/FieldStatsTests.java | 2 +- .../index/IndexSortSettingsTests.java | 2 +- .../index/analysis/PreBuiltAnalyzerTests.java | 4 +- .../index/engine/InternalEngineTests.java | 2 +- .../index/mapper/MapperTests.java | 2 +- .../query/MoreLikeThisQueryBuilderTests.java | 2 +- .../index/refresh/RefreshStatsTests.java | 11 --- .../reindex/BulkByScrollTaskStatusTests.java | 2 +- .../ingest/PipelineConfigurationTests.java | 4 +- .../resources/indices/bwc/index-5.2.2.zip | Bin 0 -> 503810 bytes .../resources/indices/bwc/index-5.3.2.zip | Bin 0 -> 399870 bytes .../test/resources/indices/bwc/repo-5.2.2.zip | Bin 0 -> 246750 bytes .../test/resources/indices/bwc/repo-5.3.2.zip | Bin 0 -> 190884 bytes .../percolator/PercolateQueryBuilder.java | 4 +- .../PercolateQueryBuilderTests.java | 2 +- .../index/reindex/RoundTripTests.java | 2 +- .../section/ClientYamlTestSectionTests.java | 2 +- .../rest/yaml/section/SetupSectionTests.java | 2 +- .../rest/yaml/section/SkipSectionTests.java | 2 +- .../yaml/section/TeardownSectionTests.java | 2 +- 86 files changed, 221 insertions(+), 247 deletions(-) create mode 100644 core/src/test/resources/indices/bwc/index-5.2.2.zip create mode 100644 core/src/test/resources/indices/bwc/index-5.3.2.zip create mode 100644 core/src/test/resources/indices/bwc/repo-5.2.2.zip create mode 100644 core/src/test/resources/indices/bwc/repo-5.3.2.zip diff --git a/core/src/main/java/org/elasticsearch/ElasticsearchException.java b/core/src/main/java/org/elasticsearch/ElasticsearchException.java index b6ed2cb7a2b46..ae575af61c8d7 100644 --- a/core/src/main/java/org/elasticsearch/ElasticsearchException.java +++ b/core/src/main/java/org/elasticsearch/ElasticsearchException.java @@ -133,7 +133,7 @@ public ElasticsearchException(StreamInput in) throws IOException { super(in.readOptionalString(), in.readException()); readStackTrace(this, in); headers.putAll(in.readMapOfLists(StreamInput::readString, StreamInput::readString)); - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { metadata.putAll(in.readMapOfLists(StreamInput::readString, StreamInput::readString)); } else { for (Iterator>> iterator = headers.entrySet().iterator(); iterator.hasNext(); ) { @@ -284,7 +284,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeOptionalString(this.getMessage()); out.writeException(this.getCause()); writeStackTraces(this, out); - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { out.writeMapOfLists(headers, StreamOutput::writeString, StreamOutput::writeString); out.writeMapOfLists(metadata, StreamOutput::writeString, StreamOutput::writeString); } else { @@ -985,11 +985,11 @@ private enum ElasticsearchExceptionHandle { STATUS_EXCEPTION(org.elasticsearch.ElasticsearchStatusException.class, org.elasticsearch.ElasticsearchStatusException::new, 145, UNKNOWN_VERSION_ADDED), TASK_CANCELLED_EXCEPTION(org.elasticsearch.tasks.TaskCancelledException.class, - org.elasticsearch.tasks.TaskCancelledException::new, 146, Version.V_5_1_1_UNRELEASED), + org.elasticsearch.tasks.TaskCancelledException::new, 146, Version.V_5_1_1), SHARD_LOCK_OBTAIN_FAILED_EXCEPTION(org.elasticsearch.env.ShardLockObtainFailedException.class, org.elasticsearch.env.ShardLockObtainFailedException::new, 147, Version.V_5_0_2), UNKNOWN_NAMED_OBJECT_EXCEPTION(org.elasticsearch.common.xcontent.NamedXContentRegistry.UnknownNamedObjectException.class, - org.elasticsearch.common.xcontent.NamedXContentRegistry.UnknownNamedObjectException::new, 148, Version.V_5_2_0_UNRELEASED); + org.elasticsearch.common.xcontent.NamedXContentRegistry.UnknownNamedObjectException::new, 148, Version.V_5_2_0); final Class exceptionClass; final CheckedFunction constructor; diff --git a/core/src/main/java/org/elasticsearch/Version.java b/core/src/main/java/org/elasticsearch/Version.java index d23417df071c5..44b989a378f92 100644 --- a/core/src/main/java/org/elasticsearch/Version.java +++ b/core/src/main/java/org/elasticsearch/Version.java @@ -55,33 +55,25 @@ public class Version implements Comparable { public static final Version V_5_0_1 = new Version(V_5_0_1_ID, org.apache.lucene.util.Version.LUCENE_6_2_1); public static final int V_5_0_2_ID = 5000299; public static final Version V_5_0_2 = new Version(V_5_0_2_ID, org.apache.lucene.util.Version.LUCENE_6_2_1); - public static final int V_5_0_3_ID_UNRELEASED = 5000399; - public static final Version V_5_0_3_UNRELEASED = new Version(V_5_0_3_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_3_0); // no version constant for 5.1.0 due to inadvertent release - public static final int V_5_1_1_ID_UNRELEASED = 5010199; - public static final Version V_5_1_1_UNRELEASED = new Version(V_5_1_1_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_3_0); - public static final int V_5_1_2_ID_UNRELEASED = 5010299; - public static final Version V_5_1_2_UNRELEASED = new Version(V_5_1_2_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_3_0); - public static final int V_5_1_3_ID_UNRELEASED = 5010399; - public static final Version V_5_1_3_UNRELEASED = new Version(V_5_1_3_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_3_0); - public static final int V_5_2_0_ID_UNRELEASED = 5020099; - public static final Version V_5_2_0_UNRELEASED = new Version(V_5_2_0_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_0); - public static final int V_5_2_1_ID_UNRELEASED = 5020199; - public static final Version V_5_2_1_UNRELEASED = new Version(V_5_2_1_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_1); - public static final int V_5_2_2_ID_UNRELEASED = 5020299; - public static final Version V_5_2_2_UNRELEASED = new Version(V_5_2_2_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_1); - public static final int V_5_2_3_ID_UNRELEASED = 5020399; - public static final Version V_5_2_3_UNRELEASED = new Version(V_5_2_3_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_1); - public static final int V_5_3_0_ID_UNRELEASED = 5030099; - public static final Version V_5_3_0_UNRELEASED = new Version(V_5_3_0_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_1); - public static final int V_5_3_1_ID_UNRELEASED = 5030199; - public static final Version V_5_3_1_UNRELEASED = new Version(V_5_3_1_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_2); - public static final int V_5_3_2_ID_UNRELEASED = 5030299; - public static final Version V_5_3_2_UNRELEASED = new Version(V_5_3_2_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_2); - public static final int V_5_4_0_ID_UNRELEASED = 5040099; - public static final Version V_5_4_0_UNRELEASED = new Version(V_5_4_0_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_5_0); - public static final int V_5_4_1_ID_UNRELEASED = 5040199; - public static final Version V_5_4_1_UNRELEASED = new Version(V_5_4_1_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_5_1); + public static final int V_5_1_1_ID = 5010199; + public static final Version V_5_1_1 = new Version(V_5_1_1_ID, org.apache.lucene.util.Version.LUCENE_6_3_0); + public static final int V_5_1_2_ID = 5010299; + public static final Version V_5_1_2 = new Version(V_5_1_2_ID, org.apache.lucene.util.Version.LUCENE_6_3_0); + public static final int V_5_2_0_ID = 5020099; + public static final Version V_5_2_0 = new Version(V_5_2_0_ID, org.apache.lucene.util.Version.LUCENE_6_4_0); + public static final int V_5_2_1_ID = 5020199; + public static final Version V_5_2_1 = new Version(V_5_2_1_ID, org.apache.lucene.util.Version.LUCENE_6_4_1); + public static final int V_5_2_2_ID = 5020299; + public static final Version V_5_2_2 = new Version(V_5_2_2_ID, org.apache.lucene.util.Version.LUCENE_6_4_1); + public static final int V_5_3_0_ID = 5030099; + public static final Version V_5_3_0 = new Version(V_5_3_0_ID, org.apache.lucene.util.Version.LUCENE_6_4_1); + public static final int V_5_3_1_ID = 5030199; + public static final Version V_5_3_1 = new Version(V_5_3_1_ID, org.apache.lucene.util.Version.LUCENE_6_4_2); + public static final int V_5_3_2_ID = 5030299; + public static final Version V_5_3_2 = new Version(V_5_3_2_ID, org.apache.lucene.util.Version.LUCENE_6_4_2); + public static final int V_5_4_0_ID = 5040099; + public static final Version V_5_4_0 = new Version(V_5_4_0_ID, org.apache.lucene.util.Version.LUCENE_6_5_0); public static final int V_5_5_0_ID_UNRELEASED = 5050099; public static final Version V_5_5_0_UNRELEASED = new Version(V_5_5_0_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_5_0); public static final int V_6_0_0_alpha1_ID_UNRELEASED = 6000001; @@ -111,32 +103,24 @@ public static Version fromId(int id) { return V_6_0_0_alpha1_UNRELEASED; case V_5_5_0_ID_UNRELEASED: return V_5_5_0_UNRELEASED; - case V_5_4_1_ID_UNRELEASED: - return V_5_4_1_UNRELEASED; - case V_5_4_0_ID_UNRELEASED: - return V_5_4_0_UNRELEASED; - case V_5_3_2_ID_UNRELEASED: - return V_5_3_2_UNRELEASED; - case V_5_3_1_ID_UNRELEASED: - return V_5_3_1_UNRELEASED; - case V_5_3_0_ID_UNRELEASED: - return V_5_3_0_UNRELEASED; - case V_5_2_3_ID_UNRELEASED: - return V_5_2_3_UNRELEASED; - case V_5_2_2_ID_UNRELEASED: - return V_5_2_2_UNRELEASED; - case V_5_2_1_ID_UNRELEASED: - return V_5_2_1_UNRELEASED; - case V_5_2_0_ID_UNRELEASED: - return V_5_2_0_UNRELEASED; - case V_5_1_3_ID_UNRELEASED: - return V_5_1_3_UNRELEASED; - case V_5_1_2_ID_UNRELEASED: - return V_5_1_2_UNRELEASED; - case V_5_1_1_ID_UNRELEASED: - return V_5_1_1_UNRELEASED; - case V_5_0_3_ID_UNRELEASED: - return V_5_0_3_UNRELEASED; + case V_5_4_0_ID: + return V_5_4_0; + case V_5_3_2_ID: + return V_5_3_2; + case V_5_3_1_ID: + return V_5_3_1; + case V_5_3_0_ID: + return V_5_3_0; + case V_5_2_2_ID: + return V_5_2_2; + case V_5_2_1_ID: + return V_5_2_1; + case V_5_2_0_ID: + return V_5_2_0; + case V_5_1_2_ID: + return V_5_1_2; + case V_5_1_1_ID: + return V_5_1_1; case V_5_0_2_ID: return V_5_0_2; case V_5_0_1_ID: @@ -296,7 +280,7 @@ public Version minimumCompatibilityVersion() { final int bwcMinor; if (this.onOrAfter(Version.V_6_0_0_alpha1_UNRELEASED)) { bwcMajor = major - 1; - bwcMinor = 0; // TODO we have to move this to the latest released minor of the last major but for now we just keep + bwcMinor = 4; } else { bwcMajor = major; bwcMinor = 0; @@ -306,7 +290,8 @@ public Version minimumCompatibilityVersion() { /** * Returns the minimum created index version that this version supports. Indices created with lower versions - * can't be used with this version. + * can't be used with this version. This should also be used for file based serialization backwards compatibility ie. on serialization + * code that is used to read / write file formats like transaction logs, cluster state, and index metadata. */ public Version minimumIndexCompatibilityVersion() { final int bwcMajor; diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainRequest.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainRequest.java index d80e58232a50f..aea1ee57dca87 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainRequest.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainRequest.java @@ -249,8 +249,8 @@ public void writeTo(StreamOutput out) throws IOException { } private void checkVersion(Version version) { - if (version.before(Version.V_5_2_0_UNRELEASED)) { - throw new IllegalArgumentException("cannot explain shards in a mixed-cluster with pre-" + Version.V_5_2_0_UNRELEASED + + if (version.before(Version.V_5_2_0)) { + throw new IllegalArgumentException("cannot explain shards in a mixed-cluster with pre-" + Version.V_5_2_0 + " nodes, node version [" + version + "]"); } } diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsRequest.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsRequest.java index 36d63bbcebea3..df38690b790a4 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsRequest.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsRequest.java @@ -134,7 +134,7 @@ public void readFrom(StreamInput in) throws IOException { routing = in.readOptionalString(); preference = in.readOptionalString(); - if (in.getVersion().onOrBefore(Version.V_5_1_1_UNRELEASED)) { + if (in.getVersion().onOrBefore(Version.V_5_1_1)) { //types in.readStringArray(); } @@ -153,7 +153,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeOptionalString(routing); out.writeOptionalString(preference); - if (out.getVersion().onOrBefore(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().onOrBefore(Version.V_5_1_1)) { //types out.writeStringArray(Strings.EMPTY_ARRAY); } diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsResponse.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsResponse.java index c2fb90434e57a..b5b28e2b8f79a 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsResponse.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsResponse.java @@ -73,7 +73,7 @@ public void readFrom(StreamInput in) throws IOException { for (int i = 0; i < nodes.length; i++) { nodes[i] = new DiscoveryNode(in); } - if (in.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_1_1)) { int size = in.readVInt(); indicesAndFilters = new HashMap<>(); for (int i = 0; i < size; i++) { @@ -95,7 +95,7 @@ public void writeTo(StreamOutput out) throws IOException { for (DiscoveryNode node : nodes) { node.writeTo(out); } - if (out.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_1_1)) { out.writeVInt(indicesAndFilters.size()); for (Map.Entry entry : indicesAndFilters.entrySet()) { out.writeString(entry.getKey()); diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java index b8302a03c28b5..2dc0ed870c025 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java @@ -59,7 +59,7 @@ public void readFrom(StreamInput in) throws IOException { super.readFrom(in); if (in.readBoolean()) { - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { source = new StoredScriptSource(in); } else { source = new StoredScriptSource(in.readString()); @@ -78,7 +78,7 @@ public void writeTo(StreamOutput out) throws IOException { } else { out.writeBoolean(true); - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { source.writeTo(out); } else { out.writeString(source.getCode()); diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java index f6a9e055399a3..ca26df75de9ac 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java @@ -123,7 +123,7 @@ public void readFrom(StreamInput in) throws IOException { id = in.readOptionalString(); content = in.readBytesReference(); - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { xContentType = XContentType.readFrom(in); } else { xContentType = XContentFactory.xContentType(content); @@ -137,7 +137,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(lang == null ? "" : lang); out.writeOptionalString(id); out.writeBytesReference(content); - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { xContentType.writeTo(out); } } diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/analyze/AnalyzeResponse.java b/core/src/main/java/org/elasticsearch/action/admin/indices/analyze/AnalyzeResponse.java index b92839638d84d..1e54def2385f8 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/analyze/AnalyzeResponse.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/analyze/AnalyzeResponse.java @@ -117,7 +117,7 @@ public void readFrom(StreamInput in) throws IOException { startOffset = in.readInt(); endOffset = in.readInt(); position = in.readVInt(); - if (in.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_2_0)) { Integer len = in.readOptionalVInt(); if (len != null) { positionLength = len; @@ -135,7 +135,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeInt(startOffset); out.writeInt(endOffset); out.writeVInt(position); - if (out.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_2_0)) { out.writeOptionalVInt(positionLength > 1 ? positionLength : null); } out.writeOptionalString(type); diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/validate/query/QueryExplanation.java b/core/src/main/java/org/elasticsearch/action/admin/indices/validate/query/QueryExplanation.java index f03bb49fdaeae..df9c12c95f4c9 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/validate/query/QueryExplanation.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/validate/query/QueryExplanation.java @@ -76,7 +76,7 @@ public String getExplanation() { @Override public void readFrom(StreamInput in) throws IOException { index = in.readString(); - if (in.getVersion().onOrAfter(Version.V_5_4_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_4_0)) { shard = in.readInt(); } else { shard = RANDOM_SHARD; @@ -89,7 +89,7 @@ public void readFrom(StreamInput in) throws IOException { @Override public void writeTo(StreamOutput out) throws IOException { out.writeString(index); - if (out.getVersion().onOrAfter(Version.V_5_4_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_4_0)) { out.writeInt(shard); } out.writeBoolean(valid); diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/validate/query/ValidateQueryRequest.java b/core/src/main/java/org/elasticsearch/action/admin/indices/validate/query/ValidateQueryRequest.java index 18ccf1ede7d5c..5953a5548c465 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/validate/query/ValidateQueryRequest.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/validate/query/ValidateQueryRequest.java @@ -154,7 +154,7 @@ public void readFrom(StreamInput in) throws IOException { } explain = in.readBoolean(); rewrite = in.readBoolean(); - if (in.getVersion().onOrAfter(Version.V_5_4_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_4_0)) { allShards = in.readBoolean(); } } @@ -169,7 +169,7 @@ public void writeTo(StreamOutput out) throws IOException { } out.writeBoolean(explain); out.writeBoolean(rewrite); - if (out.getVersion().onOrAfter(Version.V_5_4_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_4_0)) { out.writeBoolean(allShards); } } diff --git a/core/src/main/java/org/elasticsearch/action/bulk/BulkItemResponse.java b/core/src/main/java/org/elasticsearch/action/bulk/BulkItemResponse.java index 68cede5d25178..0ddd01187a6c2 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/BulkItemResponse.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/BulkItemResponse.java @@ -37,7 +37,6 @@ import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.seqno.SequenceNumbers; import org.elasticsearch.index.seqno.SequenceNumbersService; import org.elasticsearch.rest.RestStatus; @@ -421,7 +420,7 @@ public static BulkItemResponse readBulkItem(StreamInput in) throws IOException { @Override public void readFrom(StreamInput in) throws IOException { id = in.readVInt(); - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { opType = OpType.fromId(in.readByte()); } else { opType = OpType.fromString(in.readString()); @@ -448,7 +447,7 @@ public void readFrom(StreamInput in) throws IOException { @Override public void writeTo(StreamOutput out) throws IOException { out.writeVInt(id); - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { out.writeByte(opType.getId()); } else { out.writeString(opType.getLowercase()); diff --git a/core/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java b/core/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java index 4e1419099b0bf..5b99ed02cf870 100644 --- a/core/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java +++ b/core/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java @@ -415,10 +415,10 @@ public enum ReplicaItemExecutionMode { } static { - assert Version.CURRENT.minimumCompatibilityVersion().after(Version.V_5_0_0) == false: + assert Version.CURRENT.minimumCompatibilityVersion().after(Version.V_6_0_0_alpha1_UNRELEASED) == false: "Remove logic handling NoOp result from primary response; see TODO in replicaItemExecutionMode" + " as the current minimum compatible version [" + - Version.CURRENT.minimumCompatibilityVersion() + "] is after 5.0"; + Version.CURRENT.minimumCompatibilityVersion() + "] is after 6.0"; } /** diff --git a/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStats.java b/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStats.java index 5f85b95c7fed4..44b330dc37cc7 100644 --- a/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStats.java +++ b/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStats.java @@ -323,14 +323,14 @@ public final void writeTo(StreamOutput out) throws IOException { out.writeLong(sumTotalTermFreq); out.writeBoolean(isSearchable); out.writeBoolean(isAggregatable); - if (out.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_2_0)) { out.writeBoolean(hasMinMax); if (hasMinMax) { writeMinMax(out); } } else { assert hasMinMax : "cannot serialize null min/max fieldstats in a mixed-cluster " + - "with pre-" + Version.V_5_2_0_UNRELEASED + " nodes, remote version [" + out.getVersion() + "]"; + "with pre-" + Version.V_5_2_0 + " nodes, remote version [" + out.getVersion() + "]"; writeMinMax(out); } } @@ -705,7 +705,7 @@ public static FieldStats readFrom(StreamInput in) throws IOException { boolean isSearchable = in.readBoolean(); boolean isAggregatable = in.readBoolean(); boolean hasMinMax = true; - if (in.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_2_0)) { hasMinMax = in.readBoolean(); } switch (type) { diff --git a/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStatsResponse.java b/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStatsResponse.java index f126c73d04d0a..2046aeddc1b6a 100644 --- a/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStatsResponse.java +++ b/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStatsResponse.java @@ -93,7 +93,7 @@ public void writeTo(StreamOutput out) throws IOException { for (Map.Entry> entry1 : indicesMergedFieldStats.entrySet()) { out.writeString(entry1.getKey()); int size = entry1.getValue().size(); - if (out.getVersion().before(Version.V_5_2_0_UNRELEASED)) { + if (out.getVersion().before(Version.V_5_2_0)) { // filter fieldstats without min/max information for (FieldStats stats : entry1.getValue().values()) { if (stats.hasMinMax() == false) { @@ -103,7 +103,7 @@ public void writeTo(StreamOutput out) throws IOException { } out.writeVInt(size); for (Map.Entry entry2 : entry1.getValue().entrySet()) { - if (entry2.getValue().hasMinMax() || out.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (entry2.getValue().hasMinMax() || out.getVersion().onOrAfter(Version.V_5_2_0)) { out.writeString(entry2.getKey()); entry2.getValue().writeTo(out); } diff --git a/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStatsShardResponse.java b/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStatsShardResponse.java index 133a94e69a420..d2f3a7d5e4564 100644 --- a/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStatsShardResponse.java +++ b/core/src/main/java/org/elasticsearch/action/fieldstats/FieldStatsShardResponse.java @@ -68,7 +68,7 @@ public void readFrom(StreamInput in) throws IOException { public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); final Map > stats; - if (out.getVersion().before(Version.V_5_2_0_UNRELEASED)) { + if (out.getVersion().before(Version.V_5_2_0)) { /** * FieldStats with null min/max are not (de)serializable in versions prior to {@link Version.V_5_2_0_UNRELEASED} */ diff --git a/core/src/main/java/org/elasticsearch/action/index/IndexRequest.java b/core/src/main/java/org/elasticsearch/action/index/IndexRequest.java index 41780ca0c7a9f..ee9a5b2dfe684 100644 --- a/core/src/main/java/org/elasticsearch/action/index/IndexRequest.java +++ b/core/src/main/java/org/elasticsearch/action/index/IndexRequest.java @@ -534,7 +534,7 @@ public void readFrom(StreamInput in) throws IOException { pipeline = in.readOptionalString(); isRetry = in.readBoolean(); autoGeneratedTimestamp = in.readLong(); - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { contentType = in.readOptionalWriteable(XContentType::readFrom); } else { contentType = XContentFactory.xContentType(source); @@ -558,7 +558,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeBytesReference(source); out.writeByte(opType.getId()); // ES versions below 5.1.2 don't know about resolveVersionDefaults but resolve the version eagerly (which messes with validation). - if (out.getVersion().before(Version.V_5_1_2_UNRELEASED)) { + if (out.getVersion().before(Version.V_5_1_2)) { out.writeLong(resolveVersionDefaults()); } else { out.writeLong(version); @@ -567,7 +567,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeOptionalString(pipeline); out.writeBoolean(isRetry); out.writeLong(autoGeneratedTimestamp); - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { out.writeOptionalWriteable(contentType); } } diff --git a/core/src/main/java/org/elasticsearch/action/ingest/PutPipelineRequest.java b/core/src/main/java/org/elasticsearch/action/ingest/PutPipelineRequest.java index f5d5ab2d34bb7..394349ca01691 100644 --- a/core/src/main/java/org/elasticsearch/action/ingest/PutPipelineRequest.java +++ b/core/src/main/java/org/elasticsearch/action/ingest/PutPipelineRequest.java @@ -80,7 +80,7 @@ public void readFrom(StreamInput in) throws IOException { super.readFrom(in); id = in.readString(); source = in.readBytesReference(); - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { xContentType = XContentType.readFrom(in); } else { xContentType = XContentFactory.xContentType(source); @@ -92,7 +92,7 @@ public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeString(id); out.writeBytesReference(source); - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { xContentType.writeTo(out); } } diff --git a/core/src/main/java/org/elasticsearch/action/ingest/SimulatePipelineRequest.java b/core/src/main/java/org/elasticsearch/action/ingest/SimulatePipelineRequest.java index 170af18968114..30beb32681aea 100644 --- a/core/src/main/java/org/elasticsearch/action/ingest/SimulatePipelineRequest.java +++ b/core/src/main/java/org/elasticsearch/action/ingest/SimulatePipelineRequest.java @@ -103,7 +103,7 @@ public void readFrom(StreamInput in) throws IOException { id = in.readOptionalString(); verbose = in.readBoolean(); source = in.readBytesReference(); - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { xContentType = XContentType.readFrom(in); } else { xContentType = XContentFactory.xContentType(source); @@ -116,7 +116,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeOptionalString(id); out.writeBoolean(verbose); out.writeBytesReference(source); - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { xContentType.writeTo(out); } } diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java index 2ecfc213f9761..9dd2125d5e2fe 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchTransportService.java @@ -120,7 +120,7 @@ public void sendExecuteQuery(Transport.Connection connection, final ShardSearchT // this used to be the QUERY_AND_FETCH which doesn't exists anymore. final boolean fetchDocuments = request.numberOfShards() == 1; Supplier supplier = fetchDocuments ? QueryFetchSearchResult::new : QuerySearchResult::new; - if (connection.getVersion().before(Version.V_5_3_0_UNRELEASED) && fetchDocuments) { + if (connection.getVersion().before(Version.V_5_3_0) && fetchDocuments) { // this is a BWC layer for pre 5.3 indices if (request.scroll() != null) { /** diff --git a/core/src/main/java/org/elasticsearch/action/termvectors/TermVectorsRequest.java b/core/src/main/java/org/elasticsearch/action/termvectors/TermVectorsRequest.java index de2577f573be8..0fe83e214463a 100644 --- a/core/src/main/java/org/elasticsearch/action/termvectors/TermVectorsRequest.java +++ b/core/src/main/java/org/elasticsearch/action/termvectors/TermVectorsRequest.java @@ -499,7 +499,7 @@ public void readFrom(StreamInput in) throws IOException { if (in.readBoolean()) { doc = in.readBytesReference(); - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { xContentType = XContentType.readFrom(in); } else { xContentType = XContentFactory.xContentType(doc); @@ -544,7 +544,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeBoolean(doc != null); if (doc != null) { out.writeBytesReference(doc); - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { xContentType.writeTo(out); } } diff --git a/core/src/main/java/org/elasticsearch/cluster/NamedDiff.java b/core/src/main/java/org/elasticsearch/cluster/NamedDiff.java index 9da3167ae88c5..b7f2a96688ad4 100644 --- a/core/src/main/java/org/elasticsearch/cluster/NamedDiff.java +++ b/core/src/main/java/org/elasticsearch/cluster/NamedDiff.java @@ -30,7 +30,7 @@ public interface NamedDiff> extends Diff, NamedWriteabl * The minimal version of the recipient this custom object can be sent to */ default Version getMinimalSupportedVersion() { - return Version.CURRENT.minimumCompatibilityVersion(); + return Version.CURRENT.minimumIndexCompatibilityVersion(); } } diff --git a/core/src/main/java/org/elasticsearch/cluster/NamedDiffable.java b/core/src/main/java/org/elasticsearch/cluster/NamedDiffable.java index 0797442209689..b548b49fe1910 100644 --- a/core/src/main/java/org/elasticsearch/cluster/NamedDiffable.java +++ b/core/src/main/java/org/elasticsearch/cluster/NamedDiffable.java @@ -30,6 +30,6 @@ public interface NamedDiffable extends Diffable, NamedWriteable { * The minimal version of the recipient this custom object can be sent to */ default Version getMinimalSupportedVersion() { - return Version.CURRENT.minimumCompatibilityVersion(); + return Version.CURRENT.minimumIndexCompatibilityVersion(); } } diff --git a/core/src/main/java/org/elasticsearch/cluster/SnapshotDeletionsInProgress.java b/core/src/main/java/org/elasticsearch/cluster/SnapshotDeletionsInProgress.java index 446f4ae07410d..e0336f61e3961 100644 --- a/core/src/main/java/org/elasticsearch/cluster/SnapshotDeletionsInProgress.java +++ b/core/src/main/java/org/elasticsearch/cluster/SnapshotDeletionsInProgress.java @@ -40,7 +40,7 @@ public class SnapshotDeletionsInProgress extends AbstractNamedDiffable i public static final String TYPE = "snapshot_deletions"; // the version where SnapshotDeletionsInProgress was introduced - public static final Version VERSION_INTRODUCED = Version.V_5_2_0_UNRELEASED; + public static final Version VERSION_INTRODUCED = Version.V_5_2_0; // the list of snapshot deletion request entries private final List entries; diff --git a/core/src/main/java/org/elasticsearch/cluster/SnapshotsInProgress.java b/core/src/main/java/org/elasticsearch/cluster/SnapshotsInProgress.java index 5b8d391889480..53ac626820892 100644 --- a/core/src/main/java/org/elasticsearch/cluster/SnapshotsInProgress.java +++ b/core/src/main/java/org/elasticsearch/cluster/SnapshotsInProgress.java @@ -51,7 +51,7 @@ public class SnapshotsInProgress extends AbstractNamedDiffable implement // a snapshot in progress from a pre 5.2.x node public static final long UNDEFINED_REPOSITORY_STATE_ID = -2L; // the version where repository state ids were introduced - private static final Version REPOSITORY_ID_INTRODUCED_VERSION = Version.V_5_2_0_UNRELEASED; + private static final Version REPOSITORY_ID_INTRODUCED_VERSION = Version.V_5_2_0; @Override public boolean equals(Object o) { diff --git a/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java b/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java index caafb82c657a7..a651d957a9b95 100644 --- a/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java +++ b/core/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java @@ -34,7 +34,6 @@ import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.Predicate; @@ -222,7 +221,7 @@ public DiscoveryNode(StreamInput in) throws IOException { this.ephemeralId = in.readString().intern(); this.hostName = in.readString().intern(); this.hostAddress = in.readString().intern(); - if (in.getVersion().onOrAfter(Version.V_5_0_3_UNRELEASED)) { + if (in.getVersion().after(Version.V_5_0_2)) { this.address = new TransportAddress(in); } else { // we need to do this to preserve the host information during pinging and joining of a master. Since the version of the diff --git a/core/src/main/java/org/elasticsearch/cluster/routing/allocation/NodeAllocationResult.java b/core/src/main/java/org/elasticsearch/cluster/routing/allocation/NodeAllocationResult.java index 3740ded306073..0d3fe2df920f6 100644 --- a/core/src/main/java/org/elasticsearch/cluster/routing/allocation/NodeAllocationResult.java +++ b/core/src/main/java/org/elasticsearch/cluster/routing/allocation/NodeAllocationResult.java @@ -80,7 +80,7 @@ public NodeAllocationResult(DiscoveryNode node, Decision decision, int weightRan public NodeAllocationResult(StreamInput in) throws IOException { node = new DiscoveryNode(in); shardStoreInfo = in.readOptionalWriteable(ShardStoreInfo::new); - if (in.getVersion().before(Version.V_5_2_1_UNRELEASED)) { + if (in.getVersion().before(Version.V_5_2_1)) { canAllocateDecision = Decision.readFrom(in); } else { canAllocateDecision = in.readOptionalWriteable(Decision::readFrom); @@ -93,7 +93,7 @@ public NodeAllocationResult(StreamInput in) throws IOException { public void writeTo(StreamOutput out) throws IOException { node.writeTo(out); out.writeOptionalWriteable(shardStoreInfo); - if (out.getVersion().before(Version.V_5_2_1_UNRELEASED)) { + if (out.getVersion().before(Version.V_5_2_1)) { if (canAllocateDecision == null) { Decision.NO.writeTo(out); } else { diff --git a/core/src/main/java/org/elasticsearch/common/transport/TransportAddress.java b/core/src/main/java/org/elasticsearch/common/transport/TransportAddress.java index b6c93e389f40b..5ed7f40fad0ab 100644 --- a/core/src/main/java/org/elasticsearch/common/transport/TransportAddress.java +++ b/core/src/main/java/org/elasticsearch/common/transport/TransportAddress.java @@ -74,8 +74,8 @@ public TransportAddress(StreamInput in) throws IOException { } /** - * Read from a stream and use the {@code hostString} when creating the InetAddress if the input comes from a version prior - * {@link Version#V_5_0_3_UNRELEASED} as the hostString was not serialized + * Read from a stream and use the {@code hostString} when creating the InetAddress if the input comes from a version on or prior + * {@link Version#V_5_0_2} as the hostString was not serialized */ public TransportAddress(StreamInput in, @Nullable String hostString) throws IOException { if (in.getVersion().before(Version.V_6_0_0_alpha1_UNRELEASED)) { // bwc layer for 5.x where we had more than one transport address @@ -88,7 +88,7 @@ public TransportAddress(StreamInput in, @Nullable String hostString) throws IOEx final byte[] a = new byte[len]; // 4 bytes (IPv4) or 16 bytes (IPv6) in.readFully(a); final InetAddress inetAddress; - if (in.getVersion().onOrAfter(Version.V_5_0_3_UNRELEASED)) { + if (in.getVersion().after(Version.V_5_0_2)) { String host = in.readString(); // the host string was serialized so we can ignore the passed in version inetAddress = InetAddress.getByAddress(host, a); } else { @@ -107,7 +107,7 @@ public void writeTo(StreamOutput out) throws IOException { byte[] bytes = address.getAddress().getAddress(); // 4 bytes (IPv4) or 16 bytes (IPv6) out.writeByte((byte) bytes.length); // 1 byte out.write(bytes, 0, bytes.length); - if (out.getVersion().onOrAfter(Version.V_5_0_3_UNRELEASED)) { + if (out.getVersion().after(Version.V_5_0_2)) { out.writeString(address.getHostString()); } // don't serialize scope ids over the network!!!! diff --git a/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java b/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java index d798f8b7745b1..99a51adf96183 100644 --- a/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java +++ b/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java @@ -223,7 +223,7 @@ private void ensureNoPre019State() throws Exception { final String name = stateFile.getFileName().toString(); if (name.startsWith("metadata-")) { throw new IllegalStateException("Detected pre 0.19 metadata file please upgrade to a version before " - + Version.CURRENT.minimumCompatibilityVersion() + + Version.CURRENT.minimumIndexCompatibilityVersion() + " first to upgrade state structures - metadata found: [" + stateFile.getParent().toAbsolutePath()); } } @@ -294,7 +294,7 @@ private void ensureNoPre019ShardState(NodeEnvironment nodeEnv) throws Exception try (DirectoryStream stream = Files.newDirectoryStream(stateLocation, "shards-*")) { for (Path stateFile : stream) { throw new IllegalStateException("Detected pre 0.19 shard state file please upgrade to a version before " - + Version.CURRENT.minimumCompatibilityVersion() + + Version.CURRENT.minimumIndexCompatibilityVersion() + " first to upgrade state structures - shard state found: [" + stateFile.getParent().toAbsolutePath()); } } diff --git a/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java b/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java index b6eb84b03b7f2..768f7e7fd068c 100644 --- a/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java @@ -213,7 +213,7 @@ public InnerHitBuilder(StreamInput in) throws IOException { name = in.readOptionalString(); nestedPath = in.readOptionalString(); parentChildType = in.readOptionalString(); - if (in.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_2_0)) { ignoreUnmapped = in.readBoolean(); } from = in.readVInt(); @@ -254,7 +254,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeOptionalString(name); out.writeOptionalString(nestedPath); out.writeOptionalString(parentChildType); - if (out.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_2_0)) { out.writeBoolean(ignoreUnmapped); } out.writeVInt(from); diff --git a/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java index 553adf88b76ed..7889dee26dd1e 100644 --- a/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java @@ -228,7 +228,7 @@ public Item(@Nullable String index, @Nullable String type, XContentBuilder doc) type = in.readOptionalString(); if (in.readBoolean()) { doc = (BytesReference) in.readGenericValue(); - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { xContentType = XContentType.readFrom(in); } else { xContentType = XContentFactory.xContentType(doc); @@ -250,7 +250,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeBoolean(doc != null); if (doc != null) { out.writeGenericValue(doc); - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { xContentType.writeTo(out); } } else { diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java index fd6f33e27ba87..0269249e8254f 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java @@ -212,11 +212,11 @@ public QueryStringQueryBuilder(StreamInput in) throws IOException { autoGeneratePhraseQueries = in.readBoolean(); allowLeadingWildcard = in.readOptionalBoolean(); analyzeWildcard = in.readOptionalBoolean(); - if (in.getVersion().before(Version.V_5_1_1_UNRELEASED)) { + if (in.getVersion().before(Version.V_5_1_1)) { in.readBoolean(); // lowercase_expanded_terms } enablePositionIncrements = in.readBoolean(); - if (in.getVersion().before(Version.V_5_1_1_UNRELEASED)) { + if (in.getVersion().before(Version.V_5_1_1)) { in.readString(); // locale } fuzziness = new Fuzziness(in); @@ -232,7 +232,7 @@ public QueryStringQueryBuilder(StreamInput in) throws IOException { timeZone = in.readOptionalTimeZone(); escape = in.readBoolean(); maxDeterminizedStates = in.readVInt(); - if (in.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_1_1)) { splitOnWhitespace = in.readBoolean(); useAllFields = in.readOptionalBoolean(); } else { @@ -256,11 +256,11 @@ protected void doWriteTo(StreamOutput out) throws IOException { out.writeBoolean(this.autoGeneratePhraseQueries); out.writeOptionalBoolean(this.allowLeadingWildcard); out.writeOptionalBoolean(this.analyzeWildcard); - if (out.getVersion().before(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().before(Version.V_5_1_1)) { out.writeBoolean(true); // lowercase_expanded_terms } out.writeBoolean(this.enablePositionIncrements); - if (out.getVersion().before(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().before(Version.V_5_1_1)) { out.writeString(Locale.ROOT.toLanguageTag()); // locale } this.fuzziness.writeTo(out); @@ -276,7 +276,7 @@ protected void doWriteTo(StreamOutput out) throws IOException { out.writeOptionalTimeZone(timeZone); out.writeBoolean(this.escape); out.writeVInt(this.maxDeterminizedStates); - if (out.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_1_1)) { out.writeBoolean(this.splitOnWhitespace); out.writeOptionalBoolean(this.useAllFields); } diff --git a/core/src/main/java/org/elasticsearch/index/query/RangeQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/RangeQueryBuilder.java index bc1bd4bea93dd..22c17e4ddbac8 100644 --- a/core/src/main/java/org/elasticsearch/index/query/RangeQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/RangeQueryBuilder.java @@ -112,7 +112,7 @@ public RangeQueryBuilder(StreamInput in) throws IOException { if (formatString != null) { format = Joda.forPattern(formatString); } - if (in.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_2_0)) { String relationString = in.readOptionalString(); if (relationString != null) { relation = ShapeRelation.getRelationByName(relationString); @@ -133,7 +133,7 @@ protected void doWriteTo(StreamOutput out) throws IOException { formatString = this.format.format(); } out.writeOptionalString(formatString); - if (out.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_2_0)) { String relationString = null; if (this.relation != null) { relationString = this.relation.getRelationName(); diff --git a/core/src/main/java/org/elasticsearch/index/query/SimpleQueryStringBuilder.java b/core/src/main/java/org/elasticsearch/index/query/SimpleQueryStringBuilder.java index 8312d56c3f1ce..2efa4e815a495 100644 --- a/core/src/main/java/org/elasticsearch/index/query/SimpleQueryStringBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/SimpleQueryStringBuilder.java @@ -157,19 +157,19 @@ public SimpleQueryStringBuilder(StreamInput in) throws IOException { flags = in.readInt(); analyzer = in.readOptionalString(); defaultOperator = Operator.readFromStream(in); - if (in.getVersion().before(Version.V_5_1_1_UNRELEASED)) { + if (in.getVersion().before(Version.V_5_1_1)) { in.readBoolean(); // lowercase_expanded_terms } settings.lenient(in.readBoolean()); - if (in.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_1_1)) { this.lenientSet = in.readBoolean(); } settings.analyzeWildcard(in.readBoolean()); - if (in.getVersion().before(Version.V_5_1_1_UNRELEASED)) { + if (in.getVersion().before(Version.V_5_1_1)) { in.readString(); // locale } minimumShouldMatch = in.readOptionalString(); - if (in.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_1_1)) { settings.quoteFieldSuffix(in.readOptionalString()); useAllFields = in.readOptionalBoolean(); } @@ -186,19 +186,19 @@ protected void doWriteTo(StreamOutput out) throws IOException { out.writeInt(flags); out.writeOptionalString(analyzer); defaultOperator.writeTo(out); - if (out.getVersion().before(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().before(Version.V_5_1_1)) { out.writeBoolean(true); // lowercase_expanded_terms } out.writeBoolean(settings.lenient()); - if (out.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_1_1)) { out.writeBoolean(lenientSet); } out.writeBoolean(settings.analyzeWildcard()); - if (out.getVersion().before(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().before(Version.V_5_1_1)) { out.writeString(Locale.ROOT.toLanguageTag()); // locale } out.writeOptionalString(minimumShouldMatch); - if (out.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_1_1)) { out.writeOptionalString(settings.quoteFieldSuffix()); out.writeOptionalBoolean(useAllFields); } diff --git a/core/src/main/java/org/elasticsearch/index/refresh/RefreshStats.java b/core/src/main/java/org/elasticsearch/index/refresh/RefreshStats.java index 8fc16ae1b1d68..729bb47802d3f 100644 --- a/core/src/main/java/org/elasticsearch/index/refresh/RefreshStats.java +++ b/core/src/main/java/org/elasticsearch/index/refresh/RefreshStats.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.refresh; -import org.elasticsearch.Version; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Streamable; @@ -106,20 +105,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws public void readFrom(StreamInput in) throws IOException { total = in.readVLong(); totalTimeInMillis = in.readVLong(); - if (in.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { - listeners = in.readVInt(); - } else { - listeners = 0; - } + listeners = in.readVInt(); } @Override public void writeTo(StreamOutput out) throws IOException { out.writeVLong(total); out.writeVLong(totalTimeInMillis); - if (out.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { - out.writeVInt(listeners); - } + out.writeVInt(listeners); } @Override diff --git a/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java index a582248af11ae..5bfae5fde924a 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java @@ -402,7 +402,7 @@ public void readFrom(StreamInput in) throws IOException { retryBackoffInitialTime = new TimeValue(in); maxRetries = in.readVInt(); requestsPerSecond = in.readFloat(); - if (in.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_1_1)) { slices = in.readVInt(); } else { slices = 1; @@ -421,12 +421,12 @@ public void writeTo(StreamOutput out) throws IOException { retryBackoffInitialTime.writeTo(out); out.writeVInt(maxRetries); out.writeFloat(requestsPerSecond); - if (out.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_1_1)) { out.writeVInt(slices); } else { if (slices > 1) { throw new IllegalArgumentException("Attempting to send sliced reindex-style request to a node that doesn't support " - + "it. Version is [" + out.getVersion() + "] but must be [" + Version.V_5_1_1_UNRELEASED + "]"); + + "it. Version is [" + out.getVersion() + "] but must be [" + Version.V_5_1_1 + "]"); } } } diff --git a/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollTask.java b/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollTask.java index 18c6dac92064a..284fea7a38bfc 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollTask.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/BulkByScrollTask.java @@ -189,7 +189,7 @@ public Status(List sliceStatuses, @Nullable String reasonCanc } public Status(StreamInput in) throws IOException { - if (in.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_1_1)) { sliceId = in.readOptionalVInt(); } else { sliceId = null; @@ -207,7 +207,7 @@ public Status(StreamInput in) throws IOException { requestsPerSecond = in.readFloat(); reasonCancelled = in.readOptionalString(); throttledUntil = new TimeValue(in); - if (in.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_1_1)) { sliceStatuses = in.readList(stream -> stream.readOptionalWriteable(StatusOrException::new)); } else { sliceStatuses = emptyList(); @@ -216,7 +216,7 @@ public Status(StreamInput in) throws IOException { @Override public void writeTo(StreamOutput out) throws IOException { - if (out.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_1_1)) { out.writeOptionalVInt(sliceId); } out.writeVLong(total); @@ -232,7 +232,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeFloat(requestsPerSecond); out.writeOptionalString(reasonCancelled); throttledUntil.writeTo(out); - if (out.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_1_1)) { out.writeVInt(sliceStatuses.size()); for (StatusOrException sliceStatus : sliceStatuses) { out.writeOptionalWriteable(sliceStatus); diff --git a/core/src/main/java/org/elasticsearch/index/reindex/RemoteInfo.java b/core/src/main/java/org/elasticsearch/index/reindex/RemoteInfo.java index 878a9c61e4c7c..105afcc95bc38 100644 --- a/core/src/main/java/org/elasticsearch/index/reindex/RemoteInfo.java +++ b/core/src/main/java/org/elasticsearch/index/reindex/RemoteInfo.java @@ -90,7 +90,7 @@ public RemoteInfo(StreamInput in) throws IOException { headers.put(in.readString(), in.readString()); } this.headers = unmodifiableMap(headers); - if (in.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_2_0)) { socketTimeout = new TimeValue(in); connectTimeout = new TimeValue(in); } else { @@ -112,7 +112,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(header.getKey()); out.writeString(header.getValue()); } - if (out.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_2_0)) { socketTimeout.writeTo(out); connectTimeout.writeTo(out); } diff --git a/core/src/main/java/org/elasticsearch/ingest/PipelineConfiguration.java b/core/src/main/java/org/elasticsearch/ingest/PipelineConfiguration.java index d58d76fb10c78..1d7ba958f1498 100644 --- a/core/src/main/java/org/elasticsearch/ingest/PipelineConfiguration.java +++ b/core/src/main/java/org/elasticsearch/ingest/PipelineConfiguration.java @@ -118,7 +118,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } public static PipelineConfiguration readFrom(StreamInput in) throws IOException { - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { return new PipelineConfiguration(in.readString(), in.readBytesReference(), XContentType.readFrom(in)); } else { final String id = in.readString(); @@ -135,7 +135,7 @@ public static Diff readDiffFrom(StreamInput in) throws IO public void writeTo(StreamOutput out) throws IOException { out.writeString(id); out.writeBytesReference(config); - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { xContentType.writeTo(out); } } diff --git a/core/src/main/java/org/elasticsearch/monitor/os/OsStats.java b/core/src/main/java/org/elasticsearch/monitor/os/OsStats.java index d01b1f9b432e1..c3783c600ea4c 100644 --- a/core/src/main/java/org/elasticsearch/monitor/os/OsStats.java +++ b/core/src/main/java/org/elasticsearch/monitor/os/OsStats.java @@ -52,7 +52,7 @@ public OsStats(StreamInput in) throws IOException { this.cpu = new Cpu(in); this.mem = new Mem(in); this.swap = new Swap(in); - if (in.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_1_1)) { this.cgroup = in.readOptionalWriteable(Cgroup::new); } else { this.cgroup = null; @@ -65,7 +65,7 @@ public void writeTo(StreamOutput out) throws IOException { cpu.writeTo(out); mem.writeTo(out); swap.writeTo(out); - if (out.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_1_1)) { out.writeOptionalWriteable(cgroup); } } diff --git a/core/src/main/java/org/elasticsearch/plugins/PluginInfo.java b/core/src/main/java/org/elasticsearch/plugins/PluginInfo.java index 943f9018e6f88..666cc22b92655 100644 --- a/core/src/main/java/org/elasticsearch/plugins/PluginInfo.java +++ b/core/src/main/java/org/elasticsearch/plugins/PluginInfo.java @@ -81,7 +81,7 @@ public PluginInfo(final StreamInput in) throws IOException { this.description = in.readString(); this.version = in.readString(); this.classname = in.readString(); - if (in.getVersion().onOrAfter(Version.V_5_4_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_4_0)) { hasNativeController = in.readBoolean(); } else { hasNativeController = false; @@ -94,7 +94,7 @@ public void writeTo(final StreamOutput out) throws IOException { out.writeString(description); out.writeString(version); out.writeString(classname); - if (out.getVersion().onOrAfter(Version.V_5_4_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_4_0)) { out.writeBoolean(hasNativeController); } } diff --git a/core/src/main/java/org/elasticsearch/script/Script.java b/core/src/main/java/org/elasticsearch/script/Script.java index 9f8a774398cf1..7397ecfd89056 100644 --- a/core/src/main/java/org/elasticsearch/script/Script.java +++ b/core/src/main/java/org/elasticsearch/script/Script.java @@ -485,7 +485,7 @@ public Script(ScriptType type, String lang, String idOrCode, Map public Script(StreamInput in) throws IOException { // Version 5.3 allows lang to be an optional parameter for stored scripts and expects // options to be null for stored and file scripts. - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { this.type = ScriptType.readFrom(in); this.lang = in.readOptionalString(); this.idOrCode = in.readString(); @@ -496,7 +496,7 @@ public Script(StreamInput in) throws IOException { // Version 5.1 to 5.3 (exclusive) requires all Script members to be non-null and supports the potential // for more options than just XContentType. Reorders the read in contents to be in // same order as the constructor. - } else if (in.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + } else if (in.getVersion().onOrAfter(Version.V_5_1_1)) { this.type = ScriptType.readFrom(in); this.lang = in.readString(); @@ -554,7 +554,7 @@ public Script(StreamInput in) throws IOException { public void writeTo(StreamOutput out) throws IOException { // Version 5.3+ allows lang to be an optional parameter for stored scripts and expects // options to be null for stored and file scripts. - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { type.writeTo(out); out.writeOptionalString(lang); out.writeString(idOrCode); @@ -565,7 +565,7 @@ public void writeTo(StreamOutput out) throws IOException { // Version 5.1 to 5.3 (exclusive) requires all Script members to be non-null and supports the potential // for more options than just XContentType. Reorders the written out contents to be in // same order as the constructor. - } else if (out.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + } else if (out.getVersion().onOrAfter(Version.V_5_1_1)) { type.writeTo(out); if (lang == null) { diff --git a/core/src/main/java/org/elasticsearch/script/ScriptMetaData.java b/core/src/main/java/org/elasticsearch/script/ScriptMetaData.java index 87afc21a8c020..f69302ce0140b 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptMetaData.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptMetaData.java @@ -329,7 +329,7 @@ public ScriptMetaData(StreamInput in) throws IOException { // Split the id to find the language then use StoredScriptSource to parse the // expected BytesReference after which a new StoredScriptSource is created // with the appropriate language and options. - if (in.getVersion().before(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().before(Version.V_5_3_0)) { int split = id.indexOf('#'); if (split == -1) { @@ -353,7 +353,7 @@ public ScriptMetaData(StreamInput in) throws IOException { public void writeTo(StreamOutput out) throws IOException { // Version 5.3+ will output the contents of the scripts' Map using // StoredScriptSource to stored the language, code, and options. - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { out.writeVInt(scripts.size()); for (Map.Entry entry : scripts.entrySet()) { diff --git a/core/src/main/java/org/elasticsearch/script/StoredScriptSource.java b/core/src/main/java/org/elasticsearch/script/StoredScriptSource.java index b4e6e257eb718..7236e6eab39f2 100644 --- a/core/src/main/java/org/elasticsearch/script/StoredScriptSource.java +++ b/core/src/main/java/org/elasticsearch/script/StoredScriptSource.java @@ -365,7 +365,7 @@ public StoredScriptSource(String lang, String code, Map options) * only the code parameter will be read in as a bytes reference. */ public StoredScriptSource(StreamInput in) throws IOException { - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { this.lang = in.readString(); this.code = in.readString(); @SuppressWarnings("unchecked") @@ -385,7 +385,7 @@ public StoredScriptSource(StreamInput in) throws IOException { */ @Override public void writeTo(StreamOutput out) throws IOException { - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { out.writeString(lang); out.writeString(code); @SuppressWarnings("unchecked") diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/support/IncludeExclude.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/support/IncludeExclude.java index aabe5f585da81..46e371a3dfe0d 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/support/IncludeExclude.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/support/IncludeExclude.java @@ -64,8 +64,8 @@ public class IncludeExclude implements Writeable, ToXContent { public static final ParseField PARTITION_FIELD = new ParseField("partition"); public static final ParseField NUM_PARTITIONS_FIELD = new ParseField("num_partitions"); // Needed to add this seed for a deterministic term hashing policy - // otherwise tests fail to get expected results and worse, shards - // can disagree on which terms hash to the required partition. + // otherwise tests fail to get expected results and worse, shards + // can disagree on which terms hash to the required partition. private static final int HASH_PARTITIONING_SEED = 31; // for parsing purposes only @@ -427,7 +427,7 @@ public IncludeExclude(StreamInput in) throws IOException { } else { excludeValues = null; } - if (in.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_2_0)) { incNumPartitions = in.readVInt(); incZeroBasedPartition = in.readVInt(); } else { @@ -460,7 +460,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeBytesRef(value); } } - if (out.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_2_0)) { out.writeVInt(incNumPartitions); out.writeVInt(incZeroBasedPartition); } diff --git a/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java b/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java index 37d7eb5b02756..98c4f110590fb 100644 --- a/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java @@ -220,7 +220,7 @@ public SearchSourceBuilder(StreamInput in) throws IOException { profile = in.readBoolean(); searchAfterBuilder = in.readOptionalWriteable(SearchAfterBuilder::new); sliceBuilder = in.readOptionalWriteable(SliceBuilder::new); - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { collapse = in.readOptionalWriteable(CollapseBuilder::new); } } @@ -271,7 +271,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeBoolean(profile); out.writeOptionalWriteable(searchAfterBuilder); out.writeOptionalWriteable(sliceBuilder); - if (out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_3_0)) { out.writeOptionalWriteable(collapse); } } diff --git a/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/AbstractHighlighterBuilder.java b/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/AbstractHighlighterBuilder.java index 3a3c1cfd66d57..e5db6639ad82a 100644 --- a/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/AbstractHighlighterBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/AbstractHighlighterBuilder.java @@ -128,14 +128,14 @@ protected AbstractHighlighterBuilder(StreamInput in) throws IOException { order(in.readOptionalWriteable(Order::readFromStream)); highlightFilter(in.readOptionalBoolean()); forceSource(in.readOptionalBoolean()); - if (in.getVersion().onOrAfter(Version.V_5_4_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_4_0)) { boundaryScannerType(in.readOptionalWriteable(BoundaryScannerType::readFromStream)); } boundaryMaxScan(in.readOptionalVInt()); if (in.readBoolean()) { boundaryChars(in.readString().toCharArray()); } - if (in.getVersion().onOrAfter(Version.V_5_4_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_4_0)) { if (in.readBoolean()) { boundaryScannerLocale(in.readString()); } @@ -167,7 +167,7 @@ public final void writeTo(StreamOutput out) throws IOException { out.writeOptionalWriteable(order); out.writeOptionalBoolean(highlightFilter); out.writeOptionalBoolean(forceSource); - if (out.getVersion().onOrAfter(Version.V_5_4_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_4_0)) { out.writeOptionalWriteable(boundaryScannerType); } out.writeOptionalVInt(boundaryMaxScan); @@ -176,7 +176,7 @@ public final void writeTo(StreamOutput out) throws IOException { if (hasBounaryChars) { out.writeString(String.valueOf(boundaryChars)); } - if (out.getVersion().onOrAfter(Version.V_5_4_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_4_0)) { boolean hasBoundaryScannerLocale = boundaryScannerLocale != null; out.writeBoolean(hasBoundaryScannerLocale); if (hasBoundaryScannerLocale) { diff --git a/core/src/main/java/org/elasticsearch/search/internal/AliasFilter.java b/core/src/main/java/org/elasticsearch/search/internal/AliasFilter.java index 46fba77627749..7c6bea8ce903f 100644 --- a/core/src/main/java/org/elasticsearch/search/internal/AliasFilter.java +++ b/core/src/main/java/org/elasticsearch/search/internal/AliasFilter.java @@ -54,7 +54,7 @@ public AliasFilter(QueryBuilder filter, String... aliases) { public AliasFilter(StreamInput input) throws IOException { aliases = input.readStringArray(); - if (input.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (input.getVersion().onOrAfter(Version.V_5_1_1)) { filter = input.readOptionalNamedWriteable(QueryBuilder.class); reparseAliases = false; } else { @@ -90,7 +90,7 @@ AliasFilter rewrite(QueryRewriteContext context) throws IOException { @Override public void writeTo(StreamOutput out) throws IOException { out.writeStringArray(aliases); - if (out.getVersion().onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_1_1)) { out.writeOptionalNamedWriteable(filter); } } diff --git a/core/src/main/java/org/elasticsearch/search/internal/ShardSearchLocalRequest.java b/core/src/main/java/org/elasticsearch/search/internal/ShardSearchLocalRequest.java index d21fc2faf5c20..d193812aba32f 100644 --- a/core/src/main/java/org/elasticsearch/search/internal/ShardSearchLocalRequest.java +++ b/core/src/main/java/org/elasticsearch/search/internal/ShardSearchLocalRequest.java @@ -181,7 +181,7 @@ protected void innerReadFrom(StreamInput in) throws IOException { source = in.readOptionalWriteable(SearchSourceBuilder::new); types = in.readStringArray(); aliasFilter = new AliasFilter(in); - if (in.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_2_0)) { indexBoost = in.readFloat(); } else { // Nodes < 5.2.0 doesn't send index boost. Read it from source. @@ -209,7 +209,7 @@ protected void innerWriteTo(StreamOutput out, boolean asKey) throws IOException out.writeOptionalWriteable(source); out.writeStringArray(types); aliasFilter.writeTo(out); - if (out.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (out.getVersion().onOrAfter(Version.V_5_2_0)) { out.writeFloat(indexBoost); } if (!asKey) { diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java index 433a631821319..37cce0ac60174 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java @@ -69,7 +69,7 @@ public final class SnapshotInfo implements Comparable, ToXContent, private static final String TOTAL_SHARDS = "total_shards"; private static final String SUCCESSFUL_SHARDS = "successful_shards"; - private static final Version VERSION_INCOMPATIBLE_INTRODUCED = Version.V_5_2_0_UNRELEASED; + private static final Version VERSION_INCOMPATIBLE_INTRODUCED = Version.V_5_2_0; public static final Version VERBOSE_INTRODUCED = Version.V_5_5_0_UNRELEASED; private static final Comparator COMPARATOR = diff --git a/core/src/main/java/org/elasticsearch/transport/TcpTransport.java b/core/src/main/java/org/elasticsearch/transport/TcpTransport.java index ba355e41477b9..5713cc27c09c3 100644 --- a/core/src/main/java/org/elasticsearch/transport/TcpTransport.java +++ b/core/src/main/java/org/elasticsearch/transport/TcpTransport.java @@ -1324,9 +1324,15 @@ public final void messageReceived(BytesReference reference, Channel channel, Str } streamIn = compressor.streamInput(streamIn); } - if (version.isCompatible(getCurrentVersion()) == false) { + // for handshakes we are compatible with N-2 since otherwise we can't figure out our initial version + // since we are compatible with N-1 and N+1 so we always send our minCompatVersion as the initial version in the + // handshake. This looks odd but it's required to establish the connection correctly we check for real compatibility + // once the connection is established + final Version compatibilityVersion = TransportStatus.isHandshake(status) ? getCurrentVersion().minimumCompatibilityVersion() + : getCurrentVersion(); + if (version.isCompatible(compatibilityVersion) == false) { throw new IllegalStateException("Received message from unsupported version: [" + version - + "] minimal compatible version is: [" + getCurrentVersion().minimumCompatibilityVersion() + "]"); + + "] minimal compatible version is: [" + compatibilityVersion.minimumCompatibilityVersion() + "]"); } streamIn = new NamedWriteableAwareStreamInput(streamIn, namedWriteableRegistry); streamIn.setVersion(version); diff --git a/core/src/test/java/org/elasticsearch/ExceptionSerializationTests.java b/core/src/test/java/org/elasticsearch/ExceptionSerializationTests.java index c0430001bb830..106c24982a87a 100644 --- a/core/src/test/java/org/elasticsearch/ExceptionSerializationTests.java +++ b/core/src/test/java/org/elasticsearch/ExceptionSerializationTests.java @@ -972,7 +972,7 @@ public void testBWCHeadersAndMetadata() throws IOException { try (StreamInput in = decoded.streamInput()) { //randomize the version across released and unreleased ones Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, - Version.V_5_0_3_UNRELEASED, Version.V_5_1_1_UNRELEASED, Version.V_5_1_2_UNRELEASED, Version.V_5_2_0_UNRELEASED); + Version.V_5_1_1, Version.V_5_1_2, Version.V_5_2_0); in.setVersion(version); ElasticsearchException exception = new ElasticsearchException(in); assertEquals("test message", exception.getMessage()); diff --git a/core/src/test/java/org/elasticsearch/VersionTests.java b/core/src/test/java/org/elasticsearch/VersionTests.java index 06a4fe117faf5..96a0c9aa81bfa 100644 --- a/core/src/test/java/org/elasticsearch/VersionTests.java +++ b/core/src/test/java/org/elasticsearch/VersionTests.java @@ -33,7 +33,7 @@ import java.util.Map; import java.util.Set; -import static org.elasticsearch.Version.V_5_3_0_UNRELEASED; +import static org.elasticsearch.Version.V_5_3_0; import static org.elasticsearch.Version.V_6_0_0_alpha2_UNRELEASED; import static org.elasticsearch.test.VersionUtils.randomVersion; import static org.hamcrest.CoreMatchers.equalTo; @@ -46,30 +46,30 @@ public class VersionTests extends ESTestCase { public void testVersionComparison() throws Exception { - assertThat(V_5_3_0_UNRELEASED.before(V_6_0_0_alpha2_UNRELEASED), is(true)); - assertThat(V_5_3_0_UNRELEASED.before(V_5_3_0_UNRELEASED), is(false)); - assertThat(V_6_0_0_alpha2_UNRELEASED.before(V_5_3_0_UNRELEASED), is(false)); + assertThat(V_5_3_0.before(V_6_0_0_alpha2_UNRELEASED), is(true)); + assertThat(V_5_3_0.before(V_5_3_0), is(false)); + assertThat(V_6_0_0_alpha2_UNRELEASED.before(V_5_3_0), is(false)); - assertThat(V_5_3_0_UNRELEASED.onOrBefore(V_6_0_0_alpha2_UNRELEASED), is(true)); - assertThat(V_5_3_0_UNRELEASED.onOrBefore(V_5_3_0_UNRELEASED), is(true)); - assertThat(V_6_0_0_alpha2_UNRELEASED.onOrBefore(V_5_3_0_UNRELEASED), is(false)); + assertThat(V_5_3_0.onOrBefore(V_6_0_0_alpha2_UNRELEASED), is(true)); + assertThat(V_5_3_0.onOrBefore(V_5_3_0), is(true)); + assertThat(V_6_0_0_alpha2_UNRELEASED.onOrBefore(V_5_3_0), is(false)); - assertThat(V_5_3_0_UNRELEASED.after(V_6_0_0_alpha2_UNRELEASED), is(false)); - assertThat(V_5_3_0_UNRELEASED.after(V_5_3_0_UNRELEASED), is(false)); - assertThat(V_6_0_0_alpha2_UNRELEASED.after(V_5_3_0_UNRELEASED), is(true)); + assertThat(V_5_3_0.after(V_6_0_0_alpha2_UNRELEASED), is(false)); + assertThat(V_5_3_0.after(V_5_3_0), is(false)); + assertThat(V_6_0_0_alpha2_UNRELEASED.after(V_5_3_0), is(true)); - assertThat(V_5_3_0_UNRELEASED.onOrAfter(V_6_0_0_alpha2_UNRELEASED), is(false)); - assertThat(V_5_3_0_UNRELEASED.onOrAfter(V_5_3_0_UNRELEASED), is(true)); - assertThat(V_6_0_0_alpha2_UNRELEASED.onOrAfter(V_5_3_0_UNRELEASED), is(true)); + assertThat(V_5_3_0.onOrAfter(V_6_0_0_alpha2_UNRELEASED), is(false)); + assertThat(V_5_3_0.onOrAfter(V_5_3_0), is(true)); + assertThat(V_6_0_0_alpha2_UNRELEASED.onOrAfter(V_5_3_0), is(true)); assertTrue(Version.fromString("5.0.0-alpha2").onOrAfter(Version.fromString("5.0.0-alpha1"))); assertTrue(Version.fromString("5.0.0").onOrAfter(Version.fromString("5.0.0-beta2"))); assertTrue(Version.fromString("5.0.0-rc1").onOrAfter(Version.fromString("5.0.0-beta24"))); assertTrue(Version.fromString("5.0.0-alpha24").before(Version.fromString("5.0.0-beta0"))); - assertThat(V_5_3_0_UNRELEASED, is(lessThan(V_6_0_0_alpha2_UNRELEASED))); - assertThat(V_5_3_0_UNRELEASED.compareTo(V_5_3_0_UNRELEASED), is(0)); - assertThat(V_6_0_0_alpha2_UNRELEASED, is(greaterThan(V_5_3_0_UNRELEASED))); + assertThat(V_5_3_0, is(lessThan(V_6_0_0_alpha2_UNRELEASED))); + assertThat(V_5_3_0.compareTo(V_5_3_0), is(0)); + assertThat(V_6_0_0_alpha2_UNRELEASED, is(greaterThan(V_5_3_0))); } public void testMin() { @@ -100,7 +100,7 @@ public void testMinimumIndexCompatibilityVersion() { assertEquals(Version.V_5_0_0, Version.V_6_0_0_alpha2_UNRELEASED.minimumIndexCompatibilityVersion()); assertEquals(Version.fromId(2000099), Version.V_5_0_0.minimumIndexCompatibilityVersion()); assertEquals(Version.fromId(2000099), - Version.V_5_1_1_UNRELEASED.minimumIndexCompatibilityVersion()); + Version.V_5_1_1.minimumIndexCompatibilityVersion()); assertEquals(Version.fromId(2000099), Version.V_5_0_0_alpha1.minimumIndexCompatibilityVersion()); } @@ -157,7 +157,7 @@ public void testVersionNoPresentInSettings() { public void testIndexCreatedVersion() { // an actual index has a IndexMetaData.SETTING_INDEX_UUID final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_2, - Version.V_5_2_0_UNRELEASED, Version.V_6_0_0_alpha2_UNRELEASED); + Version.V_5_2_0, Version.V_6_0_0_alpha2_UNRELEASED); assertEquals(version, Version.indexCreated(Settings.builder().put(IndexMetaData.SETTING_INDEX_UUID, "foo").put(IndexMetaData.SETTING_VERSION_CREATED, version).build())); } @@ -311,8 +311,8 @@ public void testLuceneVersionIsSameOnMinorRelease() { } if (other.isAlpha() == false && version.isAlpha() == false && other.major == version.major && other.minor == version.minor) { - assertEquals(other.luceneVersion.major, version.luceneVersion.major); - assertEquals(other.luceneVersion.minor, version.luceneVersion.minor); + assertEquals(version + " vs. " + other, other.luceneVersion.major, version.luceneVersion.major); + assertEquals(version + " vs. " + other, other.luceneVersion.minor, version.luceneVersion.minor); // should we also assert the lucene bugfix version? } } @@ -326,11 +326,12 @@ public static void assertUnknownVersion(Version version) { public void testIsCompatible() { assertTrue(isCompatible(Version.CURRENT, Version.CURRENT.minimumCompatibilityVersion())); - assertTrue(isCompatible(Version.V_5_0_0, Version.V_6_0_0_alpha2_UNRELEASED)); + assertTrue(isCompatible(Version.V_5_5_0_UNRELEASED, Version.V_6_0_0_alpha2_UNRELEASED)); assertFalse(isCompatible(Version.fromId(2000099), Version.V_6_0_0_alpha2_UNRELEASED)); assertFalse(isCompatible(Version.fromId(2000099), Version.V_5_0_0)); } + public boolean isCompatible(Version left, Version right) { boolean result = left.isCompatible(right); assert result == right.isCompatible(left); diff --git a/core/src/test/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsResponseTests.java b/core/src/test/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsResponseTests.java index 5181e943c2df4..90eb7cdcfd46a 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsResponseTests.java +++ b/core/src/test/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsResponseTests.java @@ -93,7 +93,7 @@ public void testSerialization() throws Exception { assertEquals(clusterSearchShardsGroup.getShardId(), deserializedGroup.getShardId()); assertArrayEquals(clusterSearchShardsGroup.getShards(), deserializedGroup.getShards()); } - if (version.onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (version.onOrAfter(Version.V_5_1_1)) { assertEquals(clusterSearchShardsResponse.getIndicesAndFilters(), deserialized.getIndicesAndFilters()); } else { assertNull(deserialized.getIndicesAndFilters()); diff --git a/core/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequestTests.java b/core/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequestTests.java index 5f3e107942178..aec8349ea8d51 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequestTests.java @@ -51,7 +51,7 @@ public void testSerialization() throws IOException { public void testSerializationBwc() throws IOException { final byte[] rawStreamBytes = Base64.getDecoder().decode("ADwDCG11c3RhY2hlAQZzY3JpcHQCe30A"); final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, - Version.V_5_0_3_UNRELEASED, Version.V_5_1_1_UNRELEASED, Version.V_5_1_2_UNRELEASED, Version.V_5_2_0_UNRELEASED); + Version.V_5_1_1, Version.V_5_1_2, Version.V_5_2_0); try (StreamInput in = StreamInput.wrap(rawStreamBytes)) { in.setVersion(version); PutStoredScriptRequest serialized = new PutStoredScriptRequest(); diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestTests.java b/core/src/test/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestTests.java index 590eba3666610..97e1bf2930377 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestTests.java @@ -51,8 +51,8 @@ public void testSerialization() throws IOException { public void testSerializationBwc() throws IOException { final byte[] data = Base64.getDecoder().decode("ADwDAANmb28APAMBB215X3R5cGULeyJ0eXBlIjp7fX0AAAD////+AA=="); - final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, - Version.V_5_0_3_UNRELEASED, Version.V_5_1_1_UNRELEASED, Version.V_5_1_2_UNRELEASED, Version.V_5_2_0_UNRELEASED); + final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, Version.V_5_1_1, Version.V_5_1_2, + Version.V_5_2_0); try (StreamInput in = StreamInput.wrap(data)) { in.setVersion(version); CreateIndexRequest serialized = new CreateIndexRequest(); diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/mapping/put/PutMappingRequestTests.java b/core/src/test/java/org/elasticsearch/action/admin/indices/mapping/put/PutMappingRequestTests.java index fd7f830e59230..2870b04fdb747 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/mapping/put/PutMappingRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/mapping/put/PutMappingRequestTests.java @@ -94,7 +94,7 @@ public void testPutMappingRequestSerialization() throws IOException { public void testSerializationBwc() throws IOException { final byte[] data = Base64.getDecoder().decode("ADwDAQNmb28MAA8tLS0KZm9vOiAiYmFyIgoAPAMAAAA="); final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, - Version.V_5_0_3_UNRELEASED, Version.V_5_1_1_UNRELEASED, Version.V_5_1_2_UNRELEASED, Version.V_5_2_0_UNRELEASED); + Version.V_5_1_1, Version.V_5_1_2, Version.V_5_2_0); try (StreamInput in = StreamInput.wrap(data)) { in.setVersion(version); PutMappingRequest request = new PutMappingRequest(); diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequestTests.java b/core/src/test/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequestTests.java index 48b2ae79cf39c..453efb2a6059f 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequestTests.java @@ -89,7 +89,7 @@ public void testPutIndexTemplateRequestSerializationXContent() throws IOExceptio public void testPutIndexTemplateRequestSerializationXContentBwc() throws IOException { final byte[] data = Base64.getDecoder().decode("ADwDAANmb28IdGVtcGxhdGUAAAAAAAABA2Jhcg8tLS0KZm9vOiAiYmFyIgoAAAAAAAAAAAAAAAA="); final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, - Version.V_5_0_3_UNRELEASED, Version.V_5_1_1_UNRELEASED, Version.V_5_1_2_UNRELEASED, Version.V_5_2_0_UNRELEASED); + Version.V_5_1_1, Version.V_5_1_2, Version.V_5_2_0); try (StreamInput in = StreamInput.wrap(data)) { in.setVersion(version); PutIndexTemplateRequest request = new PutIndexTemplateRequest(); diff --git a/core/src/test/java/org/elasticsearch/action/fieldstats/FieldStatsRequestTests.java b/core/src/test/java/org/elasticsearch/action/fieldstats/FieldStatsRequestTests.java index f1cbaa9df3bde..309e8445054ef 100644 --- a/core/src/test/java/org/elasticsearch/action/fieldstats/FieldStatsRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/fieldstats/FieldStatsRequestTests.java @@ -113,7 +113,7 @@ public void testFieldStatsBWC() throws Exception { FieldStatsShardResponse deserialized = new FieldStatsShardResponse(); deserialized.readFrom(input); final Map> expected; - if (version.before(Version.V_5_2_0_UNRELEASED)) { + if (version.before(Version.V_5_2_0)) { expected = deserialized.filterNullMinMax(); } else { expected = deserialized.getFieldStats(); diff --git a/core/src/test/java/org/elasticsearch/action/index/IndexRequestTests.java b/core/src/test/java/org/elasticsearch/action/index/IndexRequestTests.java index 4fb1d0c648ea2..73a44ff145cdb 100644 --- a/core/src/test/java/org/elasticsearch/action/index/IndexRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/index/IndexRequestTests.java @@ -178,7 +178,7 @@ public void testIndexRequestXContentSerialization() throws IOException { public void testIndexRequestXContentSerializationBwc() throws IOException { final byte[] data = Base64.getDecoder().decode("AAD////+AgQDZm9vAAAAAQNiYXIBATEAAAAAAnt9AP/////////9AAAA//////////8AAAAAAAA="); final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, - Version.V_5_0_3_UNRELEASED, Version.V_5_1_1_UNRELEASED, Version.V_5_1_2_UNRELEASED, Version.V_5_2_0_UNRELEASED); + Version.V_5_1_1, Version.V_5_1_2, Version.V_5_2_0); try (StreamInput in = StreamInput.wrap(data)) { in.setVersion(version); IndexRequest serialized = new IndexRequest(); diff --git a/core/src/test/java/org/elasticsearch/action/ingest/PutPipelineRequestTests.java b/core/src/test/java/org/elasticsearch/action/ingest/PutPipelineRequestTests.java index 01aed87947acf..37a15c5b3592f 100644 --- a/core/src/test/java/org/elasticsearch/action/ingest/PutPipelineRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/ingest/PutPipelineRequestTests.java @@ -49,7 +49,7 @@ public void testSerializationWithXContent() throws IOException { public void testSerializationBwc() throws IOException { final byte[] data = Base64.getDecoder().decode("ADwDATECe30="); final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, - Version.V_5_0_3_UNRELEASED, Version.V_5_1_1_UNRELEASED, Version.V_5_1_2_UNRELEASED, Version.V_5_2_0_UNRELEASED); + Version.V_5_1_1, Version.V_5_1_2, Version.V_5_2_0); try (StreamInput in = StreamInput.wrap(data)) { in.setVersion(version); PutPipelineRequest request = new PutPipelineRequest(); diff --git a/core/src/test/java/org/elasticsearch/action/ingest/SimulatePipelineRequestTests.java b/core/src/test/java/org/elasticsearch/action/ingest/SimulatePipelineRequestTests.java index e3ca936bb84f2..ecd0256b11068 100644 --- a/core/src/test/java/org/elasticsearch/action/ingest/SimulatePipelineRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/ingest/SimulatePipelineRequestTests.java @@ -74,7 +74,7 @@ public void testSerializationWithXContent() throws IOException { public void testSerializationWithXContentBwc() throws IOException { final byte[] data = Base64.getDecoder().decode("AAAAAnt9AAA="); final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, - Version.V_5_0_3_UNRELEASED, Version.V_5_1_1_UNRELEASED, Version.V_5_1_2_UNRELEASED, Version.V_5_2_0_UNRELEASED); + Version.V_5_1_1, Version.V_5_1_2, Version.V_5_2_0); try (StreamInput in = StreamInput.wrap(data)) { in.setVersion(version); SimulatePipelineRequest request = new SimulatePipelineRequest(); diff --git a/core/src/test/java/org/elasticsearch/action/termvectors/TermVectorsUnitTests.java b/core/src/test/java/org/elasticsearch/action/termvectors/TermVectorsUnitTests.java index e034cff3f1de0..2018218cc5456 100644 --- a/core/src/test/java/org/elasticsearch/action/termvectors/TermVectorsUnitTests.java +++ b/core/src/test/java/org/elasticsearch/action/termvectors/TermVectorsUnitTests.java @@ -269,7 +269,7 @@ public void testStreamRequest() throws IOException { public void testStreamRequestWithXContentBwc() throws IOException { final byte[] data = Base64.getDecoder().decode("AAABBWluZGV4BHR5cGUCaWQBAnt9AAABDnNvbWVQcmVmZXJlbmNlFgAAAAEA//////////0AAAA="); final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, - Version.V_5_0_3_UNRELEASED, Version.V_5_1_1_UNRELEASED, Version.V_5_1_2_UNRELEASED, Version.V_5_2_0_UNRELEASED); + Version.V_5_1_1, Version.V_5_1_2, Version.V_5_2_0); try (StreamInput in = StreamInput.wrap(data)) { in.setVersion(version); TermVectorsRequest request = new TermVectorsRequest(); diff --git a/core/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeTests.java b/core/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeTests.java index 548f9d407ccc1..91b7a18236f68 100644 --- a/core/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeTests.java @@ -76,8 +76,10 @@ public void testDiscoveryNodeSerializationToOldVersion() throws Exception { assertEquals(transportAddress.getAddress(), serialized.getHostAddress()); assertEquals(transportAddress.getAddress(), serialized.getAddress().getAddress()); assertEquals(transportAddress.getPort(), serialized.getAddress().getPort()); - assertFalse("if the minimum compatibility version moves past 5.0.3, remove the special casing in DiscoverNode(StreamInput) and " + - "the TransportAddress(StreamInput, String) constructor", - Version.CURRENT.minimumCompatibilityVersion().onOrAfter(Version.V_5_0_3_UNRELEASED)); + assertFalse("if the minimum index compatibility version moves past 5.0.3, remove the special casing in DiscoverNode(StreamInput)" + + " and the TransportAddress(StreamInput, String) constructor", + Version.CURRENT.minimumIndexCompatibilityVersion().after(Version.V_5_0_2)); + // serialization can happen from an old cluster-state in a full cluster restart + // hence we need to maintain this until we drop index bwc } } diff --git a/core/src/test/java/org/elasticsearch/cluster/serialization/ClusterSerializationTests.java b/core/src/test/java/org/elasticsearch/cluster/serialization/ClusterSerializationTests.java index 4e77741694e7b..3b2fb365ca8c9 100644 --- a/core/src/test/java/org/elasticsearch/cluster/serialization/ClusterSerializationTests.java +++ b/core/src/test/java/org/elasticsearch/cluster/serialization/ClusterSerializationTests.java @@ -139,7 +139,7 @@ public void testSnapshotDeletionsInProgressSerialization() throws Exception { // serialize with old version outStream = new BytesStreamOutput(); - outStream.setVersion(Version.CURRENT.minimumCompatibilityVersion()); + outStream.setVersion(Version.CURRENT.minimumIndexCompatibilityVersion()); diffs.writeTo(outStream); inStream = outStream.bytes().streamInput(); inStream = new NamedWriteableAwareStreamInput(inStream, new NamedWriteableRegistry(ClusterModule.getNamedWriteables())); diff --git a/core/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java b/core/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java index b67000e2b2313..34a48862e18c9 100644 --- a/core/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java +++ b/core/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java @@ -805,7 +805,7 @@ public void testVLong() throws IOException { } assertTrue("If we're not compatible with 5.1.1 we can drop the assertion below", - Version.CURRENT.minimumCompatibilityVersion().onOrBefore(Version.V_5_1_1_UNRELEASED)); + Version.CURRENT.minimumIndexCompatibilityVersion().onOrBefore(Version.V_5_1_1)); /* Read -1 as serialized by a version of Elasticsearch that supported writing negative numbers with writeVLong. Note that this * should be the same test as the first case (when value is negative) but we've kept some bytes so no matter what we do to * writeVLong in the future we can be sure we can read bytes as written by Elasticsearch before 5.1.2 */ diff --git a/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java b/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java index b961b6d6fbdbe..65856add565b2 100644 --- a/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java +++ b/core/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java @@ -361,8 +361,8 @@ public void testValidateOnUnsupportedIndexVersionCreated() throws Exception { IllegalStateException ex = expectThrows(IllegalStateException.class, () -> request.messageReceived(new MembershipAction.ValidateJoinRequest(stateBuilder.build()), null)); assertEquals("index [test] version not supported: " - + VersionUtils.getPreviousVersion(Version.CURRENT.minimumCompatibilityVersion()) - + " minimum compatible index version is: " + Version.CURRENT.minimumCompatibilityVersion(), ex.getMessage()); + + VersionUtils.getPreviousVersion(Version.CURRENT.minimumIndexCompatibilityVersion()) + + " minimum compatible index version is: " + Version.CURRENT.minimumIndexCompatibilityVersion(), ex.getMessage()); } else { AtomicBoolean sendResponse = new AtomicBoolean(false); request.messageReceived(new MembershipAction.ValidateJoinRequest(stateBuilder.build()), new TransportChannel() { diff --git a/core/src/test/java/org/elasticsearch/fieldstats/FieldStatsTests.java b/core/src/test/java/org/elasticsearch/fieldstats/FieldStatsTests.java index ee1f654642ce7..2fa56fa34a0e0 100644 --- a/core/src/test/java/org/elasticsearch/fieldstats/FieldStatsTests.java +++ b/core/src/test/java/org/elasticsearch/fieldstats/FieldStatsTests.java @@ -606,7 +606,7 @@ public void testMetaFieldsNotIndexed() { public void testSerialization() throws IOException { for (Version version : new Version[] {Version.CURRENT, Version.V_5_0_1}){ for (int i = 0; i < 20; i++) { - assertSerialization(randomFieldStats(version.onOrAfter(Version.V_5_2_0_UNRELEASED)), version); + assertSerialization(randomFieldStats(version.onOrAfter(Version.V_5_2_0)), version); } } } diff --git a/core/src/test/java/org/elasticsearch/index/IndexSortSettingsTests.java b/core/src/test/java/org/elasticsearch/index/IndexSortSettingsTests.java index af3fdf9adbc24..74ec1cc02d93f 100644 --- a/core/src/test/java/org/elasticsearch/index/IndexSortSettingsTests.java +++ b/core/src/test/java/org/elasticsearch/index/IndexSortSettingsTests.java @@ -152,7 +152,7 @@ public void testInvalidVersion() throws IOException { .put("index.sort.field", "field1") .build(); IllegalArgumentException exc = - expectThrows(IllegalArgumentException.class, () -> indexSettings(settings, Version.V_5_4_0_UNRELEASED)); + expectThrows(IllegalArgumentException.class, () -> indexSettings(settings, Version.V_5_4_0)); assertThat(exc.getMessage(), containsString("unsupported index.version.created:5.4.0, " + "can't set index.sort on versions prior to 6.0.0-alpha1")); diff --git a/core/src/test/java/org/elasticsearch/index/analysis/PreBuiltAnalyzerTests.java b/core/src/test/java/org/elasticsearch/index/analysis/PreBuiltAnalyzerTests.java index 214515d1702ca..b20972adeda08 100644 --- a/core/src/test/java/org/elasticsearch/index/analysis/PreBuiltAnalyzerTests.java +++ b/core/src/test/java/org/elasticsearch/index/analysis/PreBuiltAnalyzerTests.java @@ -63,8 +63,8 @@ public void testThatInstancesAreCachedAndReused() { assertSame(PreBuiltAnalyzers.ARABIC.getAnalyzer(Version.CURRENT), PreBuiltAnalyzers.ARABIC.getAnalyzer(Version.CURRENT)); // same lucene version should be cached - assertSame(PreBuiltAnalyzers.ARABIC.getAnalyzer(Version.V_5_2_2_UNRELEASED), - PreBuiltAnalyzers.ARABIC.getAnalyzer(Version.V_5_2_3_UNRELEASED)); + assertSame(PreBuiltAnalyzers.ARABIC.getAnalyzer(Version.V_5_2_1), + PreBuiltAnalyzers.ARABIC.getAnalyzer(Version.V_5_2_2)); assertNotSame(PreBuiltAnalyzers.ARABIC.getAnalyzer(Version.V_5_0_0), PreBuiltAnalyzers.ARABIC.getAnalyzer(Version.V_5_0_1)); diff --git a/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java b/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java index 7dc94d972c02b..e72f68e1d2b4c 100644 --- a/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java +++ b/core/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java @@ -1486,7 +1486,7 @@ public void testOutOfOrderDocsOnReplicaOldPrimary() throws IOException { IndexSettings oldSettings = IndexSettingsModule.newIndexSettings("testOld", Settings.builder() .put(IndexSettings.INDEX_GC_DELETES_SETTING.getKey(), "1h") // make sure this doesn't kick in on us .put(EngineConfig.INDEX_CODEC_SETTING.getKey(), codecName) - .put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_5_4_0_UNRELEASED) + .put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_5_4_0) .put(MapperService.INDEX_MAPPING_SINGLE_TYPE_SETTING.getKey(), true) .put(IndexSettings.MAX_REFRESH_LISTENERS_PER_SHARD.getKey(), between(10, 10 * IndexSettings.MAX_REFRESH_LISTENERS_PER_SHARD.get(Settings.EMPTY))) diff --git a/core/src/test/java/org/elasticsearch/index/mapper/MapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/MapperTests.java index b42bda0a5a38b..72b1c95d8bd02 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/MapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/MapperTests.java @@ -56,7 +56,7 @@ public void testExceptionForIncludeInAll() throws IOException { "As a replacement, you can use an [copy_to] on mapping fields to create your own catch all field.", e.getMessage()); - settings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_5_3_0_UNRELEASED).build(); + settings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_5_3_0).build(); // Create the mapping service with an older index creation version final MapperService oldMapperService = MapperTestUtils.newMapperService(xContentRegistry(), createTempDir(), settings, "test"); diff --git a/core/src/test/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilderTests.java index d19e8e32ffa06..ec34f6d87e353 100644 --- a/core/src/test/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilderTests.java @@ -336,7 +336,7 @@ public void testItemFromXContent() throws IOException { public void testItemSerializationBwc() throws IOException { final byte[] data = Base64.getDecoder().decode("AQVpbmRleAEEdHlwZQEODXsiZm9vIjoiYmFyIn0A/wD//////////QAAAAAAAAAA"); final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, - Version.V_5_0_3_UNRELEASED, Version.V_5_1_1_UNRELEASED, Version.V_5_1_2_UNRELEASED, Version.V_5_2_0_UNRELEASED); + Version.V_5_1_1, Version.V_5_1_2, Version.V_5_2_0); try (StreamInput in = StreamInput.wrap(data)) { in.setVersion(version); Item item = new Item(in); diff --git a/core/src/test/java/org/elasticsearch/index/refresh/RefreshStatsTests.java b/core/src/test/java/org/elasticsearch/index/refresh/RefreshStatsTests.java index 91ac42628e62b..27221b0af99d7 100644 --- a/core/src/test/java/org/elasticsearch/index/refresh/RefreshStatsTests.java +++ b/core/src/test/java/org/elasticsearch/index/refresh/RefreshStatsTests.java @@ -34,15 +34,4 @@ protected RefreshStats createTestInstance() { protected RefreshStats createBlankInstance() { return new RefreshStats(); } - - public void testPre5Dot2() throws IOException { - // We can drop the compatibility once the assertion just below this list fails - assertTrue(Version.CURRENT.minimumCompatibilityVersion().before(Version.V_5_2_0_UNRELEASED)); - - RefreshStats instance = createTestInstance(); - RefreshStats copied = copyInstance(instance, Version.V_5_1_1_UNRELEASED); - assertEquals(instance.getTotal(), copied.getTotal()); - assertEquals(instance.getTotalTimeInMillis(), copied.getTotalTimeInMillis()); - assertEquals(0, copied.getListeners()); - } } diff --git a/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java index 982198c8fee47..e2bf25ce1b531 100644 --- a/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java +++ b/core/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java @@ -75,7 +75,7 @@ public static void assertTaskStatusEquals(Version version, BulkByScrollTask.Stat assertEquals(expected.getRequestsPerSecond(), actual.getRequestsPerSecond(), 0f); assertEquals(expected.getReasonCancelled(), actual.getReasonCancelled()); assertEquals(expected.getThrottledUntil(), actual.getThrottledUntil()); - if (version.onOrAfter(Version.V_5_1_1_UNRELEASED)) { + if (version.onOrAfter(Version.V_5_1_1)) { assertThat(actual.getSliceStatuses(), Matchers.hasSize(expected.getSliceStatuses().size())); for (int i = 0; i < expected.getSliceStatuses().size(); i++) { BulkByScrollTask.StatusOrException sliceStatus = expected.getSliceStatuses().get(i); diff --git a/core/src/test/java/org/elasticsearch/ingest/PipelineConfigurationTests.java b/core/src/test/java/org/elasticsearch/ingest/PipelineConfigurationTests.java index f41d01b32c89d..6ca6b0ea8c279 100644 --- a/core/src/test/java/org/elasticsearch/ingest/PipelineConfigurationTests.java +++ b/core/src/test/java/org/elasticsearch/ingest/PipelineConfigurationTests.java @@ -54,9 +54,9 @@ public void testSerialization() throws IOException { public void testSerializationBwc() throws IOException { final byte[] data = Base64.getDecoder().decode("ATECe30AAAA="); - final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, - Version.V_5_0_3_UNRELEASED, Version.V_5_1_1_UNRELEASED, Version.V_5_1_2_UNRELEASED, Version.V_5_2_0_UNRELEASED); try (StreamInput in = StreamInput.wrap(data)) { + final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, + Version.V_5_1_1, Version.V_5_1_2, Version.V_5_2_0); in.setVersion(version); PipelineConfiguration configuration = PipelineConfiguration.readFrom(in); assertEquals(XContentType.JSON, configuration.getXContentType()); diff --git a/core/src/test/resources/indices/bwc/index-5.2.2.zip b/core/src/test/resources/indices/bwc/index-5.2.2.zip new file mode 100644 index 0000000000000000000000000000000000000000..63f7e72821d2a54c0f18fdbcebed53a9ebcee54e GIT binary patch literal 503810 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%0A)H783J{q0L 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�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 literal 0 HcmV?d00001 diff --git a/core/src/test/resources/indices/bwc/index-5.3.2.zip b/core/src/test/resources/indices/bwc/index-5.3.2.zip new file mode 100644 index 0000000000000000000000000000000000000000..f0bed79b790aff978c5a8a64a28b5d5e7a925250 GIT binary patch literal 399870 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�cdck-K49g*dD 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

v*-{<^kQ*A6)FZ;KDMCdsaKi+8HIGE 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 |2Js3rPFk;mt`oHbdk$03Xg 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�fXYQVtNyAL?N_f_pOq;Z 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

)Iug+P)pTWsBNXU=&y@(gAyy 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

x}`#2D6K!WSBvzm!28^|TQwvMW@@h# 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

D6o+%aoqpsHY*M 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

E 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%0DJ{YcK9RfBJ*Bew%ox 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

)I#PD6QbxNlEdxUbTD<>yC# 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

4I00Br>Kh>U3&eG$!zd!){$MQ(_7f0P_2% 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 |2J zs3rPFk;mt`oHbdk$03XgyX`oSLXdErhJOn!3QT4 zMns-RD92Dnl+Y<&h8rOBOqsM(W8-HFMF}gfSC(@!v#s_Nku;J|AzmWE8|bh1ZZw9) zk3D(i(tV2U^XUro0M;h{4#l59;Yxjq*ebaePo0YIsi zu@vj6k~r-Y>PrD}HsEoug)eyuz9hWWV|1#oc|H96D~=(aiU3s_E4Pq4;_C~^9vwQXlUb#RYIkTto z`9b>ww>wp6yJR{Z3Ik2&f;ym6dX5BA@IHFvC#iigtJEJVj4UxC@6=aM%)%O7VIWLr3qpS%TJniPd4s-Y zlv~}Za#Ac1r&CdPvjKbZzs5+c$EQqQ(?a-Dxqaq^-+W^{Ut%4iDg`}^VG6LKGX(mt zq^4V;(O6_v^;t)mTXu##5{t322)Y$MM;tV_92ISuvGbXUr++`GI%wWJp_T9xR&OEdO0sj^C4dqOxWD z-t$SwJpQW%wyp8n5GtA2$vX$dboj=qo~Oy=J=HuX!)pW_B9Y-oco;+DSZEVn{C!ej z)5g@vPcz#RGhSu6pyq8`_uV+`M^Le!A<^K&xHLk$!DFe}Wgd_p2mDqxr=ghzDW8wS z&Rj?rvu15mLeDCRn=9@|X8p~(Y90=~55W_N&`zU^HxEZ23BE8#>GVXD;h>B!b6Elb zVcZdtgVYzcpIvD-GVB+8Sr^a2=Z8r*y|Q=`dg@*>6W7Ls1glPA2N*!c_G&~UhTvL3Xb`^DiSkt%*u~LIq<_hF-wIUcZ=2TPh*a+eKcsK?)T6aZ~3qRcc z(oN_c*46W0C0qp?o}1M+0_iE8yd@y?)k=;=DrZzD&BX-U?2Gam8gY3E4#b)jWH=0% z<+tA6cHhExR(yNt%9mrlyZW{5N5SfUgvLPIx+F(I_5qE!A|g-B5|4~?Mj74*z3pofRFi_o?)-B&Y_E(w#^DS%4UVuv{= z&{$**kK3N|=M&z7S(*Qh1XkRXLEGUk0`rgE|MKpySMM2D zD$n?OQo0kj3Owd^DzcDF`GYiCG#jPg0!HCGL>VtDBIpr<8s3+ZTFM%p7K)%1De&{c z{+k(_Za)6y!>%^dK;|XOBS_4RFFC> zLQ&eA6em(LS!y<jQL;G9L1NFpgldslz+(oTIqY>9R-eov4_9Zqbspv`hpYTNTi{&@QAmQS%U z+~HJOm-ryaD!-?L6UkY(*;PJaD(2K!bv!Q1;ggE?fO+C#gO>_ZuCprD()$SSUbpha zixaOu0WIeH+ZbKKD=jo1N*WXnn7Cm3_8WIx z7jAqh#wMJky`w^zT#Ui6kUGWi@`{xQ8(HIH>-BZ3x@OLE`TTMNb_)uiJrul5xbz>M zH#%$l8p}w0;;Y+#eIFe3;?J-#-E^9t=7Ku6QaQEvMZ!4b9n)I?ByR8zIsN@p8N@rCTrsByIIeLi*BBgrqhvR9V$dBLFI)>7^ zX!jGScN|71GCvrLXFk>rYR75#0iq+9kUN z>mG+;z3t$;f*ASdw?E$h-q_clvM!(Z^U$`(2kfMFsR%=^#<0i0myNY{a`%9T;YgFZ ziZc{5Y6ZT$-l~&Dlisb}7aZ>woBg|^o2UQ>!rKcZ_GJ#wj()a#<=Y6#kdas zL6kWa57M|!Ds3@tfXGg|%{;f;70{RRQZ_Fe3N-kWnAix>!{k;$?CQ(tKSI+tE@yo8 zl=FW-UU>SYf*Pg%Ic{|B+6i646CkDha@YVVri-u(hPXcKlE{MANF!z_Ii$}*G23f9 zE|Ub1iaLMk&dr;3Yb{9n{I>Z|4LNrewL_9@MI9)1C)mC=61|z4YFW-Or$;G`s)aq8 zD&HEAX&Y$*7^jU0#{o-a{Jd2)VB~`6!&>SL!W9M&W`#~n29dN zK`gCFFlkWe_mKyPNscGWQEBr=ccjQm=?(s%mHlitHiFL|2A={fl6zE-@0XpN?s@84 zxjypzzE6?DtI@1H=&h)Np9=E7Qnnh7WJOYg-{^OiWkQ?Y zTM+G{lF6G#!-oLzY}>8h_srcf<-Tvs`=zrVICZKIp)lLf<7msOpMOQSTxAUX>O159^)v0&-WTa$p&b z!qj(xdIKbGlCQv8^*fS{U?va@sg35Gi){-=BODD>QsK6?4Yp55CjXI*uFl&(T5s8( zIsd{{-(r+MN%Uz<#&QOI^rQh&U8nXH*lD*)FO;Zy%35K%p{zXy${t&@b_NUV?FHVa zF1lJDyKU&TnqxOCdh0OSAp)VSkA`$fA0X2gUo}8VN9(n!R#MHmB*C1+Vem*bWk-rY z#vU9$Xwvb(mHg}T*y}B$){kF4=aVfuFoqz|LVtkV1@dD0w6P#X(o3>Rd7fKP#zKY! zUl?VlJh4f5GMPra0-ggbu>-x6XjfG>OFE}czc}yWi23UgClPX0!kn{^$k~msK3G5HN|O`+h4w)Ih|~QaSB5@1^vS7h(~<;=oy5pp z-6c4J(qD&MDoMo2cPM&-epX!}*N3fHy+r{@k%ypXq%L8*<~! zq8Zys?GS>4-%5f-_iYsBR3^fa2xqWp!-H!$px{thYCeTBU)S<$o}$36%d`FEPz!m2 zL;`bk0Yst@#k04JJ^A+Sr~PlVb@zVr5=d@FQ)y$loRtXmn_-<$c$p+i6{J3k(~|Ky zK?+*ayGuT!6+YLUI5-NJD4c%BEk{0nk})h_;MHj`5d7`Fm)6&M}Ur{ zEItn&>2|5lD-@SYI*!@qs#Ya#hrPnjfg|wZ5I720xSB6V9)B%8PGPlQzTz66{a%Gq zh7ghEI7qzsFrh$Nt(ncklSw zdBtz@J+r6#w)P>|gJkA~At-a(Xq2`OZ01D9#AfHBJ_m>ISF*A4>lD_?((~^gD9s(RdOgy?%@Y_-661MdCl@w%Cb*u6fiLmbygGK; z$u&KPWByvy|%YhK5j#aRzrPYJXN+mE2sDR}VbySOLmqq$6Q28$)t zAP6I6nNyb2dNiCkHz@}5(k|>9>|w<3WM?-$`~qRuy2_GIM~8kYxmG8!H`F|LoTsfQQe{rI;NFAB$aLZ*66{8+x9vI8x%eAzwdLXQRy)1G$x`zX@8Qsq_EQn58_r;u4frDg<{IB7i7)O3eQ<$zrn{wNbb4{EDqR6 z^7@duGm=l;Yp<{Q{I_qeoz=IJK~#YdXV{5mt~Wi)Xx2dj&49&ULZopfKG{j2$&m*x^C`q zMSGlPfk|018}w{DFArAgm>KY4zyi7J&4O4s<|BTcb1i*x?b}31 zq>aDbvvQ62<;}P0?l?!H-Zhc#Ahk-KL#SQUPVVz<0|Zf;t4&p0WoOwD&ep6T_|#Wk zMHvBBDwtx})PL$m&3#9wn?GOMi@)&IZT?*|QOdJKWF;~(ed*fK5*m^I9Yp6N26ozG zDLCV>|L;Kz5CrKT=`3&Ys657QrP&)Zauu_z&uwJCk)@li( zT<&t}9bSVQnt);_NrS!OG>z`WBd6c$ev_!&t1CXRT!nT>j-vFP_%7jk1Rh{STVC&u zJL-Cq*RHY2e7tBVl56ll279QZRd85z8I|AXT0Bk1EvsjfM7O`UxhyBO^NYiozHw+5 zZz#1>+9Z-Vde|(Rx5_Ih6cSHJl=qtgRXGX7lM`suF>~*0b+R%>Io~Np@`?{I!X9_V+?}%>K0y+k`*r zT@O*sb^RN`->+Nuug@mt@r>OLyE@k)UEOkYx&+cQB^=20wAeDyaz4lQ zv5P5@I4kzmMVXqdA0bVYLcN-%XtI>jyZzBwp-V@8e&uqgYtx&3$RrV8jy73>DzsIy z4~1I&Qb@bdH?#?Ls$vM2WsRscV&`fD9;mZ6Nh0Y(E|S2cPrCZh2*dK~e%@{8@BO^` zAQxS71f+6GbdVVPJc6A<#|ZDlAvDPI62`R-Mbaqd@O(}-%OOoh67J-kIPxR`cjKTY z=drd=sl3adMTZU?*|GA(11r$Q8xb&9_apc_abvnSA~>dQkYPoRH;idP%wQ2qtjb!Q zXOEcWvUKfdlr&My-|(+T^wRF5hj4fQzI*rX(f8K(&wl7%m+vc;`&{|X#=q)3i75y?kaY-N-xFN9(r zzJG^&=)nj&{}?)!51FyYP-H(!c?=mX{SeRMH`~$FJ=uVmWngENA-Bn>bA(GSz2q_Q z?ZtwBS)(JR-~N7R=fmRRoNYs@?@-PhL?;O)z5fM&ckw3SI|UG736%Vzh%BY3v$RoT zv=WGjWuC$!94LeT)tXJd+W=)in!i)0;NCm<rJ6>-ebBe2w=lf--YK}`YlswFSR9;d!d+y2{6hMA0a4x+HiMzS~v ztO*XH^m*WG$0Aa3KAL5v0&ZE+tj*^n;<$Y*s!Q-VrSGOFa)FeeLhyza+jsYX?XADZ20o zO4&q&?cxj`#u9R)AYF+B9cER7ZAuF*;!M@73Dn%e#?K@$3~Zc(9NoqXD~GZ_d}!5c zkDgVhh>bl_LMu;z;tvfv)-n-@|A_Cv4X%N@oKwpx0ySf;stc&g8jdH*;Wl-eKgR|Y zJKi46Su^ojcFUgl9Q7x$b6-rtw@Xfvn3M46bRJwvA|3cnVO}Zh^k+S)nxc}DyOfPs zRtfDpCi28j{j1thON}-?^5sxu>wkvh->2*@fb>U9A);)orA`^cn}%ZzW(S zP}o;T|EFbuRM6Vl?yycBws49jTinR<;{tJ7A1kFza+Au%FFE9^$k-&2v>&+u5%es) z!9l3~_JP*w!<5PIzVN{mhHSXy{48{=K=c|OKMRH5nmG$aSbP}GNuqk*A(z`-T9%^W z?#ZWm>Mlmhd&dWn3TX^RRuLXA9IcL`|zkQsD?UHXA zSW(?zBtd;u<~#<%_yiktA5+GpVP&B#;p@aKzB~Z3TD2_Ow3hn`9i&ty6wQ- z{~Q?c$X{>H?gb^qSSETMhJ^^zc}+9W1z_=5ZF-|RlFn4cEThwtNk;;#NV4g-U?2_N z4qT5j3_ls&x$`blVq@c($L;|&mOu{u6uZQClUY1bH<&4wAgR$-9rk>pAoArDiL}I{ zt3d%|{%6=Aq5AT&ce~EryJ_mH!TEg;y|e7Cckq-hCUY?k$NUsSX!n35&m&c&L_&Ys zRpsP0s(QZWP4HDvy8>Q@!#06A`#1djV8s6Sly+m{&H77iZ!#zI#c=|tDj;9;B#0Z! zah-hV*qm*I3@MQ(6b|G=4V%6u8X}Rww)q7zjDa6 ztN%d@1d0$t-k`SFNnQLN3Y`sxF*T}*sz?--8zqfKBPn9bQ;i~26AiV3^Z|TWKI_oV zt-XYuybmT{y3)bhI-A}uJVP4A0m-wNN$liL2ZQk4<&?*57xQ!qxmIsWMRjVw?_+3E zvX(YTF#mo|*yp_U>-h^h?;n5L0$)f5lGZ;+jH{Vl+wg$OYJ&9Y(&KyZha(z4N~rJ^8(tt`YnsI77nLA;=6A8;|eej>mU$p|(gs ztcqAQrHX{3Dm!CQc1B(b2G@f3nKo?DRWbEk*LA5GXJymP?qJ^F$;?plu_z@_xWm4`3jMgXhudMOKzDuNNp2iHNF^v6vfSg%0un zcTxu(@5VlM&*evTKS}!P#!Zp~Ybkf*DDx<=ZzD(O67?aSLg@7)F(kurekN9T29^24wBTxNTW%cyt@0T{#y(8Ma;k9G8&AL|(Mp;85KrqApNALPWpmml zF#EV#j$LlHsS0kLDs>V@r+=kMCVm!CEb@MSdmG_%!V`AFfBF&TG6ubi3!%PQV18vH zL0K9r1oL4{!J~?%R63hd(`17$q733$_^s3bqORWf$&+5qk=8r#qZU!xK{y}Xy@)bH zAQ(?=`gP@NDtW|UsWvn{h6t-D@Mo&(94N9VmBAa}$5FK2|ID8B`?sFD5KXo#qijZzMEFlM8$)wWX%;aI*ZSr5BQ8EduT3{?yE9tGFTg;1(wH}xVJvKkL) z&s642oOyXw@zT8?PB(py-tor}$~&W&9ums54?$*dIT&q#P?efYN{K3*)CBSotI;QfY^6e^NEFp_Pk@c3wXH)``ha4U?nP!ziqWN4ARGO58r*ux|K6`7ew*Y9#lnIg6k09(t^Iy z231A`XGttk6Qp*i>j9Bn>XA0U>1_9E%!Q=e4!buYeH)_G+8g{Q$GFeWI67hYyQ0ls zC@rLR>1`BdeK>4uKsrTnkWj^Pyq;psUu`IpzM9OLi8xDsp#th&NR2}-Hybq;u6+I( zQf`mr>s^FnjcYrO^&xGdD+I!`ZHzalUBZEuPT?{r8sPIsf;9t6;f^|0(vlrsH}GIY z;J-l~Y}PO>xhaQxgW~yY=yz@3tuuYQ4A&t%iZYi$tJY8_ODKd_o(;P+Tu+3!thkhG z*7g)7ey=3A7v{p_M_nSpUX8tDZhUu=@Y0xy;I^O7JaDBSUDrOM`?F!RJ@_u3p42IZ zL`zoHA~qL9f@+=Xma{$Dn3ClQtwk_w_8_g{(%IKUTVLKe+C~0z>HJv_mS&@41h0&s zcM0w$cS@k*SRqx8YW=kkhcAos%`zuD@8qfuqZowUVVUuy;pWk6IzP)_ePI94fA0Tt z$R0eUe>Ag7L0b7Q(K^Dzx|^IL9mkNC>PmW!E>nxB(wRb1Ur4A>j0DNJ5K0{<-+1mW z%Kkf_t?Qys@{)HwMWhf%BP$UsM(C0pMmr_*aRZn!-Ec*8Jb|!~S9^30hb=B}xN6sL z>n37%ldm-4)EPJ2Utjt1u_smDJMNQr4RIVs$qR7Ig;(Lm2q7Z~(t4vpfuyLeWQD$1 zT%a*j)TNv>)sJ9M_OA)2&U@^-r{_F=blKHE1-FiP|N2!eZIt~OvyX-!BU!;{^3I4> zmegVkg)>4`LZwY;*t)WnzheYO-bx%4RGjs@S@vG^i1v=It9N#PcK$q#*v`F|%=~s3 z!aNBUoOo8p0Lf($vQr{$QRgqF5_(atP`0L&5PcN=+rVu7ZQiQ&KVSXmk!6o7fJU|#8oH+qRTeVA@;`>r%9jhA2s1#z3JSsa&BDV|V z^yU^g?IN$Lj(_2vXLwcTdR|~jQoJdSzvGbkLLdk39 z!+NP{AOn`IOwCEi1U<;zn(~iNzVp$6kMYdn)l}yGu?YP&>HxV`t2la;1$Ipv_VXP+ zOOS1g=xiV3Nh4uN+5~oYx7_#PH9L3JeBV!G zI-8|-nVnBp3<)TJm@f-#Ax@820@7;`X@8&(HUPYIDf05>rM5#SU%I37vzb>;gZxi; zmV|;*?KxsAzZFllkUB-p{gY6MJt4O$mvHzcN{QXd^J?NyXc%NQtwK}RW%Sb3*ZSU3 z_Kkn!^sKg7k$0Ceh}8aebT1w`hhiI%=~CVzMyF&Wcq*MRz{-TmmO@c%kC>dkipOtG zWeDWql5=1vGZ<>zgKs?Yf%(ZdKB>R`^(#@+ea;~rqH#>-1_al|3$%1{;Vp?FWzB~w zW+RWSSFVMSF;9LC6N(Vc}|PXYVkB+%ai!Tpn#EM z%Jesi^Ix6v+RpIDv(G;G{t1*i9S=FWD{#!eh9LAmrw$ND@PJ6tFtV( zkv|F}&%GKx4D3K&CgQLAO3;7q%xkLNqlSA{lbMl`^e&+W?c^=T4GAVrcj(>9Mhqt==kv2&; zlkf|jsiz~%o=$}RDHG&la(z9l>;XkcLm_ddGt!9ET7`Yk(k--BA*1~=dc%-H`MRTz zURiiX8vXNk%NzUg?V>BBw(e&U>{C4RM+{+*#}5#F3YEyA_clBdUr?V>@Jwu*^;II7 z3^iY%(AYck?>O1**_&%mKeF)aefwvvL#SKZ>3$LunnLU810-#j8)pT~g?Q2uc3D_j zN!V4d6$s=Jyyc85L?P@99{qjbVt;Z8f35ZRrDwk2_DrYpr!n?1rwfE`5`EZcuqS=7 ztlMLC8p>Ku-qYa496^r_>KaN4t>CM`&fty9Cb>eNP5tED#`N|D6TeivkEdMef@W)s zLTjgl&^|y^7Y*WoOQ4kTR0=^d8j`z11u#Pt<=`qAvF){l8IKR(4v7yCJyR89sO^F` zX3%~GXHkOCDLf3@^el3ITw`X(qc)L7BA4@75pEe?op2KbFxhnI%M*s>EqhK6SfeUW2xY1K1uK7e+MFz#?xcUHqvELz9&*<2{N&ChRuS& zF=rI~7+{HXq(AJPk~y{JhnIevxX`!rVjn`8IhMJFh@(%!4+?n}3r4xjW!B5N`9xk_ z6>;oksel5?7r~@KQu@}uH&KO-><@03bLo>&w;y_%*d_+K#?!>PY@SO#eIF`2cJ}RV<)+WxjJ9-ezaNHz zHTE>IOS%k4e-k$7sS|llQW*|=id+weqe=3m7CD)Qk)}epSPEX~2JaJ}a=x!5|D0nY z-80WjSdUPi9N$b>xG4htWw1mlJWHg?F@&f~WylNeK{endTh5`A;s*c!GPSx&H@6V__9-98l+e(zmBsQh3e;kI=Am>mn@bjE} zg*KfIS(3bn&m427yx@gK@IPU~LBQT4FR5DcOCR(w?uk7tB8G2*kwA>%l_(U^>EeHg zrz^PwM5)o$V{33d?nXSN5i~R@hk*O!cnrIjF=#M)?nS<0y4y23JZi;~duIrX{Ybm$ zc_MRr8_2e&fjadhw5#$5rGa4GoQvtj9#b+=kvT(x7eJ=`5E%>tw%_mv|4zJa>cidY z-E-(=!hwSik=uni0_`C((>)cTLwz{Ak{@+?ox*fV!sW;Hyh6`T7;ohqM!8h0;$qAk>!jra3(CMPjG;9tKRa?PeppoGd2d ztV)V2;IgZn1QZJs+{3ttK`dk89VYhW(ALRwUp3Xgx_oXqx)l9k8bynuGw`$#MCPw_ zgz*|!aDs&2VM^OlF=3(+O%!S|liHs64J?(LyIO^ZB$v@c6YV_b_(czIn>+Q{`ET0p zjs^=RuU9B{>Mu;dMv7l; z1EYYoe-+`4%Wv=e{iW|Vzw_0RVSCd)67}XTG)jWKw)`1P`X|suQe0v%O5yiq22+tVjFSBbag+!os)gV=`|7lWd&;j?om~ zAd;1H=1NSLu*Vuzwt#yOBM;*q!aXo&N|4ci^z~L!pEfRFeFa z-pMVtLV{|>p>+yUo?yV~vlQYvPPOzChK=A)>V^v+yv}IKeWPcN_ibNw%c=GKhF|7D zJ-12CBmE;hW5?)D$Xg@ImAQl}FOGXS6}c}M3`Y_^lytnJSFG7YNU$f7h`cLwKEi?)`hPfPvFquS9WuBpfb0?`Pb255;Dv@<^s(eQ> z81h)?SUdj(VzUGq!JYnTPOA?qbwjqpnsGU-%DaVnh>ouLo zqD^`@WxXfiGb9tvSPOY1_}$Y@@72%VEbO}b?k?dAFFeq9%jQFt`Lrgwxqj`&{=VM+ z-VJ^IYvF$(KDl;%|DbDTANV==(T4R9;%xqKZ9n|khV_uK*}r~cQ-u~rYeBh5rNH6!QNI-GnlzfBp|}QzZUx;3h{hgTwz9 z+Y}Dz@#%NG=6JQkrIbvv7=CWQreGPf(U0u`O@PLt{A&(N%+!R zt6sWgKDwwMX_efM;I(KSLZ;wTt3dW~67A$K1;@U{q^t(`tXi5=$~X-<8OLUKM*9%* zL?OQqyfbY7k?i_CHSgJn3=@q{BA*Z(x1sa=nY>Ds@Hr)*#+fAL{_D=CpZ@TJp{XaCPZyKQArpt5 zmZOuTf_{W1N1@&{HBMl0o5Hwmo0wAyX9MnrKqFI%RH>9kY=`#K!bP}CWZ3QDVw`*K zn{PccNXX1P?vuTyLKh!IM~Zuq*6ur+Ox4ApP^S?Q+A;8os(ix{stlxl@-A4AsMR~%S1Zif2(4(aLVw?8up-yzvToSx0~Azjj==qM5F(yz%> z#zdf&wb(2{Yg}XG+t@|D8YNE@2#=zJGLE~@mOG!GB*m|u*>>0W%%3ML!?pAG;>UES z3CI}8O{9TCn3$>hJh^hB$HCS+Y`RLUVbiFW;jl>%TyIK=Jo?v&zOSD*{9&Jc*Sfvv z;?p)#D^G~xlLX`?Ja!13ArQ<)NAV%-k`Y9E5(#eDB#>qdeqPmH+whUNdvex|3^zhtPIOlF&p;LDZL;Nu82I;E@z8pXA)HLe42QvDHz!Do9#FMAFt##UA=zm*Bheyz(C@si1foKV>U|r8B~Hs zRaFvb$O46wO5h4)QmM34Vu81l(kG$RCD<<03|CTPuX<7@u|2WjKIfHd4xv~#9*GfJ zo*>Q;fgF7l2R@2aoDk-@Mq|KHu8E=rvBs74uxI^y=LC+Q^Dn>hxc!3!D}VCRYaO~T z2l_z9RHA@l0~w?t+yOBlp;hb6S|vV>hEohW6)u-3Sp`dRB3HBtGm(fz;KF5t(cZVF{c3j8MKu?N?TmuDm+!Eu}nBVL_j4672T;%h4>Z zc_)1BBKy6OoA!>F_voD~K(hZH9x4=d36G(4IEI%SaoaK+QN1P+*-VL;$SLC}kD(ZO z#J^IlH+vqCXRdjVdClxS>rQe6%su#a?jAgohoMcQ`lc3XiffAIY#EJ+Ut{@0R+B{} zt0qhk@KvFgn%Bpx9Anl~mi&C%%*gH!uU*H!a}R!u1sfarnh(sY@P?8`FULl{^@K@urdmKAw+^Fkviyph} zAlfdPg)$GKXqR|D)+vS*J#Qx2x>?or8o5>d|HbgM9u1}~9sil3eH8AF`^ z@+B@B`gpT)D79TwrXV;f$V^A`C`3qP9v~%54KvG^u5pWsilkgI>GF1NU>u%|y@S8f zyhmR4;mV75=D&P)>mT2(=)S*_>P0Xq9odgI+1Vg~fkne-fSfQu?4ci2^*7>ke%5U0scjR@$5D86W(yVV5<+>M zEJ!)31-XpZFge_Lr%0!bd(DZN3`~FK~>mI?0?uXvSkCC1t z(YhpyaHE78up4UxEepYJ3@e}>76{dk4oZ*lWXOMA{lv5DD6pm`9>L}KaS+9Avy&X@AEIM`uy)3 zdTyC>{J_zhi@Sb4LFy3QN9)Sn&L}#p$@W& z{H&QB73IA>{!&6L6Kw*e)WV^I7_`8`DZg3Qu$2svcvR`;2l>lz` zH%vi)7($tVXFNmg;!eQRXTk1S1+QN92%K5J)+7z^{VFx6@0zloal#;sTA%*hj_lC2 z_kQ^2mTy`;pWW1ljFCJ*qjgEQGY2pCSwW@2ZZ{buo_tkk1_yS`5zNAiQqjlQpi1|} zXWv&1rJhOVj_kiQgs{4IF^=*piD@Q*G>mf*rT#(c z=i~b!Nn5}eF^HY8Kd;TW&rW}3z30UC)+OTbRw88xmHB520-L;rTnp@azq6N943gq&Z%xq?ajjVt1GYV!u@KEgi&4uzT3YkM7&B|LnJ-ZEww{-u*>o z4<37!iR?q6huSjZUpio%I@vji5`Wb5)~1K_c?mmS$e$Ey$*G_m8N2 z@XW-|k;UxWW}zL@gD7JUzE%7@LS6W8Z%xb`&T;bWic%Ah39QmYpukT?;aBEfYQ55g z3+8_n|FrivT6F$T1Ht5$!}ES6Q8*ZL5jf9ypJJWRr-2k_=|irtATP|=6Haz0riyww zC5T$={nt=x>7g5JKTJ6m4qDESZTs`w^AqqLd=<*L1IN6Fj5NL2(_)@8pyTKC1*atM zt8q+`vLi=>{%71pxGT*I`iFL`imyB|al@63D+Ju1?R`oVjZyI3!91NtFv3ov~m~^7x=o#bE4*%&UKVNagjF6HiWmGT8D&`(FpRSi9&n3Avtx zO~#Mm_qHLl5(sguOx>Dm@I$Pk(^C;}9168hmV5_9?XTb^Jy?5}(w6?|2QIzv(Qsrvnw zy0*|qzd8E*k&E!X$RwedM5Om33=mj^FkWkwMx8F5t(&AOmOI0)7>WTS>wAA`jAJ(m1+ds zm^1$!R1E?d9Z3jL^Zd}s8!}z(4~-alv-+@i=*H3bc2OUZIT{a6L4!(&0h7^!p!LW44*UZ+E=4T|C!OWNl(iF09#%$6Gu!@}<^7s-$e zdZS?J=r`6seA0Wv$0y1^wXQ=KZy81HA7O&EU42ZKR6_6MeFfH>CKO~lVliu}M=o~i z9IAlS$acVs!3<@P6Wa0afF5H$&$-oj#~rCp2Mik!N}9s#M{rEE4WUDEnRGQ?lBINE zjac3js;KhnAkT6fB$dl?gP72R7t%QS=uyv>iU+3*-*NY?E6E+)eR!17hMh#2;C%0t zI9disB7xYDi03`LdM+nXm9z?5+Ly``$k=U+LDs|1cfY^e?e&B@z^$o5)~YqDt7Tw2&ZpQFA|yVX z&J2%q28VCSx9$FYW|8w~AA)^^@9GB0^0rY3{bdlj`Myv{&a&{m9*rWKw>V`1li$@e zhue=I6p#LXX(##cOE(42i$Hi8RP=dwkX+l}$a3iWrv=~e#o4o*a$|WL**@P`Eub;Yo*|q1sI5=_c_QQ9h z(t&xo=T=s&^#nqzVjgDZ0J`LL*UhepvHi!0jeL~w4MB(C*#8$y7 zg!wWNV~zv?Mwo3GAey+wL^!Ujt9tT1CXU!F^f-8*;mPD|%Vm-X;%xbk40nF5c-wgL z!;h{NKXBi73anjv3S~~A;=1@#sGX9#p(SA`p;!3p!bl-w3I!|9xVU6i-9-T*_bMVB z27DR%o;xXP8a+>}D7?06>1*`XsZ^1O)9oCBtR!Qf;K4H?ot($W14Na!n69w10d7er zHWm!_sIU~zwPGVg6#^Uye4W;wqpw~3_RF`fozZviywz7fe-xc075Bo{4;h1UaPpg%oZ0j;k$MzG z%}rGG3JKI`OK=0kR8?(qRzy5oI4TWlYervKm7j%S6L{jbwpP&%lP}`$J7YBewq5qs z-|5?00-sEI6kU*MVNRgZpv@Z%Vq7(aKEoM^JV&HFLS|kdq0D#-GHAyU4#&AHUMir@ z$|3=oi>34%xo$|J7ru)};24#!E0+^jjmUS!i8JZ<{pm>(+IcW#C*ry!Cr~=P0gedu zPPV)nGRJMfc(SNY3(^66I|ch!{^!>F99Kpi5Dgo(&@VZB|iuQnKnEL#xRTJ~a$Bxv_&BT}78p{%>W;Ww4 zaFYE=>J-7;t6JpQBbj8#uQh9p2Cb;4UJ}Gt;K*betyQ!a4DrT}dwb#s!X?eL^_TXI zlHBnqnL3fmJcZ)M2xP+$8kB7|DHiWsb6`^k$IzSASqV+USY)&PmUWF#7GYZYl>oE*F zPeKl1m(k*TXCBIabl)rwmc-br9E;DBC?}`V&Xc-?7fAH+&=JBcvM2>+N5q>}r4@y& zBxF()ouKrWOv57wuq$Z(hr5}J?2U%<{;7|j*^S$J9HpG4GmoQSDpug=e?ca+K&E%P z!ZCfZqSy3rBt?Z?(wG5GV(BFkQks4l?VnFEX>OVL+MT1WzwYVrw|(FvQ5H~`7fEgO zkHJBn(TinfX{79BmoqX^s>16r>9w%yfOj<&EK3MuJ^a`%@mE`Jz8u9rG2=TvrSoHa zhvYhp@iD$jJO$q=ybKb2HK!s=i_Gy#slex{6KR!)pHhILmeMyUeKyiC|DpcCtM9+` zwRdA%=15B)(g6}^`Z@Y^vGnI*oqR~slSj&7RoNHI3Ekl$%Mvmxtoft@C5yNm4m4$f zKo;@m8;5*3w*P?}pEn!qdGKm7EqzR1CgW5CVUx$H%GvE%|ltr7!mn`cO zAh9S0#j&5@`8^3gsKopI#yPXE*?)4`uM@|P@7{iS!39zWPr_ubVjx}O_wh}QGm=xs zOI37@oSqZY31xg~!(&n=mq8tz({%V@K_E1)-0<>>&6Bj)5&GYIL->1ggbwaujF}_g zm}WXCNMUb@+nO}gojj3EkmtsuiHg0hm-B~#q83JWh}bwUy#LPiuPT;T7GKz^86x`u z3?e**(x>B_oex5YF`9#hq*qrh);Z2ZwN}vRY~>szGLxU84Jz>pxAos}*U#@M5ACEb zxw<7j;{@8lW#J(R)+GQtzBGA&Buu8Iyn>J?jM^$jgG?t>D`Ffd`6cWhq{|$W-u~^@ z>d|vErme${Sl4|v9p5gTM1|TwT~aXBle?i_lC7AHu(bY^Nv`sWe?eTKoIbr~+;4y>}k^6Lc6*p-8%g#u>p}4Qic{?I57bJfU(rqz_ryE+@}iDwx$8 z?>=~&32lHNWOE&H&2|qZKk&IFl-~S_`o*J2yxX> zN5-HG2`W83U~`i2L&4NQxZ@Ao1GqhFKKFe3@VWzsemUE@9-+Q61=4JJ{tnt0XprNz zHpKoaC#VZIgeFJX<`HF0uuJkb%3!}VrS!+ItIwsE7LUBWMV5V>yB_I~?uATU#$G&~ z#~dI9)wV>wVm1}^0Z&<14+KMY69@VnaQBUXPXSwFoZXua8=rpg!53EeGjAI|VG`RU zHxdYQhA^2#JmUcdeNG1mrebcfmNgmD@^rykEAiNYq)2m-M5ZhoY^!4JNWE2}-rtd& z)gts<7RYS;{gYW$pz4#BCWNdRO0 z&}LN|1qZzD1vyc(f(P8lHa+;V%Mo>C}`;Zi-Uv+xB)UReU- z2_J-5B5c$7#%fD-;INm{?|jPmars}{TZtXQU+MI92=f~Zp^HIS3yT9~c1mbcBxMOh z&{7uo>sHAgJcb=an^u{%?OXma8>yV`DbH^?xW;n!*dR>V*S~&4@A?gEp{#80`t@tp zZdkiq4n;oMTZYGD20=W;9sUY@JEgkC0$1`b83~s`rjws#vr6F{G9=bmM z|N8n5_$Z6-eHgy)t@m!S*(9_?mJNo^0^1vcuzfe1Z0}&n_TD#Jlp<9GLF#ijn1x?J+~kuM4PM3<)o}K)sfQ5zFRnbM^=h7f`}qxKj-$?p!a zMaEp0QPXMT_$y{8M=5~%i10;arB1xDXVXCcHqNo-Mon(!NwkUo9g5$GG__ws!B;j4 z2H~mW@hnNT(MjBq^LYJclP-~9TOvV4Ol!zNotCjYaW7IUzWw|}YvOBqd*nU!xDOx5 z#eX`9w(`c~r{}YU6Ogv+B+3$VVP!X_(!0&tK!uaCtHQZ(vFr%NlvNCi3etB1`+h*a zc4qOVFBcP*>`w1-jjl)MB?#aEA{-Qxfp3IjQjoustPMFV1z)sBq^g9yS#zWqjp@9_ zop@4<-ErflDwr0m6ZFu_Xo>OLZ*RNhn6hC#rDx03ZNwJA zakLHW9x_=fS%_o7lI#G<11_ne-d9M+b3GLfTg4GX0}5E0;X<}GScD7y{KxxGO?h$_ z`*Y*RU)_5ce9K6_=sT2t8pW7=HOi9m;5enQJ{y!9D$$bLXyD1LYH6t>whTvZ5uB;n z!OrGq?;ey}HMBu|XY;V1bgQYv7U3C`HXc7hC`TE5xcTLRQXFu1<`eR`JDAiMvtCWq zdkR)wxv=sIi*S37UMny7Wo@f#Zsz{yUg%FQ!BL-feAr);-Td)OS)EknkmJ;4>NzzUqa`S ziQ}aFg*XTunt>u$Nz_}>;hgDU%h*M3)>g_&Q#Pk1rVpz*9)YED7-|=gh*zr^#Q|E( zc+1g-gtqa!2}pe#h|j$Sv(;?Nq$;Hd z&*<>TI?NeARFuTt#b2#zi^soimQ8*7^Ojeoj+tYq>zB$U1rK>|I?QJkj zsiT`y4rXO4x2}T~R|#0GxXli~NHhk!LWXU)A3yT)o~!RSEudUAORk->TF@l{N~=hQ z(kWD28y8L|Mj0To6t1$#;wp*#LStScQfW&S!)X#WP7Eeo4Fmrf^ZWE6mwq{Hcy0fp zHpXYCw;-+D_wn>C2#zUSj;p=2ak;E`-k3GUbrrrL74mrWm5S^Ph@Bp-uN95HyJO4C z=l8E!@we#SR{yp?@KdlB$vKqMo=iQ4qwL0y;5sJK}{_6jn2tWX4>b6{KHr;pyu%^VMZznUeMe70)i^**G9Pa&db(Yn9un34t} zOHh5x*UE|txm_faM=Akc$_Gxq+Gu>1NG31GfpNg6G3s$d!+g@D!v~iNU+y)4(0HUs zDn#+0P9q4;RY$=W@0C|~UZT$er?N4dr5wFmn32TjoRGFR7ag6ZoP5@yQ zu+O6VAo-E8S9+e-Og+#0&no~f2@Fu>1UQK{*y|WVHi$kt6(MiPWA=N}tVA&>^Ygf6 zg?=wc5~U~6Ya}sDi6)I7e$Sc_!HrLTm>%)t>;MlVnh1{c!m z`fPS-#_7oR@Z_a{FyR)2ZNWl6GPp`1kP%LQCJ=k?&dgnptvx>Y$eZXr9^mxPpbbN* z^gZ}SkT?|Wu8Q8G3+NPiORSJlbOv&sr8sN|FHQg-0rQ|4#9{k3YfJN=Q|q^4uWj@1 z#ZwQ{8qPH{CG#6<9W~l6c}O4%#SB($)-7-<6I`*+57~9NH&_E0@85ai>CwWQFZdQO zeB$<>@^Z9|w-;a6U?S0uL+JpMx+wApGnJ^iJIPIWLrSghRVp@=`#cfO0c^(e}bLcZZo!P636Ud}1b#M&errvmb{NJw}+d`fBgJo-a(eObh&{p9=484eAGf}2s z5e|}65}lc_wOeel$kh(7mRn{UBxd9LbZnduT!YnH*YmU`_Oh8^pVc<|J@Lm4;EHSF z&m<9kBf)kG`Yt?D2bI#~4zaU4qV*cAK8Mb1;iPSzPSGkdM(S>avjJX;c>A}}n|~CJ z8*(!tzeS`!MAWx zdu8#I&B3cA%Ul-wHxe21H`WT;19$!SEX5Y5gu8BgY2)O%{{i>L??W0U;+bMKjn)d% zRFT+k5a&}xtJ*D>nmgr6u9epTt1J89^CtvMUwEzS*Ml6b1N{^C0p0fMXf0syQY6}qzlNNg^>5?u17Ah&bv)|2^x2;ekz2VN>gf-WQThoQ!T?@! zfD~sHtYx9d=ytP#vrY+PadkBPJBd6@Y`TFBeDuOYpKiFnd)1#WZ!eOsdp;(y+(#67n!4*h!D;#!TI#baptO|LwWDqG&fJ^arFb`PRzU|!O6g%U| zZM!KW?^!jdLYslMioU>LtSRUN4)QZdH>s`gD_stmj31Zs+?)=ztJ~87NnRz8Is!5{ z&%gWceV@$4m*@Pjk}{;{v9V!7D+uQ4VFHdR4HFnbh?jH;*jZ^h*zH%DQx&~asp;&= z!a#n~?6){CX2?flyKK|n z9kH1{2~VY8YDSp+7l^bUTS3U8V24AFqJ^Uo^A!pG}Fso?fe(3m_0jl~neRh6aO8BRHt z6qZV1pC@3Ix+IW_nv6jL1lY)gTQc#h3FFQ%x-*YH^yfSV?j)^6a1uohk+3oNkz9_C z)ao~lVhA^Z7$cUn>BX9~L*kW2l%b3u$WjQM)wD#0)*6}~%J2O-YQOZJw}JZlyrXZ- z+5vpsCXfom2*|@Y>`CD9;si$3XoQ)fzgY77#ENd8F=`5S+H5}fXiBbc0=o@6n@(>V z@zJo^KaX8Em49Ti;HMS4=`Gwx$n`1|ULw_=yiT7h9WLkOF^?`|%VoL~CLUY=2$?)o z^lZ~rFbFW;$)B8C%0eG}Xrf{7<7@wYZvr2~x=0{wX9^C}Xj<;W1*p z9EJ25v|DHcM0Y8d;}{hYSxDs)dMX}!x6Hhfj1A*`1(~RU>H3CGI&icPhYmZn?g#Ux z#_c2`HWvr~I8$7pFnCXcNI}NS=_Q_QjAP7KqAt6{SguIUHe=)ftUOh*@-gbA$4?%( zkXu=pcFUyeOG(65?z{&2(?q0UCAt0sJQQ#VnPZlcCe+1JRH9t7Clci-KsX8;9bviu zlD6?dhI;*^5(|E`+`NaBh{ zX%d+dk`BK*Vo4YhUVlM(or7}W-P~@0*5Ndl`JAj31b`T64<3#KOy2NJ-P-uUZ=NUH{k$U` zUw(TEZQ+Lr%=S|#{m)@wHb-IQ#*j>gBpO#X87Ov%i#BPxC{RHLO@4;bDDGxlLpS|! zX5j~qK409ye|ykh>mWG1&b;vX`tK*_?zEl{#GU4x1|C&T0)N z5(qWs+*D11tb><5|G8k*H_kO{K3;a;E+L4k;j2DVVj{7`!aQu)spMp$PENc-BU0#c ztVq=FbEsjo1F{zQx_Ipt{zEi8&l25u4zG0Aqhh@mHTMy5#2{=Dc=!_vTe^UL;XIo`igXVo6FP zA0^-9A5>*sCY3A|NzUGx&!-e7qnIs9=lG#$J!JoYFg{@JpD?b!Aphmn`KxYu@9LAE z9bNGpkur!%ixQYZTOBQN^8iWWH*upK6{X6X@b$3#frQRu@x4q1mmqX*u-D+ySAVWp z_fgXu>)tza``RVuTrWc1HyGvPG1&EPMX9hu5(fJ+yG!L2bV?*;WmzQ($`ZhM{r3RR zCb$y>U=Wm!sySQ{$nOy;TTGnq4iU%RBtp$FC|mDH3f^(TIOc*`ncE2 zFL!%0uyL9PDbHcA0U{#3(fH-3p64byempYviDy5;Q`e86uOg!juTmKz$P#WfcJiGu zu{0z0%FJB9tzdU}EwH%OUn{O@kN@p4{QK;y?<<_vH&nAAhM$Wedk5Ebl9*z!K~mt1 zNIEY+mDPsIa$dTpM^osD7uf0WR>&+m&{%6TyZN27+e$ogH1&3D`y;=dTap+;*))ZI z_gGw;^cxHrE`-v*@<=pa&=gA^SGdU5nawt5G$>F(&@=|C`>;ifH2#;@?geSJ}5 zSNO=WvuKM@jne0{a3C&)9*tBfWcekFo0Z~*bXluQ!Vks8P6(joK}4YlHi+#%|Mw?D zZ|!$s&yBf$m~xlqO3@%V-gf4 zIa`ad#}@CZYnC2faDL(}^{TTkj1ypOVgXjq6pWzLKpaX`<=GaW&=F_5cp(i((-G^G zhL&@%Dn$(JnQK1sMegCizjHPXmo0nXy*p>#hocT2iV88rNW+?mbS224AnCb_ZOw(a ze6_$46n46c298L{5@Hy(tnE4hmMQMIylvrapB!^unY@Db)u#W1Sd&mq!h=uhN9w-7 zGeve1L(~s0QX_}2;D>tL8nd$?uv>b<(mY%J1;ix|f$jK>w zIB)1=JT-kYeF$+7L!2iJkWIR@P-zGnJN-Fzq-Zl4vhjqWIG#=##+P7MNf0_R?Y?sT zcl#&*NL)_%T=YgzjBcMm>1=Ll*P!(CXd^?sXqzQN0))k0!mBJ#GR@_0u z$SWGIgUAg^ymody@x$6JeCECTy3fk4b#wk6kx<}5NwR=&<5u7*{!h*jYj3g--3t#Qs-awwj z<-l_&jEG7}lRhrslyltkt62y!c3$3z%u9tBPZm_J=}>mApIQ12Kw zDzInX?0uX;L~63JK|`aTqaid0#6TEySxJl^;N~?>o6Mjt^DCzEhg1-E$ z_pZa=e(^{rUiInSpYNW4r!2zJC*X1P4P=BS$G{bqP)l^_sL~=%`-+K(!6?cW*K#oO z-)uMuaCnAx-`&3UBh8z$KMb$DW$VTIGvM0L(U4_i_^Km4i88ngfV&RmrAfcTVsVQ~ zvPjMoR44VWWP(5*$K{agYelUEM?wXg*|%-xeLL?MntgPmi$ocRrys{~Ozsj2Ey(~5 zLE0Vh^^Hcc_(Nmq;jg`Bm}fMqopE)fI(Rty|yqAMam3+H~xW=ViBjF@`2X zskgI{J^1?PiS!5B5QYQ_v^e!r7GEHXxL9#(q~c;l>^+|JM0kid*Swo+``jb*+wVAM zSuXrsj z#A%C0yz=$zKkI&SFy?MMxSEVHsr0voqHWR-ha>f!U}rO(Zl5J%&3Gz`bkM;T#-%R5 zcpH&So+*NZfUSM{Rfc-=))42JId@!n^qWJ!oJ4{1rJqD=>U$CmIzWtfcX#rOoqDk= zVsYffUVWLPql^G?T2;&n?Crx(EKZt=OO{hs z==CZU;YP0V1;vtwq|62DZZ{8B{jd~AxkREbg-vevH!y@EVDvI>h{x7yRV;azL>1|9 zne^fup`JpH5iSzp!!zC`x}N#?pFz`_+nv*%|5>`-?ycABczq>-_i?zQ3GCXT;B`+34id&V9B8&m&yN2DD(f+Et?UJU~v~=E!ciled@*T*&B`@J` zLl>bNkdb_$1|e+tAJI-&XA}$jkl}pT{}fDnJl~<(@rUK6raaX!* zUD|+9-dR+kq;WzCB;^7Zs($Pc3jfz1TGsCSc;AR$W$b=PfO&7VYzbDTADPVO!@6o! zE|AX(N^eP>uge;%3s8RAb0l&W{)Oh5~HtxOhcRA&_(73iPtU?!`Lq`g@ zVw9jpZ_wmz0W;)nL0BR#?1i!Btd*#|Gh#7kg>Fkx3~o7|3^JjKU_BIgoQ{uQTG4!5 zMZMC^xTWWV9y}BqZINnFWCgBHjWS`4Rth@{G6h#Y=qR$~8k>S$3`vs?pP~cSXnEWl z4NF^3{W53v41)i|0c`quCvF^=8J{nNGei0j2xnBG4d>7*gr98m@zjcfBCAX&IWkiz zEmC{S3E2t=*j$0TLWa$t($5xCk3QRQ=Vj!2u^C;k z5kA_ZOK^23(aAh*g2+Mh!=bd^ufL`MptoZ%6)soj85j-d^`5XKAVr4eSsA!W%e zpm^Y@+FSAP$B)gQ<~pO2oSHx7n@8qyKEbz$HX-!q!EG+-gKhcH z{JmN)FRma zeM69N3zx%zgJeo9cP68PiO{2 zrQA>u<=pet&23LC+VRKT(`65(QVTbNOb+A=BP87fh6FNOvelueNg(n?I)XN{F&wtV zJG)|D@Ig{M+$gZP*IlD#+`sPLN57hRw%cIY_n?Z@!o3x3YgduL`8bZ&Lk2iWDj7+L zthSg+=rGGIV$=naG4MO+zB1@Yp>#`jbQyvNhGP zF?Nm3=&VS2rm`z(?hw&olY=&zjLQW6YVE`KI~2v+5!^x zruNN9{W$z&uJAls4>{l}6}G~hVdc{zpUGg$sO3^#IS3pHrm8)~e?H?w`S$0YdWtTb zx|n?E#W~LsM@l3QA%tg%P@HHdo+&&}Vn|@IBN^%l%T*G8w_2a_B;pmR$(Aneg0aT< z8a2xXTsOfuB5Zj&7|C6}eD1?uq)EC4A@m}&P7?hTiZHmpkOs&uUepu`CfL%fIvWYu zJ**JNC71m|sw#3;OW${dKVNp`y}w?F-A8P_N*hLah1$kVQ5xC=*Xxk_jr9Y>E}c{$ zm!)kPNutNlB?$8>zT!4wmG%C5^=*i=szjh#pWU-vu>a;C#T?%<94bKxDiliV{)vJ# zsGcDJ1Cdy%bVS~r78un=t4S;NDT`8f5K37IG7wD&w(9DAUgFl0#QM;*xj)hAuDOjw z$lQ+1BSYGIDM+0mjE`qha69yY?w+`_Cu7bkQZ85Aa2lqm7@_L;F}UW5<4ctLQrn{R8SyHHz!&sc%skV(4e&-jEph7IjWy za{KZ;CqL~CRHYU96Y;edBjW3~eDjFvrH{VP9(v^PBZqn4Vy%+>c-U7UScYQ>ghdc* z=X172V?^DHV5D!cD`fbX@ao6sJ*N*IPb_?i)o}*-xm2!8+R^2Am|We_blS)%*+}GZV(yljGKc%$pZh+V zK6;mZdQbn8t6%=)aWXcF4zt9*dZx6ANc$W=KumN9vT~o-ok)5TT^u1-BG6hs$792! zpW|!q32Qg7h&#W3yeG&v6#ls93vd=G)@ImN#}swf(IALU$_ZHb4nbPwFeHUKeO4BZ z8^JwKCQrj^9Cv?f9z1mWgeGg~mU_~9g*I-t`}o|DLmYMHJ0;z8C>?x^fa`fFEbqkpbGzIqvs%EF@(JXpC#t^=k1L!vzdzO2w= zPDxn;3rm<%xE&lzk0&QL-V4Fp6K?>8-@H8j?_Zm)J&xH~Je zIOj=S)a+X_`_jLMJox4wbSegm1oV{+_(t9pl)4nh;6ozYt_;s0adpZxmV8F2aa(*5 zQz{3+%Wn^@!EAZYt-O?dzjb)@WUwha>g-ErQ7Vs457i-1cxYhQ0I9PpB=UHTEK7zJ zPKUcWMhDonD#q$XVhv_HY4_B?OYf?;_dYrFWvMm0Y670}dJ|mVr8qj9jxhM|gZ0%^ zLYbr>T=vE*HW8QYmQ_0SLWoZH46D^3hf5}Iyf>Si{ndp#zQ^EJ~mnxM?kB`F(I~we}3Z z{$cp0@k5M?deQ8!!gdE3L)2eiKatMl%TR^@l0+1BHgV8p;D_=-q1$N;`HTgwY89D0 zRxG|ix01refXCdv@Qq~6exETrPzY(Sao?BE8C zPLaXOIu2X|luw4mbHQW3_Bk$;ZmApR+&`;-);}{*il0FH5Z~~?AQqo@j0O=qNkd-k zV5if8f>GD$VtbO7Fnqk_Pj9WUjlVzPvzP7jo_zFq$%60xee8?HPZL`O7fAHaF&tBH z3fSw@z{)Bzk5Z@=7A;{`*P`W%_W~qYO;h%QI3|K<|M^zHN2nIXsxijuwwe;{pr(NxT|CX zPwB4xdkUq@pn*Tj6wPm7aAB8ST3+lbWxPg}Fi>D=#R|R9nP1t44HJ#Q*SIP+GwOFf zxqr~3&;K*`L)OF3v0tXPg4LvNYeMOM2Eq{j07c@8nT}9GVbpaQe74R&)U5Gk?Fz`- ztf(nUgs96F+N3MUgF$Hc|NcblHVpG-I3E}h)ZwHfMg*bR>IO*NCW|{D zvL<R`rcf;IHv2&cegraXe&>KHoQV*^6Mb6 z30P8nE)-O$Os-BXyTDULq%OX)0x1Utf7R8vCFVan@@^S@_6q}Vb+`XH=cCU8jp&30r87~%~euJjnV zes{J*6N^+5>2g%+m8#XcGbnkCNCXS-68JvoKXjY-MfF3IPCqsN-_#eXQ9Fo~NqEFf zpln1M-lx-|gaLBWEbA@@qB4mj?B;4*p@O^|4TVm%f}{h|6a%AMkKDB9?E8Q0c6}W< z`Z}Y3!46`pa0ijT1cw{J-7}oQvw?s+)Zy2Ods1ePTwIZH4Z(sU$MqC9BP23^B%BMx z**~iN^=hM^`3K&5$LlO%C`CZOMF|t|$YLCA44nqtwFm=|k1VSxqy#DUQ!I6ray2QyNHx}9|T(wmTm6rsieE$omBBQ8fL%hyxk zOHx(`*WDoiF)rCkf>Qyud-8$uvV#7~r+*yT+w62uPE5eJNI-a?LhJV8>GQ{eB;p{L zibCiqca+O=ek{&Si3|Y`-^`LP$B{{mL^uv4<#{AKZqhx6b%p-^+e7(VNlM@qPPfwA z=?GIe8DD*pPW5yvdb$J_rP5vDtGsEQF>h8oNEm4Z^*R^?*l^!p3*O$}R4?NHq&_$D zf@#P;JoX_K8HUF|WGWdvlp$OJ!UUxtR!Vs!Wm|^rcd#=et$-6Qoq&Ss8(QFGfC%y+fXRrQCYDgbQ-pa1C9oN3Oll|?z$G?R z(g~Laz80SI{deZG1xsTKA7A&*>aUXGjR<803moe<;53o?n?Y=9Q21h=q&?Sdkm{r2 zWKyZERCI?xY&w+MD0yu3HB>Nebg(cfe57O58)xe8{^Mpa#ABoDZv|n`4um?0%HYmz z0J*26$j!0D!k}5}V2P$8)=}J z7|UCa+(tkiA`<$D^hPSua0q1yVH!d%Xf2A8#+~U=k6>^F}A| z>v{Rd!HI9PrWl5ea=!4$7rjU;{|pJOBVupk=_3aN*Bb$uNI9jA(Hg-x=loS?+AqUF9R%b zuBV$Nh^Z~CZc`y97gU1LoGeNKhv0^gx#-h_*On1Ar&ql>v-H_NKO;Ben?zz1e;Tb{ zh?~q8E+R9yW3d6EfGsq;Q@NDRD05hqS`k;}%KR_}YZ2Xyue#t9|CzkY$B#TZM9aPU zwd(wXV=?NbQ4QbH=(1*n1_22?XE+wfxpVd&L&De{F&6cFWfFK`%11Sg-XFsL%Faoa zKZ4Sy?|=WfkJj}etwMP-MCLBWP2q9^1eS;k9|#ouq|)iqyX=~>qaqWPvi@jQWpk*I zFcdBfY>6p<jR zcj-%tOlU8jG)xTnaA5Uq?73gIu2_F?^7zT$zQx%l->O2J#1a(01V`VEp$%g(mQ)I1 z8~lJ(YnHJx-b6O0u}LzWj)L44hbq;-*VXvj4xL;&lfE?>J=r>A;^bNPbXn^t4hH>! z7CilCIzn3sjwCy$kVh&aW4P0jVY37|wnFFYP7}zar<%Yg!5Z9u@87fh*83N|93A*| z()D&m%rLBlw-;+_{|luo$I(8+Gx)F>MkRE2sB;=cPUlPWtv*iBQ%dBkiT$Tojn!b# zBb1>ZEZDJh!l7}3IqQP2o<>`^pJL2*3#m<%A|Uk-fHW(uQ%E@uSy2(xR4OW2RF*9z z6Q?1AzTo>Clm?sUzw?EOL%H)gB+!wZU$EhgitGD-iS5cW9}+S zI{mt?h@a!Oe~Jg8C9J;vG-3F%38|U;rcM5`e%YZG)AK|M7Xx<*b`+%#18dSn8X%?& z2D4D^)$;X0Ufd*$$hA_|)9n~JJOnNYFx*XFnNAMf_0v;-t-07vm^W8222VYPBD?X3 z0>d^V^cSco?A9Z8Y4eJFzSG0$l>0bwUXRfylFVjq7;lPgBU*5J| zy8rD2j{G$YdGGg6UV!}fVZx=tO6k-s0)2(H*`=!666 z`=7t})$J8i>Zed&o!}tWB5tK3TC`4rjSzi`r}ctdh*d}gdb)L-uArKg4)H7Uh)^i6 zri%0%kE&PCZFr|oz`Zp0N!lNzDP`g^9Hn_E!bUL=)(Kx9&JaBUTfDnUlI}1k&o;We zVRzYNETtUU@pz0Z#%fG@cka78I%4wmQ$H?VvFetahA_Y=FY@V)Q*cb)$!5q8Pe^Da zAkNMPyJg+nP%&6x=`}aeDcA@q@;mz)x;E9~I)5ZgdSxN`kAHgKCB+F8#l)ue>H{2N zN2yR~>K%~8R1Dd)D^li)y+&JB)Ln>}rCj^-L=d+R2Kxg+Jo^rwyM59tzr3`1`;*Tv zqZcenl=|;@a6{LBhKCtF?JYh?HM4x1AsX=(%QAk@?oxPC-Q~h15{Q;}+<3vvzVnqx z^2A3Y#dB022A90}mJ%53%R}oMiA=r_o5bbp1};I+tq6AcGcKRFm@|fSMq4_lmVuai zET3b#A)k0`(?;&WM{{panu=>MJo*VPL7<*Q(Yq-$6-u9sN9ygc&-3 zu*y6=R-QY#4+;slH%k5iYs3D8i0tu=8_yauvh|(GYuAsYwn!c&L*>vUfyq5jqkTjj zAQtsubFkAQ$a?aEkUrtC+svHvG?0>N(SPy4&%OV&>F$?H&plC^|IEW?fdKsIEQ&10 z)%79t&%i|BMb9?I&1nNKC3Hzmft1nP)fLWJGXyf_ge1phn_w)&sbUb1ZQ_# z3)Y0EMCoshLzFxs-V0irsTl5(vIwj`d5nz&Jgw=D5}GX@*RA56SXfRC0#snfq1 z`QGodM$K@2m|`=K36v33`hA0-Rxb@!YHdBf9v<6n$OTNDd_|X8Xw3MX@Y(3mX^oQ4 zMqWb=f3tu5llH=iPk$QKc=G&u7g!9boa1^JQ7y zn6*V%U_nRmYh!%Ve1-YT)Hjw4Tk_NVm(PE->~9jiG`5bO295~!Eh^pJF=LNbFB6KT zT9;O6?Bs;SFvbuy!A2uUMon9?dFrLT_znM#$Xt=UbMr4^sEEOApG=3(+cYtEfTRo9 zm1druRnh1*ED1-R?38=M5Dp!a0z-veU^kz_D+0If^Depe8M0bvBoCk7zw*X zqHh2h2yO((VWhcW(HeGgx^)GXjh}ZFbWS%k_ph;wRm8|2Kel}^-tk4>lq=JJx&8Q1 z?~4sJ=GgxJ{`H&stCE)Mw)Af4?cK7mf8&>*<@&yj z;E(iyAA!Ht_w|B5xv_uKmUWwZ!SCz`{|y2;VX+hZZ(rZ$Dz@|g!5k|Q{%_{k|1Vig z>Hi&($>aQQ>R6jdsZVt3LrRg29rQ*jN{u(t{r?-0Ddqp4+NK0TUW}Xeu6=P6ee=Ma z*I%j+9*?4ndXbSli55Pr|4(UiGcsAo-HZ%}l}M5+rzpFFiY|9R*Ac34L%g`E0^gCx z2}J!hT+i+Azu0|oMfY*a!s&M&H1ntSBT(Dt|5PITkSP*TABf{%UDIoIz00`m3Ic6Ru1G;=wv>`ii)dP zQNGsJlhavbQKQUQlCk{mh>7omGMg=;8$xkE<$v3;Xy%He4Ik6jdq@49LFX;QwQzx> z{1O5c8)l*`t`OoB%fgV>Wr^`se7#$*G)Vb2WrQPJkC4Xk1vAkq?DW&Mw%LQfLWa_C?;0O^q-UCdf@ zol=c5-{I0aOIWZIZ434 zjQAUoNh1Ct7%_B31nL4Sm{zb2F^;FloMBa9bxwQ|twGXvk7_?U>)p8zyeH~veTVw) zei=GW!rO?@Lj)9g38~vgtcQY%>OzXc(~C{Ha>~HcapPG-w<-;}Yk82Q5JJ(1o)>)o z{O2!Cr2VrpuiiCFpt#&6Gi-AD2T;>Z^6_d>6l7@L+K?khz ziNz<-n)v7~_zmbF}d9(%?U1`j95UTX_07JekQojk5UFu9r?$$YWD_i+(m+ zrsBDUJzS$LROKEWhyMqI+z{-ut(~Ha#ew$+1*PvS_)b ztpjJJ%f&WC-HL=i95#BSWvwMtQ6|?R*ii1RV7(zh1b5zNE9XDi(_#qyv{YUH(Ry^r z&G;7X0$fu&u%hhq4EoPt0RDZiM2?n#Lo6AsIEbcGOFzf1&vmz3WqfrkI`!`#0{!ahqMGG z=Gj<{rQ!ANkD6Q5&#Znzey(d!^PErA=tz*Zg zd_D1}U+;bl_l46r@CRw61ZEj4V51MFKqvDg8-YI(Ktvti6O3`PAy!l_H(9uru%uI< z3c#mD5v=~f#)CtTp8WFlTURt1HMZ`H?-EMifk<>ak-iU)PL=|{#t^~&I<8ET^A{^B zcTv|Zi@DhbcCaG|%LG++F4%JLHGNXC{$n5RCY*YP`0qV)FZocZ7w&o$=qG(qDqS zLQ0e+kV0{+N?xz9mjZ%Jh2ypslb(uEZSGlugOQtyRBPYe|K$Sfu-BiR-lC`cux)hTtyB0Ff{8dHJ2Zq~Booy6jSxH|FLwK$b5Xsn)!EWY_SY z-gzqj&G6fdbCYV(2{AC_Qz)cdXoKFU9u`JZZo7mR^>(nmYDKX};>u}40`6JJ6E0Dp z*Wp#vyLj3`(Vl-_kbgKZY)4q~%|txKPNE;9Axs`D(tZZ^+{5KYj3%?UkYdL>lHIl* zoyXAg8E`;cNEQMsZlT+}lFP&2Ty<(}&+50AbiECeusETq{Yf%*8ia7CXf!B(VKW=M zj4`{`Vv{A)9!aOj;Sw1@;L-wo%5`YPUHBDKRb&-s+?I`X=j>Mnr%~)ZD*ZAEh4qTj zbZ`#3i>Z8%Q*9NcWoe5`Zj5>*S&(yJ`1)GQ>#}X*U;T5je(TIFLq3=)y1pHH!U=fD z;zE~T2t)W62u1^Hjf0m8DnfddF2)lGcwB`aa->ow))N1c`Qd|0Kb+r-txigQG_5~$ zFRoR3FOEK!f-r@aW`-0liG$Z&RBBx*Lq<_>88Rkc4~rXm5ga8J{vR^1TW4@bec`NV z)AO?ruF4L)vH)F-`s!LCVYeDZ2xJU0K^?{z+|R*S=89D)VCkZXl){^Uv=eF(9hDVPB!&`BF0W%Me!-BtGF z{X(VFBdvH6`ktH`1ySi?>4UOB9AZ_EX~+jpQ#&!d!2sPu&t2&Z|O zOcl`?{GXv7NYL!-)SCE0w?7BINRbt?i{WoTgnSCS24(}ycF1MMq}<=9md5Xo;TYaPGB?M-f z;@7|h#S5p49hzi0WRQD{0gb{Fv`X3FER7ZMXWYOp9lR+$I`aGc1G7Fjwjgl%o&Of$ zsF6B!dsP-$`YE2lgGDBx&8;eB(h^Nc68FjZEUQ+aP*yp16*o{zr$mIETkjs;HYPpd zKkZx`y@}W&UPo?ge}xE6q?<(B1=dy_~2T7^*EjK z><9BlJ&bd{As+tA=;zO%t=w0M4Fk=Sg~I3RX^bZDMYe=Zm|(F>azof2)Tx6#&TI@? zkGP3=og{(P)gPp*Pp97dcu=?bcjDYXpIvRRvY8Kgx}I+z~~A%8A|P8GpWkdt;gkZ2`UrzUt9io6vd~io{Bt(u!8$@9y-byE18BiYu@= z;QJu&GJKeZ)w9*j|L#k#55Ibjx3#DL_CdB=FoZ@KI;nL8T}sEbiM1$Ff0#Z%%!fp* zkcpS7xHW=8mqc%42~D?PgTNrLp!jOTyYv5j`m=jpfBkc+@mJ&0e@GPlU?kp%y+Ujh zHz8C9iS}a404bh#>9lON)0d0$Q{B$6*rx7b?FT-1C-r)@sQAb?yW0l7&F(3JM95$ey273Lt;u_B33!uG6fqu%ofLDMUclj%WAOD>?D-*7W_l1=@)N3Ik5D}7Gz7cxXto{*NC%=&vw zMUiai2+}YSEcwEk{`AhbUf4~$SAA!j`$Eqj7tbE83dFayUjhLdt%V^qK?1pCR%`Q= zN`^>5oeJ1r?+KFm@YjgT8xxD~adx}or>d41o?HDZWCgb8&$T{Y^$5WRM{QYX$GpnvuavTLG8l|5=L1HI|Vsm<7 z+STRvs6(Q-T;5qw3d$Wth^iNJv1-+T@qA?VF89!eErjM@=_{6fxEUGA6^KxL3za?q z8HjJIV+iAf0fMt=ljfx&c_L_)S=d~eE+@FuN*OERDQPujfLVlpUpsuyW0&V^{+-#r zU$X8z+9aJpB5Xz)ekIYr0SB|O3G&Z)QhBq`ZBRH9zOX9dw|o5(s3rLgR;?ZEJsp0f z!6g{;WVWfjpg!~mtR3tif-s0FP7+uWDa6Lxd$eA!o7bW5h)DdNjz~vF8z{phOtf&w z^=iMG<z>=-4o!60P++ymE4KE64AA^wIM_jXAgCmjCEPY$6@`kAOiL6C$;N z$l$F8(}^Y{p<+517sO2FbRd}0fB-GB2TvZ#eYyo)*|0=(e`(W)kDfZNII!i+*%tZe z`(~gmk`IS6+hO9}(aI8W7lNqQmv)nX}m-NArWla4=j3X zP-X0v4M!cP|7lrFT|S5kQXTYb2%%#NdJ!5C*j?C}RkJ#xqJk(dmzNX9&S0ls3C{FT z!BH9<2JH3X=qLZIz53R(GV+A`C;xtk_BA9lYxsmtr_@MOi5`!vtTc;_Vre82$V8ni znILe8Mj?`k*NIX{CL4HwM{C&s+xLByr@#HDf%y}S(lmlTgo-dF7fH3;k7E|}Xp~}} z)8KOX^a`=SDvG-nfsZ*m2CfO%q%RuSEw}#V`sTazrninw*>d|L97Wv9zT6yL7PQLc_H;cbZ&Uj%0CZ#ou#?zl14N|^yqybD{D0BUlSkd7tiaVoxkI&08 zre)K=n?xj^Bf(LC(M|mMm7_0NtB~-unPgL)1WOxlpLwVv1>SnEy7*L1B<<7_hrf*zm^IsnP(S%b!5oS z;Z(s$C_hPeKp-vDkKiX1Rt;Zn=LQOFk34OPS~L>QI11@LGK^Ef5Qp!4Xn5N8&4u^; zHyKwpy}e#fYUQ0mQ5u5n!84_L5`+H%NRFZ$z1eTdmbg%M&RI4&mA*upN+b{EzXTT+ z%2utCKajtA>Dx&gm&I4P#ABcAMcO1I>Gcge@xV(0bM?ndB_Y3CEYpU%Mc@}%j?wW% zJw`S%;Q+wkduC_O^t^?XZo07k;C98so@a@z;JA!PS|-wy6!1Cl4aQg!x{?}8s?!t^ z=&eFmFrV)7j>cnT$iffbhHhax*ZISf{&{`E6zAN7oe6M{{nUz%pwdsGw7P9@gjiY~ zh)ATWBHv`wSQRC=T<+XPBv*xlfWc4gP*vE|2hl$7^e^~izHAnl1$RG2e-GS*f>mS& zx1A0dP;wPcht;OgW@I8ur_iEx2<>$cFgg~GyfEP!I_(<&z?#)qW&XM5E6PIIbP{b6 zwc?w-%;Cr$eBI??OyNZm4R&J1v!znyM{(3k#>#@CaHhw+!rqd6SDGht@$SI8aSUv4~a1q86 ze0Q;;FiN9Jl|-HQX5FTA=SdJ~N5Squ!kkAE-+#op|1ZwR7iZ0SZs9q$9Bt)Iz&AWr zPd~*(XqlVACEr% z-d$0?R5EXUbKmBs7Je^+eu>oqs}~tbAb1)e`I6y6)W^<+6k%>asWZkbdZleKj*RqU z#FyJ^$hB-Kx$^U$=Z`OxcnuFWFEr4JE#NArnl$|B8?3_^ zTzB07(OC)jTmnOY?b2E;)}qK8w@BqojQkR{23DITUU$dB+yBjcshTu3X`6F?BLZ$4 z#7>~BBsX-BXqSco2Q5gmOCn!JX^E$V=6Fn2NF+$WZbflC&m39reZ{Dt_Cem8N zrzn0rO?~gj_*VYYM06X3XVIrKfq$Kg8z8#G*;3ZQ6?cfr_Aulwsi^%&nlWrfjWhI) zH!nSR+g;XRA^92K{~&!sB~ zp-&BNPz>t)z!sDb2ao*obHiIQ;ahi2*q@v|42-j25Hod*%M^zRwASGuSKtW*7FNz| z)fJpdp;f{4%XKhr!&Z@N%_q+$C7KJ3+U1YX*DUzvfcB#<5}n#kV@jSI$`T2NfiQ^` z&N!VGtJJ6uO$6RETBM26-XD z@AiAcsu0WUpK}uh+BFyg@+h8TZ9b5EqGx@wY2+1h+S7})ia=0pLa`Kq$(x9${Q{{* zI;?6}q~fjEZRw~|Vs!LG0-bW;9e4A=y1>Hhq&16gpY-Er%5y7bG&ug*Feyc#Y^{5LMZ`e&d$DUWm->@9*8z*SB$VZ~w;LzAAa_`o1S2 z?y_$q_;h|64`#>LAhoh1Qh964t|4<1-Ij z7TqyxwrJIouUodFOL~#UT0zrvL%Wi`8KDgqLDZtxqtANsGLO(`Nrse4t4)$EvbI35 zp=8SqsmX_)4IXGcc``Jg^QCd`#j8-Z602e$B{f*3K%hZc(yF*fho&RlV+|y;Wfezl zW_Jjq3WFThM#bRA*NDLCMtdXgjC*SP&4MBHJ*PPC@~1T z(~Mq^wY#F%7R-unyPsXqRLUJC^Es3>Rv_#}u8?6xOZuuM##{OZ^0@iPufwM;`vhI6 zM4Kd4TvUxoaRe7!uT`#7wk{bFNBEhL-kA|Q)tRVNBrrf!o|w~z)I>+FFFXG0y6W)j!qX_o6IUaIJ_Pc@-iMpQmuyDrp}M2i&9UjDUT4sqF_$z&0@=~zM`tSE?CEMCb zIUy0Gpjz5ED6)7RCX2uQws3iOT-dRPv!n5e{o|M2i-WB#O0;ewhz^(GhI8Obbp|=H zP%)ad`pvPdBP{QB>wN+ll;z}Ci!ET~C;vNoA$CFc_2}nZvdDsUM-}LJJ|E1cuZkF* zgr6)Gs?d74i#j>qmXv9@HY3-|GO5Jjib-LBjU}JruaF@$bL5Dx)}gO-{xa*IOSdfL ze<(ulnTV(KA`LGQkqt!Zi$smDs6!B2 zRl4@vcDa5jVHpaM zV4vb^O+>e?eQl0y?49&mdon$T{>%0+!C@eQZC9X^C9tgmlAv*;YO9*-4VyeHqg>1u zx_F+>&RzuSqg8A6-*PR(9LJ__yr2Bul{5xEl1aN4#PPi_(1DAdo}?(wp$(GwcO^N&6jj&x1Hw}>_)NIx=s(q&S^Ef_;W zBMuM+MFS_-ArOaD@l2;QUM!oFp=CI17!P)M!4~Kf`23Cyx6Ic?**~QBP>$k(4f1$z z;R!Dg;WP1WJX5k1$KV~p21tf(fzHHA7kgw5uS^~E1_M2A^&TiTDW(zsA;UIk;q%yx z^Xa#Vvk>Qxb1r;86rDGRg8hg=$+7halNTT`q>$f@qveQvDQ8L8(P7O*^l~##%@2T_ znzFuzT|O{pYIgApCm*tGZNEp+UAzmOA0j~DCo%^j^%?~`5NeRX5{jYv63ZE~>Wr3* z(irc_vb%Ybaw6I}4JEhmr`5XfuY{XNo{ZL?OKtz_)juZmPidvLi1U=D_7DO26~(q7 zOyMp(LoyoHBeND!*x?gMB|Vi?tWahd_4)vOtm3v(Z%~1~Tv_at{Qc=4efk&wIeuSx zG%%oFNz>b*6l;S9WpKZNoz;4=)Sk5Q6Rtv17L6-9OZHR}#(yo;+H3md=l`|j|92)wr$(CZS1n`UAAr8wr$(CyKeVO|2H~L-}A6uXUvF<$dz+s<~Mfj zd&Qobdb-*j*V=LHKyYz>ZM2;d|IzA3Sb<(cK_Z{4kRwZnkt9|6Z9(t=MbR0Jwt6xC zawY5kRGEk=`^AMq+U5T;=<|QX*S=3}Zsb;`bZD|3Z=rSth0XAXJF25c56cxWQCE>S zmT(wDac~23i53xW&gLf)kx)5X+vmx?x!vGRU+{`2eW{1mf_{wfe2Ddkh3}LJ<3|7& z%p{jrnA~bz{c!-_0)j3E@Bx7mmLIO<5tfo#vU%t7UOKe~AL33n^J7AEX zqX$|bR#q0w@^@{2yOdf16?|~_F~ZY*a%VwcN{g70g)-amEr}5WxGrW)8oKhT2s+agGTR>9*waq?!#GZLryo${*H z{|o1-h`|mQ(GE79#olfEky&@Oz-D9R;aa>qzkla;3CMUEj2Bu$H$VVOvj~_!oAIx8 zYB`t0_zB|?EGCO8gnNIWP|v7ItdR?sqrqC?Av4+ZCxr@F#}y}CC6rM3ll?Qyg?1RF zl0fa3#f$wgHr3b4(l<^9lWs3@IO_C^!S%dP)cn$@7BEqxmeYoS>Q8gbMrHDU*u5HF zI3i!S2Qq;mxH5~;8cMiZb!iq&JO-Trj0vS%44j1FlB`(D-f0sHlJoZFd7Fe^VFOv# zGw=sdf~da+BeA}8hno~6nsb3}B`_naK~ioX7|$kbk@pK5%v-D=R(Hnz?(0LY&bxd? zTZoNTbiETb(u{n@WDb$=!;t0D1Q$gAnMqFI z3SFt)M07n_OiWuwRU|l;xQ$ujab8_!6)O2?`Y|xqkaEnywuG!j{3MLBnadzZ^aG`7 zMzE9v>bN&*tg1jib*7jU1y(&afJWL5O7D*jSF(IQ10H+qsP>RK{V}wR2ZrnU`Kq`% zr9GVm!d<(oB~jZ+mnmBj6QI9@9G*rp&A+V?ZZY9wqedR5a_lnjzD4$!u4-T}cu+L! z*id-Lqj#x=@O4^*Ox`4J;g$dedFF(FJKZ6P7M)X6?{EN^JcIP|VT}eJ)WIWF*~hEb zw(&U>CnEZCrN#?~!u#F-hv45~IC}gY(`@tejyObD!5@QLJi z1_?9z^~Es6jBi>HrJ!J)AsQQVjyIYpdmtmP-y|;>l5{D z5e8B7%LFt@MOkPmM%cJSgAgnH2w+672gqhRh`QQNCu#>i5zl2J5dl;=#G z{?6oLFz^*}64WjmHLtnqC`Lr<<#*4$4O=G9|>0gs)0B${6Ak zILAUx>A;zHP}kK46tNyoc8cdq8AeR=j~S747Leb&O|UGo*;2GT9k(0l(9OOt`^f4s z00+0vKKze5>mbkogajB zO`EUm*vdPMG~*GdkF>5hfn;0Q=Ci9B1x87qpn&mc^RETMOC|ywNki8 z$WCI8A9-tf!+g)Zqq&d!!4N6;rT2|}`K$rJhw);dKaH;wBKw7! zV#f4sQ{3o-N6pojzkvP4ZH|)1t!QX13uYGf0`QHR zOzOw9|KjPa)di+-)M6YWN*PQQ(VXM=j7XWh?@*|LF#xI@CcmNi(4`59*9p zC#oBR_K67)s8V#*3yoo{x%oI zHAn9QtBBtMGFU5ryagTCR?i6MF!9(@uy2{{C{rD^x_1AXJPpokdqvNf=5r%!@&>F{ zX@}mq#ZRNy4llXNhevqA&%Q5rYi=#sr5WYU!yZ9NnxnUt5R%#S+_!h>w0atl@!a^35Aj_IBTj#+CdLi>A~h>EN8RDfm|GXJFeIL5R>+v>i#iG;p+>MgrEv9_`A5*k&czTs~&$|k%vE;q)ADF)uFXS z1YMix9!+9-B}Bj9A97)jAJ+To8f}{Xw4=-VzR|yu$m<=!57;6a;m7I~#n4Ko1ukwa z$4a%7Hs06SD0R}J{5Pq(Ge208^eRuy3|RVVXM|2$_w^SWlEvnm;SB+ z$0;LJ9`H=)A7fH~_VxBxHnSdF3Q1`Dw`ma{dTX|%Vg~f@s()UqO*Lh{sGe9p-a#ZdVsq$R z!C~Onmy$sN7EM3geC~3Tmi56%^N1$;;hbb|o-ch)mvB!L=XW?}1f>wu0$dC(wvnMn zMOWG-BDh&zpt?a=22@L4sl&Y-*nh^*lG=E@q^FAG-NSJq!m8qC`=9(xX~np^I4`(M z3pShIm@u4e2_xp+Ak)6F9J{V5iP!e*V-qzY;)-Hdy1;GJj$$e?2kv8M(_T(*$;mRw z%>au8U+;l4`8`d<7WjVqQlNQ^pZa+DF~||45=r4<+X1IL+ek8Oy1rc`^RinMM5M_2 z#yZ3+-?5xthiUgoWSN*L`K;=gUM+DWjz7K>1q6g zO?f?cm7-*YCVYt{`gyvRZ!0UMlM@S62r*CC4;D80n9>t$OhVcx2|L&gJu5%;U}3kc z#d@AxM}HQhr(lr`Xc!H~nbNy6k~)SxA7VuV;#Tr~By1lxD7DP94+@B#?D7)d2(dK9Sm6>3;KBN(%>#wPhx%&udZ{thLzz8UDJHNngNpSf zPtk@iWaQCohs-~C-Jdbr^PBw{exc#7JH(|-5=Ma=6DEv~LJCA=>QL%B5;1LBc@tlP z1DbAfu@(8`4Rp}xw|d!Zmw6kW(tFPmq~gC^hCsg^fkL_8+G0nGA*9uYd{ij$vvhr6 zZd5AIZywpDbV8Hn1Y3O`4_vRx&=pRakHMHHe6zo>stLZseFabuT4YP}2N~?cKwY~U z9a~UCA>I)~1g(1bhR(_91BOI~{tY)tdT>QA)C_$5&pg?hkN54xtW6DHz3gVA!xt?# zgzyC$epEkc(EE~ph&m)KLgaR-Gz5u&fHv0T8S;C!-p8%4TFr z%!S)=$FpUV^{KfXjn>tEfSogIJ7{^vk1jJLOgeX}eSlZ*-_5h~CocmVJWvlo0FI13 zZEeWQ!^=aAYPVje)ngt>TmLtoUKS*D1cKCP-zn7wLb@YHYr>RZ){e%$1|@|z>Fz~i z*uLaZLaz!GFbINP z*go?(xg{UtMA{ zE%Ha-mQV04DV+2CJezp_h79=N2U~k88CORDL*J@#t#)fw>72n~5sZL1+&>Gimb>8F zd%SGNg;B3XrYk$0;Flu_z0;2uX}20|29;AmhT(4CQd2DsOSu@pu^TIZK#%SBChAJ+5O!srDmI8$p%mSq*z81y5Tk+p)b{0MV zv}2<|6B+pRq5d!my+)F0jS^voERjO_Nt2<>U1-V+GiNO#FxVcyreHGgc z@h>I^)|VOkrS>fTVSzOY7H$XbRmOMR+WnNmdTJ8wSro&1TBDIN_r8#$MoH zHjsomO|rs&3lHPz%Q+|BpRdmfDO5$h>RuU%vQ^+#k+p@JU;2SYm<5X{5~0$XGHqNb zEq|QTt%16UABs}GpMy}`{I(cgf%sp!rm~!2VPWruT}f(CxUxkQ%MiDKqVi{ z&M8w%@)z%w(I|)PD%=AwU{G^(b-VeKA(wyol!y>Zg{zQ@Zp#XH4^-zlR&ybF#_zC| zLhW$7C3{TBOxPnFh~|XZnTFw0sfY6CF_$3ea;;1~g(Tr*k+x_6cQgjmC>x7xusCgg z2H%^=spIq;A7o4T{tgfi80{E3QNUUp=8Tm7O;pW?iH-Nq>^F7_d6O{EPQKn3uHrVi z*3d5|k&G%a{Q})=RO$*n0kf|Fr6Qx-bZAW4*Czs(za;03pRf=u&FH(H1({gi7KMd#eCR2h2UtBv5m>UzAxG5Qyv2Up$|9c zF5Evt{|i@zzhx9TqQ2aE8;&MDjoLW#rVln+mC~XkoHBh-a=mVv!L^4|_A?$-|IOEq z)@Ov(VtsvawX@iPn9sy!cUDtB^ZYEhNot$|`qSq6b#!<7`J)r}$ycAUyTiVl@)OCW z`q?8)$B{{K5X;rI6j6xXCVsa6@Hfp~_QokinL~N?hPYB~u+9N_UkvW=dDNil$@@V--*U0B%AzVX-=G~;k zhYz_tzTqz`5euaZi*R0XF0kAPJ)yaXWvM%z(=GhZw07c%vq?9LuLNo(CnRi7m z|2vzT+dpI%NOW6sKfP6S1<<>|0hlKC9&uxv<+OkhAY*54oEbFEXbpx98BD5U1r0lbj9A6ygzdRBh2*>*v z8*@?jWDAl>1$9A44Tg5g#;1~`N1PG!P}?jMEsz}0T7XZsghYIU2j6zguaMCayk^s6Z7AZDI8J*X86k@Ly*peQ+I=aAPdK3;Z2fab960LLTDkY z+EFjoA%sR9gxkf&W?yjLd*SmMFx%b*g~n@+8!^qH%?WOXJ$RP#8SOe#sq>)XsC*J$Ccrusu4|96MT{bpF{9 zDvFvY+}o7v#V@#PlmcP0@=4yfz5az^$FN>F3+Gg0X^Rm8mjPHra zY0ddaPP6`Dk~T-A>u+kpk|0M;WebGYoA<0FZB|P;Mq{_=$SH^l3yAp3Mom9z)fDY2 z3Ojg1)wyIqH6ESQ@oMiOGVD0Z~{dkXnW83}oG8tOe_G`}gYkz^*Z(?4+@!eC9CJ_y{3) zo$v&O<0@SX21+!OR}Lx+lS=+p&@zS3@2~em1tL;budl(K)uxA;ugb4KHhZ=L@-&() zP%ETq5d=@}-n*BcY2K>vJ7^ z9q@#O=!*DQWynL*fg&E6(j9ng{Fe}}=j!hPT?1rh6EZk!(_ZvTG5P}_=zcr{!+Uj8 z9Y5|aQ>iZ}h(0kgZ`YUJq4cE#+ z5#t745I{j$1K}0fT5_Ln&p38D7i$CSu^tfX$5W&XAY#2d`E#Og zosF`^&06uK8Uk60;gx6*1c<({8Ku45O^M&UuLz97f6`T3+!FQ(o~#ukm4jI)Ng09@ zZkqt6iJwsgsSQ%5*KJ0*oR-6Q{~R>?!H6T30|)wjrQfY52KTsZbGsZ-B_Dg{aJZZ{ot>(&mJ%z^phDAmSk_W%-)SVmG&#)KQA>WO8pOx>K?iv?x{_A(1g2v&Kz zjm9Dq4}8w>!hGItW;49t8oUH-w1IvnhLZ zyayF4P4e5{P%>8cdYFkAQGZ}Naa=<-je8hdY!@+t1CEa@5hyxek`!uF^*%e#6aAb1 ziAlnIz~mz@?(2bECg4Wl7n4~DZ!Nr=fG-5JXK7NW(q9M8ft|!nOu12r1m01ez%AX5 z?1g@5^7(kw(le{gOaCwpUGUT8G=dkw@TzJM25<&+XioHFQnq!AjF7S!tc^y%(YgokDy7t5f-rAotqh3&6 zjTq)Vf+0FPs9}VDYK0UhXz49T*A!-)r#F#c_3#9_-W#MTVXL6EE1vkXnaU%)M-w={ z9*@DprW<*!AY;O>X-;gMLquk%OX}#`%V+D!bcVvrwSS|lX;;Uqu7Uw#f?rLP4aLP_M@uPFP>D!(g%q=3jByG!x~KNa(mKVodX2`=X@O`-VOR@jq=2Ec zl?<+dJa|kWj?;MmR03nhX{qR1$Lwea6?prZHh@8j-KOxte5dg336FGmg?O)?+TPwf ziB@T(%G&ys@PluvwiFk@7=*#iOjNJEhujfd<@OGHoEIf9-kHjL&~aTd>xK9AB=7~L zRXB?{dm1Y*pptpKax}GN%M_rRE0;aGjfYB7Ql{6sYHeoTb3(el8Zp^tPBLN%7TZ@! zVfFMIsX@!5+#f9zas%z^YU3^3r)?Za25MzAD@A{X^*s*{vvA~inl5K|+puuMT;Vwy zP_kUnjmVGGknYL+>#^iE1Wa!E2K04ya_fI(0I001BWGL`Bi1$;JDT>$|A zgn$45C;+el{+ig^(OK9UTNs%*(kasGc{nK<+AFz>iCRetFtHfR3aBg4(bMVaS*j>Q z0sxOdVr%@b&)E$c01)Kq_niOxXp`6($pLI`x&1Mz6zlZmXJZSSJpC#J z`0akyj;c88=1bC|fLFNvKC)Zb$7>=tgux23oZbpj3(Z2ERGsIKDKDnUyU+$T&L^7v z_;%A+NRsLm*AeOXLgAmXpSN$vA3-j=ma36uwOk2FgkJ}T_(a~Bdn6uSJQBw_p@NIP{9z?e)FFN9=UUX)PsRH}UEl#T;jvXV3iF&l1 zUSf|JXoXm?T8Bg^N`XoNd5}3F;pZ#>&!^JMS!2poR06p~90w)Q#~Mn? z_<@YLcZ8V*uVF?gwmrrZAxVV$zvUilmtgE9oeFTbgqPixEbpRqFfUvTebekfpz&MH zkijEy4^Z6w7^Um4svQh4PqwbZA3a`gX@*X&+h~3O|7T);Q;`Ok^Z~LTK%WU zUZtMFe}rg^IC9GFZ-yUaPJ81`9}R{hpU7qohC7ag9}K#bOD?o08DS=|MlcUYC=aeK zF-BYV7Bavw6k7;t=2grRyD>nai29?JhVWw{+s$H#WAhPQ_3VB;yX$hl^Ljjfo#u7l z*|u2S@xGP4aH~|n?NH$jy7t_Z;r)r=qom9Z`=-m1&Q}TU!cl}oR4p!5PkiVewxrj2;eZOvaRgt3Ke?`PpBf>%4D^@jicZ{XWv+ON;LMerrL=`8I-i zeVF0Sk^VVapz8_G46C?Unwon5t}zJZ)8zTsDMB}4dK>HcD%$y;P3itI8QBPiucHgY z`+l~P^9mD%b{Oq%A5Ylhv27}g<^Y~k+cjWTfy}YMWL(igv{z=(ZM%XSPIMhWVqvm% z)o)rKb--T;*5m5}%sRFNv329<&H<0(_5k!Io-lRmmB2)j@V0nziOcPvxBA zcI(AGlAJ7)!Eo@XYa;X^D0Z`^JTUDOJ&r~|xK?1ca+2c)>*~lYNhT4Cm`i^WR2+3< z4}viMoPVx0rYJ2)tZ2zTa2X40CujhWN%aOfmow_lwFewHlpj}kb^vH5{UTK!WfM9p zXy!30K5AVqLAs|u%@j-aSKLnw9Y4o+8tnRTpckycvTbNL38+@}9A;TztK>i+-FRj$ z(=KUPGnAS@`6MDTqp&oZCHIL7RE{!c3Xb>AJ-hkaVYJfP4?$CiXysAQ%*wZXR!S(Z zJb&9L!7v?{LopXNoS9C@ria;O-kX=$z2yQsQ*;auF`|Uf-_#!Pyp5`uv^xlQ;>DJ% zqFt?#XI#68URJhPfoPcR$b$h0lA5_&8k6OPv9azzDg&M+9HEPARFxE2M^;Hh=Iaz3 zu={P$8Hu#8m!bGAuFLK)yke~$CH69O)p+`^Y{bEacf2qEUXnQ6zB@+^+fZS)k4j;@ z%?g~^YI^e)rft1W6FI}XtH=ZTC<;O{V%tz#xv?N_+(~EbcO1h4-O%+x)^Ox$>SR#X zZoKVrY2+_(aYjlICcs5e7WSmrBV-e$y`C&xnn%&E$xM5!)o$A^`;?pJpRX*JGBBT( z5q+CgIuCUFB@>Is>L#mu+*gCjlmK4C_-b74yR9-66yh=x^yUjk1xH}{55^e0_9})+ z0Wy5c3`iaQEhw#_q_W*IVUGBg~uBmyev zs?8#rm>A1#e?Do6V+Xp!C6z5M=bY}pSQU2SF&wY?d*cLIbjqsebRB8?$Md9M)ftYO z9gkE)r+f5}(~=G_Xum>ocS?fBw65sl4W43RX5z` zwv&-Qg&g$}L{xOIZQqNu%=C>_CdZJ#JF$}r&^kMItXCBV`RMjrO6wN~pvbFAlZV9u z#xht58e3a}-~#DwF~6Qp;{EjL^!rr<=^K8^bxaCquJ}O0dCJRU1Huo<_#kXuyRU zRvr|#E%|NJw_W_QFteWL$FE@DzKw7xjKTY5#?2TBwg_%$1_3JhDFpo^HHqLXCO{Rl z1oOax)PZurkL%SPb02gI#D+^YLKjO-cQQs85AH%B z27E{p&OsMdbUmLPC3FL2X>DzKlaKtXs6vvG!RZuIu=odP%Yj`{EV}ICQp#=z&rjPi zW$U4i@_E`v(N;B)$8xzcH*Cyfq>vB0PE)Dz&@zOwW$S2e-B_ns)7+Yk9Fha4Q#XM_ zkGA|IJzJ8nq#EPfzI2$hturK^g7-Geyc(2_EjVow@jT!eLLO2uIw~gnvfYN*DzBqu zyx}tjoMQqorNQl@n_UE~tLug=T~asO9*>;bfBUiQdspHCBb;%5ZVAO+Q&N)!pa-Tc)=9WJypBDhRPS%<Zi3~J zj*C~j{zZr03yVkTgasr5k}zqT{oPofqo#R2GhIDAyTTx4Qz0e)E{<*0zdMv@8b?oX z7~%Zt__WXy50MufLGCoZx7#%2^uxF|faXd#c<{5ZmI_AC$ks^XV2(Uf_n6Q{A8o=G zYm#=<(mA~SvaErmQauGmsdHOxUmtPo^CPQi#D=bx&yuD18?u4O;?^}fU!lt5A5>UX zg68)%nvaYqA-ox-Y;_U|0!Adysha#DK9LAH=#l3GOXy$9%v>$$P}+lG{}h}r3ALiw)A-DlcpW(3q!mfvO@Pws z7=S`v*&}gqams~J&F>dgZ3IiQi6U=(WU!?7y-MX1M|C_3r1}v{*Ka91pBpGub*b|t zYRB?yN2R#Z$XEvBAy@moSm|5270+>R2v`C}jcD7+O>kc0<(UVAZJRF`wjWk|HX;(P z*f}r5pCVB=*M~75JVX$=lW&sHEGmS!%vm{yxdLJ{#yMi0xAV#vy2osdxoRf4*M2N~ zr-_0*a5f#YxVLqcLOpxhCRgyj8D}l2oEg=}r%&Y5=l%_i8>+L{p?jrC>71S#)GM!u zIqDcDY6PpA=IZ@BRM_FkMNq(+R}KN{Sd5Q;i+A00Pi>nZZnZN`e+e}7X)ht08+5Uw z4xm8SFIT|nuo)K%kgNQI1G%bfAGdOpdvxH2d{}3bF@ykO#wY~5OVMVFc(N(Q?w_EB zXp|`R1aTK;$ZxRgU8v4kbQ4*Bg)cUk#dK9CjQhmWX@P~k>}iH}VH*`0yW!@qRS{nyLMPLteR>(fF#n?)O+PgTgmuA76|nm zI0_$Tc{JVyB2O|BZz|Knkd1WQQ2}K1d(Kv0f`lcia`K`GNy%0MfQjt5+>_aRIf;v( zIE}}qqVRcLCuO}{HTX7Cj@DOO;m*f$p!!9qPQ0Z!uD;oeBLW3-m?49?0e&&uB0tV- zowRl7251oIj7HEPo`JDs824dPQJsFk)?~0qTUUxw^;nR&8Zoc?Jhx$RlzsWB#3`bD zEyqkU-^wKMetMl@v*sweql>>nOrb&plY1Cl)s@Lcv^;`;WZ^iK$FN%6ZEVDHKw^Pg zlgiG!D{zs~VVLAN=7Kicwn`WXVF0HoBoorO0*Ir3gL44T-so;v)w1|B?MA$K>tS!4 zvxd{eX!%Mj-Q6kht`5lIbgSMwe>UPZ-qcK5=-*7Bm>rF|Z0u*l2*1Qh(p~7DM`i!d&_4)yfhD>mtccACW`i*1C z^!SO(dH&Ow_p!vWa0u=jX?+EAOM0(J(?L3_B?5RR`k0~_>^T2!YLy;_M~_p`^mX!< zVv}3LB}W+}DXKY(b~|ZxB}%vH-?>h*k$`K-_6`~9Cjq!)7+p1S)Uu0=ovClNgBwD~8(YP481b z&u}6zSg7Cmt8lQW*0{sBU=~{KrtUS$WAra@ArpkDnJSF$G)DBUgv5W~sdrlYCgHWXoh=$2t!urrKHIyyyVVSSUD9@pRI5S!o6GUYMd=;we5odEgto# zGjP8NQ}b3O9ky!Px!KaLq35CHCHvY(gcU4=1st5~euY7^w*|zP!cCE1!h`sSK36@_ z?k3@&CM-=&70X)tu`ZcbOLO{CmxcuQpYrA5L;3kiE)|$+xa1?;Ko?9~Dr?XTWb+#8 z={o7QX-Iv%Cmg$A4LlkX=(g{2pC-{O2GQE7xcP0VX|6O@5yA~Ft`>7!!+~iH&+lg_ zZk&Z`1tlz~2ZyFL78=ch2zoT<+JWb0scHjfk9igMno}1YHF8su$IcM<0G6@%dG8xR zl4|QUKPz3m7<)IK1OOv=ts18QU`^LQm^QeGI`a)oY2=L(K}6wYF;^(dv#YI^FXv0f z3<%fxq*p~2Gmq!epL?kPWSNd6wH+LZjgkp7P^8Of6618N?psZ;v3mE&VsACi4}Lxa zZ^rFDi+`6L(f_9yP!w8!8~Q8HD1-cWVu1C35(9J=Ud#J*uR+Vi)krv6)HRCDmQXLM#*O2buF zb5VoKwY5W@rN~)`qQ_jK1Xa;=$oWhzR5ixv5s(PO9{kKk0{o`Tt6Vsi|I>hac$rId zI$wCBo^yqWF#5+#9s@nVVhg>KrEK7SIXq9CG3o-ie&lZ`IkyyuFXJZAAJ*mQ?fg$ya1?17#-> zwtg_M_={rse+zm=P*w`bl+;zE@bR_hOt|Ca&?{bc< zGC>{7a%Rb$S?v+|5^V-8br)2#FHg!B)-{2r`2q-E0+2!;o4#6YWLFB<=yeJ-5wck zBE{;`B9frMrdQzPLa{X##+L|5VE$zEl*0G+GS|Xo_1)QWZl?I)MqZKgr+>7=2p*x<5w5Th{^zKfBdPIX6RF7qeVW4 zGetu-T|&Es6FKy8`{^MKDBR*QLKL)HW{~iKz27BGRNX|DE;*7!9E^}X@6*H7@iLm` zL<;I+A?*EPR5B0|UyES<_T%la929RIS}SFO5!QIK-IJQY{~F`R4cU+4jZ*UUWXi?y6vN7SI8g-_5Bokvp$* za=XwW1$A(bk%)`Gi4!x29{8)e5fJSD7I_&74V6OeZLTqQAF75@qusPVu6b`yKpSN6 z=gK$6VDtn8Q4mooJ89~9M9UY0uLQOEJZQeTg90^*RV2^l{lMnWuA-|$(EDPK$h?=Qi*sbk^@J$6e%qoqPwOOkP=GxDM5ZptNT8hIoqsx z3j|CHxRfxGxr7P<;CUX;U(gs6P1T$P5kjd(YX86&>B78XlFxm=&K1}B6TrDRBWEG7@f@#cja;3@L zvii~9yNF12DU)Neu$mCM%g)xla@|(mza7* z#3I;@QP|E$n@Pa$u!I**nL!i6{P0#8VMfH5@KMtJCB!gnWJK!)|86JjzW}V*1)Oo9 zJ(@JC*u64?hIzwvMYB~yX@$WlrQnH}Lbzdx0c~_0uW;X}U@gBZ38@09BIn~kM6nIb zIsuq%2+bao5uKQ&@j+c}PTr9-2C_?_*wBhgzwTDWaRxRq@fFJMZBg^d!D_m(V$&yh zgREc9nBO5yUR}MW1LbB*k*lM1f~Pb0v~EL!qYo2)1+rU~Bqx9^h|cG=_?3qhM;>au zx;)78eB;y5myS83=TLN+Y3Sz(appVT#9)-QO%gN&^dje!3Ol3JmfvsMivbsHrNJYA z6;gO|64I4!?rWIKOY*9IM7Y6jdQ*?|>Pt4gryNYT+c|E!2ULUfEd*1`qDqhPPZy8G zE6@-}aOoGBQU^>hHlwHiC0xA0d5iEe{f`i1(Yk-(y6h44HNAC5*VCOA3|2dnS*Q=Nx zI)>g#S5TY_dwz<83z{z`yTz8nW(Qy~h0Jf=i`%Q4i`&an5^Ax2#rG*zj@g_ilav(c ze!PoUWVtvjKq5(t`5uxXLD}u->|I(=AShR4Y6=U`ERLV0x!|uFm~n06_TDJRir3;) zwDHuI`JPHj!kVLDrjn@=(L)z6`nXi8=r{YZU&xCIvHjs~btpA=Jx{?q*&7%r6^>sf zvc%xQf+m?)sP%@bx*IQv6>`cJOt9Y6!?8tPwmZKFYt>rc_-Fy~cqiGR7))#nMr1Z{ zc)Ogrmn&lXdF?X!GMGN|wfqEoK42_i50I1$<{pGCP%(sc8J(w3H#Tg_#zC~}M|HWZ zb_o&rd0+Q`kH(xt_|C&iX+1m$yFVBzYQmrq z-Neu&iUicA2EQf7!h$IA!0t}2ZlA_s>1cjrTNK&O87?FE#v9+96j^gqx#f8qy}CpcVlfQG6EjHMPej(9Edtc<@7CKlAJ1_O%H(-w7(8K?RCqLBxa!0(7y|#PDy|%L;j|^`MPt5dJ@zerC z&^j#xPZ0UviW%SQTw4a&_U?q9we8!;E3ZgzXf%RXHYE&Q{jMRjD-!mo+-$Q*vZ3oA zmN8{Aud!_#Md!$o9rRV^*?Qh&+cGX;^=Bc=0DtN02ByAxXfEH`=VQK<&CbS8m$`H2 zR@KKu-tS_QJiJ=MHW)MbAO_2Vh`1I@@?h*3JoW$iLHie_Y~OrllGax zhc_W#)v#}?cUeB5lj(XMQ)kz?;B9@~Qmx^&>@Tuj*qRZTo}+2GhlD_h6~E|cKCDj$ zA`bgy0#3k?r^bU-GV3P*=CJCo`XN3%4w>cU!Q^Xzc`Jc4m}bM(GB0ke0YjexKiqk= zv~T(e0JCDz8}%ZCVYBRBfO$>$nawTM$rE|;PS!7*M~`DG6BXxT9Gq=oi^wz_OIto< zO4wvLjz7+qhosyzA3!}-D7sQ~t&P)uJuM)Y*AFql4INh*t;Ogb4A+#!RA<(JrRD%S zMgq+7_GHVGy`BGy0`on6oW5jgKU>YT6W4IipfzFb0U<1@*mY?83o|&FvX?dBVm#B4_ zp&6eVuO2@N0Fo`mW2gJZ>R?s<5R4gm1{Z^&j**)if;_z+P)GR<4EO^WHAbgT=x~nf z4DdgzL(l`FNZj9dKn2);cUQar-@6|EKX*0VO-@Ts*iTDI)|>qQx~SXgLF9u}WW16< z{ZF2s1TB6OMCBX_9|tu<33JmbO%FL+oiH7&v;#j38%tkVbt^LsO+&z(pFc}?VLg6Q zTzDL)DSITfv=YCMgM_Irr%MBDm@_c#fxayMrD*c&{3Hq~sc1;wg>dw1{bK(9!gFtV0|1mdg#HOyonJA!)r{ez==8iodV4Kj!0{lmOJM|o*bl)rz{JL1r2Usk+c zdSU`pb6^&$OKmNQ$uS9<%HP>y@m)D!iHjt&WaHDK!xR)pxNBkboYaJYA;#3KQDk_y zx!2CY%|*j&0i9@^+0@SfKq%tuQgVR*Edbc}PKZOlkFaGJ005f*<9_>ZL*B%VhWvb(UC!rX1 zzaEX#B9wqMb{0>wV1Rz+Fo37&zhi+bdsL9%Acjd!=6 z?-y^*i@P4(mmx2U1obJx*0VTa9ESVe+vMxF%K$xMTZ8X*07eojsIJqajA7}l@~XH@ z*+!=6E)KGWjND9gQngp~*RQNAH#4p@2xJa8Js*ZvK;uFkoZn;Z9h+*&lH>PWQke_E znc6A^c@$gTmpi=h+jY-73o86--Qvm?jwXM~wKOZ0wj^+SqJ3(=-1`itxuo1l?wZ?7 z(~6|aRG%`+3N6rvi{V{Oj>R~IF;Y6EKl)0VJO4_o8UJngTEZe22vMsuNEc8yj6=vU zJ#hpdigZ9=5fr-EePWU3x0&B912I&o!)AqfwP=kl6f@8q!f4`t zMPqUWt5HpQjXrslGVcrChymCIJ5^!a@Ki&=kef84fj^vu>+g|tdFmVy2W2^SJw#uX zS}9CgQLQ=_&Fvnyf03H_*Wr_MA@J(L;5o3oGiiih%$q|xn=b&{7IxIYaL@kzCuIOz z)D}6*ziWkyy)uOuz3)zC|0LtycO_R6QWoetPb2}D%rvy+Z8z48{VaBEkK$5JLx(2F zEf8UZw9Ic{&EPyV^j4mGQ##a4`L%R+yVjoF7F%oYMvsmRIB#z>6GG*npQKn+z{#Gc zSo8t#d?@L%f2mxXnzbksQD|e&r{>iPS_ zRn`r#CdhCDa_K~#rGL4s8^N%4G{cGTB#k<(gETSZ(1y9}I-qIUq-Pqk;y|aTy}dmjJl`QL^%wB}{8=Suif(lO=E|zyTq*K@oGTq|4eV)T zg=9pTRn6>`>D6S7$*(h7+D6>?5ea z@vXGsCoa_4*iZz5MGBs4jR_II?kBJ zyRu1}w|cU)7~7r?`my#$dZD}<#WkU%1` zK?(!}WLcKP5H_-1$Wp`#VwkC56%k|=M8Z-w$dFOC>>;wWAZ5wM-@{r<(6+Dk_s7lW z{qxTEo;%LF=iGCGN(jCeNz^e=eoB0`T{C!LWagkRRJmxL7M`qK zEJ++*vOUj9fc`L0*rnW#eKs=->voHGZ2l4`C-KA|uex9n1|oRUZi_FwWf5*;LvvX#MdJ`6x2r0$73q7GrX}51aq7;`sZpXY)OMju2Pw+#%XP0TCSTN&#@&s z*j4*E~Ow4qFZvF?Y_#pI|F>3 zWsNMTg1I5z7@^{`O=5j*ikdBki;y^KUiv)OfI&p+_`#R#4Fq~#bE!!$NbCvY?V(3Q zYVJvApz~>)8xkIpOodU#?f4u#T_NLiF148R%qwK6 zQiI)?;3U3jUMeiU^}&P}XT)>^Z?x#+E0e_NEHm;nL|s2Cl~~rbCaoJyBVo9QbrELD zoRR{}B`7zg5b0tm85h4e?)@JUFudiII;fCW*64u|`GB!Zu~;`E){+%1!@$`6V}?s| zkU(Zp1WgK@Js~usKIMIs7$mE&>k{jQiwBuD5U_b##=Ib&!Ao$+G{5G&fn~X_nii9o zfiQVym?&+sD+k2NMO(mYnPGkzouZyohqg)1acz4yJ$q#uZQ*4_x;6Mm^DvXaMr-X*N|cIJ`WdbytYZ_hjA zuHRB>?tVP&n_FnR)vvbf@*(Z*`H9e_`^rc32~L+)E=mTCzg*CgQ~wF5+kLR)+}xml z^HsPPmo|O)0nAU^jxytQpUor0ATxS$(MrBGFH%T&)v?dbj`ydES>} zK2%WR7_bWMsNTz|XDR2SpC3m)lRX}|e|Us0Ry>2YQYqIkZ?t#GLzl^*T{_lBb?Ue1 zC?;-mh$ppReo2Zy6LZe^Q1-LN-(qF3%o}f#QYkIh&ep&~F3vV&224#FS95+ZX`C1` z?Rfu+es>%w1OX^_l>60KI)14el?csx5;UZ+B#leXSbL_!cg42ckX`YagYTn-yU^Th01HTBMqH)J^&;SbZ;Y+Y^QZ#JGp@#p4FC~S2LG_-SK-%;u@H{M?`g8k)UOf6<)Vc@1u?+iPQCrD`8gWpaR4AU*#{nKs3P z^cM~kOd);NihQ2lhJ$FX{Iba4CojSmPly!_1E=?>? zF2g4zawU~LhWuo_S~b#GfhG&GrMNcrK}$eVf0Yg3Ehh|*@%PTthm1y@BulbksC=E_z z&%&-(4OhP^2b}vXuh(0vZsps1+}%l=xQCL!!U_i(^pA!0A6Aa_ZGD3pRff=-WVSJK zD>9Z_T|)A%t4^x3(YiEMFfTcSW0>e4aIiMIC&~Jf?bRWdJ+9viI$}ObGunsOd_yhm z`YUmH(|=Q>P~j}RvNGs3l1ID5k}-jha$2Zy&d5nBrj?+I?N3@DMi8^!)n-Q|>kZBM zt}Fb|GtrWNe(PwbBg07{eg5UbEUXzD=QTXpkLoXKjbyOXFB_E)J}lK(sdO87T7~2Q z{(0AHXubOe%vi=GtB?CW*Wj;h*F+h#m*TPWNx#7~Y?;+$u5{%29neUKcMovH)QK00 ztrRqy1jU{urwDGQS98pNxjiX>oF02O_9WWh*(_)#KhrJYx)nQfyLS}kST^H;R>5q~ zxqBn?$b*ptZZ)c!!2EdUsBBEZ;`Z#%uYD`#_mS@KVAd4xY9Qt?47AS^@gGhg%(`Olm0b_gZus|0>fPLf%pqF_hUmkcogt~MF@AqqNRJRl#F+dn>h`k{gx z{Pe^EnN{0){Fv%)YC`pU8vC9U_TNSQnJ2#z9jSgEc&P+v44*^+8vv3Ltn!Db&rUix ziB2Oz_Ef;TG(cnh+_@czn ziu{X;fo~zfNJ#S_Nk^ssp7rO9ionyk!~m%tKokGW+8KW%;FC!*P=BG-P8|dglli-O z`4eaH{tCeqK=A>>NpXV|;BhiwaYW_#eWG90E*4mHV6~G4i=&HFY-9%nvor10?$AE) zI61I5^&@auCq4+IFTNnb7(%!3Gq$+rv9a!9gmlnR~t8+(>1&_mk z#R(Oz#HItdYhNrCEqm~|vtV(k0^br~93i?uBi(m~IPf?uSRBpGxBX}o@I?mJ3Mp>= zU*PP);#vfUJ-_||%18pQ9S$rm9(i-T9~j6lw>dl+Jk9|u4sC%i-#IW{`eN=pAA-j@ zg2lz#^&$$X$suNF@;!5>mjWK=1QthHHo4nG0~x2FtqM>?3GVPfTFbW^r<@HQ2UPZf z!;xkS?#AhrfyV*4gCKDvDQmlNhzjsHAb|}Wj`Y5EH!iIKJPx?M1&1TOa@~zP^#VK& zxPS$RBfVhUjq7Rzi~E&^*qh7naqSOtpEB_eD0M`-HD9P@Q q#P@va$p48%8V1-+;_v%J`sTr<0@4I_f(uZ{NuZKgr62HJApZj$iX=(^ literal 0 HcmV?d00001 diff --git a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java index 0cd58365bf484..90e036bbad9ca 100644 --- a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java +++ b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java @@ -184,7 +184,7 @@ public PercolateQueryBuilder(String field, String documentType, String indexedDo } document = in.readOptionalBytesReference(); if (document != null) { - if (in.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (in.getVersion().onOrAfter(Version.V_5_3_0)) { documentXContentType = XContentType.readFrom(in); } else { documentXContentType = XContentFactory.xContentType(document); @@ -210,7 +210,7 @@ protected void doWriteTo(StreamOutput out) throws IOException { out.writeBoolean(false); } out.writeOptionalBytesReference(document); - if (document != null && out.getVersion().onOrAfter(Version.V_5_3_0_UNRELEASED)) { + if (document != null && out.getVersion().onOrAfter(Version.V_5_3_0)) { documentXContentType.writeTo(out); } } diff --git a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolateQueryBuilderTests.java b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolateQueryBuilderTests.java index 67829d3572859..d811a26cb8e35 100644 --- a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolateQueryBuilderTests.java +++ b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolateQueryBuilderTests.java @@ -250,7 +250,7 @@ public void testCreateMultiDocumentSearcher() throws Exception { public void testSerializationBwc() throws IOException { final byte[] data = Base64.getDecoder().decode("P4AAAAAFZmllbGQEdHlwZQAAAAAAAA57ImZvbyI6ImJhciJ9AAAAAA=="); final Version version = randomFrom(Version.V_5_0_0, Version.V_5_0_1, Version.V_5_0_2, - Version.V_5_0_3_UNRELEASED, Version.V_5_1_1_UNRELEASED, Version.V_5_1_2_UNRELEASED, Version.V_5_2_0_UNRELEASED); + Version.V_5_1_1, Version.V_5_1_2, Version.V_5_2_0); try (StreamInput in = StreamInput.wrap(data)) { in.setVersion(version); PercolateQueryBuilder queryBuilder = new PercolateQueryBuilder(in); diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java index 40377fe96462d..a65463c5ed5c6 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RoundTripTests.java @@ -159,7 +159,7 @@ private void assertRequestEquals(Version version, ReindexRequest request, Reinde assertEquals(request.getRemoteInfo().getUsername(), tripped.getRemoteInfo().getUsername()); assertEquals(request.getRemoteInfo().getPassword(), tripped.getRemoteInfo().getPassword()); assertEquals(request.getRemoteInfo().getHeaders(), tripped.getRemoteInfo().getHeaders()); - if (version.onOrAfter(Version.V_5_2_0_UNRELEASED)) { + if (version.onOrAfter(Version.V_5_2_0)) { assertEquals(request.getRemoteInfo().getSocketTimeout(), tripped.getRemoteInfo().getSocketTimeout()); assertEquals(request.getRemoteInfo().getConnectTimeout(), tripped.getRemoteInfo().getConnectTimeout()); } else { diff --git a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/ClientYamlTestSectionTests.java b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/ClientYamlTestSectionTests.java index dd6a297934412..ecee131c7a28e 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/ClientYamlTestSectionTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/ClientYamlTestSectionTests.java @@ -140,7 +140,7 @@ public void testParseTestSectionWithDoSetAndSkipSectionsNoSkip() throws Exceptio assertThat(testSection.getSkipSection(), notNullValue()); assertThat(testSection.getSkipSection().getLowerVersion(), equalTo(Version.V_5_0_0)); assertThat(testSection.getSkipSection().getUpperVersion(), - equalTo(Version.V_5_2_0_UNRELEASED)); + equalTo(Version.V_5_2_0)); assertThat(testSection.getSkipSection().getReason(), equalTo("Update doesn't return metadata fields, waiting for #3259")); assertThat(testSection.getExecutableSections().size(), equalTo(2)); DoSection doSection = (DoSection)testSection.getExecutableSections().get(0); diff --git a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/SetupSectionTests.java b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/SetupSectionTests.java index 7b3022dd937cc..cb9ab009b2594 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/SetupSectionTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/SetupSectionTests.java @@ -76,7 +76,7 @@ public void testParseSetupAndSkipSectionNoSkip() throws Exception { assertThat(setupSection.getSkipSection(), notNullValue()); assertThat(setupSection.getSkipSection().getLowerVersion(), equalTo(Version.V_5_0_0)); assertThat(setupSection.getSkipSection().getUpperVersion(), - equalTo(Version.V_5_3_0_UNRELEASED)); + equalTo(Version.V_5_3_0)); assertThat(setupSection.getSkipSection().getReason(), equalTo("Update doesn't return metadata fields, waiting for #3259")); assertThat(setupSection.getDoSections().size(), equalTo(2)); assertThat(setupSection.getDoSections().get(0).getApiCallSection().getApi(), equalTo("index1")); diff --git a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/SkipSectionTests.java b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/SkipSectionTests.java index 1ba31ed288d30..3ab9583335e7c 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/SkipSectionTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/SkipSectionTests.java @@ -62,7 +62,7 @@ public void testParseSkipSectionVersionNoFeature() throws Exception { SkipSection skipSection = SkipSection.parse(parser); assertThat(skipSection, notNullValue()); assertThat(skipSection.getLowerVersion(), equalTo(VersionUtils.getFirstVersion())); - assertThat(skipSection.getUpperVersion(), equalTo(Version.V_5_1_1_UNRELEASED)); + assertThat(skipSection.getUpperVersion(), equalTo(Version.V_5_1_1)); assertThat(skipSection.getFeatures().size(), equalTo(0)); assertThat(skipSection.getReason(), equalTo("Delete ignores the parent param")); } diff --git a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/TeardownSectionTests.java b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/TeardownSectionTests.java index de8e83692b83d..15ca1ec0096e3 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/TeardownSectionTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/section/TeardownSectionTests.java @@ -76,7 +76,7 @@ public void testParseWithSkip() throws Exception { assertThat(section, notNullValue()); assertThat(section.getSkipSection().isEmpty(), equalTo(false)); assertThat(section.getSkipSection().getLowerVersion(), equalTo(Version.V_5_0_0)); - assertThat(section.getSkipSection().getUpperVersion(), equalTo(Version.V_5_3_0_UNRELEASED)); + assertThat(section.getSkipSection().getUpperVersion(), equalTo(Version.V_5_3_0)); assertThat(section.getSkipSection().getReason(), equalTo("there is a reason")); assertThat(section.getDoSections().size(), equalTo(2)); assertThat(section.getDoSections().get(0).getApiCallSection().getApi(), equalTo("delete")); From 9fc9db26fdc19ba840f0895af41e390a020a00c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 17 May 2017 18:55:57 +0200 Subject: [PATCH 396/619] Add parsing for InternalScriptedMetric aggregation (#24738) --- .../common/xcontent/ObjectParser.java | 3 +- .../scripted/InternalScriptedMetric.java | 2 +- .../scripted/ParsedScriptedMetric.java | 92 ++++++++++++++++ .../aggregations/AggregationsTests.java | 2 + .../scripted/InternalScriptedMetricTests.java | 101 +++++++++++++++++- .../test/InternalAggregationTestCase.java | 3 + 6 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ParsedScriptedMetric.java diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java b/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java index 5f9f7b7efa668..ed1d85b5a7644 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java @@ -412,7 +412,8 @@ public enum ValueType { OBJECT_OR_STRING(START_OBJECT, VALUE_STRING), OBJECT_ARRAY_BOOLEAN_OR_STRING(START_OBJECT, START_ARRAY, VALUE_BOOLEAN, VALUE_STRING), OBJECT_ARRAY_OR_STRING(START_OBJECT, START_ARRAY, VALUE_STRING), - VALUE(VALUE_BOOLEAN, VALUE_NULL, VALUE_EMBEDDED_OBJECT, VALUE_NUMBER, VALUE_STRING); + VALUE(VALUE_BOOLEAN, VALUE_NULL, VALUE_EMBEDDED_OBJECT, VALUE_NUMBER, VALUE_STRING), + VALUE_OBJECT_ARRAY(VALUE_BOOLEAN, VALUE_NULL, VALUE_EMBEDDED_OBJECT, VALUE_NUMBER, VALUE_STRING, START_OBJECT, START_ARRAY); private final EnumSet tokens; diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetric.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetric.java index 73975b25778df..ea0fa4ce19678 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetric.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetric.java @@ -124,7 +124,7 @@ public Object getProperty(List path) { @Override public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { - return builder.field("value", aggregation()); + return builder.field(CommonFields.VALUE.getPreferredName(), aggregation()); } @Override diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ParsedScriptedMetric.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ParsedScriptedMetric.java new file mode 100644 index 0000000000000..949ff49cc7747 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ParsedScriptedMetric.java @@ -0,0 +1,92 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.aggregations.metrics.scripted; + +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser.ValueType; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParser.Token; +import org.elasticsearch.search.aggregations.ParsedAggregation; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +public class ParsedScriptedMetric extends ParsedAggregation implements ScriptedMetric { + private List aggregation; + + @Override + public String getType() { + return ScriptedMetricAggregationBuilder.NAME; + } + + @Override + public Object aggregation() { + assert aggregation.size() == 1; // see InternalScriptedMetric#aggregations() for why we can assume this + return aggregation.get(0); + } + + @Override + public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + return builder.field(CommonFields.VALUE.getPreferredName(), aggregation()); + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedScriptedMetric.class.getSimpleName(), true, + ParsedScriptedMetric::new); + + static { + declareAggregationFields(PARSER); + PARSER.declareField((agg, value) -> agg.aggregation = Collections.singletonList(value), + ParsedScriptedMetric::parseValue, CommonFields.VALUE, ValueType.VALUE_OBJECT_ARRAY); + } + + private static Object parseValue(XContentParser parser) throws IOException { + Token token = parser.currentToken(); + Object value = null; + if (token == XContentParser.Token.VALUE_NULL) { + value = null; + } else if (token.isValue()) { + if (token == XContentParser.Token.VALUE_STRING) { + //binary values will be parsed back and returned as base64 strings when reading from json and yaml + value = parser.text(); + } else if (token == XContentParser.Token.VALUE_NUMBER) { + value = parser.numberValue(); + } else if (token == XContentParser.Token.VALUE_BOOLEAN) { + value = parser.booleanValue(); + } else if (token == XContentParser.Token.VALUE_EMBEDDED_OBJECT) { + //binary values will be parsed back and returned as BytesArray when reading from cbor and smile + value = new BytesArray(parser.binaryValue()); + } + } else if (token == XContentParser.Token.START_OBJECT) { + value = parser.map(); + } else if (token == XContentParser.Token.START_ARRAY) { + value = parser.list(); + } + return value; + } + + public static ParsedScriptedMetric fromXContent(XContentParser parser, final String name) { + ParsedScriptedMetric aggregation = PARSER.apply(parser, null); + aggregation.setName(name); + return aggregation; + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java index 85deb604a6677..d47f9357f824e 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/AggregationsTests.java @@ -58,6 +58,7 @@ import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.InternalHDRPercentilesTests; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentilesRanksTests; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentilesTests; +import org.elasticsearch.search.aggregations.metrics.scripted.InternalScriptedMetricTests; import org.elasticsearch.search.aggregations.metrics.sum.InternalSumTests; import org.elasticsearch.search.aggregations.metrics.valuecount.InternalValueCountTests; import org.elasticsearch.search.aggregations.pipeline.InternalSimpleValueTests; @@ -130,6 +131,7 @@ private static List getAggsTests() { aggsTests.add(new InternalAdjacencyMatrixTests()); aggsTests.add(new SignificantLongTermsTests()); aggsTests.add(new SignificantStringTermsTests()); + aggsTests.add(new InternalScriptedMetricTests()); return Collections.unmodifiableList(aggsTests); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java index 75975d5a39ff8..f1fb42a4903ac 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.search.aggregations.metrics.scripted; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; @@ -30,20 +31,46 @@ import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptSettings; import org.elasticsearch.script.ScriptType; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.test.InternalAggregationTestCase; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.function.Supplier; public class InternalScriptedMetricTests extends InternalAggregationTestCase { private static final String REDUCE_SCRIPT_NAME = "reduceScript"; - // randomized only once so that any random test instance has the same value - private boolean hasReduceScript = randomBoolean(); + private boolean hasReduceScript; + private Supplier[] valueTypes; + private final Supplier[] leafValueSuppliers = new Supplier[] { () -> randomInt(), () -> randomLong(), () -> randomDouble(), + () -> randomFloat(), () -> randomBoolean(), () -> randomAlphaOfLength(5), () -> new GeoPoint(randomDouble(), randomDouble()), + () -> null }; + private final Supplier[] nestedValueSuppliers = new Supplier[] { () -> new HashMap(), + () -> new ArrayList<>() }; + + @Override + public void setUp() throws Exception { + super.setUp(); + hasReduceScript = randomBoolean(); + // we want the same value types (also for nested lists, maps) for all random aggregations + int levels = randomIntBetween(1, 3); + valueTypes = new Supplier[levels]; + for (int i = 0; i < levels; i++) { + if (i < levels - 1) { + valueTypes[i] = randomFrom(nestedValueSuppliers); + } else { + // the last one needs to be a leaf value, not map or list + valueTypes[i] = randomFrom(leafValueSuppliers); + } + } + } @Override protected InternalScriptedMetric createTestInstance(String name, List pipelineAggregators, @@ -56,7 +83,27 @@ protected InternalScriptedMetric createTestInstance(String name, List[] valueTypes, int level) { + Object value = valueTypes[level].get(); + if (value instanceof Map) { + int elements = randomIntBetween(1, 5); + Map map = (Map) value; + for (int i = 0; i < elements; i++) { + map.put(randomAlphaOfLength(5), randomValue(valueTypes, level + 1)); + } + } else if (value instanceof List) { + int elements = randomIntBetween(1,5); + List list = (List) value; + for (int i = 0; i < elements; i++) { + list.add(randomValue(valueTypes, level + 1)); + } + } + return value; } /** @@ -105,4 +152,52 @@ protected Reader instanceReader() { return InternalScriptedMetric::new; } + @Override + protected void assertFromXContent(InternalScriptedMetric aggregation, ParsedAggregation parsedAggregation) { + assertTrue(parsedAggregation instanceof ParsedScriptedMetric); + ParsedScriptedMetric parsed = (ParsedScriptedMetric) parsedAggregation; + + assertValues(aggregation.aggregation(), parsed.aggregation()); + } + + private static void assertValues(Object expected, Object actual) { + if (expected instanceof Long) { + // longs that fit into the integer range are parsed back as integer + if (actual instanceof Integer) { + assertEquals(((Long) expected).intValue(), actual); + } else { + assertEquals(expected, actual); + } + } else if (expected instanceof Float) { + // based on the xContent type, floats are sometimes parsed back as doubles + if (actual instanceof Double) { + assertEquals(expected, ((Double) actual).floatValue()); + } else { + assertEquals(expected, actual); + } + } else if (expected instanceof GeoPoint) { + assertTrue(actual instanceof Map); + GeoPoint point = (GeoPoint) expected; + Map pointMap = (Map) actual; + assertEquals(point.getLat(), pointMap.get("lat")); + assertEquals(point.getLon(), pointMap.get("lon")); + } else if (expected instanceof Map) { + Map expectedMap = (Map) expected; + Map actualMap = (Map) actual; + assertEquals(expectedMap.size(), actualMap.size()); + for (String key : expectedMap.keySet()) { + assertValues(expectedMap.get(key), actualMap.get(key)); + } + } else if (expected instanceof List) { + List expectedList = (List) expected; + List actualList = (List) actual; + assertEquals(expectedList.size(), actualList.size()); + Iterator actualIterator = actualList.iterator(); + for (Object element : expectedList) { + assertValues(element, actualIterator.next()); + } + } else { + assertEquals(expected, actual); + } + } } diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java index c256275b99482..da3b96b559dd7 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java @@ -96,6 +96,8 @@ import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentiles; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentiles; +import org.elasticsearch.search.aggregations.metrics.scripted.ParsedScriptedMetric; +import org.elasticsearch.search.aggregations.metrics.scripted.ScriptedMetricAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.stats.ParsedStats; import org.elasticsearch.search.aggregations.metrics.stats.StatsAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.stats.extended.ExtendedStatsAggregationBuilder; @@ -182,6 +184,7 @@ public abstract class InternalAggregationTestCase map.put(AdjacencyMatrixAggregationBuilder.NAME, (p, c) -> ParsedAdjacencyMatrix.fromXContent(p, (String) c)); map.put(SignificantLongTerms.NAME, (p, c) -> ParsedSignificantLongTerms.fromXContent(p, (String) c)); map.put(SignificantStringTerms.NAME, (p, c) -> ParsedSignificantStringTerms.fromXContent(p, (String) c)); + map.put(ScriptedMetricAggregationBuilder.NAME, (p, c) -> ParsedScriptedMetric.fromXContent(p, (String) c)); namedXContents = map.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) From 01bcbd868d2c522b43d1eedcb71f6990659f9a4c Mon Sep 17 00:00:00 2001 From: Lee Hinman Date: Wed, 17 May 2017 10:58:50 -0600 Subject: [PATCH 397/619] [TEST] Fix TransportReplicationActionTests.testRetryOnReplica for replica request (#24745) * [TEST] Fix TransportReplicationActionTests.testRetryOnReplica for replica request We were improperly testing that it was a `ConcreteShardRequest` instead of a `ConcreteReplicaRequest`. This adds that change and also ensures that the checkpoint is retrievable from the request. * Fix line-length --- .../replication/TransportReplicationActionTests.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java b/core/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java index b402feb6d81dd..0f8071cce367c 100644 --- a/core/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java +++ b/core/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java @@ -888,9 +888,10 @@ protected ReplicaResult shardOperationOnReplica(Request request, IndexShard repl final TestAction.ReplicaOperationTransportHandler replicaOperationTransportHandler = action.new ReplicaOperationTransportHandler(); final PlainActionFuture listener = new PlainActionFuture<>(); final Request request = new Request().setShardId(shardId); + final long checkpoint = randomNonNegativeLong(); request.primaryTerm(state.metaData().getIndexSafe(shardId.getIndex()).primaryTerm(shardId.id())); replicaOperationTransportHandler.messageReceived( - new TransportReplicationAction.ConcreteReplicaRequest<>(request, replica.allocationId().getId(), randomNonNegativeLong()), + new TransportReplicationAction.ConcreteReplicaRequest<>(request, replica.allocationId().getId(), checkpoint), createTransportChannel(listener), task); if (listener.isDone()) { listener.get(); // fail with the exception if there @@ -911,7 +912,9 @@ protected ReplicaResult shardOperationOnReplica(Request request, IndexShard repl assertThat(capturedRequests.size(), equalTo(1)); final CapturingTransport.CapturedRequest capturedRequest = capturedRequests.get(0); assertThat(capturedRequest.action, equalTo("testActionWithExceptions[r]")); - assertThat(capturedRequest.request, instanceOf(TransportReplicationAction.ConcreteShardRequest.class)); + assertThat(capturedRequest.request, instanceOf(TransportReplicationAction.ConcreteReplicaRequest.class)); + assertThat(((TransportReplicationAction.ConcreteReplicaRequest) capturedRequest.request).getGlobalCheckpoint(), + equalTo(checkpoint)); assertConcreteShardRequest(capturedRequest.request, request, replica.allocationId()); } From 7ce0b1b7c6db8ddc444ff2c31adb80b348e7ff2a Mon Sep 17 00:00:00 2001 From: Jay Modi Date: Wed, 17 May 2017 13:07:57 -0400 Subject: [PATCH 398/619] Allow SearchOperationListeners to validate a search context (#24650) This commit adds a new method to the SearchOperationListener that allows implementers to validate the SearchContext immediately after it is retrieved from the active contexts. The listener may throw a runtime exception if it deems the SearchContext is not valid and that the use of the context should be terminated. --- .../index/shard/SearchOperationListener.java | 22 +++++++++ .../elasticsearch/search/SearchService.java | 10 +++- .../shard/SearchOperationListenerTests.java | 48 +++++++++++++++++-- 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/shard/SearchOperationListener.java b/core/src/main/java/org/elasticsearch/index/shard/SearchOperationListener.java index 11723c3d50a01..583bcbc561d6a 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/SearchOperationListener.java +++ b/core/src/main/java/org/elasticsearch/index/shard/SearchOperationListener.java @@ -21,6 +21,7 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; +import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.search.internal.SearchContext; import java.util.List; @@ -104,6 +105,14 @@ public interface SearchOperationListener { */ default void onFreeScrollContext(SearchContext context) {}; + /** + * Executed prior to using a {@link SearchContext} that has been retrieved + * from the active contexts. If the context is deemed invalid a runtime + * exception can be thrown, which will prevent the context from being used. + * @param context the context retrieved from the active contexts + */ + default void validateSearchContext(SearchContext context) {} + /** * A Composite listener that multiplexes calls to each of the listeners methods. */ @@ -225,5 +234,18 @@ public void onFreeScrollContext(SearchContext context) { } } } + + @Override + public void validateSearchContext(SearchContext context) { + Exception exception = null; + for (SearchOperationListener listener : listeners) { + try { + listener.validateSearchContext(context); + } catch (Exception e) { + exception = ExceptionsHelper.useOrSuppress(exception, e); + } + } + ExceptionsHelper.reThrowIfNotNull(exception); + } } } diff --git a/core/src/main/java/org/elasticsearch/search/SearchService.java b/core/src/main/java/org/elasticsearch/search/SearchService.java index f9d0b3dc338e6..4174da3724368 100644 --- a/core/src/main/java/org/elasticsearch/search/SearchService.java +++ b/core/src/main/java/org/elasticsearch/search/SearchService.java @@ -437,7 +437,15 @@ private SearchContext findContext(long id) throws SearchContextMissingException if (context == null) { throw new SearchContextMissingException(id); } - return context; + + SearchOperationListener operationListener = context.indexShard().getSearchOperationListener(); + try { + operationListener.validateSearchContext(context); + return context; + } catch (Exception e) { + processFailure(context, e); + throw e; + } } final SearchContext createAndPutContext(ShardSearchRequest request) throws IOException { diff --git a/core/src/test/java/org/elasticsearch/index/shard/SearchOperationListenerTests.java b/core/src/test/java/org/elasticsearch/index/shard/SearchOperationListenerTests.java index 1721e5f5e5d83..fafdbe6755bc3 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/SearchOperationListenerTests.java +++ b/core/src/test/java/org/elasticsearch/index/shard/SearchOperationListenerTests.java @@ -18,9 +18,6 @@ */ package org.elasticsearch.index.shard; -import org.apache.lucene.index.Term; -import org.elasticsearch.client.Client; -import org.elasticsearch.index.engine.Engine; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.TestSearchContext; @@ -32,6 +29,9 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.sameInstance; + public class SearchOperationListenerTests extends ESTestCase { // this test also tests if calls are correct if one or more listeners throw exceptions @@ -46,6 +46,7 @@ public void testListenersAreExecuted() { AtomicInteger freeContext = new AtomicInteger(); AtomicInteger newScrollContext = new AtomicInteger(); AtomicInteger freeScrollContext = new AtomicInteger(); + AtomicInteger validateSearchContext = new AtomicInteger(); AtomicInteger timeInNanos = new AtomicInteger(randomIntBetween(0, 10)); SearchOperationListener listener = new SearchOperationListener() { @Override @@ -109,17 +110,26 @@ public void onFreeScrollContext(SearchContext context) { assertNotNull(context); freeScrollContext.incrementAndGet(); } + + @Override + public void validateSearchContext(SearchContext context) { + assertNotNull(context); + validateSearchContext.incrementAndGet(); + } }; SearchOperationListener throwingListener = (SearchOperationListener) Proxy.newProxyInstance( SearchOperationListener.class.getClassLoader(), new Class[]{SearchOperationListener.class}, (a,b,c) -> { throw new RuntimeException();}); + int throwingListeners = 0; final List indexingOperationListeners = new ArrayList<>(Arrays.asList(listener, listener)); if (randomBoolean()) { indexingOperationListeners.add(throwingListener); + throwingListeners++; if (randomBoolean()) { indexingOperationListeners.add(throwingListener); + throwingListeners++; } } Collections.shuffle(indexingOperationListeners, random()); @@ -137,6 +147,7 @@ public void onFreeScrollContext(SearchContext context) { assertEquals(0, newScrollContext.get()); assertEquals(0, freeContext.get()); assertEquals(0, freeScrollContext.get()); + assertEquals(0, validateSearchContext.get()); compositeListener.onFetchPhase(ctx, timeInNanos.get()); assertEquals(0, preFetch.get()); @@ -149,6 +160,7 @@ public void onFreeScrollContext(SearchContext context) { assertEquals(0, newScrollContext.get()); assertEquals(0, freeContext.get()); assertEquals(0, freeScrollContext.get()); + assertEquals(0, validateSearchContext.get()); compositeListener.onPreQueryPhase(ctx); assertEquals(0, preFetch.get()); @@ -161,6 +173,7 @@ public void onFreeScrollContext(SearchContext context) { assertEquals(0, newScrollContext.get()); assertEquals(0, freeContext.get()); assertEquals(0, freeScrollContext.get()); + assertEquals(0, validateSearchContext.get()); compositeListener.onPreFetchPhase(ctx); assertEquals(2, preFetch.get()); @@ -173,6 +186,7 @@ public void onFreeScrollContext(SearchContext context) { assertEquals(0, newScrollContext.get()); assertEquals(0, freeContext.get()); assertEquals(0, freeScrollContext.get()); + assertEquals(0, validateSearchContext.get()); compositeListener.onFailedFetchPhase(ctx); assertEquals(2, preFetch.get()); @@ -185,6 +199,7 @@ public void onFreeScrollContext(SearchContext context) { assertEquals(0, newScrollContext.get()); assertEquals(0, freeContext.get()); assertEquals(0, freeScrollContext.get()); + assertEquals(0, validateSearchContext.get()); compositeListener.onFailedQueryPhase(ctx); assertEquals(2, preFetch.get()); @@ -197,6 +212,7 @@ public void onFreeScrollContext(SearchContext context) { assertEquals(0, newScrollContext.get()); assertEquals(0, freeContext.get()); assertEquals(0, freeScrollContext.get()); + assertEquals(0, validateSearchContext.get()); compositeListener.onNewContext(ctx); assertEquals(2, preFetch.get()); @@ -209,6 +225,7 @@ public void onFreeScrollContext(SearchContext context) { assertEquals(0, newScrollContext.get()); assertEquals(0, freeContext.get()); assertEquals(0, freeScrollContext.get()); + assertEquals(0, validateSearchContext.get()); compositeListener.onNewScrollContext(ctx); assertEquals(2, preFetch.get()); @@ -221,6 +238,7 @@ public void onFreeScrollContext(SearchContext context) { assertEquals(2, newScrollContext.get()); assertEquals(0, freeContext.get()); assertEquals(0, freeScrollContext.get()); + assertEquals(0, validateSearchContext.get()); compositeListener.onFreeContext(ctx); assertEquals(2, preFetch.get()); @@ -233,6 +251,7 @@ public void onFreeScrollContext(SearchContext context) { assertEquals(2, newScrollContext.get()); assertEquals(2, freeContext.get()); assertEquals(0, freeScrollContext.get()); + assertEquals(0, validateSearchContext.get()); compositeListener.onFreeScrollContext(ctx); assertEquals(2, preFetch.get()); @@ -245,5 +264,28 @@ public void onFreeScrollContext(SearchContext context) { assertEquals(2, newScrollContext.get()); assertEquals(2, freeContext.get()); assertEquals(2, freeScrollContext.get()); + assertEquals(0, validateSearchContext.get()); + + if (throwingListeners == 0) { + compositeListener.validateSearchContext(ctx); + } else { + RuntimeException expected = expectThrows(RuntimeException.class, () -> compositeListener.validateSearchContext(ctx)); + assertNull(expected.getMessage()); + assertEquals(throwingListeners - 1, expected.getSuppressed().length); + if (throwingListeners > 1) { + assertThat(expected.getSuppressed()[0], not(sameInstance(expected))); + } + } + assertEquals(2, preFetch.get()); + assertEquals(2, preQuery.get()); + assertEquals(2, failedFetch.get()); + assertEquals(2, failedQuery.get()); + assertEquals(2, onQuery.get()); + assertEquals(2, onFetch.get()); + assertEquals(2, newContext.get()); + assertEquals(2, newScrollContext.get()); + assertEquals(2, freeContext.get()); + assertEquals(2, freeScrollContext.get()); + assertEquals(2, validateSearchContext.get()); } } From 1a7a926a0357818ea013ff591fddf941e1b8eb52 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Wed, 17 May 2017 13:19:30 -0400 Subject: [PATCH 399/619] Fix jvm-example assertions in packaging tests These assertions were on the yaml extension but we have migrated to yml everywhere so these assertions need to be updated too. --- qa/vagrant/src/test/resources/packaging/utils/plugins.bash | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qa/vagrant/src/test/resources/packaging/utils/plugins.bash b/qa/vagrant/src/test/resources/packaging/utils/plugins.bash index 14650b7130b12..f0f78e6ded33b 100644 --- a/qa/vagrant/src/test/resources/packaging/utils/plugins.bash +++ b/qa/vagrant/src/test/resources/packaging/utils/plugins.bash @@ -106,9 +106,9 @@ install_jvm_example() { config_owner=$(find "$ESCONFIG" -maxdepth 0 -printf "%g") # directories should user the user file-creation mask assert_file "$ESCONFIG/jvm-example" d $config_user $config_owner 750 - assert_file "$ESCONFIG/jvm-example/example.yaml" f $config_user $config_owner 660 + assert_file "$ESCONFIG/jvm-example/example.yml" f $config_user $config_owner 660 - run sudo -E -u vagrant LANG="en_US.UTF-8" cat "$ESCONFIG/jvm-example/example.yaml" + run sudo -E -u vagrant LANG="en_US.UTF-8" cat "$ESCONFIG/jvm-example/example.yml" [ $status = 1 ] [[ "$output" == *"Permission denied"* ]] || { echo "Expected permission denied but found $output:" @@ -126,7 +126,7 @@ remove_jvm_example() { assert_file_not_exist "$ESHOME/bin/jvm-example" assert_file_exist "$ESCONFIG/jvm-example" - assert_file_exist "$ESCONFIG/jvm-example/example.yaml" + assert_file_exist "$ESCONFIG/jvm-example/example.yml" } # Install a plugin with a special prefix. For the most part prefixes are just From da669f0554e3dbab0f70ad6b36c42daa3ae91b20 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 17 May 2017 20:28:33 +0200 Subject: [PATCH 400/619] Add fromXContent method to SearchResponse (#24720) SearchResponse#fromXContent allows to parse a search response, including search hits, aggregations, suggestions and profile results. Only the aggs that we can parse today are supported (which means all of them but a couple that are left to support). SearchResponseTests reuses the existing test infra to randomize aggregations, suggestions and profile response. Relates to #23331 --- .../action/search/SearchResponse.java | 107 ++++++++++- .../rest/action/RestActions.java | 17 +- .../org/elasticsearch/search/SearchHits.java | 8 +- .../elasticsearch/search/suggest/Suggest.java | 2 +- .../action/search/SearchResponseTests.java | 180 ++++++++++++++++++ .../search/ShardSearchFailureTests.java | 3 +- .../aggregations/AggregationsTests.java | 2 +- .../SearchProfileShardResultsTests.java | 2 +- .../search/suggest/SuggestTests.java | 17 +- .../search/suggest/SuggestionTests.java | 4 +- 10 files changed, 318 insertions(+), 24 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchResponse.java b/core/src/main/java/org/elasticsearch/action/search/SearchResponse.java index 3e83996c7c630..6e37a9333daf1 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchResponse.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchResponse.java @@ -21,30 +21,45 @@ import org.elasticsearch.action.ActionResponse; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.StatusToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.action.RestActions; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.aggregations.Aggregations; import org.elasticsearch.search.internal.InternalSearchResponse; import org.elasticsearch.search.profile.ProfileShardResult; +import org.elasticsearch.search.profile.SearchProfileShardResults; import org.elasticsearch.search.suggest.Suggest; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import static org.elasticsearch.action.search.ShardSearchFailure.readShardSearchFailure; +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.elasticsearch.common.xcontent.XContentParserUtils.throwUnknownField; +import static org.elasticsearch.common.xcontent.XContentParserUtils.throwUnknownToken; + /** * A response of a search request. */ public class SearchResponse extends ActionResponse implements StatusToXContentObject { + private static final ParseField SCROLL_ID = new ParseField("_scroll_id"); + private static final ParseField TOOK = new ParseField("took"); + private static final ParseField TIMED_OUT = new ParseField("timed_out"); + private static final ParseField TERMINATED_EARLY = new ParseField("terminated_early"); + private static final ParseField NUM_REDUCE_PHASES = new ParseField("num_reduce_phases"); + private SearchResponseSections internalResponse; private String scrollId; @@ -175,7 +190,8 @@ public void scrollId(String scrollId) { * * @return The profile results or an empty map */ - @Nullable public Map getProfileResults() { + @Nullable + public Map getProfileResults() { return internalResponse.profile(); } @@ -189,15 +205,15 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws public XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException { if (scrollId != null) { - builder.field("_scroll_id", scrollId); + builder.field(SCROLL_ID.getPreferredName(), scrollId); } - builder.field("took", tookInMillis); - builder.field("timed_out", isTimedOut()); + builder.field(TOOK.getPreferredName(), tookInMillis); + builder.field(TIMED_OUT.getPreferredName(), isTimedOut()); if (isTerminatedEarly() != null) { - builder.field("terminated_early", isTerminatedEarly()); + builder.field(TERMINATED_EARLY.getPreferredName(), isTerminatedEarly()); } if (getNumReducePhases() != 1) { - builder.field("num_reduce_phases", getNumReducePhases()); + builder.field(NUM_REDUCE_PHASES.getPreferredName(), getNumReducePhases()); } RestActions.buildBroadcastShardsHeader(builder, params, getTotalShards(), getSuccessfulShards(), getFailedShards(), getShardFailures()); @@ -205,6 +221,85 @@ public XContentBuilder innerToXContent(XContentBuilder builder, Params params) t return builder; } + public static SearchResponse fromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.currentToken(), parser::getTokenLocation); + XContentParser.Token token; + String currentFieldName = null; + SearchHits hits = null; + Aggregations aggs = null; + Suggest suggest = null; + SearchProfileShardResults profile = null; + boolean timedOut = false; + Boolean terminatedEarly = null; + int numReducePhases = 1; + long tookInMillis = -1; + int successfulShards = -1; + int totalShards = -1; + String scrollId = null; + List failures = new ArrayList<>(); + while((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (SCROLL_ID.match(currentFieldName)) { + scrollId = parser.text(); + } else if (TOOK.match(currentFieldName)) { + tookInMillis = parser.longValue(); + } else if (TIMED_OUT.match(currentFieldName)) { + timedOut = parser.booleanValue(); + } else if (TERMINATED_EARLY.match(currentFieldName)) { + terminatedEarly = parser.booleanValue(); + } else if (NUM_REDUCE_PHASES.match(currentFieldName)) { + numReducePhases = parser.intValue(); + } else { + throwUnknownField(currentFieldName, parser.getTokenLocation()); + } + } else if (token == XContentParser.Token.START_OBJECT) { + if (SearchHits.Fields.HITS.equals(currentFieldName)) { + hits = SearchHits.fromXContent(parser); + } else if (Aggregations.AGGREGATIONS_FIELD.equals(currentFieldName)) { + aggs = Aggregations.fromXContent(parser); + } else if (Suggest.NAME.equals(currentFieldName)) { + suggest = Suggest.fromXContent(parser); + } else if (SearchProfileShardResults.PROFILE_FIELD.equals(currentFieldName)) { + profile = SearchProfileShardResults.fromXContent(parser); + } else if (RestActions._SHARDS_FIELD.match(currentFieldName)) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (RestActions.FAILED_FIELD.match(currentFieldName)) { + parser.intValue(); // we don't need it but need to consume it + } else if (RestActions.SUCCESSFUL_FIELD.match(currentFieldName)) { + successfulShards = parser.intValue(); + } else if (RestActions.TOTAL_FIELD.match(currentFieldName)) { + totalShards = parser.intValue(); + } else { + throwUnknownField(currentFieldName, parser.getTokenLocation()); + } + } else if (token == XContentParser.Token.START_ARRAY) { + if (RestActions.FAILURES_FIELD.match(currentFieldName)) { + while((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + failures.add(ShardSearchFailure.fromXContent(parser)); + } + } else { + throwUnknownField(currentFieldName, parser.getTokenLocation()); + } + } else { + throwUnknownToken(token, parser.getTokenLocation()); + } + } + } else { + throwUnknownField(currentFieldName, parser.getTokenLocation()); + } + } + } + SearchResponseSections searchResponseSections = new SearchResponseSections(hits, aggs, suggest, timedOut, terminatedEarly, + profile, numReducePhases); + return new SearchResponse(searchResponseSections, scrollId, totalShards, successfulShards, tookInMillis, + failures.toArray(new ShardSearchFailure[failures.size()])); + } + @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); diff --git a/core/src/main/java/org/elasticsearch/rest/action/RestActions.java b/core/src/main/java/org/elasticsearch/rest/action/RestActions.java index 74836b1dc2d5b..15199c5a926e3 100644 --- a/core/src/main/java/org/elasticsearch/rest/action/RestActions.java +++ b/core/src/main/java/org/elasticsearch/rest/action/RestActions.java @@ -25,6 +25,7 @@ import org.elasticsearch.action.support.broadcast.BroadcastResponse; import org.elasticsearch.action.support.nodes.BaseNodeResponse; import org.elasticsearch.action.support.nodes.BaseNodesResponse; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.lucene.uid.Versions; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent.Params; @@ -46,6 +47,12 @@ public class RestActions { + public static final ParseField _SHARDS_FIELD = new ParseField("_shards"); + public static final ParseField TOTAL_FIELD = new ParseField("total"); + public static final ParseField SUCCESSFUL_FIELD = new ParseField("successful"); + public static final ParseField FAILED_FIELD = new ParseField("failed"); + public static final ParseField FAILURES_FIELD = new ParseField("failures"); + public static long parseVersion(RestRequest request) { if (request.hasParam("version")) { return request.paramAsLong("version", Versions.MATCH_ANY); @@ -71,12 +78,12 @@ public static void buildBroadcastShardsHeader(XContentBuilder builder, Params pa public static void buildBroadcastShardsHeader(XContentBuilder builder, Params params, int total, int successful, int failed, ShardOperationFailedException[] shardFailures) throws IOException { - builder.startObject("_shards"); - builder.field("total", total); - builder.field("successful", successful); - builder.field("failed", failed); + builder.startObject(_SHARDS_FIELD.getPreferredName()); + builder.field(TOTAL_FIELD.getPreferredName(), total); + builder.field(SUCCESSFUL_FIELD.getPreferredName(), successful); + builder.field(FAILED_FIELD.getPreferredName(), failed); if (shardFailures != null && shardFailures.length > 0) { - builder.startArray("failures"); + builder.startArray(FAILURES_FIELD.getPreferredName()); final boolean group = params.paramAsBoolean("group_shard_failures", true); // we group by default for (ShardOperationFailedException shardFailure : group ? ExceptionsHelper.groupBy(shardFailures) : shardFailures) { builder.startObject(); diff --git a/core/src/main/java/org/elasticsearch/search/SearchHits.java b/core/src/main/java/org/elasticsearch/search/SearchHits.java index ada09d2e38bae..0b49ba8ec123d 100644 --- a/core/src/main/java/org/elasticsearch/search/SearchHits.java +++ b/core/src/main/java/org/elasticsearch/search/SearchHits.java @@ -105,10 +105,10 @@ public SearchHit[] internalHits() { return this.hits; } - static final class Fields { - static final String HITS = "hits"; - static final String TOTAL = "total"; - static final String MAX_SCORE = "max_score"; + public static final class Fields { + public static final String HITS = "hits"; + public static final String TOTAL = "total"; + public static final String MAX_SCORE = "max_score"; } @Override diff --git a/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java b/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java index 4af794ab42c60..c2f20f1a79455 100644 --- a/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java +++ b/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java @@ -60,7 +60,7 @@ */ public class Suggest implements Iterable>>, Streamable, ToXContent { - static final String NAME = "suggest"; + public static final String NAME = "suggest"; public static final Comparator

C(}lnA^1Veic=FFca^N)iE90~H=o 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 z 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